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