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