View Javadoc

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