org.rhq.bundle.ant.AntLauncherTest.java Source code

Java tutorial

Introduction

Here is the source code for org.rhq.bundle.ant.AntLauncherTest.java

Source

/*
 * RHQ Management Platform
 * Copyright (C) 2005-2014 Red Hat, Inc.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation, and/or the GNU Lesser
 * General Public License, version 2.1, also as published by the Free
 * Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License and the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * and the GNU Lesser General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

package org.rhq.bundle.ant;

import static org.rhq.core.util.stream.StreamUtil.slurp;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import io.undertow.Undertow;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.UnknownElement;
import org.apache.tools.ant.helper.AntXMLContext;
import org.testng.SkipException;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import org.rhq.bundle.ant.task.BundleTask;
import org.rhq.bundle.ant.type.DeploymentUnitType;
import org.rhq.bundle.ant.type.HandoverInfo;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
import org.rhq.core.domain.configuration.definition.PropertySimpleType;
import org.rhq.core.util.ZipUtil;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.core.util.updater.DeploymentsMetadata;
import org.rhq.core.util.updater.DestinationComplianceMode;
import org.rhq.core.util.updater.FileHashcodeMap;
import org.rhq.test.PortScout;

/**
 * @author John Mazzitelli
 * @author Ian Springer
 */
@Test
public class AntLauncherTest {
    private static final Log LOG = LogFactory.getLog(AntLauncherTest.class);

    private static final File DEPLOY_DIR = new File("target/test-ant-bundle").getAbsoluteFile();
    private static final File WORKING_DIR = new File("target/test-ant-working-dir").getAbsoluteFile();
    private static final String ANT_BASEDIR = "target/test-classes";
    private static final File REDHAT_RELEASE_FILE = new File("/etc/redhat-release");

    private int deploymentId;

    @BeforeClass
    public void beforeClass() {
        deploymentId = 0;
    }

    @BeforeMethod
    public void beforeTest() {
        FileUtil.purge(DEPLOY_DIR, true);
    }

    @AfterClass
    public void afterClass() {
        FileUtil.purge(new File(DEPLOY_DIR.getParentFile(), "test-ant-bundle-sibling"), true);
        FileUtil.purge(DEPLOY_DIR, true);
        FileUtil.purge(WORKING_DIR, true);
    }

    public void testParse_legacy() throws Exception {
        testParse(false, "legacy-test-bundle-v1.xml");
    }

    public void testParse() throws Exception {
        testParse(true, "test-bundle-v1.xml");
    }

    private void testParse(boolean validate, String recipeFile) throws Exception {
        AntLauncher ant = new AntLauncher(validate);

        BundleAntProject project = ant.parseBundleDeployFile(getFileFromTestClasses(recipeFile), null);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 5, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("prepareDatasource.cli"), String.valueOf(bundleFiles)); // handed over file
        assertTrue(bundleFiles.contains("test-v1.properties"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("foo-script"), String.valueOf(bundleFiles)); // from install-system-service
        assertTrue(bundleFiles.contains("foo-config"), String.valueOf(bundleFiles)); // from install-system-service

        assertEquals(project.getBundleName(), "example.com (JBoss EAP 4.3)");
        assertEquals(project.getBundleVersion(), "1.0");
        assertEquals(project.getBundleDescription(), "example.com corporate website hosted on JBoss EAP 4.3");

        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertEquals(propDef.getDefaultValue(), "8080");
        assertEquals(propDef.getDescription(), "This is where the product will listen for incoming messages");
        assertTrue(propDef.isRequired());

        // all we did was parse, nothing should really have been extracted or installed
        assertFalse(DEPLOY_DIR.exists(), "Nothing should have been installed to the deploy dir");
    }

    public void testParseWithNoDestinationComplianceCheck() throws Exception {
        //instantiate the launcher in the new validating mode (new as of RHQ 4.9.0)
        AntLauncher ant = new AntLauncher(true);

        try {
            ant.parseBundleDeployFile(getFileFromTestClasses("test-bundle-no-manage-root-dir-nor-compliance.xml"),
                    null);
            fail("Parsing a bundle with no explicit manageRootDir should have failed.");
        } catch (InvalidBuildFileException e) {
            assertEquals(e.getMessage(),
                    "The deployment unit must specifically declare compliance mode of the destination directory.");
        }

        BundleAntProject project = ant
                .parseBundleDeployFile(getFileFromTestClasses("test-bundle-with-manage-root-dir.xml"), null);
        assertNotNull(project);
        BundleTask bundleTask = findBundleTask(project);
        assertNotNull(bundleTask);
        assertNotNull(bundleTask.getDeploymentUnits());
        assertEquals(bundleTask.getDeploymentUnits().size(), 1);
        DeploymentUnitType deploymentUnit = bundleTask.getDeploymentUnits().values().iterator().next();
        assertNotNull(deploymentUnit);

        //assert the compatibility with the legacy attribute
        //noinspection deprecation
        assertEquals(deploymentUnit.getManageRootDir(), "false");
        assertEquals(DestinationComplianceMode.filesAndDirectories, deploymentUnit.getCompliance());

        // all we did was parse, nothing should really have been extracted or installed
        assertFalse(DEPLOY_DIR.exists(), "Nothing should have been installed to the deploy dir");
    }

    public void testInstall_legacy() throws Exception {
        testInstall(false, "legacy-test-bundle-v1.xml");
    }

    @Test(dependsOnMethods = "testUpgrade_legacy")
    public void testInstall() throws Exception {
        testInstall(true, "test-bundle-v1.xml");
    }

    private void testInstall(boolean validate, String recipeFile) throws Exception {
        skipNonRHLinux();

        // but we do want to add an unrelated file to see that it goes away - since we have manageRootDir=true
        File unrelatedFile = writeFile("unrelated content", DEPLOY_DIR, "unrelated-file.txt");

        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-bundle-v1-input.properties");
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 5, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("prepareDatasource.cli"), String.valueOf(bundleFiles)); // handed over file
        assertTrue(bundleFiles.contains("test-v1.properties"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("foo-script"), String.valueOf(bundleFiles)); // from install-system-service
        assertTrue(bundleFiles.contains("foo-config"), String.valueOf(bundleFiles)); // from install-system-service

        assertEquals(project.getBundleName(), "example.com (JBoss EAP 4.3)");
        assertEquals(project.getBundleVersion(), "1.0");
        assertEquals(project.getBundleDescription(), "example.com corporate website hosted on JBoss EAP 4.3");

        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertEquals(propDef.getDefaultValue(), "8080");
        assertEquals(propDef.getDescription(), "This is where the product will listen for incoming messages");
        assertTrue(propDef.isRequired());

        // make sure our test infrastruction setup the input properties correctly
        Configuration config = project.getConfiguration();
        assertEquals(config.getProperties().size(), 1, String.valueOf(config.getProperties()));
        assertEquals(config.getSimpleValue("listener.port", null), "10000", String.valueOf(config.getProperties()));

        String preinstallTargetExecuted = (String) project.getProperties().get("preinstallTargetExecuted");
        assertEquals(preinstallTargetExecuted, "1a");
        String postinstallTargetExecuted = (String) project.getProperties().get("postinstallTargetExecuted");
        assertEquals(postinstallTargetExecuted, "1b");

        assertTrue(new File(DEPLOY_DIR, "subdir/test.properties").exists(), "missing file");
        assertTrue(new File(DEPLOY_DIR, "archived-bundle-file.txt").exists(), "missing archived bundle file");
        assertTrue(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties").exists(),
                "missing subdir archive file");
        assertFalse(unrelatedFile.exists(), "unrelated file was not removed during the install");
        assertEquals(
                readPropsFile(new File(DEPLOY_DIR, "subdir/test.properties")).getProperty("junk.listener.port"),
                "10000");
        assertEquals(readPropsFile(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties"))
                .getProperty("templatized.variable"), "10000");
    }

    private void skipNonRHLinux() {
        if (!System.getProperty("os.name").equals("Linux") || !REDHAT_RELEASE_FILE.exists()) {
            throw new SkipException("This test only works on Red Hat Linux flavors");
        }
    }

    @Test(dependsOnMethods = "testInstall_legacy")
    public void testUpgrade_legacy() throws Exception {
        testUpgrade(false, "legacy-test-bundle-v2.xml");
    }

    @Test(dependsOnMethods = "testInstall")
    public void testUpgrade() throws Exception {
        testUpgrade(true, "test-bundle-v2.xml");
    }

    private void testUpgrade(boolean validate, String recipeFile) throws Exception {

        skipNonRHLinux();

        // add an unrelated file to see that it gets deleted as part of the upgrade
        File unrelatedFile = writeFile("unrelated content", DEPLOY_DIR, "unrelated-file.txt");

        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-bundle-v2-input.properties");
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 5, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("fileToHandover.zip"), String.valueOf(bundleFiles)); // handed over file
        assertTrue(bundleFiles.contains("test-v2.properties"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("foo-script"), String.valueOf(bundleFiles)); // from install-system-service
        assertTrue(bundleFiles.contains("foo-config"), String.valueOf(bundleFiles)); // from install-system-service

        assertEquals(project.getBundleName(), "example.com (JBoss EAP 4.3)");
        assertEquals(project.getBundleVersion(), "2.5");
        assertEquals(project.getBundleDescription(), "updated bundle");

        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertEquals(propDef.getDefaultValue(), "9090");
        assertEquals(propDef.getDescription(), "This is where the product will listen for incoming messages");
        assertTrue(propDef.isRequired());

        // make sure our test infrastruction setup the input properties correctly
        Configuration config = project.getConfiguration();
        assertEquals(config.getProperties().size(), 1);
        assertEquals(config.getSimpleValue("listener.port", null), "20000", String.valueOf(config.getProperties()));

        String preinstallTargetExecuted = (String) project.getProperties().get("preinstallTargetExecuted");
        assertEquals(preinstallTargetExecuted, "2a");
        String postinstallTargetExecuted = (String) project.getProperties().get("postinstallTargetExecuted");
        assertEquals(postinstallTargetExecuted, "2b");

        assertTrue(new File(DEPLOY_DIR, "subdir/test.properties").exists(), "missing file");
        assertTrue(new File(DEPLOY_DIR, "archived-bundle-file.txt").exists(), "missing archived bundle file");
        assertTrue(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties").exists(),
                "missing subdir archive file");
        assertFalse(unrelatedFile.exists(),
                "we are managing root dir so unrelated file should be removed during upgrade");
        assertEquals(
                readPropsFile(new File(DEPLOY_DIR, "subdir/test.properties")).getProperty("junk.listener.port"),
                "20000");
        assertEquals(readPropsFile(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties"))
                .getProperty("templatized.variable"), "20000");
    }

    public void testUpgradeNoManageRootDir_legacy() throws Exception {
        testUpgradeNoManageRootDir(false, "legacy-test-bundle-v2-noManageRootDir.xml");
    }

    public void testUpgradeNoManageRootDir() throws Exception {
        testUpgradeNoManageRootDir(true, "test-bundle-v2-filesAndDirectories.xml");
    }

    private void testUpgradeNoManageRootDir(boolean validate, String recipeFile) throws Exception {

        skipNonRHLinux();

        // We want to test an upgrade, so do *not* wipe out the deploy dir - let's re-invoke testInstall
        // to get us to an initial state of the v1 bundle installed
        testInstall();

        // we still want the unrelated file - we want to see that manageRootDir=false works (unrelated files should not be deleted)
        File unrelatedFile = writeFile("unrelated content", DEPLOY_DIR, "unrelated-file.txt");
        assertTrue(unrelatedFile.exists(),
                "our initial install test method should have prepared an unmanaged file");

        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-bundle-v2-input.properties");
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 4, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("test-v2.properties"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("foo-script"), String.valueOf(bundleFiles)); // from install-system-service
        assertTrue(bundleFiles.contains("foo-config"), String.valueOf(bundleFiles)); // from install-system-service

        assertEquals(project.getBundleName(), "example.com (JBoss EAP 4.3)");
        assertEquals(project.getBundleVersion(), "2.5");
        assertEquals(project.getBundleDescription(), "updated bundle");

        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertEquals(propDef.getDefaultValue(), "9090");
        assertEquals(propDef.getDescription(), "This is where the product will listen for incoming messages");
        assertTrue(propDef.isRequired());

        // make sure our test infrastruction setup the input properties correctly
        Configuration config = project.getConfiguration();
        assertEquals(config.getProperties().size(), 1);
        assertEquals(config.getSimpleValue("listener.port", null), "20000", String.valueOf(config.getProperties()));

        String preinstallTargetExecuted = (String) project.getProperties().get("preinstallTargetExecuted");
        assertEquals(preinstallTargetExecuted, "2a");
        String postinstallTargetExecuted = (String) project.getProperties().get("postinstallTargetExecuted");
        assertEquals(postinstallTargetExecuted, "2b");

        assertTrue(new File(DEPLOY_DIR, "subdir/test.properties").exists(), "missing file");
        assertTrue(new File(DEPLOY_DIR, "archived-bundle-file.txt").exists(), "missing archived bundle file");
        assertTrue(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties").exists(),
                "missing subdir archive file");
        assertTrue(unrelatedFile.exists(),
                "we are NOT managing root dir so unrelated file should NOT be removed during upgrade");
        assertEquals(
                readPropsFile(new File(DEPLOY_DIR, "subdir/test.properties")).getProperty("junk.listener.port"),
                "20000");
        assertEquals(readPropsFile(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties"))
                .getProperty("templatized.variable"), "20000");
    }

    public void testInstallCompressedZipNoDryRun_legacy() throws Exception {
        testInstallCompressedZip(false, false, "legacy-test-bundle-compressed-archives.xml");
    }

    public void testInstallCompressedZipNoDryRun() throws Exception {
        testInstallCompressedZip(false, true, "test-bundle-compressed-archives.xml");
    }

    public void testInstallCompressedZipDryRun_legacy() throws Exception {
        testInstallCompressedZip(true, false, "legacy-test-bundle-compressed-archives.xml");
    }

    public void testInstallCompressedZipDryRun() throws Exception {
        testInstallCompressedZip(true, true, "test-bundle-compressed-archives.xml");
    }

    private void testInstallCompressedZip(boolean dryRun, boolean validate, String recipeFile) throws Exception {
        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-bundle-compressed-archives-input.properties", dryRun);
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 1, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));

        assertEquals(project.getBundleName(), "test compressed archive files");
        assertEquals(project.getBundleVersion(), "1.0");
        assertNull(project.getBundleDescription());

        // while we are here, let's see that we have 0 config props
        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 0,
                String.valueOf(configDef.getPropertyDefinitions()));
        Configuration config = project.getConfiguration();
        assertEquals(config.getProperties().size(), 0, String.valueOf(config.getProperties()));

        if (!dryRun) {
            assertTrue(new File(DEPLOY_DIR, "file.zip").exists(), "should be here, we told it to stay compressed");
        } else {
            assertFalse(new File(DEPLOY_DIR, "file.zip").exists(), "dry run - should not be here");
        }

        assertFalse(new File(DEPLOY_DIR, "archived-bundle-file.txt").exists(), "should not have exploded this");
        assertFalse(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties").exists(),
                "should not have exploded this");
        assertFalse(new File(DEPLOY_DIR, "archived-subdir").isDirectory(),
                "should not still have the exploded dir");

        DeploymentsMetadata dm = new DeploymentsMetadata(DEPLOY_DIR);
        if (!dryRun) {
            FileHashcodeMap fhm = dm.getCurrentDeploymentFileHashcodes();
            assertFalse(fhm.containsKey("archived-bundle-file.txt"),
                    "should not have metadata - this is inside the compressed zip");
            assertFalse(fhm.containsKey("archived-subdir/archived-file-in-subdir.properties"),
                    "should not have metadata - this is inside the compressed zip");
            assertTrue(fhm.containsKey("file.zip"), String.valueOf(
                    "should have metadata for this - we didn't explode it, we just have this compressed file"));

            // test that we created the zip OK. Note that our test did not do any file replacing/realization of templates
            final String[] templateVarValue = new String[1];
            final int[] entries = new int[1];
            ZipUtil.walkZipFile(new File(DEPLOY_DIR, "file.zip"), new ZipUtil.ZipEntryVisitor() {
                @Override
                public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                    if (entry.getName().equals("archived-subdir/archived-file-in-subdir.properties")) {
                        Properties props = new Properties();
                        props.load(stream);
                        templateVarValue[0] = props.getProperty("templatized.variable");
                    }
                    if (!entry.isDirectory()) {
                        entries[0] = entries[0] + 1;
                    }
                    return true;
                }
            });
            assertNotNull(templateVarValue[0]);
            assertEquals(templateVarValue[0], "@@listener.port@@");
            assertEquals(entries[0], 2, String.valueOf(entries[0])); // we only counted the file entries
        } else {
            try {
                dm.getCurrentDeploymentFileHashcodes();
                fail("this was a dry run, we should not have written our metadata to the filesystem");
            } catch (Exception e) {
                // expected
            }
        }
    }

    public void testInstallCompressedZipWithTemplatizedFilesNoDryRun_legacy() throws Exception {
        testInstallCompressedZipWithTemplatizedFiles(false, false,
                "legacy-test-bundle-compressed-archives-with-replace.xml");
    }

    public void testInstallCompressedZipWithTemplatizedFilesNoDryRun() throws Exception {
        testInstallCompressedZipWithTemplatizedFiles(false, true,
                "test-bundle-compressed-archives-with-replace.xml");
    }

    public void testInstallCompressedZipWithTemplatizedFilesDryRun_legacy() throws Exception {
        testInstallCompressedZipWithTemplatizedFiles(true, false,
                "legacy-test-bundle-compressed-archives-with-replace.xml");
    }

    public void testInstallCompressedZipWithTemplatizedFilesDryRun() throws Exception {
        testInstallCompressedZipWithTemplatizedFiles(true, true,
                "test-bundle-compressed-archives-with-replace.xml");
    }

    private void testInstallCompressedZipWithTemplatizedFiles(boolean dryRun, boolean validate, String recipeFile)
            throws Exception {
        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-bundle-compressed-archives-input.properties", dryRun);
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 1, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("file.zip"), String.valueOf(bundleFiles));

        assertEquals(project.getBundleName(), "test compressed archive files");
        assertEquals(project.getBundleVersion(), "1.0");
        assertNull(project.getBundleDescription());

        // we have one property that we use to realize our content
        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertNull(propDef.getDefaultValue(), "recipe didn't define a default for our property");
        assertNull(propDef.getDescription(), "recipe didn't define a description for our property");
        assertTrue(propDef.isRequired(),
                "recipe didn't make the property required, but the default should be required");

        if (!dryRun) {
            assertTrue(new File(DEPLOY_DIR, "file.zip").exists(), "should be here, we told it to stay compressed");
        } else {
            assertFalse(new File(DEPLOY_DIR, "file.zip").exists(), "this was a dry run, should not be here");
        }
        assertFalse(new File(DEPLOY_DIR, "archived-bundle-file.txt").exists(), "should not have exploded this");
        assertFalse(new File(DEPLOY_DIR, "archived-subdir/archived-file-in-subdir.properties").exists(),
                "should not have exploded this");
        assertFalse(new File(DEPLOY_DIR, "archived-subdir").isDirectory(),
                "should not still have the exploded dir");

        DeploymentsMetadata dm = new DeploymentsMetadata(DEPLOY_DIR);
        if (!dryRun) {
            FileHashcodeMap fhm = dm.getCurrentDeploymentFileHashcodes();
            assertFalse(fhm.containsKey("archived-bundle-file.txt"),
                    "should not have metadata - this is inside the compressed zip");
            assertFalse(fhm.containsKey("archived-subdir/archived-file-in-subdir.properties"),
                    "should not have metadata - this is inside the compressed zip");
            assertTrue(fhm.containsKey("file.zip"), String.valueOf(
                    "should have metadata for this - we didn't explode it, we just have this compressed file"));

            // test that the file in the zip is realized
            final String[] templateVarValue = new String[1];
            final int[] entries = new int[1];
            ZipUtil.walkZipFile(new File(DEPLOY_DIR, "file.zip"), new ZipUtil.ZipEntryVisitor() {
                @Override
                public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                    if (entry.getName().equals("archived-subdir/archived-file-in-subdir.properties")) {
                        Properties props = new Properties();
                        props.load(stream);
                        templateVarValue[0] = props.getProperty("templatized.variable");
                    }
                    if (!entry.isDirectory()) {
                        entries[0] = entries[0] + 1;
                    }
                    return true;
                }
            });
            assertNotNull(templateVarValue[0]);
            assertEquals(templateVarValue[0], "12345");
            assertEquals(entries[0], 2, String.valueOf(entries[0])); // we only counted the file entries
        } else {
            try {
                dm.getCurrentDeploymentFileHashcodes();
                fail("this was a dry run, we should not have written our metadata to the filesystem");
            } catch (Exception e) {
                // expected
            }
        }
    }

    public void testAuditMessages_legacy() throws Exception {
        testAuditMessages(false, "legacy-test-bundle-audit.xml");
    }

    public void testAuditMessages() throws Exception {
        testAuditMessages(true, "test-bundle-audit.xml");
    }

    // this doesn't verify the audit messages getting emitted are correct
    // but it does verify the audit tag getting processed correctly.
    // you have to look at the test logs to see the audit messages
    // TODO: write a ant build listener to listen for this messages, parse them and verify they are correct
    //       this test should then ask the listener at the end if everything was OK and assertTrue(false if not
    private void testAuditMessages(boolean validate, String recipeFile) throws Exception {
        AntLauncher ant = new AntLauncher(validate);
        Properties inputProps = createInputProperties("/test-audit-input.properties");
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                buildListeners);
        assertNotNull(project);
        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 1, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("test-audit.properties"), String.valueOf(bundleFiles));

        // sanity check - make sure our recipe defined this property
        ConfigurationDefinition configDef = project.getConfigurationDefinition();
        assertEquals(configDef.getPropertyDefinitions().size(), 1,
                String.valueOf(configDef.getPropertyDefinitions()));
        PropertyDefinitionSimple propDef = configDef.getPropertyDefinitionSimple("listener.port");
        assertNotNull(propDef);

        // make sure our test infrastruction setup the input properties correctly
        Configuration config = project.getConfiguration();
        assertEquals(config.getProperties().size(), 1, String.valueOf(config.getProperties()));
        assertEquals(config.getSimpleValue("listener.port", null), "777", String.valueOf(config.getProperties()));

        String preinstallTargetExecuted = (String) project.getProperties().get("preinstallTargetExecuted");
        assertEquals(preinstallTargetExecuted, "1a");
        String postinstallTargetExecuted = (String) project.getProperties().get("postinstallTargetExecuted");
        assertEquals(postinstallTargetExecuted, "1b");

        assertTrue(new File(DEPLOY_DIR, "test-audit.properties").exists(), "missing file");
        assertEquals(readPropsFile(new File(DEPLOY_DIR, "test-audit.properties")).getProperty("my.listener.port"),
                "777");
    }

    public void testSubdirectoriesInRecipe_legacy() throws Exception {
        testSubdirectoriesInRecipe(false, "legacy-test-bundle-subdir.xml");
    }

    public void testSubdirectoriesInRecipe() throws Exception {
        testSubdirectoriesInRecipe(true, "test-bundle-subdir.xml");
    }

    private void testSubdirectoriesInRecipe(boolean validate, String origRecipeFile) throws Exception {
        // we need to create our own directory structure - let's build a temporary ant basedir
        // and put our recipe in there as well as a subdirectory with a test raw file and test zip file
        File antBasedir = FileUtil.createTempDirectory("anttest", ".test", null);
        try {
            File subdir = new File(antBasedir, "subdir"); // must match the name in the recipe
            subdir.mkdirs();
            writeFile("file0", subdir, "test0.txt"); // filename must match recipe
            writeFile("file1", subdir, "test1.txt"); // filename must match recipe
            writeFile("file2", subdir, "test2.txt"); // filename must match recipe
            createZip(new String[] { "one", "two" }, subdir, "test.zip", new String[] { "one.txt", "two.txt" });
            createZip(new String[] { "3", "4" }, subdir, "test-explode.zip",
                    new String[] { "three.txt", "four.txt" });
            createZip(new String[] { "X=@@X@@\n" }, subdir, "test-replace.zip", new String[] { "template.txt" }); // will be exploded then recompressed
            createZip(new String[] { "X=@@X@@\n" }, subdir, "test-replace2.zip", new String[] { "template.txt" }); // will be exploded then recompressed
            File recipeFile = new File(antBasedir, "deploy.xml");
            FileUtil.copyFile(new File(ANT_BASEDIR, origRecipeFile), recipeFile);

            AntLauncher ant = new AntLauncher(validate);
            Properties inputProps = new Properties();
            inputProps.setProperty(DeployPropertyNames.DEPLOY_DIR, DEPLOY_DIR.getPath());
            inputProps.setProperty(DeployPropertyNames.DEPLOY_ID, String.valueOf(++this.deploymentId));
            inputProps.setProperty(DeployPropertyNames.DEPLOY_PHASE, DeploymentPhase.INSTALL.name());
            inputProps.setProperty("X", "alpha-omega");
            List<BuildListener> buildListeners = createBuildListeners();

            BundleAntProject project = ant.executeBundleDeployFile(recipeFile, inputProps, buildListeners);
            assertNotNull(project);
            Set<String> bundleFiles = project.getBundleFileNames();
            assertNotNull(bundleFiles);
            assertEquals(bundleFiles.size(), 7, String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test0.txt"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test1.txt"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test2.txt"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test.zip"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test-explode.zip"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test-replace.zip"), String.valueOf(bundleFiles));
            assertTrue(bundleFiles.contains("subdir/test-replace2.zip"), String.valueOf(bundleFiles));

            assertTrue(new File(DEPLOY_DIR, "subdir/test0.txt").exists(),
                    "missing raw file from default destination location");
            assertTrue(new File(DEPLOY_DIR, "another/foo.txt").exists(),
                    "missing raw file from the destinationFile");
            assertTrue(new File(DEPLOY_DIR, "second.dir/test2.txt").exists(),
                    "missing raw file from the destinationDir");
            assertFalse(new File(DEPLOY_DIR, "subdir/test1.txt").exists(),
                    "should not be here because destinationFile was specified");
            assertFalse(new File(DEPLOY_DIR, "subdir/test2.txt").exists(),
                    "should not be here because destinationFile was specified");
            assertTrue(new File(DEPLOY_DIR, "subdir/test.zip").exists(), "missing unexploded zip file");
            assertTrue(new File(DEPLOY_DIR, "subdir/test-replace.zip").exists(), "missing unexploded zip file");
            assertTrue(new File(DEPLOY_DIR, "second.dir/test-replace2.zip").exists(),
                    "missing unexploded second zip file");
            assertFalse(new File(DEPLOY_DIR, "subdir/test-explode.zip").exists(), "should have been exploded");

            // test that the file in the zip is realized
            final String[] templateVarValue = new String[] { null };
            ZipUtil.walkZipFile(new File(DEPLOY_DIR, "subdir/test-replace.zip"), new ZipUtil.ZipEntryVisitor() {
                @Override
                public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                    if (entry.getName().equals("template.txt")) {
                        Properties props = new Properties();
                        props.load(stream);
                        templateVarValue[0] = props.getProperty("X");
                    }
                    return true;
                }
            });
            assertNotNull(templateVarValue[0]);
            assertEquals(templateVarValue[0], "alpha-omega");

            // test that the file in the second zip is realized
            final String[] templateVarValue2 = new String[] { null };
            ZipUtil.walkZipFile(new File(DEPLOY_DIR, "second.dir/test-replace2.zip"),
                    new ZipUtil.ZipEntryVisitor() {
                        @Override
                        public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                            if (entry.getName().equals("template.txt")) {
                                Properties props = new Properties();
                                props.load(stream);
                                templateVarValue2[0] = props.getProperty("X");
                            }
                            return true;
                        }
                    });
            assertNotNull(templateVarValue2[0]);
            assertEquals(templateVarValue2[0], "alpha-omega");

        } finally {
            FileUtil.purge(antBasedir, true);
        }
    }

    public void testUrlFilesAndArchives_legacy() throws Exception {
        testUrlFilesAndArchives(false, "legacy-test-bundle-url.xml");
    }

    public void testUrlFilesAndArchives() throws Exception {
        testUrlFilesAndArchives(true, "test-bundle-url.xml");
    }

    public void testNotDeployedPropertyFile() throws Exception {
        testNotDeployedFiles(getFileFromTestClasses("ant-properties/deploy.xml.properties-in-bundle"), true, false);
    }

    public void testNotDeployedRhqPropertyFile() throws Exception {
        testNotDeployedFiles(getFileFromTestClasses("ant-properties/deploy.xml.rhq-property-tag-in-bundle"), true,
                false);
    }

    public void testDeployedPropertyFile() throws Exception {
        testNotDeployedFiles(
                getFileFromTestClasses("ant-properties/deploy.xml.properties-in-bundle-props-deployed"), true,
                true);
    }

    public void testAntPropertiesUsedForTokenReplacement() throws Exception {
        testNotDeployedPropertyFile();
        checkPropertiesFromExternalFileReplaced();
    }

    public void testAntPropertiesLoadFromAbsolutePath() throws Exception {
        File tempDir = FileUtil.createTempDirectory("ant-launcher-test", null, null);

        try {
            //prepare the test bundle.. update the recipe with an absolute path to a properties file.
            File deployXml = new File(tempDir, "deploy.xml");

            FileUtil.copyFile(getFileFromTestClasses("ant-properties/deploy.xml.properties-out-of-bundle"),
                    deployXml);

            //copy the other file from the bundle, too, into the correct location
            FileUtil.copyFile(getFileFromTestClasses("ant-properties/deployed.file"),
                    new File(tempDir, "deployed.file"));

            File absoluteLocation = new File(tempDir, "absolute-location");
            assert absoluteLocation.mkdir() : "Failed to create dir under temp";

            String deployXmlContents = StreamUtil.slurp(new InputStreamReader(new FileInputStream(deployXml)));

            File absolutePropertiesLocation = new File(absoluteLocation, "in-bundle.properties");
            FileUtil.copyFile(getFileFromTestClasses("ant-properties/in-bundle.properties"),
                    absolutePropertiesLocation);

            deployXmlContents = deployXmlContents.replace("%%REPLACE_ME%%",
                    "file=\"" + absolutePropertiesLocation.getAbsolutePath() + "\"");

            FileUtil.writeFile(new ByteArrayInputStream(deployXmlContents.getBytes()), deployXml);

            //k, now the test itself...
            testNotDeployedFiles(deployXml, false, false);
            checkPropertiesFromExternalFileReplaced();
        } finally {
            FileUtil.purge(tempDir, true);
        }
    }

    public void testAntPropertiesLoadFromURL() throws Exception {
        File tempDir = FileUtil.createTempDirectory("ant-launcher-test", null, null);
        Undertow undertow = null;

        try {
            //prepare the test bundle.. update the recipe with an absolute path to a properties file.
            File deployXml = new File(tempDir, "deploy.xml");

            FileUtil.copyFile(getFileFromTestClasses("ant-properties/deploy.xml.properties-out-of-bundle"),
                    deployXml);

            //copy the other file from the bundle, too, into the correct location
            FileUtil.copyFile(getFileFromTestClasses("ant-properties/deployed.file"),
                    new File(tempDir, "deployed.file"));

            //fire up minimal server
            PortScout portScout = new PortScout();
            int port = 0;
            try {
                port = portScout.getNextFreePort();
            } finally {
                portScout.close();
            }

            undertow = Undertow.builder().addHttpListener(port, "localhost").setHandler(new HttpHandler() {
                @Override
                public void handleRequest(HttpServerExchange httpServerExchange) throws Exception {
                    httpServerExchange.startBlocking();
                    FileInputStream in = new FileInputStream(
                            getFileFromTestClasses("ant-properties/in-bundle.properties"));
                    try {
                        StreamUtil.copy(in, httpServerExchange.getOutputStream(), false);
                    } catch (Exception e) {
                        LOG.error("Failed to handle the HTTP request for loading properties.", e);
                        throw e;
                    } finally {
                        StreamUtil.safeClose(in);
                    }
                }
            }).build();

            undertow.start();

            String deployXmlContents = StreamUtil.slurp(new InputStreamReader(new FileInputStream(deployXml)));

            deployXmlContents = deployXmlContents.replace("%%REPLACE_ME%%",
                    "url=\"http://localhost:" + port + "\"");

            FileUtil.writeFile(new ByteArrayInputStream(deployXmlContents.getBytes()), deployXml);

            //k, now the test itself...
            testNotDeployedFiles(deployXml, false, false);
            checkPropertiesFromExternalFileReplaced();
        } finally {
            FileUtil.purge(tempDir, true);
            if (undertow != null) {
                undertow.stop();
            }
        }
    }

    public void testRhqPropertiesLoadFromDestination() throws Exception {
        File sideDir = new File(DEPLOY_DIR.getParentFile(), "test-ant-bundle-sibling");
        assert sideDir.mkdir() : "Failed to create a side directory";

        FileUtil.copyFile(getFileFromTestClasses("ant-properties/in-bundle.properties"),
                new File(sideDir, "in-bundle.properties"));

        testNotDeployedFiles(getFileFromTestClasses("ant-properties/deploy.xml.rhq-property-tag-in-destination"),
                false, false);
    }

    private void testNotDeployedFiles(File deployXml, boolean expectPropertiesFileInBundle,
            boolean expectPropertiesFileInDestination) throws Exception {
        FileUtil.purge(DEPLOY_DIR, true);
        FileUtil.purge(WORKING_DIR, true);

        FileUtil.copyDirectory(deployXml.getParentFile(), WORKING_DIR);

        deployXml = new File(WORKING_DIR, deployXml.getName());

        AntLauncher ant = new AntLauncher(true);
        Properties inputProps = createInputProperties(null);
        List<BuildListener> buildListeners = createBuildListeners();

        BundleAntProject project = ant.executeBundleDeployFile(deployXml, inputProps, buildListeners);

        assert project != null;
        Set<String> bundleFiles = project.getBundleFileNames();
        assert bundleFiles != null;
        assert bundleFiles.size() == (expectPropertiesFileInBundle ? 2 : 1) : bundleFiles;
        assert bundleFiles.contains("deployed.file") : bundleFiles;
        assert !expectPropertiesFileInBundle || bundleFiles.contains("in-bundle.properties") : bundleFiles;

        assert new File(DEPLOY_DIR, "deployed.file").exists() : "deployed.file missing";
        assert expectPropertiesFileInDestination == new File(DEPLOY_DIR, "in-bundle.properties")
                .exists() : "in-bundle.properties "
                        + (expectPropertiesFileInDestination ? "not deployed but should have"
                                : "deployed but shouldn't have");
    }

    private void checkPropertiesFromExternalFileReplaced() throws Exception {
        Properties props = readPropsFile(new File(DEPLOY_DIR, "deployed.file"));

        assert "user provided value".equals(props.getProperty("user.provided")) : "user.provided";
        assert "bundle provided value".equals(props.getProperty("bundle.provided")) : "bundle.provided";
        assert "a".equals(props.get("a.from.properties.file")) : "a.from.properties.file";
        assert "b".equals(props.get("b.from.properties.file")) : "b.from.properties.file";
    }

    private void testUrlFilesAndArchives(boolean validate, String recipeFile) throws Exception {
        // we need to create our own directory structure so we can use file: URLs
        File tmpUrlLocation = FileUtil.createTempDirectory("anttest", ".url", null);
        Set<File> downloadedFiles = null;

        try {
            File subdir = new File(tmpUrlLocation, "subdir"); // must match the name in the recipe
            subdir.mkdirs();
            writeFile("file0", subdir, "test0.txt"); // filename must match recipe
            writeFile("file1", subdir, "test1.txt"); // filename must match recipe
            writeFile("X=@@X@@\n", subdir, "test2.txt"); // filename must match recipe
            writeFile("pipo", subdir, "prepareDatasource.cli"); // filename must match recipe
            createZip(new String[] { "one", "two" }, subdir, "test.zip", new String[] { "one.txt", "two.txt" });
            createZip(new String[] { "3", "4" }, subdir, "test-explode.zip",
                    new String[] { "three.txt", "four.txt" });
            createZip(new String[] { "X=@@X@@\n" }, subdir, "test-replace.zip", new String[] { "template.txt" }); // will be exploded then recompressed
            createZip(new String[] { "one", "two" }, subdir, "fileToHandover.zip",
                    new String[] { "one.txt", "two.txt" });

            AntLauncher ant = new AntLauncher(validate);
            Properties inputProps = createInputProperties("/test-bundle-url-input.properties");
            inputProps.setProperty("rhq.test.url.dir", tmpUrlLocation.toURI().toURL().toString()); // we use this so our recipe can use URLs
            List<BuildListener> buildListeners = createBuildListeners();

            BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses(recipeFile), inputProps,
                    buildListeners);
            assertNotNull(project);

            Set<String> bundleFiles = project.getBundleFileNames();
            assertNotNull(bundleFiles);
            assertEquals(bundleFiles.size(), 0,
                    "we don't have any bundle files - only downloaded files from URLs: " + bundleFiles);

            downloadedFiles = project.getDownloadedFiles();
            assertNotNull(downloadedFiles);
            assertEquals(downloadedFiles.size(), 8, String.valueOf(downloadedFiles));
            ArrayList<String> expectedDownloadedFileNames = new ArrayList<String>();
            // remember, we store url downloaded files under the names of their destination file/dir, not source location
            expectedDownloadedFileNames.add("test0.txt");
            expectedDownloadedFileNames.add("foo.txt");
            expectedDownloadedFileNames.add("test2.txt");
            expectedDownloadedFileNames.add("prepareDatasource.cli");
            expectedDownloadedFileNames.add("test.zip");
            expectedDownloadedFileNames.add("test-explode.zip");
            expectedDownloadedFileNames.add("test-replace.zip");
            expectedDownloadedFileNames.add("fileToHandover.zip");
            for (File downloadedFile : downloadedFiles) {
                assertTrue(expectedDownloadedFileNames.contains(downloadedFile.getName()),
                        "We downloaded a file but its not in the project's list: " + downloadedFile);
            }

            assertTrue(new File(DEPLOY_DIR, "test0.txt").exists(),
                    "missing raw file from default destination location");
            assertTrue(new File(DEPLOY_DIR, "another/foo.txt").exists(),
                    "missing raw file from the destinationFile");
            assertTrue(new File(DEPLOY_DIR, "second.dir/test2.txt").exists(),
                    "missing raw file from the destinationDir");
            assertFalse(new File(DEPLOY_DIR, "test1.txt").exists(),
                    "should not be here because destinationFile was specified");
            assertFalse(new File(DEPLOY_DIR, "test2.txt").exists(),
                    "should not be here because destinationFile was specified");
            assertTrue(new File(DEPLOY_DIR, "test.zip").exists(), "missing unexploded zip file");
            assertTrue(new File(DEPLOY_DIR, "test-replace.zip").exists(), "missing unexploded zip file");
            assertFalse(new File(DEPLOY_DIR, "test-explode.zip").exists(), "should have been exploded");

            // test that the file in the zip is realized
            final String[] templateVarValue = new String[] { null };
            ZipUtil.walkZipFile(new File(DEPLOY_DIR, "test-replace.zip"), new ZipUtil.ZipEntryVisitor() {
                @Override
                public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                    if (entry.getName().equals("template.txt")) {
                        Properties props = new Properties();
                        props.load(stream);
                        templateVarValue[0] = props.getProperty("X");
                    }
                    return true;
                }
            });
            assertNotNull(templateVarValue[0]);
            assertEquals(templateVarValue[0], "9876");

            // test that our raw file was realized
            File realizedFile = new File(DEPLOY_DIR, "second.dir/test2.txt");
            Properties props = new Properties();
            FileInputStream inStream = new FileInputStream(realizedFile);
            try {
                props.load(inStream);
                assertEquals(props.getProperty("X", "<unset>"), "9876");
            } finally {
                inStream.close();
            }
        } finally {
            FileUtil.purge(tmpUrlLocation, true);
            if (downloadedFiles != null) {
                for (File doomed : downloadedFiles) {
                    doomed.delete();
                }
            }
        }
    }

    public void testHandover() throws Exception {
        AntLauncher ant = new AntLauncher(true);

        final List<HandoverInfoArgument> handoverInfoArguments = new ArrayList<HandoverInfoArgument>();
        HandoverTarget handoverTarget = new HandoverTarget() {
            @Override
            public boolean handoverContent(HandoverInfo handoverInfo) {
                HandoverInfoArgument handoverInfoArgument;
                try {
                    handoverInfoArgument = new HandoverInfoArgument(handoverInfo);
                } catch (IOException e) {
                    return false;
                }
                handoverInfoArguments.add(handoverInfoArgument);
                try {
                    FileUtil.writeFile(handoverInfo.getContent(), handoverInfoArgument.handoverInfoTestContentFile);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return true;
            }
        };
        ant.setHandoverTarget(handoverTarget);

        List<BuildListener> buildListeners = createBuildListeners();
        Properties inputProps = createInputProperties("/handover-test-bundle-input.properties");

        BundleAntProject project = ant.executeBundleDeployFile(getFileFromTestClasses("handover-test-bundle.xml"),
                inputProps, buildListeners);
        assertNotNull(project);
        assertEquals(project.getBundleName(), "example.com (EAP 6)");
        assertEquals(project.getBundleVersion(), "1.0");
        assertEquals(project.getBundleDescription(), "example.com corporate website hosted on EAP 6");

        Set<String> bundleFiles = project.getBundleFileNames();
        assertNotNull(bundleFiles);
        assertEquals(bundleFiles.size(), 2, String.valueOf(bundleFiles));
        assertTrue(bundleFiles.contains("prepareDatasource.cli"), String.valueOf(bundleFiles)); // handed over file
        assertTrue(bundleFiles.contains("fileToHandover.zip"), String.valueOf(bundleFiles)); // handed over archive

        ConfigurationDefinition projectConfigDef = project.getConfigurationDefinition();
        assertEquals(projectConfigDef.getPropertyDefinitions().size(), 3,
                String.valueOf(projectConfigDef.getPropertyDefinitions()));

        PropertyDefinitionSimple propDef = projectConfigDef
                .getPropertyDefinitionSimple("myapp.datasource.property");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertTrue(propDef.isRequired());

        propDef = projectConfigDef.getPropertyDefinitionSimple("myapp.listener.port");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.INTEGER);
        assertTrue(propDef.isRequired());

        propDef = projectConfigDef.getPropertyDefinitionSimple("myapp.runtime.name");
        assertNotNull(propDef);
        assertEquals(propDef.getType(), PropertySimpleType.STRING);
        assertTrue(propDef.isRequired());

        Configuration projectConfig = project.getConfiguration();
        assertNotNull(projectConfig);
        assertEquals(projectConfig.getProperties().size(), 3, String.valueOf(projectConfig.getProperties()));
        assertEquals(projectConfig.getSimpleValue("myapp.datasource.property"), "10",
                String.valueOf(projectConfig.getProperties()));
        assertEquals(projectConfig.getSimpleValue("myapp.listener.port"), "9777",
                String.valueOf(projectConfig.getProperties()));
        assertEquals(projectConfig.getSimpleValue("myapp.runtime.name"), "site.war",
                String.valueOf(projectConfig.getProperties()));

        assertEquals(handoverInfoArguments.size(), 2, String.valueOf(handoverInfoArguments));
        Iterator<HandoverInfoArgument> handoverInfoIterator = handoverInfoArguments.iterator();

        HandoverInfoArgument handoverInfoArgument = handoverInfoIterator.next();
        HandoverInfo handoverInfo = handoverInfoArgument.handoverInfo;
        InputStream cliScriptContent = getClass().getClassLoader().getResourceAsStream("prepareDatasource.cli");
        assertNotNull(cliScriptContent);
        FileInputStream actualContent = new FileInputStream(handoverInfoArgument.handoverInfoTestContentFile);
        assertEquals(slurp(actualContent), slurp(cliScriptContent));
        assertEquals(handoverInfo.getFilename(), "prepareDatasource.cli");
        assertEquals(handoverInfo.getAction(), "execute-script");
        assertEquals(handoverInfo.getParams(), Collections.emptyMap());
        assertEquals(handoverInfo.isRevert(), false);

        handoverInfoArgument = handoverInfoIterator.next();
        handoverInfo = handoverInfoArgument.handoverInfo;
        final Properties[] propertiesHolder = new Properties[1];
        ZipUtil.walkZipFile(handoverInfoArgument.handoverInfoTestContentFile, new ZipUtil.ZipEntryVisitor() {
            @Override
            public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception {
                String entryName = entry.getName();
                if (entryName.equals("archived-subdir/archived-file-in-subdir.properties")) {
                    Properties properties = new Properties();
                    properties.load(stream);
                    propertiesHolder[0] = properties;
                }
                return true;
            }
        });
        Properties properties = propertiesHolder[0];
        assertNotNull(properties);
        assertEquals(properties.size(), 3, String.valueOf(properties));
        assertEquals(properties.getProperty("templatized.variable"), "9777", String.valueOf(properties));
        assertEquals(handoverInfo.getFilename(), "fileToHandover.zip");
        assertEquals(handoverInfo.getAction(), "deployment");
        assertEquals(handoverInfo.getParams(), new HashMap<String, String>() {
            {
                put("runtimeName", "site.war");
            }
        });
        assertEquals(handoverInfo.isRevert(), false);
    }

    public void testHandoverFailure() throws Exception {
        AntLauncher ant = new AntLauncher(true);

        final List<HandoverInfoArgument> handoverInfoArguments = new ArrayList<HandoverInfoArgument>();
        HandoverTarget handoverTarget = new HandoverTarget() {
            @Override
            public boolean handoverContent(HandoverInfo handoverInfo) {
                HandoverInfoArgument handoverInfoArgument;
                try {
                    handoverInfoArgument = new HandoverInfoArgument(handoverInfo);
                } catch (IOException e) {
                    return false;
                }
                handoverInfoArguments.add(handoverInfoArgument);
                return false;
            }
        };
        ant.setHandoverTarget(handoverTarget);

        List<BuildListener> buildListeners = createBuildListeners();
        Properties inputProps = createInputProperties("/handover-test-bundle-input.properties");

        try {
            ant.executeBundleDeployFile(getFileFromTestClasses("handover-test-bundle.xml"), inputProps,
                    buildListeners);
            fail("Expected RuntimeException because of failed handover");
        } catch (RuntimeException expected) {
        }

        // We still expect the callback to be called twice, as the first handover has a failonerror="false" attribute
        assertEquals(handoverInfoArguments.size(), 2, String.valueOf(handoverInfoArguments));
    }

    public void testHandoverFailonerrorAttribute() throws Exception {
        AntLauncher ant = new AntLauncher(true);

        final List<HandoverInfoArgument> handoverInfoArguments = new ArrayList<HandoverInfoArgument>();
        HandoverTarget handoverTarget = new HandoverTarget() {
            @Override
            public boolean handoverContent(HandoverInfo handoverInfo) {
                HandoverInfoArgument handoverInfoArgument;
                try {
                    handoverInfoArgument = new HandoverInfoArgument(handoverInfo);
                } catch (IOException e) {
                    return false;
                }
                handoverInfoArguments.add(handoverInfoArgument);
                return !handoverInfo.getFilename().equals("prepareDatasource.cli");
            }
        };
        ant.setHandoverTarget(handoverTarget);

        List<BuildListener> buildListeners = createBuildListeners();
        Properties inputProps = createInputProperties("/handover-test-bundle-input.properties");

        ant.executeBundleDeployFile(getFileFromTestClasses("handover-test-bundle.xml"), inputProps, buildListeners);

        assertEquals(handoverInfoArguments.size(), 2, String.valueOf(handoverInfoArguments));
    }

    private List<BuildListener> createBuildListeners() {
        List<BuildListener> buildListeners = new ArrayList<BuildListener>();
        DefaultLogger logger = new DefaultLogger();
        logger.setMessageOutputLevel(Project.MSG_DEBUG);
        logger.setOutputPrintStream(System.out);
        logger.setErrorPrintStream(System.err);
        buildListeners.add(logger);
        return buildListeners;
    }

    private Properties createInputProperties(String resourcePath) throws IOException {
        return createInputProperties(resourcePath, false);
    }

    private Properties createInputProperties(String resourcePath, boolean dryRun) throws IOException {
        Properties inputProps = new Properties();
        inputProps.setProperty(DeployPropertyNames.DEPLOY_DIR, DEPLOY_DIR.getPath());
        inputProps.setProperty(DeployPropertyNames.DEPLOY_ID, String.valueOf(++this.deploymentId));
        inputProps.setProperty(DeployPropertyNames.DEPLOY_PHASE, DeploymentPhase.INSTALL.name());
        if (dryRun) {
            inputProps.setProperty(DeployPropertyNames.DEPLOY_DRY_RUN, Boolean.TRUE.toString());
        }
        if (resourcePath != null) {
            InputStream inputStream = this.getClass().getResourceAsStream(resourcePath);
            try {
                inputProps.load(inputStream);
            } finally {
                inputStream.close();
            }
        }
        return inputProps;
    }

    private Properties readPropsFile(File propFile) throws Exception {
        Properties props = new Properties();
        InputStream inputStream = new FileInputStream(propFile);
        try {
            props.load(inputStream);
        } finally {
            inputStream.close();
        }
        return props;
    }

    private File getFileFromTestClasses(String name) throws Exception {
        File file = new File(ANT_BASEDIR, name);
        assertTrue(file.exists(), "The file doesn't exist: " + file.getAbsolutePath());
        return file;
    }

    private File writeFile(String content, File fileToOverwrite) throws Exception {
        FileOutputStream out = null;

        try {
            fileToOverwrite.getParentFile().mkdirs();
            out = new FileOutputStream(fileToOverwrite);
            out.write(content.getBytes());
            return fileToOverwrite;
        } finally {
            if (out != null) {
                out.close();
            }
        }
    }

    private File writeFile(String content, File destDir, String fileName) throws Exception {
        File destFile = new File(destDir, fileName);
        return writeFile(content, destFile);
    }

    private File createZip(String[] content, File destDir, String zipName, String[] entryName) throws Exception {
        FileOutputStream stream = null;
        ZipOutputStream out = null;

        try {
            destDir.mkdirs();
            File zipFile = new File(destDir, zipName);
            stream = new FileOutputStream(zipFile);
            out = new ZipOutputStream(stream);

            assertEquals(content.length, entryName.length);
            for (int i = 0; i < content.length; i++) {
                ZipEntry zipAdd = new ZipEntry(entryName[i]);
                zipAdd.setTime(System.currentTimeMillis());
                out.putNextEntry(zipAdd);
                out.write(content[i].getBytes());
            }
            return zipFile;
        } finally {
            if (out != null) {
                out.close();
            }
            if (stream != null) {
                stream.close();
            }
        }
    }

    private BundleTask findBundleTask(BundleAntProject project) {
        AntXMLContext antParsingContext = (AntXMLContext) project.getReference("ant.parsing.context");
        Vector targets = antParsingContext.getTargets();
        for (Object targetObj : targets) {
            Target target = (Target) targetObj;
            Task[] tasks = target.getTasks();
            for (Task task : tasks) {
                if ("rhq:bundle".equals(task.getTaskName())) {
                    return (BundleTask) preconfigureTask(task);
                }
            }
        }

        return null;
    }

    private static Task preconfigureTask(Task task) {
        if (task instanceof UnknownElement) {
            task.maybeConfigure();
            Task resolvedTask = ((UnknownElement) task).getTask();
            return (resolvedTask != null) ? resolvedTask : task;
        } else {
            return task;
        }
    }

    private static class HandoverInfoArgument {
        final HandoverInfo handoverInfo;
        final File handoverInfoTestContentFile;

        private HandoverInfoArgument(HandoverInfo handoverInfo) throws IOException {
            this.handoverInfo = handoverInfo;
            handoverInfoTestContentFile = File.createTempFile(HandoverInfoArgument.class.getSimpleName() + "-",
                    ".tmp");
            handoverInfoTestContentFile.deleteOnExit();
        }

        @Override
        public String toString() {
            return "HandoverInfoArgument[" + "handoverInfo=" + handoverInfo + ", handoverInfoTestContentFile="
                    + handoverInfoTestContentFile + ']';
        }
    }
}