View Javadoc

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