View Javadoc

1   /*
2    * $Id: MuleServer.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.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;
12  
13  import org.mule.api.DefaultMuleException;
14  import org.mule.api.MuleContext;
15  import org.mule.api.MuleException;
16  import org.mule.api.config.ConfigurationBuilder;
17  import org.mule.api.config.ConfigurationException;
18  import org.mule.config.ExceptionHelper;
19  import org.mule.config.StartupContext;
20  import org.mule.config.builders.SimpleConfigurationBuilder;
21  import org.mule.config.i18n.CoreMessages;
22  import org.mule.config.i18n.Message;
23  import org.mule.context.DefaultMuleContextBuilder;
24  import org.mule.context.DefaultMuleContextFactory;
25  import org.mule.util.ClassUtils;
26  import org.mule.util.IOUtils;
27  import org.mule.util.MuleUrlStreamHandlerFactory;
28  import org.mule.util.PropertiesUtils;
29  import org.mule.util.StringMessageUtils;
30  import org.mule.util.SystemUtils;
31  
32  import java.net.URL;
33  import java.util.ArrayList;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Properties;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  
41  /**
42   * <code>MuleServer</code> is a simple application that represents a local Mule
43   * Server daemon. It is initialised with a mule-config.xml file.
44   */
45  public class MuleServer implements Runnable
46  {
47      public static final String CLI_OPTIONS[][] = {
48          {"builder", "true", "Configuration Builder Type"},
49          {"config", "true", "Configuration File"},
50          {"idle", "false", "Whether to run in idle (unconfigured) mode"},
51          {"main", "true", "Main Class"},
52          {"mode", "true", "Run Mode"},
53          {"props", "true", "Startup Properties"},
54          {"production", "false", "Production Mode"},
55          {"debug", "false", "Configure Mule for JPDA remote debugging."}
56      };
57  
58      /**
59       * Don't use a class object so the core doesn't depend on mule-module-spring-config.
60       */
61      protected static final String CLASSNAME_DEFAULT_CONFIG_BUILDER = "org.mule.config.builders.AutoConfigurationBuilder";
62  
63      /**
64       * This builder sets up the configuration for an idle Mule node - a node that
65       * doesn't do anything initially but is fed configuration during runtime
66       */
67      protected static final String CLASSNAME_DEFAULT_IDLE_CONFIG_BUILDER = "org.mule.config.builders.MuleIdleConfigurationBuilder";
68  
69      /**
70       * Required to support the '-config spring' shortcut. Don't use a class object so
71       * the core doesn't depend on mule-module-spring.
72       * for Mule 2.x
73       */
74      protected static final String CLASSNAME_SPRING_CONFIG_BUILDER = "org.mule.config.spring.SpringXmlConfigurationBuilder";
75  
76      /**
77       * If the annotations module is on the classpath, also enable annotations config builder
78       */
79      public static final String CLASSNAME_ANNOTATIONS_CONFIG_BUILDER = "org.mule.config.AnnotationsConfigurationBuilder";
80  
81      /**
82       * logger used by this class
83       */
84      private static final Log logger = LogFactory.getLog(MuleServer.class);
85  
86      public static final String DEFAULT_CONFIGURATION = "mule-config.xml";
87  
88      /**
89       * one or more configuration urls or filenames separated by commas
90       */
91      private String configurationResources = null;
92  
93      /**
94       * A FQN of the #configBuilder class, required in case MuleServer is
95       * reinitialised.
96       */
97      private static String configBuilderClassName = null;
98  
99      /**
100      * A properties file to be read at startup. This can be useful for setting
101      * properties which depend on the run-time environment (dev, test, production).
102      */
103     private static String startupPropertiesFile = null;
104 
105     /**
106      * The Runtime shutdown thread used to dispose this server
107      */
108     private static MuleShutdownHook muleShutdownHook;
109 
110     /**
111      * The MuleContext should contain anything which does not belong in the Registry.
112      * There is one MuleContext per Mule instance. Assuming it has been created, a
113      * handle to the local MuleContext can be obtained from anywhere by calling
114      * MuleServer.getMuleContext()
115      */
116     protected static MuleContext muleContext = null;
117 
118     /**
119      * Application entry point.
120      *
121      * @param args command-line args
122      * @throws Exception if there is an exception creating the MuleServer
123      */
124     public static void main(String[] args) throws Exception
125     {
126         MuleServer server = new MuleServer(args);
127         server.start(false, true);
128 
129     }
130 
131     public MuleServer()
132     {
133         init(new String[]{});
134     }
135 
136     public MuleServer(String configResources)
137     {
138         // setConfigurationResources(configResources);
139         init(new String[]{"-config", configResources});
140     }
141 
142     /**
143      * Configure the server with command-line arguments.
144      * @param args Command line args passed in from the {@link #main(String[])} method
145      * @throws IllegalArgumentException if an argument is passed in that is not recognised by the Mule Server
146      */
147     public MuleServer(String[] args) throws IllegalArgumentException
148     {
149         init(args);
150     }
151 
152     protected void init(String[] args) throws IllegalArgumentException
153     {
154         Map<String, Object> commandlineOptions;
155 
156         try
157         {
158             commandlineOptions = SystemUtils.getCommandLineOptions(args, CLI_OPTIONS);
159         }
160         catch (DefaultMuleException me)
161         {
162             throw new IllegalArgumentException(me.toString());
163         }
164 
165         // set our own UrlStreamHandlerFactory to become more independent of system
166         // properties
167         MuleUrlStreamHandlerFactory.installUrlStreamHandlerFactory();
168 
169         String config = (String) commandlineOptions.get("config");
170         // Try default if no config file was given.
171         if (config == null && !commandlineOptions.containsKey("idle"))
172         {
173             logger.warn("A configuration file was not set, using default: " + DEFAULT_CONFIGURATION);
174             // try to load the config as a file as well
175             URL configUrl = IOUtils.getResourceAsUrl(DEFAULT_CONFIGURATION, MuleServer.class, true, false);
176             if (configUrl != null)
177             {
178                 config = configUrl.toExternalForm();
179             }
180             else
181             {
182                 System.out.println(CoreMessages.configNotFoundUsage());
183                 System.exit(-1);
184             }
185         }
186 
187         if (config != null)
188         {
189             setConfigurationResources(config);
190         }
191 
192         // Configuration builder
193         String cfgBuilderClassName = (String) commandlineOptions.get("builder");
194 
195         if (commandlineOptions.containsKey("idle"))
196         {
197             setConfigurationResources("IDLE");
198             cfgBuilderClassName = CLASSNAME_DEFAULT_IDLE_CONFIG_BUILDER;
199         }
200 
201         // Configuration builder
202         if (cfgBuilderClassName != null)
203         {
204             try
205             {
206                 // Provide a shortcut for Spring: "-builder spring"
207                 if (cfgBuilderClassName.equalsIgnoreCase("spring"))
208                 {
209                     cfgBuilderClassName = CLASSNAME_SPRING_CONFIG_BUILDER;
210                 }
211                 setConfigBuilderClassName(cfgBuilderClassName);
212             }
213             catch (Exception e)
214             {
215                 logger.fatal(e);
216                 final Message message = CoreMessages.failedToLoad("Builder: " + cfgBuilderClassName);
217                 System.err.println(StringMessageUtils.getBoilerPlate("FATAL: " + message.toString()));
218                 System.exit(1);
219             }
220         }
221 
222         // Startup properties
223         String propertiesFile = (String) commandlineOptions.get("props");
224         if (propertiesFile != null)
225         {
226             setStartupPropertiesFile(propertiesFile);
227         }
228 
229         StartupContext.get().setStartupOptions(commandlineOptions);
230     }
231 
232     /**
233      * Start the mule server
234      *
235      * @param ownThread determines if the server will run in its own daemon thread or
236      *                  the current calling thread
237      * @param registerShutdownHook whether to register the default Mule Server shutdown hock.  this will shut down mule cleanly if
238      * the JVM is shutdown.  The only reason not to register this hook is to override it with a custom version
239      */
240     public void start(boolean ownThread, boolean registerShutdownHook)
241     {
242         if (registerShutdownHook)
243         {
244             registerShutdownHook();
245         }
246         if (ownThread)
247         {
248             Thread serverThread = new Thread(this, "MuleServer");
249             serverThread.setDaemon(true);
250             serverThread.start();
251         }
252         else
253         {
254             run();
255         }
256     }
257 
258     /**
259      * Overloaded the [main] thread run method. This calls initialise and shuts down
260      * if an exception occurs
261      */
262     public void run()
263     {
264         try
265         {
266             logger.info("Mule Server initializing...");
267             initialize();
268             logger.info("Mule Server starting...");
269             muleContext.start();
270         }
271         catch (Throwable e)
272         {
273             shutdown(e);
274         }
275     }
276 
277     /**
278      * Sets the configuration builder to use for this server. Note that if a builder
279      * is not set and the server's start method is called the default is an instance
280      * of <code>SpringXmlConfigurationBuilder</code>.
281      *
282      * @param builderClassName the configuration builder FQN to use
283      * @throws ClassNotFoundException if the class with the given name can not be
284      *                                loaded
285      */
286     public static void setConfigBuilderClassName(String builderClassName) throws ClassNotFoundException
287     {
288         if (builderClassName != null)
289         {
290             Class cls = ClassUtils.loadClass(builderClassName, MuleServer.class);
291             if (ConfigurationBuilder.class.isAssignableFrom(cls))
292             {
293                 MuleServer.configBuilderClassName = builderClassName;
294             }
295             else
296             {
297                 throw new IllegalArgumentException("Not a usable ConfigurationBuilder class: "
298                         + builderClassName);
299             }
300         }
301         else
302         {
303             MuleServer.configBuilderClassName = null;
304         }
305     }
306 
307     /**
308      * Returns the class name of the configuration builder used to create this
309      * MuleServer.
310      *
311      * @return FQN of the current config builder
312      */
313     public static String getConfigBuilderClassName()
314     {
315         if (configBuilderClassName != null)
316         {
317             return configBuilderClassName;
318         }
319         else
320         {
321             return CLASSNAME_DEFAULT_CONFIG_BUILDER;
322         }
323     }
324 
325     /**
326      * Initializes this daemon. Derived classes could add some extra behaviour if
327      * they wish.
328      *
329      * @throws Exception if failed to initialize
330      */
331     public void initialize() throws Exception
332     {
333         if (configurationResources == null)
334         {
335             logger.warn("A configuration file was not set, using default: " + DEFAULT_CONFIGURATION);
336             configurationResources = DEFAULT_CONFIGURATION;
337         }
338 
339         ConfigurationBuilder cfgBuilder;
340 
341         try
342         {
343             // create a new ConfigurationBuilder that is disposed afterwards
344             cfgBuilder = (ConfigurationBuilder) ClassUtils.instanciateClass(getConfigBuilderClassName(),
345                     new Object[]{configurationResources}, MuleServer.class);
346         }
347         catch (Exception e)
348         {
349             throw new ConfigurationException(CoreMessages.failedToLoad(getConfigBuilderClassName()), e);
350         }
351 
352         if (!cfgBuilder.isConfigured())
353         {
354             List<ConfigurationBuilder> builders = new ArrayList<ConfigurationBuilder>(2);
355 
356             // If the annotations module is on the classpath, add the annotations config builder to the list
357             // This will enable annotations config for this instance
358             //We need to add this builder before spring so that we can use Mule annotations in Spring
359             if (ClassUtils.isClassOnPath(CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, getClass()))
360             {
361                 Object configBuilder = ClassUtils.instanciateClass(
362                     CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, ClassUtils.NO_ARGS, getClass());
363                 builders.add((ConfigurationBuilder) configBuilder);
364             }
365 
366 
367             Properties startupProperties = null;
368             if (getStartupPropertiesFile() != null)
369             {
370                 startupProperties = PropertiesUtils.loadProperties(getStartupPropertiesFile(), getClass());
371             }
372             builders.add(new SimpleConfigurationBuilder(startupProperties));
373 
374             builders.add(cfgBuilder);
375 
376             DefaultMuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
377             muleContext = muleContextFactory.createMuleContext(builders, new DefaultMuleContextBuilder());
378         }
379     }
380 
381     /**
382      * Will shut down the server displaying the cause and time of the shutdown
383      *
384      * @param e the exception that caused the shutdown
385      */
386     public void shutdown(Throwable e)
387     {
388         doShutdown();
389         unregisterShutdownHook();
390 
391         Message msg = CoreMessages.fatalErrorWhileRunning();
392         MuleException muleException = ExceptionHelper.getRootMuleException(e);
393         int exitCode = 1;
394         if (muleException != null)
395         {
396             logger.fatal(muleException.getDetailedMessage());
397             exitCode = muleException.getExceptionCode();
398         }
399         else
400         {
401             logger.fatal(msg.toString() + " " + e.getMessage(), e);
402         }
403         List<String> msgs = new ArrayList<String>();
404         msgs.add(msg.getMessage());
405         Throwable root = ExceptionHelper.getRootException(e);
406         msgs.add(root.getMessage() + " (" + root.getClass().getName() + ")");
407         msgs.add(" ");
408         msgs.add(CoreMessages.fatalErrorInShutdown().getMessage());
409         String shutdownMessage = StringMessageUtils.getBoilerPlate(msgs, '*', 80);
410         logger.fatal(shutdownMessage);
411 
412         System.exit(exitCode);
413 
414 
415     }
416 
417     /**
418      * shutdown the server. This just displays the time the server shut down
419      */
420     public void shutdown()
421     {
422         logger.info("Mule server shutting down due to normal shutdown request");
423 
424         unregisterShutdownHook();
425         doShutdown();
426         System.exit(0);
427     }
428 
429     protected void doShutdown()
430     {
431         if (muleContext != null)
432         {
433             muleContext.dispose();
434             muleContext = null;
435         }
436     }
437 
438     public Log getLogger()
439     {
440         return logger;
441     }
442 
443     public void registerShutdownHook()
444     {
445         if (muleShutdownHook == null)
446         {
447             muleShutdownHook = new MuleShutdownHook();
448         }
449         else
450         {
451             Runtime.getRuntime().removeShutdownHook(muleShutdownHook);
452         }
453         Runtime.getRuntime().addShutdownHook(muleShutdownHook);
454     }
455 
456     public void unregisterShutdownHook()
457     {
458         if (muleShutdownHook != null)
459         {
460             Runtime.getRuntime().removeShutdownHook(muleShutdownHook);
461         }
462     }
463 
464     // /////////////////////////////////////////////////////////////////
465     // Getters and setters
466     // /////////////////////////////////////////////////////////////////
467 
468     /**
469      * Getter for property messengerURL.
470      *
471      * @return Value of property messengerURL.
472      */
473     public String getConfigurationResources()
474     {
475         return configurationResources;
476     }
477 
478     /**
479      * Setter for property configurationResources.
480      *
481      * @param configurationResources New value of property configurationResources.
482      */
483     public void setConfigurationResources(String configurationResources)
484     {
485         this.configurationResources = configurationResources;
486     }
487 
488     public static String getStartupPropertiesFile()
489     {
490         return startupPropertiesFile;
491     }
492 
493     public static void setStartupPropertiesFile(String startupPropertiesFile)
494     {
495         MuleServer.startupPropertiesFile = startupPropertiesFile;
496     }
497 
498     public MuleContext getMuleContext()
499     {
500         return muleContext;
501     }
502 
503     /**
504      * This class is installed only for MuleServer running as commandline app. A
505      * clean Mule shutdown can be achieved by disposing the
506      * {@link org.mule.DefaultMuleContext}.
507      */
508     private class MuleShutdownHook extends Thread
509     {
510         public MuleShutdownHook()
511         {
512             super();
513         }
514         
515         @Override
516         public void run()
517         {
518             doShutdown();
519             System.exit(0);
520         }
521     }
522 }