Coverage Report - org.mule.module.launcher.DefaultMuleApplication
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultMuleApplication
0%
0/117
0%
0/44
0
DefaultMuleApplication$1
0%
0/8
0%
0/3
0
DefaultMuleApplication$ConfigFileWatcher
0%
0/9
0%
0/2
0
 
 1  
 /*
 2  
  * $Id: DefaultMuleApplication.java 19329 2010-09-03 13:33:00Z 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.module.launcher;
 12  
 
 13  
 import org.mule.MuleServer;
 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.MuleProperties;
 18  
 import org.mule.api.context.notification.MuleContextNotificationListener;
 19  
 import org.mule.config.builders.AutoConfigurationBuilder;
 20  
 import org.mule.config.builders.SimpleConfigurationBuilder;
 21  
 import org.mule.config.i18n.CoreMessages;
 22  
 import org.mule.config.i18n.MessageFactory;
 23  
 import org.mule.context.DefaultMuleContextFactory;
 24  
 import org.mule.context.notification.MuleContextNotification;
 25  
 import org.mule.context.notification.NotificationException;
 26  
 import org.mule.module.launcher.descriptor.ApplicationDescriptor;
 27  
 import org.mule.module.reboot.MuleContainerBootstrapUtils;
 28  
 import org.mule.util.ClassUtils;
 29  
 import org.mule.util.FileUtils;
 30  
 import org.mule.util.StringUtils;
 31  
 
 32  
 import java.io.File;
 33  
 import java.io.IOException;
 34  
 import java.util.ArrayList;
 35  
 import java.util.List;
 36  
 import java.util.Map;
 37  
 import java.util.concurrent.Executors;
 38  
 import java.util.concurrent.ScheduledExecutorService;
 39  
 import java.util.concurrent.TimeUnit;
 40  
 
 41  
 import org.apache.commons.logging.Log;
 42  
 import org.apache.commons.logging.LogFactory;
 43  
 
 44  0
 public class DefaultMuleApplication implements Application
 45  
 {
 46  
 
 47  
     protected static final int DEFAULT_RELOAD_CHECK_INTERVAL_MS = 3000;
 48  
     protected static final String ANCHOR_FILE_BLURB = "Delete this file while Mule is running to undeploy this app in a clean way.";
 49  
 
 50  0
     protected transient final Log logger = LogFactory.getLog(getClass());
 51  
 
 52  
     protected ScheduledExecutorService watchTimer;
 53  
 
 54  
     private String appName;
 55  
     private MuleContext muleContext;
 56  
     private ClassLoader deploymentClassLoader;
 57  
     protected ApplicationDescriptor descriptor;
 58  
 
 59  
     protected String[] absoluteResourcePaths;
 60  
 
 61  
     public DefaultMuleApplication(String appName)
 62  0
     {
 63  0
         this.appName = appName;
 64  0
     }
 65  
 
 66  
     public void install()
 67  
     {
 68  0
         if (logger.isInfoEnabled())
 69  
         {
 70  0
             logger.info("Installing application: " + appName);
 71  
         }
 72  
 
 73  0
         AppBloodhound bh = new DefaultAppBloodhound();
 74  
         try
 75  
         {
 76  0
             descriptor = bh.fetch(getAppName());
 77  
         }
 78  0
         catch (IOException e)
 79  
         {
 80  0
             throw new InstallException(MessageFactory.createStaticMessage("Failed to parse the application deployment descriptor"), e);
 81  0
         }
 82  
 
 83  
         // convert to absolute paths
 84  0
         final String[] configResources = descriptor.getConfigResources();
 85  0
         absoluteResourcePaths = new String[configResources.length];
 86  0
         for (int i = 0; i < configResources.length; i++)
 87  
         {
 88  0
             String resource = configResources[i];
 89  0
             final File file = toAbsoluteFile(resource);
 90  0
             if (!file.exists())
 91  
             {
 92  0
                 throw new InstallException(
 93  
                         MessageFactory.createStaticMessage(String.format("Config for app '%s' not found: %s", getAppName(), file))
 94  
                 );
 95  
             }
 96  
 
 97  0
             absoluteResourcePaths[i] = file.getAbsolutePath();
 98  
         }
 99  
 
 100  0
         createDeploymentClassLoader();
 101  0
     }
 102  
 
 103  
     public String getAppName()
 104  
     {
 105  0
         return appName;
 106  
     }
 107  
 
 108  
     public void setAppName(String appName)
 109  
     {
 110  0
         this.appName = appName;
 111  0
     }
 112  
 
 113  
     public void start()
 114  
     {
 115  0
         if (logger.isInfoEnabled())
 116  
         {
 117  0
             logger.info("Starting application: " + appName);
 118  
         }
 119  
 
 120  
         try
 121  
         {
 122  0
             this.muleContext.start();
 123  
             // save app's state in the marker file
 124  0
             File marker = new File(MuleContainerBootstrapUtils.getMuleAppsDir(), String.format("%s-anchor.txt", getAppName()));
 125  0
             FileUtils.writeStringToFile(marker, ANCHOR_FILE_BLURB);
 126  
         }
 127  0
         catch (MuleException e)
 128  
         {
 129  
             // TODO add app name to the exception field
 130  0
             throw new DeploymentStartException(MessageFactory.createStaticMessage(appName), e);
 131  
         }
 132  0
         catch (IOException e)
 133  
         {
 134  
             // TODO add app name to the exception field
 135  0
             throw new DeploymentStartException(MessageFactory.createStaticMessage(appName), e);
 136  0
         }
 137  0
     }
 138  
 
 139  
     public void init()
 140  
     {
 141  0
         if (logger.isInfoEnabled())
 142  
         {
 143  0
             logger.info("Initializing application: " + appName);
 144  
         }
 145  
 
 146  0
         String configBuilderClassName = null;
 147  
         try
 148  
         {
 149  
             // Configuration builder
 150  
             // Provide a shortcut for Spring: "-builder spring"
 151  0
             final String builderFromDesc = descriptor.getConfigurationBuilder();
 152  0
             if ("spring".equalsIgnoreCase(builderFromDesc))
 153  
             {
 154  0
                 configBuilderClassName = ApplicationDescriptor.CLASSNAME_SPRING_CONFIG_BUILDER;
 155  
             }
 156  0
             else if (builderFromDesc == null)
 157  
             {
 158  0
                 configBuilderClassName = AutoConfigurationBuilder.class.getName();
 159  
             }
 160  
             else
 161  
             {
 162  0
                 configBuilderClassName = builderFromDesc;
 163  
             }
 164  
 
 165  0
             ConfigurationBuilder cfgBuilder = (ConfigurationBuilder) ClassUtils.instanciateClass(
 166  
                 configBuilderClassName, new Object[] {absoluteResourcePaths}, getDeploymentClassLoader());
 167  
 
 168  0
             if (!cfgBuilder.isConfigured())
 169  
             {
 170  
                 //Load application properties first since they may be needed by other configuration builders
 171  0
                 List<ConfigurationBuilder> builders = new ArrayList<ConfigurationBuilder>(2);
 172  
 
 173  0
                 final Map<String,String> appProperties = descriptor.getAppProperties();
 174  
 
 175  
                 //Add the app.home variable to the context
 176  0
                 appProperties.put(MuleProperties.APP_HOME_DIRECTORY_PROPERTY,
 177  
                         new File(MuleContainerBootstrapUtils.getMuleAppsDir(), getAppName()).getAbsolutePath());
 178  
 
 179  0
                 builders.add(new SimpleConfigurationBuilder(appProperties));
 180  
 
 181  
                 // If the annotations module is on the classpath, add the annotations config builder to the list
 182  
                 // This will enable annotations config for this instance
 183  
                 //We need to add this builder before spring so that we can use Mule annotations in Spring or any other builder
 184  0
                 if (ClassUtils.isClassOnPath(MuleServer.CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, getClass()))
 185  
                 {
 186  0
                     Object configBuilder = ClassUtils.instanciateClass(
 187  
                         MuleServer.CLASSNAME_ANNOTATIONS_CONFIG_BUILDER, ClassUtils.NO_ARGS, getClass());
 188  0
                     builders.add((ConfigurationBuilder) configBuilder);
 189  
                 }
 190  
 
 191  0
                 builders.add(cfgBuilder);
 192  
 
 193  0
                 DefaultMuleContextFactory muleContextFactory = new DefaultMuleContextFactory();
 194  0
                 this.muleContext = muleContextFactory.createMuleContext(builders, new ApplicationMuleContextBuilder(descriptor));
 195  
 
 196  0
                 if (descriptor.isRedeploymentEnabled())
 197  
                 {
 198  0
                     createRedeployMonitor();
 199  
                 }
 200  
             }
 201  
         }
 202  0
         catch (Exception e)
 203  
         {
 204  0
             throw new DeploymentInitException(CoreMessages.failedToLoad(configBuilderClassName), e);
 205  0
         }
 206  0
     }
 207  
 
 208  
     public MuleContext getMuleContext()
 209  
     {
 210  0
         return muleContext;
 211  
     }
 212  
 
 213  
     public ClassLoader getDeploymentClassLoader()
 214  
     {
 215  0
         return this.deploymentClassLoader;
 216  
     }
 217  
 
 218  
     public void dispose()
 219  
     {
 220  0
         if (muleContext == null)
 221  
         {
 222  0
             if (logger.isInfoEnabled())
 223  
             {
 224  0
                 logger.info("MuleContext not created, nothing to dispose of");
 225  
             }
 226  0
             return;
 227  
         }
 228  
 
 229  0
         if (muleContext.isStarted() && !muleContext.isDisposed())
 230  
         {
 231  0
             stop();
 232  
         }
 233  0
         if (logger.isInfoEnabled())
 234  
         {
 235  0
             logger.info("Disposing application: " + appName);
 236  
         }
 237  
 
 238  0
         muleContext.dispose();
 239  0
         muleContext = null;
 240  
         // kill any refs to the old classloader to avoid leaks
 241  0
         Thread.currentThread().setContextClassLoader(null);
 242  0
     }
 243  
 
 244  
     public void redeploy()
 245  
     {
 246  0
         if (logger.isInfoEnabled())
 247  
         {
 248  0
             logger.info("Redeploying application: " + appName);
 249  
         }
 250  0
         dispose();
 251  0
         install();
 252  
 
 253  
         // update thread with the fresh new classloader just created during the install phase
 254  0
         final ClassLoader cl = getDeploymentClassLoader();
 255  0
         Thread.currentThread().setContextClassLoader(cl);
 256  
 
 257  0
         init();
 258  0
         start();
 259  
 
 260  
         // release the ref
 261  0
         Thread.currentThread().setContextClassLoader(null);
 262  0
     }
 263  
 
 264  
     public void stop()
 265  
     {
 266  0
         if (this.muleContext == null)
 267  
         {
 268  
             // app never started, maybe due to a previous error
 269  0
             return;
 270  
         }
 271  0
         if (logger.isInfoEnabled())
 272  
         {
 273  0
             logger.info("Stopping application: " + appName);
 274  
         }
 275  
         try
 276  
         {
 277  0
             this.muleContext.stop();
 278  
         }
 279  0
         catch (MuleException e)
 280  
         {
 281  
             // TODO add app name to the exception field
 282  0
             throw new DeploymentStopException(MessageFactory.createStaticMessage(appName), e);
 283  0
         }
 284  0
     }
 285  
 
 286  
     @Override
 287  
     public String toString()
 288  
     {
 289  0
         return String.format("%s[%s]@%s", getClass().getName(),
 290  
                              appName,
 291  
                              Integer.toHexString(System.identityHashCode(this)));
 292  
     }
 293  
 
 294  
     protected void createDeploymentClassLoader()
 295  
     {
 296  0
         final String domain = descriptor.getDomain();
 297  
         ClassLoader parent;
 298  
 
 299  0
         if (StringUtils.isBlank(domain) || DefaultMuleSharedDomainClassLoader.DEFAULT_DOMAIN_NAME.equals(domain))
 300  
         {
 301  0
             parent = new DefaultMuleSharedDomainClassLoader(getClass().getClassLoader());
 302  
         }
 303  
         else
 304  
         {
 305  
             // TODO handle non-existing domains with an exception
 306  0
             parent = new MuleSharedDomainClassLoader(domain, getClass().getClassLoader());
 307  
         }
 308  
 
 309  0
         this.deploymentClassLoader = new MuleApplicationClassLoader(appName, parent);
 310  0
     }
 311  
 
 312  
     protected void createRedeployMonitor() throws NotificationException
 313  
     {
 314  0
         if (logger.isInfoEnabled())
 315  
         {
 316  0
             logger.info("Monitoring for hot-deployment: " + new File(absoluteResourcePaths [0]));
 317  
         }
 318  
 
 319  0
         final AbstractFileWatcher watcher = new ConfigFileWatcher(new File(absoluteResourcePaths [0]));
 320  
 
 321  
         // register a config monitor only after context has started, as it may take some time
 322  0
         muleContext.registerListener(new MuleContextNotificationListener<MuleContextNotification>()
 323  0
         {
 324  
 
 325  
             public void onNotification(MuleContextNotification notification)
 326  
             {
 327  0
                 final int action = notification.getAction();
 328  0
                 switch (action)
 329  
                 {
 330  
                     case MuleContextNotification.CONTEXT_STARTED:
 331  0
                         scheduleConfigMonitor(watcher);
 332  0
                         break;
 333  
                     case MuleContextNotification.CONTEXT_STOPPING:
 334  0
                         watchTimer.shutdownNow();
 335  0
                         muleContext.unregisterListener(this);
 336  
                         break;
 337  
                 }
 338  0
             }
 339  
         });
 340  0
     }
 341  
 
 342  
     protected void scheduleConfigMonitor(AbstractFileWatcher watcher)
 343  
     {
 344  0
         final int reloadIntervalMs = DEFAULT_RELOAD_CHECK_INTERVAL_MS;
 345  0
         watchTimer = Executors.newSingleThreadScheduledExecutor(new ConfigChangeMonitorThreadFactory(appName));
 346  
 
 347  0
         watchTimer.scheduleWithFixedDelay(watcher, reloadIntervalMs, reloadIntervalMs, TimeUnit.MILLISECONDS);
 348  
 
 349  0
         if (logger.isInfoEnabled())
 350  
         {
 351  0
             logger.info("Reload interval: " + reloadIntervalMs);
 352  
         }
 353  0
     }
 354  
 
 355  
     /**
 356  
      * Resolve a resource relative to an application root.
 357  
      * @param path the relative path to resolve
 358  
      * @return absolute path, may not actually exist (check with File.exists())
 359  
      */
 360  
     protected File toAbsoluteFile(String path)
 361  
     {
 362  0
         final String muleHome = System.getProperty(MuleProperties.MULE_HOME_DIRECTORY_PROPERTY);
 363  0
         String configPath = String.format("%s/apps/%s/%s", muleHome, getAppName(), path);
 364  0
         return new File(configPath);
 365  
     }
 366  
 
 367  
     protected class ConfigFileWatcher extends AbstractFileWatcher
 368  
     {
 369  
         public ConfigFileWatcher(File watchedResource)
 370  0
         {
 371  0
             super(watchedResource);
 372  0
         }
 373  
 
 374  
         @Override
 375  
         protected synchronized void onChange(File file)
 376  
         {
 377  0
             if (logger.isInfoEnabled())
 378  
             {
 379  0
                 logger.info("================== Reloading " + file);
 380  
             }
 381  
 
 382  
             // grab the proper classloader for our context
 383  0
             final ClassLoader cl = getDeploymentClassLoader();
 384  0
             Thread.currentThread().setContextClassLoader(cl);
 385  0
             redeploy();
 386  0
         }
 387  
     }
 388  
 }