View Javadoc

1   /*
2    * $Id: MuleBootstrap.java 7976 2007-08-21 14:26:13Z dirk.olmes $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.modules.boot;
12  
13  import org.mule.MuleServer;
14  
15  import java.io.File;
16  import java.lang.reflect.InvocationTargetException;
17  import java.lang.reflect.Method;
18  import java.net.URL;
19  import java.net.URLClassLoader;
20  import java.util.Iterator;
21  import java.util.List;
22  
23  /**
24   * Determine which is the main class to run and delegate control to the Java Service
25   * Wrapper. <p/> MuleBootstrap class is responsible for constructing Mule's classpath
26   * from the Mule home folder.
27   */
28  public class MuleBootstrap
29  {
30  
31      /**
32       * Do not instantiate MuleBootstrap.
33       */
34      private MuleBootstrap()
35      {
36          super();
37      }
38  
39      /**
40       * Entry point.
41       * 
42       * @param args command-line arguments
43       * @throws Exception in case of any fatal problem
44       */
45      public static void main(String args[]) throws Exception
46      {
47          // Make sure MULE_HOME is set.
48          File muleHome = null;
49  
50          String muleHomeVar = System.getProperty("mule.home");
51          // Note: we can't use StringUtils.isBlank() here because we don't have that
52          // library yet.
53          if (muleHomeVar != null && !muleHomeVar.trim().equals("") && !muleHomeVar.equals("%MULE_HOME%"))
54          {
55              muleHome = new File(muleHomeVar).getCanonicalFile();
56          }
57          if (muleHome == null || !muleHome.exists() || !muleHome.isDirectory())
58          {
59              throw new IllegalArgumentException(
60                  "Either MULE_HOME is not set or does not contain a valid directory.");
61          }
62  
63          File muleBase;
64  
65          String muleBaseVar = System.getProperty("mule.base");
66          if (muleBaseVar != null && !muleBaseVar.trim().equals("") && !muleBaseVar.equals("%MULE_BASE%"))
67          {
68              muleBase = new File(muleBaseVar).getCanonicalFile();
69          }
70          else
71          {                                                                          
72              muleBase = muleHome;
73          }
74  
75          // Build up a list of libraries from $MULE_HOME/lib/* and add them to the
76          // classpath.
77          DefaultMuleClassPathConfig classPath = new DefaultMuleClassPathConfig(muleHome, muleBase);
78          addLibrariesToClasspath(classPath.getURLs());
79  
80          // If the license ack file isn't on the classpath, we need to
81          // display the EULA and make sure the user accepts it before continuing
82          if (ReflectionHelper.getResource("META-INF/mule/license.props", MuleBootstrap.class) == null)
83          {
84              LicenseHandler licenseHandler = new LicenseHandler(muleHome, muleBase);
85              // If the user didn't accept the license, then we have to exit
86              // Exiting this way insures that the wrapper won't try again
87              // (by default it'll try to start 3 times)
88              if (!licenseHandler.getAcceptance())
89              {
90                  ReflectionHelper.wrapperStop(-1);
91              }
92          }
93  
94          // One-time download to get libraries not included in the Mule distribution
95          // due to silly licensing restrictions.
96          // 
97          // Now we will download these libraries to MULE_BASE/lib/user. In
98          // a standard installation, MULE_BASE will be MULE_HOME.
99          if (!ReflectionHelper.isClassOnPath("javax.activation.DataSource", MuleBootstrap.class))
100         {
101             LibraryDownloader downloader = new LibraryDownloader(muleBase);
102             addLibrariesToClasspath(downloader.downloadLibraries());
103         }
104 
105         // the core jar has been added dynamically, this construct will run with
106         // a new Mule classpath now
107         String mainClassName = null;
108 
109         try
110         {
111             final String[][] cliOptions = ReflectionHelper.getCliOptions();
112             mainClassName = ReflectionHelper.getCommandLineOption("main", args, cliOptions);
113         }
114         catch (Exception e)
115         {
116             System.out.println(e.toString());
117             ReflectionHelper.wrapperStop(-1);
118         }
119 
120         if (mainClassName == null)
121         {
122             mainClassName = MuleServer.class.getName();
123         }
124 
125         // Add the main class name as the first argument to the Wrapper.
126         String[] appArgs = new String[args.length + 1];
127         appArgs[0] = mainClassName;
128         System.arraycopy(args, 0, appArgs, 1, args.length);
129 
130         // Call the wrapper
131         ReflectionHelper.wrapperMain(appArgs);
132     }
133 
134     private static void addLibrariesToClasspath(List urls)
135         throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
136     {
137 
138         ClassLoader sys = ClassLoader.getSystemClassLoader();
139         if (!(sys instanceof URLClassLoader))
140         {
141             throw new IllegalArgumentException(
142                 "PANIC: Mule has been started with an unsupported classloader: " + sys.getClass().getName()
143                                 + ". " + "Please report this error to user<at>mule<dot>codehaus<dot>org");
144         }
145 
146         // system classloader is in this case the one that launched the application,
147         // which is usually something like a JDK-vendor proprietary AppClassLoader
148         URLClassLoader sysCl = (URLClassLoader)sys;
149 
150         /*
151          * IMPORTANT NOTE: The more 'natural' way would be to create a custom
152          * URLClassLoader and configure it, but then there's a chicken-and-egg
153          * problem, as all classes MuleBootstrap depends on would have been loaded by
154          * a parent classloader, and not ours. There's no straightforward way to
155          * change this, and is documented in a Sun's classloader guide. The solution
156          * would've involved overriding the ClassLoader.findClass() method and
157          * modifying the semantics to be child-first, but that way we are calling for
158          * trouble. Hacking the primordial classloader is a bit brutal, but works
159          * perfectly in case of running from the command-line as a standalone app.
160          * All Mule embedding options then delegate the classpath config to the
161          * embedder (a developer embedding Mule in the app), thus classloaders are
162          * not modified in those scenarios.
163          */
164 
165         // get a Method ref from the normal class, but invoke on a proprietary parent
166         // object,
167         // as this method is usually protected in those classloaders
168         Class refClass = URLClassLoader.class;
169         Method methodAddUrl = refClass.getDeclaredMethod("addURL", new Class[]{URL.class});
170         methodAddUrl.setAccessible(true);
171         for (Iterator it = urls.iterator(); it.hasNext();)
172         {
173             URL url = (URL)it.next();
174             // System.out.println("Adding: " + url.toExternalForm());
175             methodAddUrl.invoke(sysCl, new Object[]{url});
176         }
177     }
178 }