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.module.launcher;
8   
9   import static org.junit.Assert.assertArrayEquals;
10  import static org.junit.Assert.assertEquals;
11  import static org.junit.Assert.assertFalse;
12  import static org.junit.Assert.assertNotNull;
13  import static org.junit.Assert.assertNotSame;
14  import static org.junit.Assert.assertNull;
15  import static org.junit.Assert.assertTrue;
16  import org.mule.api.MuleContext;
17  import org.mule.api.component.JavaComponent;
18  import org.mule.api.config.MuleProperties;
19  import org.mule.api.construct.FlowConstruct;
20  import org.mule.api.registry.MuleRegistry;
21  import org.mule.config.StartupContext;
22  import org.mule.construct.SimpleService;
23  import org.mule.module.launcher.application.Application;
24  import org.mule.module.launcher.application.ApplicationWrapper;
25  import org.mule.module.launcher.application.PriviledgedMuleApplication;
26  import org.mule.module.launcher.application.TestApplicationFactory;
27  import org.mule.tck.junit4.AbstractMuleContextTestCase;
28  import org.mule.tck.probe.PollingProber;
29  import org.mule.tck.probe.Probe;
30  import org.mule.tck.probe.Prober;
31  import org.mule.tck.probe.file.FileDoesNotExists;
32  import org.mule.tck.probe.file.FileExists;
33  import org.mule.util.CollectionUtils;
34  import org.mule.util.FileUtils;
35  import org.mule.util.StringUtils;
36  import org.mule.util.concurrent.Latch;
37  
38  import java.io.File;
39  import java.io.IOException;
40  import java.net.URL;
41  import java.net.URLDecoder;
42  import java.util.Arrays;
43  import java.util.Collection;
44  import java.util.HashMap;
45  import java.util.List;
46  import java.util.Map;
47  
48  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
49  import org.apache.commons.io.filefilter.DirectoryFileFilter;
50  import org.junit.Test;
51  
52  public class DeploymentServiceTestCase extends AbstractMuleContextTestCase
53  {
54  
55      protected static final int LATCH_TIMEOUT = 20000;
56      protected static final String[] NONE = new String[0];
57  
58      protected File muleHome;
59      protected File appsDir;
60      protected DeploymentService deploymentService;
61      // these latches are re-created during the test, thus need to be declared volatile
62      protected volatile Latch deployLatch;
63      protected volatile Latch installLatch;
64      protected volatile Latch undeployLatch;
65  
66      @Override
67      protected void doSetUp() throws Exception
68      {
69          super.doSetUp();
70          // set up some mule home structure
71          final String tmpDir = System.getProperty("java.io.tmpdir");
72          muleHome = new File(tmpDir, getClass().getSimpleName() + System.currentTimeMillis());
73          appsDir = new File(muleHome, "apps");
74          appsDir.mkdirs();
75          System.setProperty(MuleProperties.MULE_HOME_DIRECTORY_PROPERTY, muleHome.getCanonicalPath());
76  
77          new File(muleHome, "lib/shared/default").mkdirs();
78  
79          deploymentService = new DeploymentService();
80          deploymentService.setDeployer(new TestDeployer());
81          installLatch = new Latch();
82          deployLatch = new Latch();
83          undeployLatch = new Latch();
84      }
85  
86      @Override
87      protected void doTearDown() throws Exception
88      {
89          // comment out the deletion to analyze results after test is done
90          FileUtils.deleteTree(muleHome);
91          if (deploymentService != null)
92          {
93              deploymentService.stop();
94          }
95          super.doTearDown();
96  
97          // this is a complex classloader setup and we can't reproduce standalone Mule 100%,
98          // so trick the next test method into thinking it's the first run, otherwise
99          // app resets CCL ref to null and breaks the next test
100         Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
101     }
102 
103     @Test
104     public void testPriviledgedApp() throws Exception
105     {
106         final URL url = getClass().getResource("/priviledged-dummy-app.zip");
107         assertNotNull("Test app file not found " + url, url);
108         addAppArchive(url);
109 
110         deploymentService.start();
111 
112         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
113 
114         assertAppsDir(NONE, new String[] {"priviledged-dummy-app"}, true);
115 
116         final Application app = findApp("priviledged-dummy-app", 1);
117         // now that we're sure it's the app we wanted, assert the registry has everything
118         // a 'priviledged' app would have had
119         final Object obj = app.getMuleContext().getRegistry().lookupObject(PriviledgedMuleApplication.REGISTRY_KEY_DEPLOYMENT_SERVICE);
120         assertNotNull("Priviledged objects have not been registered", obj);
121         assertTrue(((ApplicationWrapper) app).getDelegate() instanceof PriviledgedMuleApplication);
122     }
123 
124     @Test
125     public void testPriviledgedCrossAppAccess() throws Exception
126     {
127         URL url = getClass().getResource("/priviledged-dummy-app.zip");
128         assertNotNull("Test app file not found " + url, url);
129         addAppArchive(url);
130 
131         url = getClass().getResource("/dummy-app.zip");
132         assertNotNull("Test app file not found " + url, url);
133         addAppArchive(url);
134 
135         deploymentService.start();
136 
137         // a basic latch isn't ideal here, as there are 2 apps to deploy
138         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
139 
140         assertAppsDir(NONE, new String[] {"dummy-app", "priviledged-dummy-app"}, true);
141 
142         final Application privApp = findApp("priviledged-dummy-app", 2);
143         final Application dummyApp = findApp("dummy-app", 2);
144         assertTrue(((ApplicationWrapper) privApp).getDelegate() instanceof PriviledgedMuleApplication);
145 
146         final MuleContext muleContext1 = privApp.getMuleContext();
147         System.out.println("muleContext1 = " + muleContext1);
148         assertNotSame(muleContext1, muleContext);
149         assertNotSame(privApp.getDeploymentClassLoader(), dummyApp.getDeploymentClassLoader());
150         final Collection<FlowConstruct> flowConstructs = dummyApp.getMuleContext().getRegistry().lookupObjects(FlowConstruct.class);
151         assertFalse("No FlowConstructs found in the sibling app", flowConstructs.isEmpty());
152         FlowConstruct fc = flowConstructs.iterator().next();
153         assertTrue(fc instanceof SimpleService);
154         SimpleService service = (SimpleService) fc;
155         // note that we don't have this class available to this test directly
156         Class<?> clazz = ((JavaComponent) service.getComponent()).getObjectType();
157         assertEquals("Wrong component implementation class", "org.mule.module.launcher.EchoTest", clazz.getName());
158     }
159 
160     @Test
161     public void testDeployZipOnStartup() throws Exception
162     {
163         final URL url = getClass().getResource("/dummy-app.zip");
164         assertNotNull("Test app file not found " + url, url);
165         addAppArchive(url);
166 
167         deploymentService.start();
168 
169         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
170 
171         assertAppsDir(NONE, new String[] {"dummy-app"}, true);
172 
173         // just assert no priviledged entries were put in the registry
174         final Application app = findApp("dummy-app", 1);
175         final MuleRegistry registry = app.getMuleContext().getRegistry();
176         final Object obj = registry.lookupObject(PriviledgedMuleApplication.REGISTRY_KEY_DEPLOYMENT_SERVICE);
177         assertNull(obj);
178         assertFalse(((ApplicationWrapper) app).getDelegate() instanceof PriviledgedMuleApplication);
179 
180         // mule-app.properties from the zip archive must have loaded properly
181         assertEquals("mule-app.properties should have been loaded.", "someValue", registry.get("myCustomProp"));
182     }
183 
184     @Test
185     public void testUpdateAppViaZip() throws Exception
186     {
187         final URL url = getClass().getResource("/dummy-app.zip");
188         assertNotNull("Test app file not found " + url, url);
189         addAppArchive(url);
190 
191         deploymentService.start();
192 
193         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
194         assertAppsDir(NONE, new String[] {"dummy-app"}, true);
195         assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
196 
197         // set up a new deployment latch (can't reuse the old one)
198         deployLatch = new Latch();
199         addAppArchive(url);
200         assertTrue("Undeploy never invoked", undeployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
201         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
202         assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
203         assertAppsDir(NONE, new String[]{"dummy-app"}, true);
204     }
205 
206     @Test
207     public void testBrokenAppArchiveWithoutArgument() throws Exception
208     {
209         doBrokenAppArchiveTest();
210     }
211 
212     @Test
213     public void testBrokenAppArchiveAsArgument() throws Exception
214     {
215         Map<String, Object> startupOptions = new HashMap<String, Object>();
216         startupOptions.put("app", "broken-app");
217         StartupContext.get().setStartupOptions(startupOptions);
218 
219         doBrokenAppArchiveTest();
220     }
221 
222     public void doBrokenAppArchiveTest() throws Exception
223     {
224         final URL url = getClass().getResource("/broken-app.zip");
225         assertNotNull("Test app file not found " + url, url);
226         addAppArchive(url);
227 
228         deploymentService.start();
229 
230         assertTrue("Install never invoked", installLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
231 
232         // Resets the latch to detect a new attempt to deploy the zip file
233         installLatch = new Latch();
234 
235         // let the file system's write-behind cache commit the delete operation?
236         Thread.sleep(1000);
237 
238         // zip stays intact, no app dir created
239         assertAppsDir(new String[] {"broken-app.zip"}, NONE, true);
240         // don't assert dir contents, we want to check internal deployer state next
241         assertAppsDir(NONE, new String[] {"dummy-app"}, false);
242         assertEquals("No apps should have been registered with Mule.", 0, deploymentService.getApplications().size());
243         final Map<URL, Long> zombieMap = deploymentService.getZombieMap();
244         assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
245         final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
246         assertEquals("Wrong URL tagged as zombie.", "broken-app.zip", new File(zombie.getKey().getFile()).getName());
247         assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
248 
249         // Checks that the invalid zip was not deployed again
250         assertFalse("Install was invoked again for the broken application file", installLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
251     }
252 
253     @Test
254     public void testBrokenAppName() throws Exception
255     {
256         final URL url = getClass().getResource("/empty-app.zip");
257         assertNotNull("Test app file not found " + url, url);
258         addAppArchive(url, "app with spaces.zip");
259 
260         try
261         {
262             deploymentService.start();
263         }
264         catch (DeploymentInitException e)
265         {
266             assertTrue(e.getMessage().contains("may not contain spaces"));
267         }
268 
269         // zip stays intact, no app dir created
270         // %20 is returned by java file api :/
271         assertAppsDir(new String[] {"app with spaces.zip"}, NONE, true);
272         final Map<URL, Long> zombieMap = deploymentService.getZombieMap();
273         assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
274         final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
275         // Spaces are converted to %20 is returned by java file api :/
276         String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
277         assertEquals("Wrong URL tagged as zombie.", "app with spaces.zip",  appName);
278         assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
279     }
280     
281     @Test
282     public void testConfigurableWorkingDirectoryApp() throws Exception
283     {
284         final URL url1 = getClass().getResource("/configurableApp.zip");
285         assertNotNull("Test app file not found " + url1, url1);
286         addAppArchive(url1);
287         
288         final URL url2 = getClass().getResource("/dummy-app.zip");
289         assertNotNull("Test app file not found " + url2, url2);
290         addAppArchive(url2);
291 
292         deploymentService.start();
293 
294         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
295         deployLatch.release();
296 
297         assertAppsDir(NONE, new String[] {"configurableApp", "dummy-app"}, true);
298 
299         // just assert no priviledged entries were put in the registry
300         final Application configurableApp = findApp("configurableApp", 2);
301         final Application dummyApp = findApp("dummy-app", 2);
302         
303         final MuleRegistry registry1 = configurableApp.getMuleContext().getRegistry();
304         final Object obj1 = registry1.lookupObject(PriviledgedMuleApplication.REGISTRY_KEY_DEPLOYMENT_SERVICE);
305         assertNull(obj1);
306         assertFalse(((ApplicationWrapper) configurableApp).getDelegate() instanceof PriviledgedMuleApplication);
307         assertEquals("mule-app.properties should have been loaded.", "someValue", registry1.get("myCustomProp"));
308         assertTrue(configurableApp.getMuleContext().getConfiguration().getWorkingDirectory().endsWith(".appT/configurableApp"));
309 
310         final MuleRegistry registry2 = dummyApp.getMuleContext().getRegistry();
311         final Object obj2 = registry2.lookupObject(PriviledgedMuleApplication.REGISTRY_KEY_DEPLOYMENT_SERVICE);
312         assertNull(obj2);
313         assertFalse(((ApplicationWrapper) dummyApp).getDelegate() instanceof PriviledgedMuleApplication);
314         assertEquals("mule-app.properties should have been loaded.", "someValue", registry2.get("myCustomProp"));
315         assertTrue(dummyApp.getMuleContext().getConfiguration().getWorkingDirectory().endsWith(".mule/dummy-app"));
316     }
317 
318     @Test
319     public void testDeployAppNameWithZipSuffix() throws Exception
320     {
321         final URL url = getClass().getResource("/empty-app.zip");
322         assertNotNull("Test app file not found " + url, url);
323         addAppArchive(url, "empty-app.zip.zip");
324 
325         deploymentService.start();
326 
327         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
328         deployLatch = new Latch();
329         assertAppsDir(NONE, new String[] {"empty-app.zip"}, true);
330         assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
331 
332         // Checks that the empty-app.zip folder is not processed as a zip file
333         assertNoDeploymentInvoked();
334     }
335 
336     @Test
337     public void testDeployAsArgumentStartupOrder() throws Exception
338     {
339         final URL url = getClass().getResource("/empty-app.zip");
340         assertNotNull("Test app file not found " + url, url);
341         addAppArchive(url, "1.zip");
342         addAppArchive(url, "2.zip");
343         addAppArchive(url, "3.zip");
344 
345         Map<String, Object> startupOptions = new HashMap<String, Object>();
346         startupOptions.put("app", "3:1:2");
347         StartupContext.get().setStartupOptions(startupOptions);
348 
349         deploymentService.start();
350 
351         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
352 
353         assertAppsDir(NONE, new String[] {"1", "2", "3"}, true);
354 
355         // When apps are passed as -app app1:app2:app3 the startup order matters
356         List<Application> applications = deploymentService.getApplications();
357         assertNotNull(applications);
358         assertEquals(3, applications.size());
359         assertEquals("3", applications.get(0).getAppName());
360         assertEquals("1", applications.get(1).getAppName());
361         assertEquals("2", applications.get(2).getAppName());
362     }
363 
364     @Test
365     public void testDeploysAppJustOnce() throws Exception
366     {
367         final URL url = getClass().getResource("/dummy-app.zip");
368         assertNotNull("Test app file not found " + url, url);
369         addAppArchive(url);
370 
371         Map<String, Object> startupOptions = new HashMap<String, Object>();
372         startupOptions.put("app", "dummy-app:dummy-app:dummy-app");
373         StartupContext.get().setStartupOptions(startupOptions);
374 
375         deploymentService.start();
376 
377         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
378         assertAppsDir(NONE, new String[] {"dummy-app"}, true);
379 
380         List<Application> applications = deploymentService.getApplications();
381         assertEquals(1, applications.size());
382     }
383 
384     @Test
385     public void undeploysApplicationRemovingAnchorFile() throws Exception
386     {
387         final URL url = getClass().getResource("/dummy-app.zip");
388         assertNotNull("Test app file not found " + url, url);
389 
390         addAppArchive(url);
391         deploymentService.start();
392 
393         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
394         assertAppsDir(NONE, new String[] {"dummy-app"}, true);
395 
396         assertTrue("Unable to remove anchor file", removeAnchorFile("dummy-app"));
397 
398         assertTrue("Undeploy never invoked", undeployLatch.await(2 * LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
399         assertEquals("Application has not been properly unregistered from Mule", 0, deploymentService.getApplications().size());
400     }
401 
402     @Test
403     public void undeploysAppCompletelyEvenOnStoppingException() throws Exception
404     {
405         final URL url = getClass().getResource("/empty-app.zip");
406         assertNotNull("Test app file not found " + url, url);
407         addAppArchive(url);
408 
409         TestApplicationFactory appFactory = new TestApplicationFactory(deploymentService);
410         appFactory.setFailOnStopApplication(true);
411 
412         deploymentService.setAppFactory(appFactory);
413         deploymentService.start();
414 
415         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
416         assertAppsDir(NONE, new String[] {"empty-app"}, true);
417 
418         deployLatch = new Latch();
419         assertTrue("Unable to remove anchor file", removeAnchorFile("empty-app"));
420 
421         assertTrue("Undeploy never invoked", undeployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
422         assertEquals("Application has not been properly unregistered from Mule", 0, deploymentService.getApplications().size());
423 
424         Prober prober = new PollingProber(LATCH_TIMEOUT, 100);
425         File appFolder = new File(appsDir, "empty-app");
426         prober.check(new FileDoesNotExists(appFolder));
427     }
428 
429     @Test
430     public void undeploysAppCompletelyEvenOnDisposingException() throws Exception
431     {
432         final URL url = getClass().getResource("/empty-app.zip");
433         assertNotNull("Test app file not found " + url, url);
434         addAppArchive(url);
435 
436         TestApplicationFactory appFactory = new TestApplicationFactory(deploymentService);
437         appFactory.setFailOnDisposeApplication(true);
438         deploymentService.setAppFactory(appFactory);
439         deploymentService.start();
440 
441         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
442         assertAppsDir(NONE, new String[] {"empty-app"}, true);
443 
444         deployLatch = new Latch();
445         assertTrue("Unable to remove anchor file", removeAnchorFile("empty-app"));
446 
447         assertTrue("Undeploy never invoked", undeployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
448         assertEquals("Application has not been properly unregistered from Mule", 0, deploymentService.getApplications().size());
449 
450         Prober prober = new PollingProber(LATCH_TIMEOUT, 100);
451         File appFolder = new File(appsDir, "empty-app");
452         prober.check(new FileDoesNotExists(appFolder));
453     }
454 
455     @Test
456     public void mantainsAppFolderOnDeploymentError() throws Exception
457     {
458         final URL url = getClass().getResource("/incompleteApp.zip");
459         assertNotNull("Test app file not found " + url, url);
460         addAppArchive(url, "incompleteApp.zip");
461 
462         deploymentService.start();
463 
464         // Deploys another app to confirm that DeploymentService has execute the updater thread
465         final URL extraApp = getClass().getResource("/dummy-app.zip");
466         assertNotNull("Test app file not found " + extraApp, extraApp);
467         addAppArchive(extraApp);
468         assertTrue("Deployer never invoked", deployLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
469         assertAppsDir(NONE, new String[] {"incompleteApp", "dummy-app"}, true);
470     }
471 
472     private void assertNoDeploymentInvoked()
473     {
474         //TODO(pablo.kraan): look for a better way to test this
475         boolean invoked;
476         Prober prober = new PollingProber(DeploymentService.DEFAULT_CHANGES_CHECK_INTERVAL_MS * 2, 100);
477         try
478         {
479             prober.check(new Probe()
480             {
481                 public boolean isSatisfied()
482                 {
483                     return deploymentService.getZombieMap().size() == 1;
484                 }
485 
486                 public String describeFailure()
487                 {
488                     return "App was never redeployed";
489                 }
490             });
491 
492             invoked = true;
493         }
494         catch (AssertionError e)
495         {
496            invoked = false;
497         }
498 
499         assertFalse("Deployer was invoked", invoked);
500     }
501 
502     /**
503      * Find a deployed app, performing some basic assertions.
504      */
505     private Application findApp(final String appName, int totalAppsExpected)
506     {
507         // list all apps to validate total count
508         final List<Application> apps = deploymentService.getApplications();
509         assertNotNull(apps);
510         assertEquals(totalAppsExpected, apps.size());
511         final Application app = deploymentService.findApplication(appName);
512         assertNotNull(app);
513         return app;
514     }
515 
516     private void assertAppsDir(String[] expectedZips, String[] expectedApps, boolean performValidation)
517     {
518         final String[] actualZips = appsDir.list(DeploymentService.ZIP_APPS_FILTER);
519         if (performValidation) {
520             assertArrayEquals("Invalid Mule application archives set", expectedZips, actualZips);
521         }
522         final String[] actualApps = appsDir.list(DirectoryFileFilter.DIRECTORY);
523         if (performValidation) {
524             assertTrue("Invalid Mule exploded applications set",
525                        CollectionUtils.isEqualCollection(Arrays.asList(expectedApps), Arrays.asList(actualApps)));
526         }
527     }
528 
529     /**
530      * Copies a given app archive to the apps folder for deployment.
531      */
532     private void addAppArchive(URL url) throws IOException
533     {
534         addAppArchive(url, null);
535     }
536 
537     /**
538      * Copies a given app archive with a given target name to the apps folder for deployment
539      */
540     private void addAppArchive(URL url, String targetFile) throws IOException
541     {
542         // copy is not atomic, copy to a temp file and rename instead (rename is atomic)
543         final String tempFileName = new File((targetFile == null ? url.getFile() : targetFile) + ".part").getName();
544         final File tempFile = new File(appsDir, tempFileName);
545         FileUtils.copyURLToFile(url, tempFile);
546         tempFile.renameTo(new File(StringUtils.removeEnd(tempFile.getAbsolutePath(), ".part")));
547     }
548 
549     /**
550      * Removes a given application anchor file in order to start application undeployment
551      * @param appName name of application to undeploy
552      * @return true if anchor file was deleted, false otherwise
553      */
554     private boolean removeAnchorFile(String appName)
555     {
556         String anchorFileName = appName + DeploymentService.APP_ANCHOR_SUFFIX;
557         File anchorFile = new File(appsDir, anchorFileName);
558 
559         return anchorFile.delete();
560     }
561 
562     private class TestDeployer implements MuleDeployer
563     {
564         MuleDeployer delegate = new DefaultMuleDeployer(deploymentService);
565 
566         public void deploy(Application app)
567         {
568             System.out.println("DeploymentServiceTestCase$TestDeployer.deploy");
569             delegate.deploy(app);
570             deployLatch.release();
571         }
572 
573         public void undeploy(Application app)
574         {
575             System.out.println("DeploymentServiceTestCase$TestDeployer.undeploy");
576             delegate.undeploy(app);
577             undeployLatch.release();
578         }
579 
580         public Application installFromAppDir(String packedMuleAppFileName) throws IOException
581         {
582             installLatch.release();
583             System.out.println("DeploymentServiceTestCase$TestDeployer.installFromAppDir");
584             return delegate.installFromAppDir(packedMuleAppFileName);
585         }
586 
587         public Application installFrom(URL url) throws IOException
588         {
589             installLatch.release();
590             System.out.println("DeploymentServiceTestCase$TestDeployer.installFrom");
591             return delegate.installFrom(url);
592         }
593 
594     }
595 }