1
2
3
4
5
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
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
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
90 FileUtils.deleteTree(muleHome);
91 if (deploymentService != null)
92 {
93 deploymentService.stop();
94 }
95 super.doTearDown();
96
97
98
99
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
118
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
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
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
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
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
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
233 installLatch = new Latch();
234
235
236 Thread.sleep(1000);
237
238
239 assertAppsDir(new String[] {"broken-app.zip"}, NONE, true);
240
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
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
270
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
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
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
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
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
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
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
504
505 private Application findApp(final String appName, int totalAppsExpected)
506 {
507
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
531
532 private void addAppArchive(URL url) throws IOException
533 {
534 addAppArchive(url, null);
535 }
536
537
538
539
540 private void addAppArchive(URL url, String targetFile) throws IOException
541 {
542
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
551
552
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 }