Coverage Report - org.mule.module.launcher.DeploymentService
 
Classes in this File Line Coverage Branch Coverage Complexity
DeploymentService
0%
0/53
0%
0/20
0
DeploymentService$AppDirWatcher
0%
0/59
0%
0/18
0
 
 1  
 /*
 2  
  * $Id: DeploymentService.java 19191 2010-08-25 21:05:23Z tcarlson $
 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.config.StartupContext;
 14  
 import org.mule.module.reboot.MuleContainerBootstrapUtils;
 15  
 import org.mule.util.CollectionUtils;
 16  
 import org.mule.util.FileUtils;
 17  
 import org.mule.util.FilenameUtils;
 18  
 import org.mule.util.StringUtils;
 19  
 
 20  
 import java.io.File;
 21  
 import java.io.IOException;
 22  
 import java.util.ArrayList;
 23  
 import java.util.Arrays;
 24  
 import java.util.Collection;
 25  
 import java.util.Collections;
 26  
 import java.util.List;
 27  
 import java.util.Map;
 28  
 import java.util.concurrent.Executors;
 29  
 import java.util.concurrent.ScheduledExecutorService;
 30  
 import java.util.concurrent.TimeUnit;
 31  
 
 32  
 import org.apache.commons.beanutils.BeanPropertyValueEqualsPredicate;
 33  
 import org.apache.commons.io.filefilter.DirectoryFileFilter;
 34  
 import org.apache.commons.io.filefilter.SuffixFileFilter;
 35  
 import org.apache.commons.logging.Log;
 36  
 import org.apache.commons.logging.LogFactory;
 37  
 
 38  0
 public class DeploymentService
 39  
 {
 40  
     public static final String APP_ANCHOR_SUFFIX = "-anchor.txt";
 41  
     protected static final int DEFAULT_CHANGES_CHECK_INTERVAL_MS = 5000;
 42  
 
 43  
     protected ScheduledExecutorService appDirMonitorTimer;
 44  
 
 45  0
     protected transient final Log logger = LogFactory.getLog(getClass());
 46  0
     protected MuleDeployer deployer = new DefaultMuleDeployer();
 47  0
     private List<Application> applications = new ArrayList<Application>();
 48  
 
 49  
     public void start()
 50  
     {
 51  
         // install phase
 52  0
         final Map<String, Object> options = StartupContext.get().getStartupOptions();
 53  0
         String appString = (String) options.get("app");
 54  
 
 55  0
         final File appsDir = MuleContainerBootstrapUtils.getMuleAppsDir();
 56  
 
 57  
         // delete any leftover anchor files from previous unclean shutdowns
 58  0
         String[] appAnchors = appsDir.list(new SuffixFileFilter(APP_ANCHOR_SUFFIX));
 59  0
         for (String anchor : appAnchors)
 60  
         {
 61  
             // ignore result
 62  0
             new File(appsDir, anchor).delete();
 63  
         }
 64  
 
 65  
         String[] apps;
 66  
 
 67  
         // mule -app app1:app2:app3 will restrict deployment only to those specified apps
 68  0
         final boolean explicitAppSet = appString != null;
 69  
 
 70  0
         if (!explicitAppSet)
 71  
         {
 72  
             // explode any app zips first
 73  0
             final String[] zips = appsDir.list(new SuffixFileFilter(".zip"));
 74  0
             for (String zip : zips)
 75  
             {
 76  
                 try
 77  
                 {
 78  
                     // we don't care about the returned app object on startup
 79  0
                     deployer.installFromAppDir(zip);
 80  
                 }
 81  0
                 catch (IOException e)
 82  
                 {
 83  
                     // TODO logging
 84  0
                     e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
 85  0
                 }
 86  
             }
 87  
 
 88  
             // TODO this is a place to put a FQN of the custom sorter (use AND filter)
 89  
             // Add string shortcuts for bundled ones
 90  0
             apps = appsDir.list(DirectoryFileFilter.DIRECTORY);
 91  0
         }
 92  
         else
 93  
         {
 94  0
             apps = appString.split(":");
 95  
         }
 96  
 
 97  0
         for (String app : apps)
 98  
         {
 99  0
             final ApplicationWrapper a = new ApplicationWrapper(new DefaultMuleApplication(app));
 100  0
             applications.add(a);
 101  
         }
 102  
 
 103  
 
 104  0
         for (Application application : applications)
 105  
         {
 106  
             try
 107  
             {
 108  0
                 deployer.deploy(application);
 109  
             }
 110  0
             catch (Throwable t)
 111  
             {
 112  
                 // TODO logging
 113  0
                 t.printStackTrace();
 114  0
             }
 115  
         }
 116  
 
 117  
         // only start the monitor thread if we launched in default mode without explicitly
 118  
         // stated applications to launch
 119  0
         if (!explicitAppSet)
 120  
         {
 121  0
             scheduleChangeMonitor(appsDir);
 122  
         }
 123  0
     }
 124  
 
 125  
     protected void scheduleChangeMonitor(File appsDir)
 126  
     {
 127  0
         final int reloadIntervalMs = DEFAULT_CHANGES_CHECK_INTERVAL_MS;
 128  0
         appDirMonitorTimer = Executors.newSingleThreadScheduledExecutor(new AppDeployerMonitorThreadFactory());
 129  
 
 130  0
         appDirMonitorTimer.scheduleWithFixedDelay(new AppDirWatcher(appsDir),
 131  
                                                   0,
 132  
                                                   reloadIntervalMs,
 133  
                                                   TimeUnit.MILLISECONDS);
 134  
 
 135  0
         if (logger.isInfoEnabled())
 136  
         {
 137  0
             logger.info("Application directory check interval: " + reloadIntervalMs);
 138  
         }
 139  0
     }
 140  
 
 141  
     public void stop()
 142  
     {
 143  0
         if (appDirMonitorTimer != null)
 144  
         {
 145  0
             appDirMonitorTimer.shutdownNow();
 146  
         }
 147  
 
 148  
         // tear down apps in reverse order
 149  0
         Collections.reverse(applications);
 150  0
         for (Application application : applications)
 151  
         {
 152  
             try
 153  
             {
 154  0
                 application.stop();
 155  0
                 application.dispose();
 156  
             }
 157  0
             catch (Throwable t)
 158  
             {
 159  
                 // TODO logging
 160  0
                 t.printStackTrace();
 161  0
             }
 162  
         }
 163  
 
 164  0
     }
 165  
 
 166  
     /**
 167  
      * @return immutable applications list
 168  
      */
 169  
     public List<Application> getApplications()
 170  
     {
 171  0
         return Collections.unmodifiableList(applications);
 172  
     }
 173  
 
 174  
     public MuleDeployer getDeployer()
 175  
     {
 176  0
         return deployer;
 177  
     }
 178  
 
 179  
     public void setDeployer(MuleDeployer deployer)
 180  
     {
 181  0
         this.deployer = deployer;
 182  0
     }
 183  
 
 184  
     /**
 185  
      * Not thread safe. Correctness is guaranteed by a single-threaded executor.
 186  
      */
 187  0
     protected class AppDirWatcher implements Runnable
 188  
     {
 189  
         protected File appsDir;
 190  
 
 191  
         protected String[] deployedApps;
 192  
 
 193  
         // written on app start, will be used to cleanly undeploy the app without file locking issues
 194  0
         protected String[] appAnchors = new String[0];
 195  
 
 196  
         public AppDirWatcher(File appsDir)
 197  0
         {
 198  0
             this.appsDir = appsDir;
 199  
             // save the list of known apps on startup
 200  0
             this.deployedApps = new String[applications.size()];
 201  0
             for (int i = 0; i < applications.size(); i++)
 202  
             {
 203  0
                 deployedApps[i] = applications.get(i).getAppName();
 204  
 
 205  
             }
 206  0
         }
 207  
 
 208  
         // Cycle is:
 209  
         //   undeploy removed apps
 210  
         //   deploy archives
 211  
         //   deploy exploded
 212  
         public void run()
 213  
         {
 214  
             // list new apps
 215  0
             final String[] zips = appsDir.list(new SuffixFileFilter(".zip"));
 216  0
             String[] apps = appsDir.list(DirectoryFileFilter.DIRECTORY);
 217  
             // we care only about removed anchors
 218  0
             String[] currentAnchors = appsDir.list(new SuffixFileFilter(APP_ANCHOR_SUFFIX));
 219  
             @SuppressWarnings("unchecked")
 220  0
             final Collection<String> deletedAnchors = CollectionUtils.subtract(Arrays.asList(appAnchors), Arrays.asList(currentAnchors));
 221  0
             for (String deletedAnchor : deletedAnchors)
 222  
             {
 223  
                 // apps.find ( it.appName = (removedAnchor - suffix))
 224  0
                 String appName = StringUtils.removeEnd(deletedAnchor, APP_ANCHOR_SUFFIX);
 225  
                 try
 226  
                 {
 227  0
                     onApplicationUndeployRequested(appName);
 228  
                 }
 229  0
                 catch (Throwable t)
 230  
                 {
 231  0
                     logger.error("Failed to undeploy application: " + appName, t);
 232  0
                 }
 233  0
             }
 234  0
             appAnchors = currentAnchors;
 235  
 
 236  
 
 237  
             // new packed Mule apps
 238  0
             for (String zip : zips)
 239  
             {
 240  
                 try
 241  
                 {
 242  
                     // check if this app is running first, undeploy it then
 243  0
                     final String appName = StringUtils.removeEnd(zip, ".zip");
 244  0
                     Application app = (Application) CollectionUtils.find(applications, new BeanPropertyValueEqualsPredicate("appName", appName));
 245  0
                     if (app != null)
 246  
                     {
 247  0
                         onApplicationUndeployRequested(appName);
 248  
                     }
 249  0
                     onNewApplicationArchive(new File(appsDir, zip));
 250  
                 }
 251  0
                 catch (Throwable t)
 252  
                 {
 253  0
                     logger.error("Failed to deploy application archive: " + zip, t);
 254  0
                 }
 255  
             }
 256  
 
 257  
             // re-scan exploded apps and update our state, as deploying Mule app archives might have added some
 258  0
             if (zips.length > 0)
 259  
             {
 260  0
                 apps = appsDir.list(DirectoryFileFilter.DIRECTORY);
 261  0
                 deployedApps = apps;
 262  
             }
 263  
 
 264  
             // new exploded Mule apps
 265  
             @SuppressWarnings("unchecked")
 266  0
             final Collection<String> addedApps = CollectionUtils.subtract(Arrays.asList(apps), Arrays.asList(deployedApps));
 267  0
             for (String addedApp : addedApps)
 268  
             {
 269  
                 try
 270  
                 {
 271  0
                     onNewExplodedApplication(addedApp);
 272  
                 }
 273  0
                 catch (Throwable t)
 274  
                 {
 275  0
                     logger.error("Failed to deploy exploded application: " + addedApp, t);
 276  0
                 }
 277  
             }
 278  
 
 279  0
             deployedApps = apps;
 280  0
         }
 281  
 
 282  
         protected void onApplicationUndeployRequested(String appName) throws Exception
 283  
         {
 284  0
             if (logger.isInfoEnabled())
 285  
             {
 286  0
                 logger.info("================== Request to Undeploy Application: " + appName);
 287  
             }
 288  
 
 289  0
             Application app = (Application) CollectionUtils.find(applications, new BeanPropertyValueEqualsPredicate("appName", appName));
 290  0
             applications.remove(app);
 291  0
             deployer.undeploy(app);
 292  0
         }
 293  
 
 294  
         /**
 295  
          * @param appName application name as it appears in $MULE_HOME/apps
 296  
          */
 297  
         protected void onNewExplodedApplication(String appName) throws Exception
 298  
         {
 299  0
             if (logger.isInfoEnabled())
 300  
             {
 301  0
                 logger.info("================== New Exploded Application: " + appName);
 302  
             }
 303  
 
 304  0
             Application a = new ApplicationWrapper(new DefaultMuleApplication(appName));
 305  
             // add to the list of known apps first to avoid deployment loop on failure
 306  0
             applications.add(a);
 307  0
             deployer.deploy(a);
 308  0
         }
 309  
 
 310  
         protected void onNewApplicationArchive(File file) throws Exception
 311  
         {
 312  0
             if (logger.isInfoEnabled())
 313  
             {
 314  0
                 logger.info("================== New Application Archive: " + file);
 315  
             }
 316  
 
 317  
             // check if there are any broken leftovers and clean it up before exploded an updated zip
 318  0
             final String appName = FilenameUtils.getBaseName(file.getName());
 319  0
             FileUtils.deleteTree(new File(appsDir, appName));
 320  
 
 321  0
             Application app = deployer.installFrom(file.toURL());
 322  
             // add to the list of known apps first to avoid deployment loop on failure
 323  0
             applications.add(app);
 324  0
             deployer.deploy(app);
 325  0
         }
 326  
     }
 327  
 
 328  
 }