Package org.mule.module.launcher

Source Code of org.mule.module.launcher.DeploymentServiceTestCase

/*
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.module.launcher;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.mule.api.MuleContext;
import org.mule.api.config.MuleProperties;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.registry.MuleRegistry;
import org.mule.config.StartupContext;
import org.mule.module.launcher.application.Application;
import org.mule.module.launcher.application.ApplicationStatus;
import org.mule.module.launcher.application.MuleApplicationClassLoaderFactory;
import org.mule.module.launcher.application.TestApplicationFactory;
import org.mule.module.launcher.domain.Domain;
import org.mule.module.launcher.domain.MuleDomainClassLoaderRepository;
import org.mule.module.launcher.domain.TestDomainFactory;
import org.mule.module.launcher.nativelib.DefaultNativeLibraryFinderFactory;
import org.mule.tck.junit4.AbstractMuleContextTestCase;
import org.mule.tck.junit4.rule.SystemProperty;
import org.mule.tck.probe.JUnitProbe;
import org.mule.tck.probe.PollingProber;
import org.mule.tck.probe.Probe;
import org.mule.tck.probe.Prober;
import org.mule.tck.probe.file.FileDoesNotExists;
import org.mule.tck.probe.file.FileExists;
import org.mule.util.CollectionUtils;
import org.mule.util.FileUtils;
import org.mule.util.StringUtils;
import org.mule.util.concurrent.Latch;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationMode;

public class DeploymentServiceTestCase extends AbstractMuleContextTestCase
{

    protected static final int DEPLOYMENT_TIMEOUT = 10000;
    protected static final String[] NONE = new String[0];
    protected static final int ONE_HOUR_IN_MILLISECONDS = 3600000;

    private static final String MULE_CONFIG_XML_FILE = "mule-config.xml";
    private static final String EMPTY_APP_CONFIG_XML = "/empty-config.xml";
    private static final String BAD_APP_CONFIG_XML = "/bad-app-config.xml";

    //APP constants
    private static final ArtifactDescriptor dummyAppDescriptor = new ArtifactDescriptor("dummy-app", "/dummy-app.zip", "/dummy-app", null, null);
    private static final ArtifactDescriptor emptyAppDescriptor = new ArtifactDescriptor("empty-app", "/empty-app.zip", null, "empty-app.zip", null);
    private static final ArtifactDescriptor brokenAppDescriptor = new ArtifactDescriptor("broken-app", "/broken-app.zip", null, "brokenApp.zip", null);
    private static final ArtifactDescriptor brokenAppWithFunkyNameDescriptor = new ArtifactDescriptor("broken-app+", "/broken-app+.zip", null, "brokenApp+.zip", null);
    private static final ArtifactDescriptor incompleteAppDescriptor = new ArtifactDescriptor("incompleteApp", "/incompleteApp.zip", "/incompleteApp", "incompleteApp.zip", null);
    private static final ArtifactDescriptor waitAppDescriptor = new ArtifactDescriptor("wait-app", "/wait-app.zip", "/wait-app", "wait-app.zip", "mule-config.xml");

    //Domain constants
    private static final ArtifactDescriptor brokenDomainDescriptor = new ArtifactDescriptor("brokenDomain", "/broken-domain.zip", null, "brokenDomain.zip", "/broken-config.xml");
    private static final ArtifactDescriptor dummyDomainDescriptor = new ArtifactDescriptor("dummy-domain", "/dummy-domain.zip", "/dummy-domain", null, "mule-domain-config.xml");
    private static final ArtifactDescriptor dummyDomainApp1Descriptor = new ArtifactDescriptor("dummy-domain-app1", "/dummy-domain-app1.zip", null, null, null);
    private static final ArtifactDescriptor dummyDomainApp2Descriptor = new ArtifactDescriptor("dummy-domain-app2", "/dummy-domain-app2.zip", "/dummy-domain-app2", null, null);
    private static final ArtifactDescriptor dummyDomainApp3Descriptor = new ArtifactDescriptor("dummy-domain-app3", "/dummy-domain-app3.zip", "/dummy-domain-app3", null, null);
    private static final ArtifactDescriptor dummyDomainBundleDescriptor = new ArtifactDescriptor("dummy-domain-bundle", "/dummy-domain-bundle.zip", null, null, null);
    private static final ArtifactDescriptor emptyDomainDescriptor = new ArtifactDescriptor("empty-domain", "/empty-domain.zip", null, "empty-domain.zip", EMPTY_APP_CONFIG_XML);
    private static final ArtifactDescriptor incompleteDomainDescriptor = new ArtifactDescriptor("incompleteDomain", "/incompleteDomain.zip", null, "incompleteDomain.zip", null);
    private static final ArtifactDescriptor invalidDomainBundle = new ArtifactDescriptor("invalid-domain-bundle", "/invalid-domain-bundle.zip", null, null, null);
    private static final ArtifactDescriptor httpSharedDomainBundle = new ArtifactDescriptor("http-shared-domain", "/http-shared-domain.zip", null, null, null);
    private static final ArtifactDescriptor waitDomainDescriptor = new ArtifactDescriptor("wait-domain", "/wait-domain.zip", "/wait-domain", "wait-domain.zip", "mule-domain-config.xml");


    protected File muleHome;
    protected File appsDir;
    protected File domainsDir;
    protected MuleDeploymentService deploymentService;
    protected DeploymentListener applicationDeploymentListener;
    protected DeploymentListener domainDeploymentListener;

    @Rule
    public SystemProperty changeChangeInterval = new SystemProperty(DeploymentDirectoryWatcher.CHANGE_CHECK_INTERVAL_PROPERTY, "10");

    @Override
    protected void doSetUp() throws Exception
    {
        super.doSetUp();
        // set up some mule home structure
        final String tmpDir = System.getProperty("java.io.tmpdir");
        muleHome = new File(tmpDir, getClass().getSimpleName() + System.currentTimeMillis());
        appsDir = new File(muleHome, "apps");
        appsDir.mkdirs();
        domainsDir = new File(muleHome, "domains");
        domainsDir.mkdirs();
        System.setProperty(MuleProperties.MULE_HOME_DIRECTORY_PROPERTY, muleHome.getCanonicalPath());

        new File(muleHome, "lib/shared/default").mkdirs();

        applicationDeploymentListener = mock(DeploymentListener.class);
        domainDeploymentListener = mock(DeploymentListener.class);
        deploymentService = new MuleDeploymentService(new MulePluginClassLoaderManager());
        deploymentService.addDeploymentListener(applicationDeploymentListener);
        deploymentService.addDomainDeploymentListener(domainDeploymentListener);
    }

    @Override
    protected void doTearDown() throws Exception
    {
        // comment out the deletion to analyze results after test is done
        FileUtils.deleteTree(muleHome);
        if (deploymentService != null)
        {
            deploymentService.stop();
        }
        super.doTearDown();

        // this is a complex classloader setup and we can't reproduce standalone Mule 100%,
        // so trick the next test method into thinking it's the first run, otherwise
        // app resets CCL ref to null and breaks the next test
        Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());
    }

    @Test
    public void deploysAppZipOnStartup() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertApplicationAnchorFileExists(dummyAppDescriptor.id);

        // just assert no privileged entries were put in the registry
        final Application app = findApp(dummyAppDescriptor.id, 1);
        final MuleRegistry registry = getMuleRegistry(app);

        // mule-app.properties from the zip archive must have loaded properly
        assertEquals("mule-app.properties should have been loaded.", "someValue", registry.get("myCustomProp"));
    }

    @Test
    public void deploysExplodedAppAndVerifyAnchorFileIsCreatedAfterDeploymentEnds() throws Exception
    {
        Action deployExplodedWaitAppAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                addExplodedAppFromResource(waitAppDescriptor.zipPath);
            }
        };
        deploysAppAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(deployExplodedWaitAppAction);
    }

    @Test
    public void deploysPackagedAppAndVerifyAnchorFileIsCreatedAfterDeploymentEnds() throws Exception
    {
        Action deployPackagedWaitAppAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                addPackedAppFromResource(waitAppDescriptor.zipPath);
            }
        };
        deploysAppAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(deployPackagedWaitAppAction);
    }

    @Test
    public void deploysAppZipAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertApplicationAnchorFileExists(dummyAppDescriptor.id);

        // just assert no privileged entries were put in the registry
        final Application app = findApp(dummyAppDescriptor.id, 1);
        final MuleRegistry registry = getMuleRegistry(app);

        // mule-app.properties from the zip archive must have loaded properly
        assertEquals("mule-app.properties should have been loaded.", "someValue", registry.get("myCustomProp"));
    }

    @Test
    public void deploysBrokenAppZipOnStartup() throws Exception
    {
        addPackedAppFromResource(brokenAppDescriptor.zipPath, brokenAppDescriptor.targetPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, "brokenApp");

        assertAppsDir(new String[] {"brokenApp.zip"}, NONE, true);

        assertApplicationAnchorFileDoesNotExists(brokenAppDescriptor.id);

        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", "brokenApp.zip", new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    /**
     * This tests deploys a broken app which name has a weird character.
     * It verifies that after failing deploying that app, it doesn't try to do it
     * again, which is a behavior than can be seen in some file systems due to
     * path handling issues
     */
    @Test
    public void dontRetryBrokenAppWithFunkyName() throws Exception
    {
        addPackedAppFromResource(brokenAppWithFunkyNameDescriptor.zipPath, brokenAppWithFunkyNameDescriptor.targetPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, "brokenApp+");

        assertAppsDir(new String[] {"brokenApp+.zip"}, NONE, true);

        assertApplicationAnchorFileDoesNotExists(brokenAppDescriptor.id);

        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", "brokenApp+.zip", new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);

        reset(applicationDeploymentListener);

        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertDeploymentFailure(applicationDeploymentListener, "brokenApp+", never());

        addPackedAppFromResource(emptyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);
        assertDeploymentFailure(applicationDeploymentListener, "brokenApp+", never());
    }

    @Test
    public void deploysBrokenAppZipAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(brokenAppDescriptor.zipPath, "brokenApp.zip");

        assertDeploymentFailure(applicationDeploymentListener, "brokenApp");

        assertAppsDir(new String[] {"brokenApp.zip"}, NONE, true);

        assertApplicationAnchorFileDoesNotExists(brokenAppDescriptor.id);

        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", "brokenApp.zip", new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void redeploysAppZipDeployedOnStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());

        reset(applicationDeploymentListener);

        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertUndeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
    }

    @Test
    public void redeploysAppZipDeployedAfterStartup() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());

        reset(applicationDeploymentListener);

        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertUndeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
    }

    @Test
    public void deploysExplodedAppOnStartup() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertApplicationAnchorFileExists(dummyAppDescriptor.id);
    }

    @Test
    public void deploysPackagedAppOnStartupWhenExplodedAppIsAlsoPresent() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath);
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        // Checks that dummy app was deployed just once
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
    }

    @Test
    public void deploysExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertApplicationAnchorFileExists(dummyAppDescriptor.id);
    }

    @Test
    public void deploysInvalidExplodedAppOnStartup() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath, "app with spaces");

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, "app with spaces");

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {"app with spaces"}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "app with spaces", appName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysInvalidExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(dummyAppDescriptor.zipPath, "app with spaces");

        assertDeploymentFailure(applicationDeploymentListener, "app with spaces");

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {"app with spaces"}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "app with spaces", appName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysInvalidExplodedOnlyOnce() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(dummyAppDescriptor.zipPath, "app with spaces");
        assertDeploymentFailure(applicationDeploymentListener, "app with spaces", atLeast(1));

        addExplodedAppFromResource(dummyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        addExplodedAppFromResource(emptyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        // After three update cycles should have only one deployment failure notification for the broken app
        assertDeploymentFailure(applicationDeploymentListener, "app with spaces");
    }

    @Test
    public void deploysBrokenExplodedAppOnStartup() throws Exception
    {
        final URL url = getClass().getResource(incompleteAppDescriptor.zipPath);
        assertNotNull("Test app file not found " + url, url);

        addExplodedAppFromResource(incompleteAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        assertApplicationAnchorFileDoesNotExists(incompleteAppDescriptor.id);

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {incompleteAppDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysBrokenExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        assertApplicationAnchorFileDoesNotExists(incompleteAppDescriptor.id);

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {incompleteAppDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void redeploysExplodedAppOnStartup() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        reset(applicationDeploymentListener);

        File configFile = new File(appsDir + dummyAppDescriptor.path, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(configFile.lastModified() + 1000);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
    }

    @Test
    public void redeploysExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        reset(applicationDeploymentListener);

        File configFile = new File(appsDir + dummyAppDescriptor.path, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(configFile.lastModified() + 1000);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
    }

    @Test
    public void redeploysBrokenExplodedAppOnStartup() throws Exception
    {
        addExplodedAppFromResource(incompleteAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {incompleteAppDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);

        reset(applicationDeploymentListener);

        File configFile = new File(appsDir + incompleteAppDescriptor.path, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(configFile.lastModified() + 1000);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);
    }

    @Test
    public void redeploysBrokenExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Maintains app dir created
        assertAppsDir(NONE, new String[] {incompleteAppDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);

        reset(applicationDeploymentListener);

        File configFile = new File(appsDir + incompleteAppDescriptor.path, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(configFile.lastModified() + 1000);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);
    }

    @Test
    public void redeploysInvalidExplodedAppAfterSuccessfulDeploymentOnStartup() throws IOException, URISyntaxException
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath, dummyAppDescriptor.id);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        reset(applicationDeploymentListener);

        File originalConfigFile = new File(appsDir + dummyAppDescriptor.path, MULE_CONFIG_XML_FILE);
        URL url = getClass().getResource(brokenDomainDescriptor.configFilePath);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);

        assertDeploymentFailure(applicationDeploymentListener, dummyAppDescriptor.id);
        assertStatus(dummyAppDescriptor.id, ApplicationStatus.DEPLOYMENT_FAILED);
    }

    @Test
    public void redeploysInvalidExplodedAppAfterSuccessfulDeploymentAfterStartup() throws IOException, URISyntaxException
    {
        deploymentService.start();

        addExplodedAppFromResource(dummyAppDescriptor.zipPath, dummyAppDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        reset(applicationDeploymentListener);

        File originalConfigFile = new File(appsDir + dummyAppDescriptor.path, MULE_CONFIG_XML_FILE);
        URL url = getClass().getResource(brokenDomainDescriptor.configFilePath);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);

        assertDeploymentFailure(applicationDeploymentListener, dummyAppDescriptor.id);
        assertStatus(dummyAppDescriptor.id, ApplicationStatus.DEPLOYMENT_FAILED);
    }

    @Test
    public void redeploysFixedAppAfterBrokenExplodedAppOnStartup() throws Exception
    {
        addExplodedAppFromResource(incompleteAppDescriptor.zipPath, incompleteAppDescriptor.id);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        reset(applicationDeploymentListener);

        File originalConfigFile = new File(appsDir + incompleteAppDescriptor.path, MULE_CONFIG_XML_FILE);
        URL url = getClass().getResource(emptyDomainDescriptor.configFilePath);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, incompleteAppDescriptor.id);

        addPackedAppFromResource(dummyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Check that the failed application folder is still there
        assertAppFolderIsMaintained(incompleteAppDescriptor.id);
    }

    @Test
    public void redeploysFixedAppAfterBrokenExplodedAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedAppFromResource(incompleteAppDescriptor.zipPath, incompleteAppDescriptor.id);


        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        reset(applicationDeploymentListener);

        File originalConfigFile = new File(appsDir + incompleteAppDescriptor.path, MULE_CONFIG_XML_FILE);
        URL url = getClass().getResource(emptyDomainDescriptor.configFilePath);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, incompleteAppDescriptor.id);

        addPackedAppFromResource(dummyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Check that the failed application folder is still there
        assertAppFolderIsMaintained(incompleteAppDescriptor.id);
    }

    @Test
    public void redeploysZipAppOnConfigChanges() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());

        reset(applicationDeploymentListener);

        File configFile = new File(appsDir + dummyAppDescriptor.path, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(configFile.lastModified() + 1000);

        assertUndeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);
    }

    @Test
    public void testBrokenAppArchiveWithoutArgument() throws Exception
    {
        doBrokenAppArchiveTest();
    }

    @Test
    public void testBrokenAppArchiveAsArgument() throws Exception
    {
        Map<String, Object> startupOptions = new HashMap<String, Object>();
        startupOptions.put("app", brokenAppDescriptor.id);
        StartupContext.get().setStartupOptions(startupOptions);

        doBrokenAppArchiveTest();
    }

    @Test
    public void deploysInvalidZipAppOnStartup() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath, "app with spaces.zip");

        deploymentService.start();
        assertDeploymentFailure(applicationDeploymentListener, "app with spaces");

        // zip stays intact, no app dir created
        assertAppsDir(new String[] {"app with spaces.zip"}, NONE, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "app with spaces.zip", appName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysInvalidZipAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(emptyAppDescriptor.zipPath, "app with spaces.zip");

        assertDeploymentFailure(applicationDeploymentListener, "app with spaces");

        // zip stays intact, no app dir created
        assertAppsDir(new String[] {"app with spaces.zip"}, NONE, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "app with spaces.zip", appName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void testDeployAppNameWithZipSuffix() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath, "empty-app.zip.zip");

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.targetPath);
        reset(applicationDeploymentListener);

        assertAppsDir(NONE, new String[] {emptyAppDescriptor.targetPath}, true);
        assertEquals("Application has not been properly registered with Mule", 1, deploymentService.getApplications().size());

        // Checks that the empty-app.zip folder is not processed as a zip file
        assertNoDeploymentInvoked(applicationDeploymentListener);
    }

    @Test
    public void deploysPackedAppsInOrderWhenAppArgumentIsUsed() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath, "1.zip");
        addPackedAppFromResource(emptyAppDescriptor.zipPath, "2.zip");
        addPackedAppFromResource(emptyAppDescriptor.zipPath, "3.zip");

        Map<String, Object> startupOptions = new HashMap<String, Object>();
        startupOptions.put("app", "3:1:2");
        StartupContext.get().setStartupOptions(startupOptions);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, "1");
        assertApplicationDeploymentSuccess(applicationDeploymentListener, "2");
        assertApplicationDeploymentSuccess(applicationDeploymentListener, "3");
        assertAppsDir(NONE, new String[] {"1", "2", "3"}, true);

        // When apps are passed as -app app1:app2:app3 the startup order matters
        List<Application> applications = deploymentService.getApplications();
        assertNotNull(applications);
        assertEquals(3, applications.size());
        assertEquals("3", applications.get(0).getArtifactName());
        assertEquals("1", applications.get(1).getArtifactName());
        assertEquals("2", applications.get(2).getArtifactName());
    }

    @Test
    public void deploysExplodedAppsInOrderWhenAppArgumentIsUsed() throws Exception
    {
        addExplodedAppFromResource(emptyAppDescriptor.zipPath, "1");
        addExplodedAppFromResource(emptyAppDescriptor.zipPath, "2");
        addExplodedAppFromResource(emptyAppDescriptor.zipPath, "3");

        Map<String, Object> startupOptions = new HashMap<String, Object>();
        startupOptions.put("app", "3:1:2");
        StartupContext.get().setStartupOptions(startupOptions);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, "1");
        assertApplicationDeploymentSuccess(applicationDeploymentListener, "2");
        assertApplicationDeploymentSuccess(applicationDeploymentListener, "3");

        assertAppsDir(NONE, new String[] {"1", "2", "3"}, true);

        // When apps are passed as -app app1:app2:app3 the startup order matters
        List<Application> applications = deploymentService.getApplications();
        assertNotNull(applications);
        assertEquals(3, applications.size());
        assertEquals("3", applications.get(0).getArtifactName());
        assertEquals("1", applications.get(1).getArtifactName());
        assertEquals("2", applications.get(2).getArtifactName());
    }

    @Test
    public void deploysAppJustOnce() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        Map<String, Object> startupOptions = new HashMap<String, Object>();
        startupOptions.put("app", "dummy-app:dummy-app:dummy-app");
        StartupContext.get().setStartupOptions(startupOptions);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        List<Application> applications = deploymentService.getApplications();
        assertEquals(1, applications.size());
    }

    @Test
    public void tracksAppConfigUpdateTime() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        // Sets a modification time in the future
        File appFolder = new File(appsDir.getPath(), dummyAppDescriptor.id);
        File configFile = new File(appFolder, MULE_CONFIG_XML_FILE);
        configFile.setLastModified(System.currentTimeMillis() + ONE_HOUR_IN_MILLISECONDS);

        deploymentService.start();
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        reset(applicationDeploymentListener);

        assertNoDeploymentInvoked(applicationDeploymentListener);
    }

    @Test
    public void redeployedFailedAppAfterTouched() throws Exception
    {
        addExplodedAppFromResource(dummyAppDescriptor.zipPath);

        File appFolder = new File(appsDir.getPath(), dummyAppDescriptor.id);

        File configFile = new File(appFolder, MULE_CONFIG_XML_FILE);
        FileUtils.writeStringToFile(configFile, "you shall not pass");

        deploymentService.start();
        assertDeploymentFailure(applicationDeploymentListener, dummyAppDescriptor.id);
        reset(applicationDeploymentListener);

        URL url = getClass().getResource(emptyDomainDescriptor.configFilePath);
        FileUtils.copyFile(new File(url.toURI()), configFile);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
    }

    @Test
    public void receivesMuleContextDeploymentNotifications() throws Exception
    {
        // NOTE: need an integration test like this because DefaultMuleApplication
        // class cannot be unit tested.
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertMuleContextCreated(applicationDeploymentListener, dummyAppDescriptor.id);
        assertMuleContextInitialized(applicationDeploymentListener, dummyAppDescriptor.id);
        assertMuleContextConfigured(applicationDeploymentListener, dummyAppDescriptor.id);
    }

    @Test
    public void undeploysStoppedApp() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        final Application app = findApp(dummyAppDescriptor.id, 1);
        app.stop();
        assertStatus(app, ApplicationStatus.STOPPED);

        deploymentService.undeploy(app);
    }

    @Test
    public void undeploysApplicationRemovingAnchorFile() throws Exception
    {
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        Application app = findApp(dummyAppDescriptor.id, 1);

        assertTrue("Unable to remove anchor file", removeAppAnchorFile(dummyAppDescriptor.id));

        assertUndeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertStatus(app, ApplicationStatus.DESTROYED);
    }

    @Test
    public void undeploysAppCompletelyEvenOnStoppingException() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        TestApplicationFactory appFactory = new TestApplicationFactory(new MuleApplicationClassLoaderFactory(new MuleDomainClassLoaderRepository(), new DefaultNativeLibraryFinderFactory()));
        appFactory.setFailOnStopApplication(true);

        deploymentService.setAppFactory(appFactory);
        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);
        Application app = findApp(emptyAppDescriptor.id, 1);

        assertTrue("Unable to remove anchor file", removeAppAnchorFile(emptyAppDescriptor.id));

        assertUndeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        assertAppFolderIsDeleted(emptyAppDescriptor.id);
        assertStatus(app, ApplicationStatus.DESTROYED);
    }

    @Test
    public void undeploysAppCompletelyEvenOnDisposingException() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        TestApplicationFactory appFactory = new TestApplicationFactory(new MuleApplicationClassLoaderFactory(new MuleDomainClassLoaderRepository(), new DefaultNativeLibraryFinderFactory()));
        appFactory.setFailOnDisposeApplication(true);
        deploymentService.setAppFactory(appFactory);
        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);
        Application app = findApp(emptyAppDescriptor.id, 1);

        assertTrue("Unable to remove anchor file", removeAppAnchorFile(emptyAppDescriptor.id));

        assertUndeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);
        assertStatus(app, ApplicationStatus.STOPPED);
        assertAppFolderIsDeleted(emptyAppDescriptor.id);
    }

    @Test
    public void deploysIncompleteZipAppOnStartup() throws Exception
    {
        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Check that the failed application folder is still there
        assertAppFolderIsMaintained(incompleteAppDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void deploysIncompleteZipAppAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Check that the failed application folder is still there
        assertAppFolderIsMaintained(incompleteAppDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void mantainsAppFolderOnExplodedAppDeploymentError() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Check that the failed application folder is still there
        assertAppFolderIsMaintained(incompleteAppDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysZipAppAfterDeploymentErrorOnStartup() throws Exception
    {
        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(emptyAppDescriptor.zipPath, incompleteAppDescriptor.targetPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, incompleteAppDescriptor.id);

        assertEquals("Failed app still appears as zombie after a successful redeploy", 0, deploymentService.getZombieApplications().size());
    }

    @Test
    public void redeploysZipAppAfterDeploymentErrorAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(emptyAppDescriptor.zipPath, incompleteAppDescriptor.targetPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, incompleteAppDescriptor.id);

        assertEquals("Failed app still appears as zombie after a successful redeploy", 0, deploymentService.getZombieApplications().size());
    }

    @Test
    public void redeploysInvalidZipAppAfterSuccessfulDeploymentOnStartup() throws IOException
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        deploymentService.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        addPackedAppFromResource(incompleteAppDescriptor.zipPath, emptyAppDescriptor.targetPath);

        assertDeploymentFailure(applicationDeploymentListener, emptyAppDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", emptyAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipAppAfterSuccessfulDeploymentAfterStartup() throws IOException
    {
        deploymentService.start();

        addPackedAppFromResource(emptyAppDescriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        addPackedAppFromResource(incompleteAppDescriptor.zipPath, emptyAppDescriptor.targetPath);
        assertDeploymentFailure(applicationDeploymentListener, emptyAppDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", emptyAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipAppAfterFailedDeploymentOnStartup() throws IOException
    {
        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        reset(applicationDeploymentListener);

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipAppAfterFailedDeploymentAfterStartup() throws IOException
    {
        deploymentService.start();

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);
        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        reset(applicationDeploymentListener);

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);
        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieApplications().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteAppDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysExplodedAppAfterDeploymentError() throws Exception
    {
        deploymentService.start();

        addPackedAppFromResource(incompleteAppDescriptor.zipPath);

        assertDeploymentFailure(applicationDeploymentListener, incompleteAppDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedAppFromResource(dummyAppDescriptor.zipPath);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);

        // Redeploys a fixed version for incompleteApp
        addExplodedAppFromResource(emptyAppDescriptor.zipPath, incompleteAppDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, incompleteAppDescriptor.id);
        assertEquals("Failed app still appears as zombie after a successful redeploy", 0, deploymentService.getZombieApplications().size());
    }

    @Test
    public void synchronizesDeploymentOnStart() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        Thread deploymentServiceThread = new Thread(new Runnable()
        {
            public void run()
            {
                deploymentService.start();
            }
        });

        final boolean[] lockedFromClient = new boolean[1];

        Mockito.doAnswer(new Answer()
        {
            public Object answer(InvocationOnMock invocation) throws Throwable
            {

                Thread deploymentClientThread = new Thread(new Runnable()
                {
                    public void run()
                    {
                        ReentrantLock deploymentLock = deploymentService.getLock();

                        try
                        {
                            try
                            {
                                lockedFromClient[0] = deploymentLock.tryLock(1000, TimeUnit.MILLISECONDS);
                            }
                            catch (InterruptedException e)
                            {
                                // Ignore
                            }
                        }
                        finally
                        {
                            if (deploymentLock.isHeldByCurrentThread())
                            {
                                deploymentLock.unlock();
                            }
                        }
                    }
                });

                deploymentClientThread.start();
                deploymentClientThread.join();

                return null;
            }
        }).when(applicationDeploymentListener).onDeploymentStart(emptyAppDescriptor.id);

        deploymentServiceThread.start();

        assertApplicationDeploymentSuccess(applicationDeploymentListener, emptyAppDescriptor.id);

        assertFalse("Able to lock deployment service during start", lockedFromClient[0]);
    }

    @Test
    public void deploysDomainZipOnStartup() throws Exception
    {
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);

        final Domain domain = findADomain(dummyDomainDescriptor.id, 1);
        assertNotNull(domain);
        assertNotNull(domain.getMuleContext());
        assertDomainAnchorFileExists(dummyDomainDescriptor.id);
    }

    @Test
    public void deploysPackagedDomainAndVerifyAnchorFileIsCreatedAfterDeploymentEnds() throws Exception
    {
        Action deployPackagedWaitDomainAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                addPackedDomainFromResource(waitDomainDescriptor.zipPath);
            }
        };
        deploysDomainAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(deployPackagedWaitDomainAction);
    }

    @Test
    public void deploysExplodedDomainAndVerifyAnchorFileIsCreatedAfterDeploymentEnds() throws Exception
    {
        Action deployExplodedWaitDomainAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                addExplodedDomainFromResource(waitDomainDescriptor.zipPath);
            }
        };
        deploysDomainAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(deployExplodedWaitDomainAction);
    }

    @Test
    public void deploysExplodedDomainBundleOnStartup() throws Exception
    {
        addExplodedDomainFromResource(dummyDomainBundleDescriptor.zipPath);

        deploymentService.start();

        deploysDomainBundle();
    }

    @Test
    public void deploysExplodedDomainBundleAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(dummyDomainBundleDescriptor.zipPath);

        deploysDomainBundle();
    }

    @Test
    public void deploysDomainBundleZipOnStartup() throws Exception
    {
        addPackedDomainFromResource(dummyDomainBundleDescriptor.zipPath);

        deploymentService.start();

        deploysDomainBundle();
    }

    @Test
    public void deploysDomainBundleZipAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(dummyDomainBundleDescriptor.zipPath);

        deploysDomainBundle();
    }

    private void deploysDomainBundle()
    {
        assertDeploymentSuccess(domainDeploymentListener, dummyDomainBundleDescriptor.id);

        assertDomainDir(NONE, new String[] {dummyDomainBundleDescriptor.id}, true);

        final Domain domain = findADomain(dummyDomainBundleDescriptor.id, 1);
        assertNotNull(domain);
        assertNull(domain.getMuleContext());

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyAppDescriptor.id);
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, true);

        final Application app = findApp(dummyAppDescriptor.id, 1);
        assertNotNull(app);
    }

    @Test
    public void deploysInvalidExplodedDomainBundleOnStartup() throws Exception
    {
        addExplodedDomainFromResource(invalidDomainBundle.zipPath);

        deploymentService.start();

        deploysInvalidDomainBundleZip();
    }

    @Test
    public void deploysInvalidExplodedDomainBundleAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(invalidDomainBundle.zipPath);

        deploysInvalidDomainBundleZip();
    }

    @Test
    public void deploysInvalidDomainBundleZipOnStartup() throws Exception
    {
        addPackedDomainFromResource(invalidDomainBundle.zipPath);

        deploymentService.start();

        deploysInvalidDomainBundleZip();
    }

    @Test
    public void deploysInvalidDomainBundleZipAfterStartup() throws Exception
    {
        addPackedDomainFromResource(invalidDomainBundle.zipPath);

        deploymentService.start();

        deploysInvalidDomainBundleZip();
    }

    private void deploysInvalidDomainBundleZip()
    {
        assertDeploymentFailure(domainDeploymentListener, invalidDomainBundle.id);

        assertDomainDir(NONE, new String[] {invalidDomainBundle.id}, true);

        assertAppsDir(NONE, new String[] {}, true);
    }

    @Test
    public void deploysDomainZipAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);

        final Domain domain = findADomain(dummyDomainDescriptor.id, 1);
        assertNotNull(domain);
        assertNotNull(domain.getMuleContext());
        assertDomainAnchorFileExists(dummyDomainDescriptor.id);
    }

    @Test
    public void deploysBrokenDomainZipOnStartup() throws Exception
    {
        addPackedDomainFromResource(brokenDomainDescriptor.zipPath, brokenDomainDescriptor.targetPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, brokenDomainDescriptor.id);

        assertDomainDir(new String[] {brokenDomainDescriptor.targetPath}, NONE, true);

        assertDomainAnchorFileDoesNotExists(brokenDomainDescriptor.id);

        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as domain", brokenDomainDescriptor.targetPath, new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysBrokenDomainZipAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(brokenDomainDescriptor.zipPath, brokenDomainDescriptor.targetPath);

        assertDeploymentFailure(domainDeploymentListener, brokenDomainDescriptor.id);

        assertDomainDir(new String[] {brokenDomainDescriptor.targetPath}, NONE, true);

        assertDomainAnchorFileDoesNotExists(brokenDomainDescriptor.id);

        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as domain.", brokenDomainDescriptor.targetPath, new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void redeploysDomainZipDeployedOnStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
        assertEquals("Domain has not been properly registered with Mule", 1, deploymentService.getDomains().size());

        reset(domainDeploymentListener);

        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertUndeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertEquals("Domain has not been properly registered with Mule", 1, deploymentService.getDomains().size());
        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
    }

    @Test
    public void redeploysDomainZipDeployedAfterStartup() throws Exception
    {
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
        assertEquals("Domain has not been properly registered with Mule", 1, deploymentService.getDomains().size());

        reset(domainDeploymentListener);

        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertUndeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertEquals("Domain has not been properly registered with Mule", 1, deploymentService.getDomains().size());
        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
    }

    @Test
    public void deploysExplodedDomainOnStartup() throws Exception
    {
        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
        assertDomainAnchorFileExists(dummyDomainDescriptor.id);
    }

    @Test
    public void deploysPackagedDomainOnStartupWhenExplodedDomainIsAlsoPresent() throws Exception
    {
        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        addExplodedDomainFromResource(emptyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        // Checks that dummy app was deployed just once
        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
    }

    @Test
    public void deploysExplodedDomainAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, true);
        assertDomainAnchorFileExists(dummyDomainDescriptor.id);
    }

    @Test
    public void deploysInvalidExplodedDomainOnStartup() throws Exception
    {
        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath, "domain with spaces");

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, "domain with spaces");

        // Maintains app dir created
        assertDomainDir(NONE, new String[] {"domain with spaces"}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String domainName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "domain with spaces", domainName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysInvalidExplodedDomainAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath, "domain with spaces");

        assertDeploymentFailure(domainDeploymentListener, "domain with spaces");

        // Maintains app dir created
        assertDomainDir(NONE, new String[] {"domain with spaces"}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        // Spaces are converted to %20 is returned by java file api :/
        String appName = URLDecoder.decode(new File(zombie.getKey().getFile()).getName(), "UTF-8");
        assertEquals("Wrong URL tagged as zombie.", "domain with spaces", appName);
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysInvalidExplodedDomainOnlyOnce() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath, "domain with spaces");
        assertDeploymentFailure(domainDeploymentListener, "domain with spaces", atLeast(1));

        addExplodedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);
        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        addExplodedDomainFromResource(emptyDomainDescriptor.zipPath);
        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        // After three update cycles should have only one deployment failure notification for the broken app
        assertDeploymentFailure(domainDeploymentListener, "domain with spaces");
    }

    @Test
    public void deploysBrokenExplodedDomainOnStartup() throws Exception
    {
        final URL url = getClass().getResource(incompleteDomainDescriptor.zipPath);
        assertNotNull("Test app file not found " + url, url);

        addExplodedDomainFromResource(incompleteDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Maintains app dir created
        assertDomainDir(NONE, new String[] {incompleteDomainDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void deploysBrokenExplodedDomainAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Maintains app dir created
        assertDomainDir(NONE, new String[] {incompleteDomainDescriptor.id}, true);
        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);
    }

    @Test
    public void receivesDomainMuleContextDeploymentNotifications() throws Exception
    {
        // NOTE: need an integration test like this because DefaultMuleApplication
        // class cannot be unit tested.
        addPackedDomainFromResource(httpSharedDomainBundle.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, httpSharedDomainBundle.id);
        assertMuleContextCreated(domainDeploymentListener, httpSharedDomainBundle.id);
        assertMuleContextInitialized(domainDeploymentListener, httpSharedDomainBundle.id);
        assertMuleContextConfigured(domainDeploymentListener, httpSharedDomainBundle.id);
    }

    @Test
    public void undeploysStoppedDomain() throws Exception
    {
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
        final Domain domain = findADomain(dummyDomainDescriptor.id, 1);
        domain.stop();

        deploymentService.undeploy(domain);
    }

    @Test
    public void undeploysDomainRemovingAnchorFile() throws Exception
    {
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertTrue("Unable to remove anchor file", removeDomainAnchorFile(dummyDomainDescriptor.id));

        assertUndeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
    }

    @Test
    public void undeploysDomainAndDomainsApps() throws Exception
    {
        doDomainUndeployAndVerifyAppsAreUndeployed(new Action()
        {
            @Override
            public void perform()
            {
                Domain domain = findADomain(dummyDomainDescriptor.id, 1);
                deploymentService.undeploy(domain);
            }
        });
    }

    @Test
    public void undeploysDomainAndDomainsAppsRemovingAnchorFile() throws Exception
    {
        doDomainUndeployAndVerifyAppsAreUndeployed(createUndeployDummyDomainAction());
    }

    @Test
    public void undeployDomainDoesNotDeployAllApplications() throws Exception
    {
        addPackedAppFromResource(emptyAppDescriptor.zipPath);

        doDomainUndeployAndVerifyAppsAreUndeployed(createUndeployDummyDomainAction());

        assertThat(findApp(emptyAppDescriptor.id, 1), notNullValue());
    }

    @Test(expected = IllegalArgumentException.class)
    public void findDomainApplicationsWillNullDomainFails()
    {
        deploymentService.findDomainApplications(null);
    }

    @Test
    public void findDomainApplicationsWillNonExistentDomainReturnsEmptyCollection()
    {
        Collection<Application> domainApplications = deploymentService.findDomainApplications("");
        assertThat(domainApplications, notNullValue());
        assertThat(domainApplications.isEmpty(), is(true));
    }

    @Test
    public void undeploysDomainCompletelyEvenOnStoppingException() throws Exception
    {
        addPackedDomainFromResource(emptyDomainDescriptor.zipPath);

        TestDomainFactory testDomainFactory = new TestDomainFactory(new MuleDomainClassLoaderRepository());
        testDomainFactory.setFailOnStopApplication();

        deploymentService.setDomainFactory(testDomainFactory);
        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        assertTrue("Unable to remove anchor file", removeDomainAnchorFile(emptyDomainDescriptor.id));

        assertUndeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        assertAppFolderIsDeleted(emptyDomainDescriptor.id);
    }

    @Test
    public void undeploysDomainCompletelyEvenOnDisposingException() throws Exception
    {
        addPackedDomainFromResource(emptyDomainDescriptor.zipPath);

        TestDomainFactory testDomainFactory = new TestDomainFactory(new MuleDomainClassLoaderRepository());
        testDomainFactory.setFailOnDisposeApplication();
        deploymentService.setDomainFactory(testDomainFactory);
        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        assertTrue("Unable to remove anchor file", removeDomainAnchorFile(emptyDomainDescriptor.id));

        assertUndeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        assertAppFolderIsDeleted(emptyDomainDescriptor.id);
    }

    @Test
    public void deploysIncompleteZipDomainOnStartup() throws Exception
    {
        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Check that the failed application folder is still there
        assertDomainFolderIsMaintained(incompleteDomainDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void deploysIncompleteZipDomainAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Check that the failed application folder is still there
        assertDomainFolderIsMaintained(incompleteDomainDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void mantainsDomainFolderOnExplodedAppDeploymentError() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Check that the failed application folder is still there
        assertDomainFolderIsMaintained(incompleteDomainDescriptor.id);
        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysZipDomainAfterDeploymentErrorOnStartup() throws Exception
    {
        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(emptyDomainDescriptor.zipPath, incompleteDomainDescriptor.targetPath);
        assertDeploymentSuccess(domainDeploymentListener, incompleteDomainDescriptor.id);

        assertEquals("Failed domain still appears as zombie after a successful redeploy", 0, deploymentService.getZombieDomains().size());
    }

    @Test
    public void redeploysZipDomainAfterDeploymentErrorAfterStartup() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(emptyDomainDescriptor.zipPath, incompleteDomainDescriptor.targetPath);
        assertDeploymentSuccess(domainDeploymentListener, incompleteDomainDescriptor.id);

        assertEquals("Failed domain still appears as zombie after a successful redeploy", 0, deploymentService.getZombieDomains().size());
    }

    @Test
    public void redeploysInvalidZipDomainAfterSuccessfulDeploymentOnStartup() throws IOException
    {
        addPackedDomainFromResource(emptyDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath, emptyDomainDescriptor.targetPath);

        assertDeploymentFailure(domainDeploymentListener, emptyDomainDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", emptyDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipDomainAfterSuccessfulDeploymentAfterStartup() throws IOException
    {
        deploymentService.start();

        addPackedDomainFromResource(emptyDomainDescriptor.zipPath);
        assertDeploymentSuccess(domainDeploymentListener, emptyDomainDescriptor.id);

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath, emptyDomainDescriptor.targetPath);
        assertDeploymentFailure(domainDeploymentListener, emptyDomainDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", emptyDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipDomainAfterFailedDeploymentOnStartup() throws IOException
    {
        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        reset(domainDeploymentListener);

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysInvalidZipDomainAfterFailedDeploymentAfterStartup() throws IOException
    {
        deploymentService.start();

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);
        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        reset(domainDeploymentListener);

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);
        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        final Map.Entry<URL, Long> zombie = deploymentService.getZombieDomains().entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", incompleteDomainDescriptor.id, new File(zombie.getKey().getFile()).getParentFile().getName());
    }

    @Test
    public void redeploysExplodedDomainAfterDeploymentError() throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(incompleteDomainDescriptor.zipPath);

        assertDeploymentFailure(domainDeploymentListener, incompleteDomainDescriptor.id);

        // Deploys another app to confirm that DeploymentService has execute the updater thread
        addPackedDomainFromResource(DeploymentServiceTestCase.dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        // Redeploys a fixed version for incompleteDomain
        addExplodedDomainFromResource(emptyDomainDescriptor.zipPath, incompleteDomainDescriptor.id);

        assertDeploymentSuccess(domainDeploymentListener, incompleteDomainDescriptor.id);
        assertEquals("Failed domain still appears as zombie after a successful redeploy", 0, deploymentService.getZombieDomains().size());
    }

    private Action createUndeployDummyDomainAction()
    {
        return new Action()
        {
            @Override
            public void perform()
            {
                removeDomainAnchorFile(dummyDomainDescriptor.id);
            }
        };
    }

    private void doDomainUndeployAndVerifyAppsAreUndeployed(Action undeployAction) throws Exception
    {
        deploymentService.start();

        addPackedDomainFromResource(dummyDomainDescriptor.zipPath);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        addPackedAppFromResource(dummyDomainApp1Descriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);

        addPackedAppFromResource(dummyDomainApp2Descriptor.zipPath);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);

        undeployAction.perform();

        assertDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);
        assertUndeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);
    }


    @Test
    public void redeploysFixedDomainAfterBrokenExplodedDomainOnStartup() throws Exception
    {
        addExplodedDomainFromResource("/incompleteDomain.zip", "incompleteDomain");

        deploymentService.start();

        doRedeployFixedDomainAfterBrokenDomain();
    }

    @Test
    public void redeploysFixedDomainAfterBrokenExplodedDomainAfterStartup() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource("/incompleteDomain.zip", "incompleteDomain");

        doRedeployFixedDomainAfterBrokenDomain();
    }

    @Test
    public void redeploysDomainAndItsApplications() throws Exception
    {
        addExplodedDomainFromResource(dummyDomainDescriptor.zipPath, dummyDomainDescriptor.id);

        addExplodedAppFromResource(dummyDomainApp1Descriptor.zipPath, dummyDomainApp1Descriptor.id);
        addExplodedAppFromResource(dummyDomainApp2Descriptor.zipPath, dummyDomainApp2Descriptor.id);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);

        reset(domainDeploymentListener);
        reset(applicationDeploymentListener);

        doRedeployDummyDomainByChangingConfigFileWithGoodOne();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);
    }

    @Test
    public void redeploysDomainAndFails() throws Exception
    {
        addExplodedDomainFromResource(dummyDomainDescriptor.zipPath, dummyDomainDescriptor.id);

        addExplodedAppFromResource(dummyDomainApp1Descriptor.zipPath, dummyDomainApp1Descriptor.id);
        addExplodedAppFromResource(dummyDomainApp2Descriptor.zipPath, dummyDomainApp2Descriptor.id);

        deploymentService.start();

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);

        reset(domainDeploymentListener);
        reset(applicationDeploymentListener);

        doRedeployDummyDomainByChangingConfigFileWithBadOne();

        assertDeploymentFailure(domainDeploymentListener, dummyDomainDescriptor.id);

        assertNoDeploymentInvoked(applicationDeploymentListener);
    }

    @Test
    public void redeploysDomainWithOneApplicationFailedOnFirstDeployment() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(dummyDomainDescriptor.zipPath, dummyDomainDescriptor.id);

        addExplodedAppFromResource(dummyDomainApp1Descriptor.zipPath, dummyDomainApp1Descriptor.id);
        addExplodedAppFromResource(dummyDomainApp2Descriptor.zipPath, dummyDomainApp2Descriptor.id);
        addExplodedAppFromResource(dummyDomainApp3Descriptor.zipPath, dummyDomainApp3Descriptor.id);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);
        assertDeploymentFailure(applicationDeploymentListener, dummyDomainApp3Descriptor.id);

        reset(domainDeploymentListener);
        reset(applicationDeploymentListener);

        deploymentService.getLock().lock();
        try
        {
            doRedeployDummyDomainByChangingConfigFileWithGoodOne();
            doRedeployAppByChangingConfigFileWithGoodOne(dummyDomainApp3Descriptor.path);
        }
        finally
        {
            deploymentService.getLock().unlock();
        }

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);
        assertDeploymentSuccess(applicationDeploymentListener, dummyDomainApp3Descriptor.id);
    }

    @Test
    public void redeploysDomainWithOneApplicationFailedAfterRedeployment() throws Exception
    {
        deploymentService.start();

        addExplodedDomainFromResource(dummyDomainDescriptor.zipPath, dummyDomainDescriptor.id);

        addExplodedAppFromResource(dummyDomainApp1Descriptor.zipPath, dummyDomainApp1Descriptor.id);
        addExplodedAppFromResource(dummyDomainApp2Descriptor.zipPath, dummyDomainApp2Descriptor.id);

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp2Descriptor.id);

        reset(domainDeploymentListener);
        reset(applicationDeploymentListener);

        deploymentService.getLock().lock();
        try
        {
            doRedeployDummyDomainByChangingConfigFileWithGoodOne();
            doRedeployAppByChangingConfigFileWithBadOne(dummyDomainApp2Descriptor.path);
        }
        finally
        {
            deploymentService.getLock().unlock();
        }

        assertDeploymentSuccess(domainDeploymentListener, dummyDomainDescriptor.id);

        assertApplicationDeploymentSuccess(applicationDeploymentListener, dummyDomainApp1Descriptor.id);
        assertDeploymentFailure(applicationDeploymentListener, dummyDomainApp2Descriptor.id);
    }

    private void doRedeployAppByChangingConfigFileWithGoodOne(String applicationPath) throws Exception
    {
        doRedeployAppByChangingConfigFile(applicationPath, EMPTY_APP_CONFIG_XML);
    }

    private void doRedeployAppByChangingConfigFileWithBadOne(String applicationPath) throws Exception
    {
        doRedeployAppByChangingConfigFile(applicationPath, BAD_APP_CONFIG_XML);
    }

    private void doRedeployAppByChangingConfigFile(String applicationPath, String configFile) throws Exception
    {
        File originalConfigFile = new File(appsDir + applicationPath, MULE_CONFIG_XML_FILE);
        URL url = getClass().getResource(configFile);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);
    }

    private void doRedeployDummyDomainByChangingConfigFileWithGoodOne() throws URISyntaxException, IOException
    {
        doRedeployDummyDomainByChangingConfigFile("/empty-domain-config.xml");
    }

    private void doRedeployDummyDomainByChangingConfigFileWithBadOne() throws URISyntaxException, IOException
    {
        doRedeployDummyDomainByChangingConfigFile("/bad-domain-config.xml");
    }

    private void doRedeployDummyDomainByChangingConfigFile(String configFile) throws URISyntaxException, IOException
    {
        File originalConfigFile = new File(domainsDir + dummyDomainDescriptor.path, dummyDomainDescriptor.configFilePath);
        URL url = getClass().getResource(configFile);
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);
    }

    private void doRedeployFixedDomainAfterBrokenDomain() throws URISyntaxException, IOException
    {
        assertDeploymentFailure(domainDeploymentListener, "incompleteDomain");

        reset(domainDeploymentListener);

        File originalConfigFile = new File(domainsDir + "/incompleteDomain", "mule-domain-config.xml");
        URL url = getClass().getResource("/empty-domain-config.xml");
        File newConfigFile = new File(url.toURI());
        FileUtils.copyFile(newConfigFile, originalConfigFile);
        assertDeploymentSuccess(domainDeploymentListener, "incompleteDomain");

        addPackedDomainFromResource("/dummy-domain.zip");
        assertDeploymentSuccess(domainDeploymentListener, "dummy-domain");

        // Check that the failed application folder is still there
        assertDomainFolderIsMaintained("incompleteDomain");
    }

    public void doBrokenAppArchiveTest() throws Exception
    {
        addPackedAppFromResource(brokenAppDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(applicationDeploymentListener, brokenAppDescriptor.id);
        reset(applicationDeploymentListener);

        // let the file system's write-behind cache commit the delete operation?
        Thread.sleep(1000);

        // zip stays intact, no app dir created
        assertAppsDir(new String[] {"broken-app.zip"}, NONE, true);
        // don't assert dir contents, we want to check internal deployer state next
        assertAppsDir(NONE, new String[] {dummyAppDescriptor.id}, false);
        assertEquals("No apps should have been registered with Mule.", 0, deploymentService.getApplications().size());
        final Map<URL, Long> zombieMap = deploymentService.getZombieApplications();
        assertEquals("Wrong number of zombie apps registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", "broken-app.zip", new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);

        // Checks that the invalid zip was not deployed again
        try
        {
            assertDeploymentFailure(applicationDeploymentListener, "broken-app.zip");
            fail("Install was invoked again for the broken application file");
        }
        catch (AssertionError expected)
        {
        }
    }

    public void doBrokenDomainArchiveTest() throws Exception
    {
        addPackedDomainFromResource(brokenDomainDescriptor.zipPath);

        deploymentService.start();

        assertDeploymentFailure(domainDeploymentListener, "broken-domain");
        reset(domainDeploymentListener);

        // let the file system's write-behind cache commit the delete operation?
        Thread.sleep(1000);

        // zip stays intact, no app dir created
        assertDomainDir(new String[] {"broken-domain.zip"}, NONE, true);
        // don't assert dir contents, we want to check internal deployer state next
        assertDomainDir(NONE, new String[] {dummyDomainDescriptor.id}, false);
        assertEquals("No domains should have been registered with Mule.", 0, deploymentService.getDomains().size());
        final Map<URL, Long> zombieMap = deploymentService.getZombieDomains();
        assertEquals("Wrong number of zombie domains registered.", 1, zombieMap.size());
        final Map.Entry<URL, Long> zombie = zombieMap.entrySet().iterator().next();
        assertEquals("Wrong URL tagged as zombie.", "broken-domain.zip", new File(zombie.getKey().getFile()).getName());
        assertTrue("Invalid lastModified value for file URL.", zombie.getValue() != -1);

        // Checks that the invalid zip was not deployed again
        try
        {
            assertDeploymentFailure(domainDeploymentListener, "broken-domain.zip");
            fail("Install was invoked again for the broken doamin file");
        }
        catch (AssertionError expected)
        {
        }
    }

    private void deploysAppAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(Action deployArtifactAction) throws Exception
    {
        Action verifyAnchorFileDoesNotExistsAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertApplicationAnchorFileDoesNotExists(waitAppDescriptor.id);
            }
        };
        Action verifyDeploymentSuccessfulAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertApplicationDeploymentSuccess(applicationDeploymentListener, waitAppDescriptor.id);
            }
        };
        Action verifyAnchorFileExistsAction = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertApplicationAnchorFileExists(waitAppDescriptor.id);
            }
        };
        deploysArtifactAndVerifyAnchorFileCreatedWhenDeploymentEnds(deployArtifactAction, verifyAnchorFileDoesNotExistsAction, verifyDeploymentSuccessfulAction, verifyAnchorFileExistsAction);
    }

    private void deploysDomainAndVerifyAnchorFileIsCreatedAfterDeploymentEnds(Action deployArtifactAction) throws Exception
    {
        Action verifyAnchorFileDoesNotExists = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertDomainAnchorFileDoesNotExists(waitDomainDescriptor.id);
            }
        };
        Action verifyDeploymentSuccessful = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertDeploymentSuccess(domainDeploymentListener, waitDomainDescriptor.id);
            }
        };
        Action verifyAnchorFileExists = new Action()
        {
            @Override
            public void perform() throws Exception
            {
                assertDomainAnchorFileExists(waitDomainDescriptor.id);
            }
        };
        deploysArtifactAndVerifyAnchorFileCreatedWhenDeploymentEnds(deployArtifactAction, verifyAnchorFileDoesNotExists, verifyDeploymentSuccessful, verifyAnchorFileExists);
    }

    private void deploysArtifactAndVerifyAnchorFileCreatedWhenDeploymentEnds(Action deployArtifactAction,
                                                                             Action verifyAnchorFileDoesNotExistsAction,
                                                                             Action verifyDeploymentSuccessfulAction,
                                                                             Action verifyAnchorFileExistsAction
    ) throws Exception
    {
        WaitComponent.reset();
        deploymentService.start();
        deployArtifactAction.perform();
        try
        {
            if (!WaitComponent.componentInitializedLatch.await(DEPLOYMENT_TIMEOUT, TimeUnit.MILLISECONDS))
            {
                fail("WaitComponent should be initilaized already. Probably app deployment failed");
            }
            verifyAnchorFileDoesNotExistsAction.perform();
        }
        finally
        {
            WaitComponent.waitLatch.release();
        }
        verifyDeploymentSuccessfulAction.perform();
        verifyAnchorFileExistsAction.perform();
    }

    private void assertApplicationDeploymentSuccess(DeploymentListener listener, String artifactName)
    {
        assertDeploymentSuccess(listener, artifactName);
        assertStatus(artifactName, ApplicationStatus.STARTED);
    }

    private void assertDeploymentSuccess(final DeploymentListener listener, final String artifactName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, times(1)).onDeploymentSuccess(artifactName);
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return "Failed to deploy application: " + artifactName;
            }
        });
    }

    private void assertMuleContextCreated(final DeploymentListener listener, final String appName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, times(1)).onMuleContextCreated(eq(appName), any(MuleContext.class));
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return String.format("Did not received notification '%s' for app '%s'", "onMuleContextCreated", appName);
            }
        });
    }

    private void assertMuleContextInitialized(final DeploymentListener listener, final String appName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, times(1)).onMuleContextInitialised(eq(appName), any(MuleContext.class));
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return String.format("Did not received notification '%s' for app '%s'", "onMuleContextInitialised", appName);
            }
        });
    }

    private void assertMuleContextConfigured(final DeploymentListener listener, final String appName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, times(1)).onMuleContextConfigured(eq(appName), any(MuleContext.class));
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return String.format("Did not received notification '%s' for app '%s'", "onMuleContextConfigured", appName);
            }
        });
    }

    private void assertUndeploymentSuccess(final DeploymentListener listener, final String appName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, times(1)).onUndeploymentSuccess(appName);
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return "Failed to undeployArtifact application: " + appName;
            }
        });
    }

    private MuleRegistry getMuleRegistry(Application app)
    {
        final ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        try
        {
            Thread.currentThread().setContextClassLoader(app.getArtifactClassLoader().getClassLoader());
            return app.getMuleContext().getRegistry();
        }
        finally
        {
            Thread.currentThread().setContextClassLoader(currentClassLoader);
        }
    }

    private void assertDeploymentFailure(final DeploymentListener listener, final String artifactName)
    {
        assertDeploymentFailure(listener, artifactName, times(1));
    }

    private void assertStatus(String appName, ApplicationStatus status)
    {
        assertStatus(appName, status, -1);
    }

    private void assertStatus(String appName, ApplicationStatus status, int expectedApps)
    {
        Application app = findApp(appName, expectedApps);
        assertThat(app, notNullValue());
        assertStatus(app, status);
    }

    private void assertStatus(final Application application, final ApplicationStatus status)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new JUnitProbe()
        {
            @Override
            protected boolean test() throws Exception
            {
                assertThat(application.getStatus(), is(status));
                return true;
            }

            @Override
            public String describeFailure()
            {
                return String.format("Application %s was expected to be in status %s but was %s instead",
                                     application.getArtifactName(), status.name(), application.getStatus().name());
            }
        });

    }

    private void assertDeploymentFailure(final DeploymentListener listener, final String artifactName, final VerificationMode mode)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        prober.check(new Probe()
        {
            public boolean isSatisfied()
            {
                try
                {
                    verify(listener, mode).onDeploymentFailure(eq(artifactName), any(Throwable.class));
                    return true;
                }
                catch (AssertionError e)
                {
                    return false;
                }
            }

            public String describeFailure()
            {
                return "Application deployment was supposed to fail for: " + artifactName;
            }
        });
    }

    private void assertNoDeploymentInvoked(final DeploymentListener deploymentListener)
    {
        //TODO(pablo.kraan): look for a better way to test this
        boolean invoked;
        Prober prober = new PollingProber(DeploymentDirectoryWatcher.DEFAULT_CHANGES_CHECK_INTERVAL_MS * 2, 100);
        try
        {
            prober.check(new Probe()
            {
                public boolean isSatisfied()
                {
                    try
                    {
                        verify(deploymentListener, times(1)).onDeploymentStart(any(String.class));
                        return true;
                    }
                    catch (AssertionError e)
                    {
                        return false;
                    }
                }

                public String describeFailure()
                {
                    return "No deployment has started";
                }
            });

            invoked = true;
        }
        catch (AssertionError e)
        {
            invoked = false;
        }

        assertFalse("A deployment was started", invoked);
    }

    /**
     * Find a deployed app, performing some basic assertions.
     */
    private Application findApp(final String appName, int totalAppsExpected)
    {
        // list all apps to validate total count
        final List<Application> apps = deploymentService.getApplications();
        assertNotNull(apps);

        if (totalAppsExpected >= 0)
        {
            assertEquals(totalAppsExpected, apps.size());
        }

        final Application app = deploymentService.findApplication(appName);
        assertNotNull(app);
        return app;
    }

    /**
     * Find a deployed domain, performing some basic assertions.
     */
    private Domain findADomain(final String domainName, int totalAppsExpected)
    {
        // list all apps to validate total count
        final List<Domain> apps = deploymentService.getDomains();
        assertNotNull(apps);
        assertEquals(totalAppsExpected, apps.size());
        final Domain domain = deploymentService.findDomain(domainName);
        assertNotNull(domain);
        return domain;
    }

    private void assertAppsDir(String[] expectedZips, String[] expectedApps, boolean performValidation)
    {
        assertArtifactDir(appsDir, expectedZips, expectedApps, performValidation);
    }

    private void assertDomainDir(String[] expectedZips, String[] expectedDomains, boolean performValidation)
    {
        assertArtifactDir(domainsDir, expectedZips, expectedDomains, performValidation);
    }

    private void assertArtifactDir(File artifactDir, String[] expectedZips, String[] expectedArtifacts, boolean performValidation)
    {
        final String[] actualZips = artifactDir.list(MuleDeploymentService.ZIP_ARTIFACT_FILTER);
        if (performValidation)
        {
            assertArrayEquals("Invalid Mule artifact archives set", expectedZips, actualZips);
        }
        final String[] actualArtifacts = artifactDir.list(DirectoryFileFilter.DIRECTORY);
        if (performValidation)
        {
            assertTrue("Invalid Mule exploded artifact set",
                       CollectionUtils.isEqualCollection(Arrays.asList(expectedArtifacts), Arrays.asList(actualArtifacts)));
        }
    }

    /**
     * Copies a given app archive to the apps folder for deployment.
     */
    private void addPackedAppFromResource(String resource) throws IOException
    {
        addPackedAppFromResource(resource, null);
    }

    private void addPackedAppFromResource(String resource, String targetName) throws IOException
    {
        addPackedArtifactFromResource(appsDir, resource, targetName);
    }

    /**
     * Copies a given app archive to the apps folder for deployment.
     */
    private void addPackedDomainFromResource(String resource) throws IOException
    {
        addPackedDomainFromResource(resource, null);
    }

    private void addPackedDomainFromResource(String resource, String targetName) throws IOException
    {
        addPackedArtifactFromResource(domainsDir, resource, targetName);
    }

    private void addPackedArtifactFromResource(File targetDir, String resource, String targetName) throws IOException
    {
        URL url = getClass().getResource(resource);
        assertNotNull("Test resource not found: " + url, url);
        addArchive(targetDir, url, targetName);
    }

    /**
     * Copies a given app archive with a given target name to the apps folder for deployment
     */
    private void addAppArchive(URL url, String targetFile) throws IOException
    {
        addArchive(appsDir, url, targetFile);
    }

    /**
     * Copies a given app archive with a given target name to the apps folder for deployment
     */
    private void addDomainArchive(URL url, String targetFile) throws IOException
    {
        addArchive(domainsDir, url, targetFile);
    }

    private void addArchive(File outputDir ,URL url, String targetFile) throws IOException
    {
        ReentrantLock lock = deploymentService.getLock();

        lock.lock();
        try
        {
            // copy is not atomic, copy to a temp file and rename instead (rename is atomic)
            final String tempFileName = new File((targetFile == null ? url.getFile() : targetFile) + ".part").getName();
            final File tempFile = new File(outputDir, tempFileName);
            FileUtils.copyURLToFile(url, tempFile);
            tempFile.renameTo(new File(StringUtils.removeEnd(tempFile.getAbsolutePath(), ".part")));
        }
        finally
        {
            lock.unlock();
        }
    }

    private void addExplodedAppFromResource(String resource) throws IOException, URISyntaxException
    {
        addExplodedAppFromResource(resource, null);
    }

    private void addExplodedDomainFromResource(String resource) throws IOException, URISyntaxException
    {
        addExplodedDomainFromResource(resource, null);
    }

    private void addExplodedDomainFromResource(String resource, String domainName) throws IOException, URISyntaxException
    {
        addExplodedArtifactFromResource(resource, domainName, "mule-domain-config.xml", domainsDir);
    }

    private void addExplodedAppFromResource(String resource, String appName) throws IOException, URISyntaxException
    {
        addExplodedArtifactFromResource(resource, appName, MULE_CONFIG_XML_FILE, appsDir);
    }

    private void addExplodedArtifactFromResource(String resource, String artifactName, String configFileName, File destinationDir) throws IOException, URISyntaxException
    {
        URL url = getClass().getResource(resource);
        assertNotNull("Test resource not found: " + url, url);

        String artifactFolder = artifactName;
        if (artifactFolder == null)
        {
            File file = new File(url.getFile());
            int index = file.getName().lastIndexOf(".");

            if (index > 0)
            {
                artifactFolder = file.getName().substring(0, index);
            }
            else
            {
                artifactFolder = file.getName();
            }
        }

        addExplodedArtifact(url, artifactFolder, configFileName, destinationDir);
    }

    /**
     * Copies a given app archive with a given target name to the apps folder for deployment
     */
    private void addExplodedArtifact(URL url, String artifactName, String configFileName, File destinationDir) throws IOException, URISyntaxException
    {
        ReentrantLock lock = deploymentService.getLock();

        lock.lock();
        try
        {
            File tempFolder = new File(muleHome, artifactName);
            FileUtils.unzip(new File(url.toURI()), tempFolder);

            // Under some platforms, file.lastModified is managed at second level, not milliseconds.
            // Need to update the config file lastModified ere to ensure that is different from previous value
            File configFile = new File(tempFolder, configFileName);
            if (configFile.exists())
            {
                configFile.setLastModified(System.currentTimeMillis() + 1000);
            }

            File appFolder = new File(destinationDir, artifactName);

            if (appFolder.exists())
            {
                FileUtils.deleteTree(appFolder);
            }

            FileUtils.moveDirectory(tempFolder, appFolder);
        }
        finally
        {
            lock.unlock();
        }
    }

    /**
     * Removes a given application anchor file in order to start application undeployment
     *
     * @param appName name of application to undeployArtifact
     * @return true if anchor file was deleted, false otherwise
     */
    private boolean removeAppAnchorFile(String appName)
    {
        File anchorFile = getArtifactAnchorFile(appName, appsDir);
        return anchorFile.delete();
    }

    /**
     * Removes a given domain anchor file in order to start application undeployment
     *
     * @param domainName name of application to undeployArtifact
     * @return true if anchor file was deleted, false otherwise
     */
    private boolean removeDomainAnchorFile(String domainName)
    {
        File anchorFile = getArtifactAnchorFile(domainName, domainsDir);
        return anchorFile.delete();
    }

    private void assertApplicationAnchorFileExists(String applicationName)
    {
        assertThat(getArtifactAnchorFile(applicationName, appsDir).exists(), is(true));
    }

    private void assertApplicationAnchorFileDoesNotExists(String applicationName)
    {
        assertThat(getArtifactAnchorFile(applicationName, appsDir).exists(), is(false));
    }

    private void assertDomainAnchorFileDoesNotExists(String domainName)
    {
        assertThat(getArtifactAnchorFile(domainName, domainsDir).exists(), is(false));
    }

    private void assertDomainAnchorFileExists(String domainName)
    {
        assertThat(getArtifactAnchorFile(domainName, domainsDir).exists(), is(true));
    }

    private File getArtifactAnchorFile(String artifactName, File artifactDir)
    {
        String anchorFileName = artifactName + MuleDeploymentService.ARTIFACT_ANCHOR_SUFFIX;
        return new File(artifactDir, anchorFileName);
    }

    private void assertAppFolderIsDeleted(String appName)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        File appFolder = new File(appsDir, appName);
        prober.check(new FileDoesNotExists(appFolder));
    }

    private void assertAppFolderIsMaintained(String appName)
    {
        assetArtifactFolderIsMaintained(appName, appsDir);
    }

    private void assertDomainFolderIsMaintained(String domainName)
    {
        assetArtifactFolderIsMaintained(domainName, domainsDir);
    }

    private void assetArtifactFolderIsMaintained(String artifactName, File artifactDir)
    {
        Prober prober = new PollingProber(DEPLOYMENT_TIMEOUT, 100);
        File appFolder = new File(artifactDir, artifactName);
        prober.check(new FileExists(appFolder));
    }

    /**
     * Allows to execute custom actions before or after executing logic or checking preconditions / verifications.
     */
    private interface Action
    {
        void perform() throws Exception;
    }

    public static class ArtifactDescriptor
    {

        public String id;
        public String zipPath;
        public String path;
        public String targetPath;
        public String configFilePath;

        public ArtifactDescriptor(String id, String zipPath, String path, String targetPath, String configFilePath)
        {
            this.id = id;
            this.zipPath = zipPath;
            this.path = path;
            this.targetPath = targetPath;
            this.configFilePath = configFilePath;
        }
    }

    public static class WaitComponent implements Initialisable
    {

        public static Latch componentInitializedLatch = new Latch();
        public static Latch waitLatch = new Latch();

        @Override
        public void initialise() throws InitialisationException
        {
            try
            {
                componentInitializedLatch.release();
                waitLatch.await();
            }
            catch (InterruptedException e)
            {
                throw new InitialisationException(e, this);
            }
        }

        public static void reset()
        {
            componentInitializedLatch = new Latch();
            waitLatch = new Latch();
        }
    }

}
TOP

Related Classes of org.mule.module.launcher.DeploymentServiceTestCase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.