jeffaschenk.tomcat.instance.generator.builders.TomcatInstanceBuilderHelper.java Source code

Java tutorial

Introduction

Here is the source code for jeffaschenk.tomcat.instance.generator.builders.TomcatInstanceBuilderHelper.java

Source

package jeffaschenk.tomcat.instance.generator.builders;

import jeffaschenk.tomcat.instance.generator.logger.GenerationLogger;
import jeffaschenk.tomcat.instance.generator.model.TomcatArchive;
import jeffaschenk.tomcat.instance.generator.model.TomcatAvailableArchives;
import jeffaschenk.tomcat.instance.generator.model.TomcatInstance;
import jeffaschenk.tomcat.instance.generator.model.TomcatInstanceProperty;
import jeffaschenk.tomcat.knowledgebase.DefaultDefinitions;
import jeffaschenk.tomcat.util.ResourceHelper;
import org.apache.commons.io.FileUtils;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.io.*;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.MalformedInputException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import static jeffaschenk.tomcat.knowledgebase.DefaultDefinitions.*;
import static jeffaschenk.tomcat.knowledgebase.ReplacementDefinitions.*;

/**
 * TomcatInstanceBuilderHelper
 * <p>
 * Created by jeffaschenk@gmail.com on 2/16/2017.
 */
public class TomcatInstanceBuilderHelper {

    /**
     * Constants
     */
    private static final String JAVA_OPTS = "JAVA_OPTS";
    private static final String WIN_SET = "SET ";
    private static final String WIN_LS = "\r\n";
    private static final String NIX_LS = "\n";
    private static final String README = "README.txt";

    /**
     * Pull specified Version of Tomcat from Internet Download Site ...
     *
     * @param GENERATION_LOGGER Logger
     * @param tomcatInstance    POJO
     */
    protected static boolean pullTomcatVersionFromApacheMirror(GenerationLogger GENERATION_LOGGER,
            TomcatAvailableArchives tomcatAvailableArchives, TomcatInstance tomcatInstance) {
        /**
         * First determine the Latest Release based upon our Short name.
         */
        TomcatArchive tomcatArchive = tomcatAvailableArchives
                .getAvailableArchiveByShortName(tomcatInstance.getTomcatVersion());
        if (tomcatArchive == null || tomcatArchive.getShortVersion() == null) {
            GENERATION_LOGGER.error("Unable to determine a Download Archive for Tomcat Version: "
                    + tomcatInstance.getTomcatVersion() + ", Notify Engineering to Support new Version of Tomcat!");
            return false;
        }
        tomcatInstance.setTomcatArchive(tomcatArchive); // Set a Reference to archive used.
        /**
         * Now check to see if the Artifact has already been pulled?
         */
        if (validateTomcatDownloadedVersion(GENERATION_LOGGER, tomcatInstance, false)) {
            GENERATION_LOGGER.info("Using previously Downloaded Archive: " + tomcatArchive.getName() + ".zip");
            return true;
        }
        /**
         * Proceed to Pull Archive ...
         */
        GENERATION_LOGGER.info("Pulling Tomcat Version from Apache Mirror ...");

        URL url = null;
        URLConnection con = null;
        int i;
        try {
            /**
             * Construct the Apache Mirror URL to Pull Tomcat Instance.
             * Assume a V8 version ...
             */
            String tcHeadVersion;
            if (tomcatInstance.getTomcatVersion().startsWith("v8")) {
                tcHeadVersion = DefaultDefinitions.APACHE_TOMCAT_8_MIRROR_HEAD;
            } else if (tomcatInstance.getTomcatVersion().startsWith("v9")) {
                tcHeadVersion = DefaultDefinitions.APACHE_TOMCAT_9_MIRROR_HEAD;
            } else {
                GENERATION_LOGGER.error("Unable to determine a Download URL for Tomcat Version: "
                        + tomcatInstance.getTomcatVersion()
                        + ", Notify Engineering to Support new Version of Tomcat!");
                return false;
            }
            /**
             * Now construct the URL to use to Pull over Internet.
             */
            url = new URL(tomcatAvailableArchives.getApacheMirrorHeadUrl() + "/" + tcHeadVersion + "/v"
                    + tomcatArchive.getShortVersion() + "/bin/" + tomcatArchive.getName() + ".zip");
            GENERATION_LOGGER.info("Using URL for Downloading Artifact: " + url.toString());

            con = url.openConnection();
            BufferedInputStream bis = new BufferedInputStream(con.getInputStream());
            BufferedOutputStream bos = new BufferedOutputStream(
                    new FileOutputStream(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                            + tomcatArchive.getName() + ".zip"));
            while ((i = bis.read()) != -1) {
                bos.write(i);
            }
            bos.flush();
            bis.close();
            GENERATION_LOGGER.info("Successfully Pulled Tomcat Version from Apache Mirror ...");
            return true;
        } catch (MalformedInputException malformedInputException) {
            malformedInputException.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        /**
         * Indicate a Failure has Occurred
         */
        return false;
    }

    /**
     * Validate the Tomcat Version Archive from our Download
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean indicating if process was successful or not.
     */
    protected static boolean validateTomcatDownloadedVersion(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) {
        return validateTomcatDownloadedVersion(GENERATION_LOGGER, tomcatInstance, true);
    }

    /**
     * Validate the Tomcat Version Archive from our Download
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @param verbose           indicator for logging.
     * @return boolean indicating if process was successful or not.
     */
    protected static boolean validateTomcatDownloadedVersion(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance, boolean verbose) {
        if (verbose) {
            GENERATION_LOGGER.info("Validating Tomcat Version Archive ...");
        }
        /**
         * Now determine the Latest Release based upon our Short name.
         */
        TomcatArchive tomcatArchive = tomcatInstance.getTomcatArchive();
        if (tomcatArchive == null || tomcatArchive.getShortVersion() == null) {
            GENERATION_LOGGER.error("Unable to determine a Download Archive for Tomcat Version: "
                    + tomcatInstance.getTomcatVersion() + ", Notify Engineering to Support new Version of Tomcat!");
            return false;
        }
        try {
            File archiveFile = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                    + tomcatArchive.getName() + ".zip");
            if (!archiveFile.exists()) {
                GENERATION_LOGGER.warn("Attempting to Validate previously Downloaded Archive: "
                        + archiveFile.getAbsolutePath() + ", however, the archive does not exist!");
                return false;
            }

            /**
             * Validate the Size and CheckSums
             */
            if (tomcatArchive.getCheckSum().equalsIgnoreCase(getFileCheckSum(archiveFile.getAbsolutePath()))) {
                if (verbose) {
                    GENERATION_LOGGER.info("Validated Downloaded Archive: " + archiveFile.getAbsolutePath()
                            + ", CheckSum Correct.");
                }
            } else {
                if (verbose) {
                    GENERATION_LOGGER.error("Downloaded Archive: " + archiveFile.getAbsolutePath()
                            + ", Expected CheckSum is incorrect, unable to continue!");
                }
                return false;
            }
            /**
             * Validate the Size.
             */
            if (archiveFile.length() == tomcatArchive.getSize()) {
                if (verbose) {
                    GENERATION_LOGGER.info("Validated Downloaded Archive: " + archiveFile.getAbsolutePath()
                            + ", File Size Correct.");
                }
                return true;
            } else {
                if (verbose) {
                    GENERATION_LOGGER.error("Downloaded Archive: " + archiveFile.getAbsolutePath()
                            + ", File Size Not Correct, unable to continue!");
                }
                return false;
            }
        } catch (Exception e) {
            GENERATION_LOGGER
                    .error("Exception encountered during exploding Tomcat Instance Download Archive Validation: "
                            + e.getMessage());
            e.printStackTrace();
            /**
             * Indicate a Failure has Occurred
             */
            return false;
        }
    }

    /**
     * Explode the Tomcat Version Archive for Customization
     *
     * @return boolean indicating if process was successful or not.
     */
    protected static boolean explodeTomcatVersionForCustomization(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) {
        /**
         * Now determine the Latest Release based upon our Short name.
         */
        TomcatArchive tomcatArchive = tomcatInstance.getTomcatArchive();
        if (tomcatArchive == null || tomcatArchive.getShortVersion() == null) {
            GENERATION_LOGGER.error("Unable to determine a Download Archive for Tomcat Version: "
                    + tomcatInstance.getTomcatVersion() + ", Notify Engineering to Support new Version of Tomcat!");
            return false;
        }
        /**
         * Check for a Previous Download exploded Head ...
         */
        File headDirectory = new File(
                tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator + tomcatArchive.getName());
        if (headDirectory.exists()) {
            GENERATION_LOGGER.error("Existing Unzip Directory Head: " + headDirectory.getAbsolutePath()
                    + ", already exists, unable to continue!");

            GENERATION_LOGGER.error("Please Clean up existing Unzip Directory Head before re-running process!");
            return false;
        }
        /**
         * Begin Exploding Archive
         */
        GENERATION_LOGGER.info("Exploding Tomcat Version Archive for Customizations ...");
        try {
            unZipArchive(GENERATION_LOGGER,
                    tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                            + tomcatArchive.getName() + ".zip",
                    tomcatInstance.getDestinationFolder().getAbsolutePath());
            return true;
        } catch (Exception e) {
            GENERATION_LOGGER.error(
                    "Exception encountered during exploding Tomcat Version for Customization: " + e.getMessage());
            e.printStackTrace();
            /**
             * Indicate a Failure has Occurred
             */
            return false;
        }
    }

    /**
     * renameReferencedExplodedArtifact
     * <p>
     * Rename the exploded Artifact to a new Instance Name.
     *
     * @param GENERATION_LOGGER Logger
     * @param tomcatInstance    POJO
     * @return boolean Indicating if this process/method was successful or not.
     */
    protected static boolean renameReferencedExplodedArtifact(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) {
        try {
            File explodedArtifactFolder = new File(tomcatInstance.referenceDownloadedArchiveFolder());
            if (explodedArtifactFolder.exists() && explodedArtifactFolder.isDirectory()) {
                String newArtifactFolderName = tomcatInstance.referenceTomcatInstanceFolder();
                File newArtifactFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath()
                        + File.separator + newArtifactFolderName);
                if (newArtifactFolder.exists()) {
                    GENERATION_LOGGER
                            .error("Unable to Rename Original Archive from: " + explodedArtifactFolder.getName()
                                    + " to " + newArtifactFolderName + ", as new Folder Already Exists!");
                    return false;
                }
                GENERATION_LOGGER.info("Renaming Exploded Archive from: " + explodedArtifactFolder.getName()
                        + " to " + newArtifactFolderName);
                return explodedArtifactFolder.renameTo(newArtifactFolder);
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * unZip Archive
     *
     * @param zipFilePath  input zip file
     * @param outputFolder zip file output folder
     */
    protected static void unZipArchive(GenerationLogger GENERATION_LOGGER, String zipFilePath, String outputFolder)
            throws IOException {
        /**
         * Create Output Directory, but should already Exist.
         */
        File folder = new File(outputFolder);
        if (!folder.exists()) {
            folder.mkdir();
        }
        ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
        ZipEntry entry = zipIn.getNextEntry();
        // iterates over entries in the zip file
        while (entry != null) {
            String filePath = outputFolder + File.separator + entry.getName();
            if (!entry.isDirectory()) {
                // if the entry is a file, extracts it
                extractFile(GENERATION_LOGGER, zipIn, filePath);
            } else {
                // if the entry is a directory, make the directory
                File dir = new File(filePath);
                dir.mkdir();
            }
            zipIn.closeEntry();
            entry = zipIn.getNextEntry();
        }
        zipIn.close();
    }

    /**
     * Extracts a zip entry (file entry)
     *
     * @param zipIn
     * @param filePath
     * @throws IOException
     */
    protected static void extractFile(GenerationLogger GENERATION_LOGGER, ZipInputStream zipIn, String filePath)
            throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        byte[] bytesIn = new byte[4096];
        int read = 0;
        while ((read = zipIn.read(bytesIn)) != -1) {
            bos.write(bytesIn, 0, read);
        }
        bos.close();
        GENERATION_LOGGER.debug("Extracted zip Entry: " + filePath);
    }

    /**
     * Validate a Tomcat Archives Check Sum
     *
     * @param filename FileName to check
     * @return boolean Indicates if checkSums are Equal and Valid or not.
     * @throws IOException              Raised if, File IO Issues
     * @throws NoSuchAlgorithmException Raised if, SHA1 Algorithm does not Exist
     */
    public static String getFileCheckSum(String filename) throws IOException, NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA1");
        FileInputStream fis = new FileInputStream(filename);
        byte[] dataBytes = new byte[8192];

        int nread = 0;

        while ((nread = fis.read(dataBytes)) != -1) {
            md.update(dataBytes, 0, nread);
        }
        ;

        byte[] mdbytes = md.digest();

        //convert the byte to hex format
        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < mdbytes.length; i++) {
            sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        /**
         * Return the Formulated CheckSum
         */
        return sb.toString();
    }

    /**
     * Zip File into Archive
     *
     * @param GENERATION_LOGGER Reference
     * @param zipFilePath       Zip file Destination.
     * @param fileFolder        Folder to be zipped Up ...
     * @throws IOException
     */
    protected static boolean zipFile(GenerationLogger GENERATION_LOGGER, String zipFilePath, String fileFolder)
            throws IOException {
        /**
         * First get All Nodes with Folder
         */
        List<String> fileList = new ArrayList<>();
        generateFileListForCompression(new File(fileFolder), fileList);
        File sourceFolder = new File(fileFolder);
        byte[] buffer = new byte[8192];
        try {
            FileOutputStream fos = new FileOutputStream(zipFilePath);
            ZipOutputStream zos = new ZipOutputStream(fos);

            GENERATION_LOGGER.info("Compressing Instance Generation to Zip: " + zipFilePath);
            /**
             * Loop Over Files
             */
            for (String filename : fileList) {
                String zipEntryName = filename.substring(sourceFolder.getParent().length() + 1, filename.length());
                ZipEntry ze = new ZipEntry(zipEntryName);
                zos.putNextEntry(ze);
                FileInputStream in = new FileInputStream(filename);
                int len;
                while ((len = in.read(buffer)) > 0) {
                    zos.write(buffer, 0, len);
                }
                in.close();
                GENERATION_LOGGER.debug("File Added to Archive: " + zipEntryName);
            }
            /**
             * Close
             */
            zos.closeEntry();
            zos.close();
            return true;
        } catch (IOException ex) {
            GENERATION_LOGGER.error(ex.getMessage());
            ex.printStackTrace();
            return false;
        }
    }

    /**
     * Traverse a directory and get all files,
     * and add the file into fileList
     *
     * @param node file or directory
     */
    protected static void generateFileListForCompression(File node, List<String> fileList) {
        if (node.isFile()) {
            fileList.add(node.getAbsolutePath());
        }
        if (node.isDirectory()) {
            String[] subNote = node.list();
            for (String filename : subNote) {
                generateFileListForCompression(new File(node, filename), fileList);
            }
        }
    }

    /**
     * Generate any Additional Directories per our Customizations.
     *
     * @param GENERATION_LOGGER Logger Reference
     * @param tomcatInstance    Tomcat Instance POJO
     * @return boolean to indicate that the Additional Directories were or were not created...
     */
    protected static boolean additionalDirectories(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) {
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder());
        if (destinationFolder.exists() && destinationFolder.isDirectory()) {
            for (String additionalDirectory : ADDITIONAL_DIRECTORIES_TO_BE_ADDED) {
                File newDirectory = new File(destinationFolder + File.separator + additionalDirectory);
                if (newDirectory.exists()) {
                    GENERATION_LOGGER
                            .info("Tomcat Sub-Directory: " + additionalDirectory + ", already exists, Ignoring!");
                } else {
                    GENERATION_LOGGER.info("Creating new Tomcat Sub-Directory: " + additionalDirectory);
                    newDirectory.mkdirs();
                }
            }
            return true;
        } else {
            GENERATION_LOGGER
                    .error("Could not find Destination Folder: " + destinationFolder + ", unable to Continue!");
            return false;
        }
    }

    /**
     * Seed Any Additional Directories which are empty to begin with, as they will not be
     * Archived if a compressed Archive is requested. So ensure these empty directories have a dummy file
     * in there to ensure the directory is copied during compression.
     *
     * @param GENERATION_LOGGER Logger Reference
     * @param tomcatInstance    Tomcat Instance POJO
     * @return boolean to indicate that the Additional Directories were or were not created...
     */
    protected static boolean seedDirectories(GenerationLogger GENERATION_LOGGER, TomcatInstance tomcatInstance) {
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder());
        if (destinationFolder.exists() && destinationFolder.isDirectory()) {
            for (int i = 0; i < EMPTY_DIRECTORIES_TO_BE_SEEDED.length; i++) {
                String seedDirectory = EMPTY_DIRECTORIES_TO_BE_SEEDED[i];
                File newDirectory = new File(destinationFolder + File.separator + seedDirectory);
                if (!newDirectory.exists()) {
                    GENERATION_LOGGER.error("Could not find Destination Folder: " + newDirectory
                            + ", should have been created, unable to Continue!");
                    return false;
                }
                /**
                 * Now Add a README.txt File to describe the Directory.
                 */
                ResourceHelper.persistStringDataAsFile(EMPTY_DIRECTORIES_README_CONTENTS[i],
                        newDirectory.getAbsolutePath() + File.separator + README);
            }
            return true;
        } else {
            GENERATION_LOGGER
                    .error("Could not find Destination Folder: " + destinationFolder + ", unable to Continue!");
            return false;
        }
    }

    /**
     * Add Additional Libraries to the Tomcat Instance as prescribed by our Default Definitions.
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean indicates if process was successfully for not.
     * @throws IOException If IO failure Occurs.
     */
    protected static boolean additionalLibArtifacts(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) throws IOException {
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder() + File.separator + "lib");
        if (destinationFolder.exists() && destinationFolder.isDirectory()) {

            for (String additionalLibArtifact : ADDITIONAL_LIB_ARTIFACTS_TO_BE_ADDED) {
                GENERATION_LOGGER.info("Adding Additional Tomcat 'lib' Artifact: " + additionalLibArtifact);
                ResourceHelper.readResourceToBinaryFile("tc/lib_additions/" + additionalLibArtifact,
                        destinationFolder.getAbsolutePath() + File.separator + additionalLibArtifact);
            }
            return true;
        } else {
            GENERATION_LOGGER
                    .error("Could not find Destination Folder: " + destinationFolder + ", unable to Continue!");
            return false;
        }
    }

    /**
     * Add Additional Libraries to the Tomcat Instance as prescribed by our Default Definitions.
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean indicates if process was successfully for not.
     * @throws IOException If IO failure Occurs.
     */
    protected static boolean additionalWebApps(GenerationLogger GENERATION_LOGGER, TomcatInstance tomcatInstance)
            throws IOException {
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder() + File.separator + "webapps");
        if (destinationFolder.exists() && destinationFolder.isDirectory()) {
            /**
             * Add any Specific Apps that are related to enable Instance Management...
             */
            if (tomcatInstance.isInstanceManagement()) {
                for (String additionalWebApp : ADDITIONAL_WEBAPPS_TO_BE_ADDED_FOR_MANAGED_INSTANCE) {
                    GENERATION_LOGGER.info("Adding Additional Tomcat Management Application: " + additionalWebApp);
                    ResourceHelper.readResourceToBinaryFile("tc/webapps/" + additionalWebApp,
                            destinationFolder.getAbsolutePath() + File.separator + additionalWebApp);
                }
            }
            return true;
        } else {
            GENERATION_LOGGER
                    .error("Could not find Destination Folder: " + destinationFolder + ", unable to Continue!");
            return false;
        }
    }

    /**
     * Generate YAML Configuration File for Instance Being Generated
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    Instance to be Converted to YAML.
     * @return boolean Indicates if Method process was successful or not.
     */
    public static boolean generateYAMLConfigurationForInstance(GenerationLogger GENERATION_LOGGER,
            TomcatInstance tomcatInstance) {
        /**
         * Instantiate YAML processor
         */
        Yaml yaml = new Yaml();
        Writer writer = null;
        /**
         * Dump our Tomcat Instance Configuration to a YAML File.
         */
        File destinationYAMLFile = tomcatInstance.referenceSourceYAMLFile();
        GENERATION_LOGGER.info("Creating YAML Configuration File: " + destinationYAMLFile.getAbsolutePath());
        try {
            /**
             * We shall persist the YAML in the form of a Map for easy mobility.
             */
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destinationYAMLFile), "utf-8"));
            yaml.dump(tomcatInstance.map(), writer);
            return true;
        } catch (IOException ex) {
            GENERATION_LOGGER.error(
                    "Unable to Create YAML Configuration File: " + destinationYAMLFile + ", unable to Continue!");
            return false;
        } finally {
            try {
                if (writer != null)
                    writer.close();
            } catch (Exception ex) {
                /*ignore*/}
        }
    }

    /**
     * loadYAMLConfigurationForInstance
     *
     * @param fileName To Read in TomcatInstance YAML Configuration.
     * @return TomcatInstance instantiate from persisted Configuraiton File.
     * @throws IOException Thrown if issues reading YAML File.
     */
    public static final TomcatInstance loadYAMLConfigurationForInstance(String fileName) throws IOException {
        Yaml yaml = new Yaml(new Constructor(Map.class));
        InputStream input = new FileInputStream(new File(fileName));
        Map<String, Object> mapFromYaml = (Map) yaml.load(input);
        return new TomcatInstance(mapFromYaml);
    }

    /**
     * Copy over our new YAML Configuration File into the Base of the Tomcat Instance.
     *
     * @param tomcatInstance POJO
     * @throws Exception thrown if issues arise ...
     */
    protected static void copyYAMLConfigurationFileToTomcatInstanceDirectory(TomcatInstance tomcatInstance)
            throws Exception {
        /**
         * Copy over the Generated YAML File into our Instance Folder for safe keeping.
         */
        File yamlSourceFile = tomcatInstance.referenceSourceYAMLFile();
        File yamlDestinationFile = tomcatInstance.referenceDestinationYAMLFile();
        FileUtils.copyFile(yamlSourceFile, yamlDestinationFile);
    }

    /**
     * Customize the Manager Application for Deployments and such ...
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean Indicates if Method process was successful or not.
     */
    public static boolean customizeManagerApp(GenerationLogger GENERATION_LOGGER, TomcatInstance tomcatInstance) {
        /**
         * Get Short Version Name.
         */
        String shortVersionName = lookupTomcatVersionShortName(tomcatInstance);
        /**
         * Formulate the Destination Folder
         */
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder() + File.separator + "webapps" + File.separator
                + "manager" + File.separator + "WEB-INF");
        if (destinationFolder.exists() && destinationFolder.isDirectory()) {
            /**
             * Update the Manager WebApp with our Customizations ...
             */
            if (!ResourceHelper.persistFilteredResourceAsFile("tc/manager/" + shortVersionName + "/web.xml",
                    destinationFolder.getAbsolutePath() + File.separator + "web.xml", null)) {
                GENERATION_LOGGER.error("Issue performing Customization of 'manager'  App, unable to Continue!");
                return false;
            }
            return true;
        } else {
            GENERATION_LOGGER.error("Could not find Destination Folder: " + destinationFolder
                    + ", for Customizing 'manager' App, unable to Continue!");
            return false;
        }
    }

    /**
     * Customize the 'conf' aspects of the Tomcat Instance ...
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean Indicates if Method process was successful or not.
     */
    public static boolean customizeConf(GenerationLogger GENERATION_LOGGER, TomcatInstance tomcatInstance) {
        /**
         * Get Short Version Name.
         */
        String shortVersionName = lookupTomcatVersionShortName(tomcatInstance);
        /**
         * Formulate the Destination Folder and verify it's existence ...
         */
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder() + File.separator + "conf");

        if (!destinationFolder.exists() || !destinationFolder.isDirectory()) {
            GENERATION_LOGGER.error("Could not find Destination Folder: " + destinationFolder
                    + ", for Customizing 'conf' aspects, unable to Continue!");
            return false;
        }

        /**
         * Create a Replacement Map for our Various Setting at this Phase.
         */
        Map<String, String> replacementMap = tomcatInstance.configurationReplacementMapPhase1();

        /**
         * Iterate over files to be customized for 'conf' ...
         */
        for (String confResourceName : CONF_FILES_TO_BE_ADDED_FILTERED) {
            if (!ResourceHelper.persistFilteredResourceAsFile(
                    "tc/conf/" + shortVersionName + "/" + confResourceName,
                    destinationFolder.getAbsolutePath() + File.separator + confResourceName, replacementMap)) {
                GENERATION_LOGGER.error("Issue performing Customization of 'conf' Resource: " + confResourceName
                        + ", unable to Continue!");
                return false;
            } else {
                GENERATION_LOGGER.info("Customization of 'conf' Resource: " + confResourceName + ", successful.");
            }
        }
        return true;
    }

    /**
     * Customize the 'bin' aspects of the Tomcat Instance ...
     *
     * @param GENERATION_LOGGER Reference
     * @param tomcatInstance    POJO
     * @return boolean Indicates if Method process was successful or not.
     */
    public static boolean customizeBin(GenerationLogger GENERATION_LOGGER, TomcatInstance tomcatInstance) {
        /**
         * Get Short Version Name.
         */
        String shortVersionName = lookupTomcatVersionShortName(tomcatInstance);
        /**
         * Formulate the Destination Folder and verify it's existence ...
         */
        File destinationFolder = new File(tomcatInstance.getDestinationFolder().getAbsolutePath() + File.separator
                + tomcatInstance.referenceTomcatInstanceFolder() + File.separator + "bin");

        if (!destinationFolder.exists() || !destinationFolder.isDirectory()) {
            GENERATION_LOGGER.error("Could not find Destination Folder: " + destinationFolder
                    + ", for Customizing 'bin' aspects, unable to Continue!");
            return false;
        }

        /**
         * Iterate over files to be customized for 'bin' ...
         */
        for (String binResourceName : BIN_FILES_TO_BE_ADDED_FILTERED) {
            /**
             * Create a Replacement Map for our Various Setting at this Phase.
             */
            Map<String, String> replacementMap = tomcatInstance.configurationReplacementMapPhase1();

            /**
             * Add Additional Elements to the replacement Map for primary JAVA JVM Options,
             * Instance Properties and Management Properties.
             *
             * We recreate for each file, as we need to understand the OS type of File
             * we are dealing with as that will affect that replacement data.
             */
            addReplacementsForJvmOptions(binResourceName, replacementMap, tomcatInstance);
            addReplacementsForInstanceProperties(binResourceName, replacementMap, tomcatInstance);
            addReplacementsForManagementProperties(binResourceName, replacementMap, tomcatInstance);

            /**
             * Perform the replacements for this File and Persist accordingly ...
             */
            if (!ResourceHelper.persistFilteredResourceAsFile("tc/bin/" + binResourceName,
                    destinationFolder.getAbsolutePath() + File.separator + binResourceName, replacementMap)) {
                GENERATION_LOGGER.error("Issue performing Customization of 'bin' Resource: " + binResourceName
                        + ", unable to Continue!");
                return false;
            } else {
                GENERATION_LOGGER.info("Customization of 'bin' Resource: " + binResourceName + ", successful.");
            }
        }
        return true;
    }

    /**
     * Lookup our Tomcat Version Short Name for use to obtain embedded Artifacts which are
     * version specific.
     *
     * @param tomcatInstance POJO to read Version
     * @return String containing shorthave or short name of Tomcat Version.
     */
    public static String lookupTomcatVersionShortName(TomcatInstance tomcatInstance) {
        String shortName = TOMCAT_VERSIONS_TO_SHORT_NAME.get(tomcatInstance.getTomcatVersion());
        if (shortName == null || shortName.isEmpty()) {
            throw new IllegalStateException("Unable to determine Short Name for Tomcat Version: "
                    + tomcatInstance.getTomcatVersion() + ", Update Code Base!");
        } else {
            return shortName;
        }
    }

    /**
     * Add Replacements for JVM Options
     *
     * @param binResourceName Resource Name we need to check the FileType.
     * @param replacementMap  Replacements Key value Pairs.
     * @param tomcatInstance  POJO
     */
    public static void addReplacementsForJvmOptions(String binResourceName, Map<String, String> replacementMap,
            TomcatInstance tomcatInstance) {
        boolean WIN = binResourceName.endsWith(".bat");
        StringBuilder sb = new StringBuilder();
        /**
         * Construct High Level JVM Options ...
         */
        buildOptionalJVMOption(tomcatInstance.getJvmOptionXms(), "-Xms", sb, WIN);

        buildOptionalJVMOption(tomcatInstance.getJvmOptionXmx(), "-Xmx", sb, WIN);

        buildOptionalJVMOption(tomcatInstance.getJvmOptionXss(), "-Xss", sb, WIN);

        buildOptionalJVMOption(tomcatInstance.getJvmOptionXXMaxMetaspaceSize(), "-XX:MaxMetaspaceSize=", sb, WIN);

        /**
         * Obtain Additional Options
         */
        for (String jvmOption : tomcatInstance.getJvmOptions()) {
            if (jvmOption.startsWith("#")) {
                // Simple Write a Comment as is...
                if (WIN) {
                    sb.append("rem ");
                }
                sb.append(jvmOption);
            } else {
                if (WIN) {
                    sb.append(WIN_SET).append(JAVA_OPTS).append("=").append("%").append(JAVA_OPTS).append("%");
                    sb.append(" ").append(jvmOption);
                } else {
                    sb.append(JAVA_OPTS).append("=\"").append("${").append(JAVA_OPTS).append("}");
                    sb.append(" ").append(getNIXValue(jvmOption)).append("\"");
                }
            }
            if (WIN) {
                sb.append(WIN_LS);
            } else {
                sb.append(NIX_LS);
            }
        }
        /**
         * Save the Options for Replacement ...
         */
        replacementMap.put(TOMCAT_JVM_OPTIONS, sb.toString());
    }

    protected static void buildOptionalJVMOption(String jvmOption, String prefix, StringBuilder sb, boolean WIN) {
        if (!jvmOption.equalsIgnoreCase("NONE")) {
            if (WIN) {
                sb.append(WIN_SET).append(JAVA_OPTS).append("=").append("%").append(JAVA_OPTS).append("%");
                sb.append(" ").append(prefix).append(jvmOption);
            } else {
                sb.append(JAVA_OPTS).append("=\"").append("${").append(JAVA_OPTS).append("}");
                sb.append(" ").append(prefix).append(getNIXValue(jvmOption)).append("\"");
            }
        } else {
            if (WIN) {
                sb.append("rem ").append(WIN_SET).append(JAVA_OPTS).append("=").append("%").append(JAVA_OPTS)
                        .append("%");
                sb.append(" ").append(prefix).append(" None Specified for this configuration.");
            } else {
                sb.append("# ").append(JAVA_OPTS).append("=\"").append("${").append(JAVA_OPTS).append("}");
                sb.append(" ").append(prefix).append("?\"").append(" #  None Specified for this configuration.");
            }
        }
        if (WIN) {
            sb.append(WIN_LS);
        } else {
            sb.append(NIX_LS);
        }
    }

    /**
     * Add Replacements for Instance Properties ...
     *
     * @param binResourceName Resource Name we need to check the FileType.
     * @param replacementMap  Replacements Key value Pairs.
     * @param tomcatInstance  POJO
     */
    public static void addReplacementsForInstanceProperties(String binResourceName,
            Map<String, String> replacementMap, TomcatInstance tomcatInstance) {
        boolean WIN = binResourceName.endsWith(".bat");
        StringBuilder sb = new StringBuilder();
        for (TomcatInstanceProperty tomcatInstanceProperty : tomcatInstance.getInstanceProperties()) {
            if (tomcatInstanceProperty.getPropertyName().startsWith("#")) {
                // Simple Write a Comment as is...
                if (WIN) {
                    sb.append("rem ");
                }
                sb.append(tomcatInstanceProperty.getPropertyName()).append("=")
                        .append(tomcatInstanceProperty.getPropertyValue());
            } else {
                if (WIN) {
                    sb.append(WIN_SET).append(JAVA_OPTS).append("=").append("%").append(JAVA_OPTS).append("%")
                            .append(" ");
                    sb.append("-D").append(tomcatInstanceProperty.getPropertyName()).append("=")
                            .append(tomcatInstanceProperty.getPropertyValue());
                } else {
                    sb.append(JAVA_OPTS).append("=\"").append("${").append(JAVA_OPTS).append("}").append(" ");
                    sb.append("-D").append(tomcatInstanceProperty.getPropertyName()).append("=")
                            .append(getNIXValue(tomcatInstanceProperty.getPropertyValue())).append("\"");
                }
            }
            if (WIN) {
                sb.append(WIN_LS);
            } else {
                sb.append(NIX_LS);
            }
        }

        /**
         * Save the Properties for Replacement ...
         */
        replacementMap.put(TOMCAT_INSTANCE_PROPERTIES, sb.toString());
    }

    /**
     * Add Replacements for Management Property Options if applicable ...
     *
     * @param binResourceName Resource Name we need to check the FileType.
     * @param replacementMap  Replacements Key value Pairs.
     * @param tomcatInstance  POJO
     */
    public static void addReplacementsForManagementProperties(String binResourceName,
            Map<String, String> replacementMap, TomcatInstance tomcatInstance) {
        /**
         * Create Additional Internal Filters for Management Properties, which if have not been modified,
         * should be replaced.
         */
        Map<String, String> internalReplacementMap = new HashMap<>();
        internalReplacementMap.put(TOMCAT_INSTANCE_NAME_TAG, tomcatInstance.getInstanceName());
        internalReplacementMap.put(TOMCAT_INSTANCE_HOSTNAME_TAG, getThisDefaultInetAddress());
        internalReplacementMap.put(TOMCAT_PRIMARY_PORT_TAG, tomcatInstance.getPrimaryPort().toString());

        /**
         * Obtain our ManageCat License Information and other specifics if necessary.
         */
        Map<String, String> manageCatResolvedProperties = resolvedExternalManagementProperties();
        internalReplacementMap.put(MANAGECAT_LICENSE_KEY_TAG,
                manageCatResolvedProperties.get(DefaultDefinitions.MANAGECAT_LICENSE_KEY_INTERNAL_PROPERTY_NAME));

        /**
         * If the Management Options was specified, add the applicable Replacements.
         */
        boolean WIN = binResourceName.endsWith(".bat");
        StringBuilder sb = new StringBuilder();
        for (TomcatInstanceProperty tomcatInstanceProperty : tomcatInstance.getInstanceManagementProperties()) {
            if (tomcatInstanceProperty.getPropertyName().startsWith("#")) {
                // Simple Write a Comment as is...
                if (WIN) {
                    sb.append("rem ");
                }
                sb.append(tomcatInstanceProperty.getPropertyName()).append("=")
                        .append(tomcatInstanceProperty.getPropertyValue());
            } else {
                String linePrefix = "";
                if (!tomcatInstance.isInstanceManagement()) {
                    linePrefix = (WIN) ? "rem " : "# ";
                }
                if (WIN) {
                    sb.append(linePrefix).append(WIN_SET).append(JAVA_OPTS).append("=").append("%")
                            .append(JAVA_OPTS).append("%").append(" ");
                    sb.append("-D").append(tomcatInstanceProperty.getPropertyName()).append("=")
                            .append(tomcatInstanceProperty.getPropertyValue());
                } else {
                    sb.append(linePrefix).append(JAVA_OPTS).append("=\"").append("${").append(JAVA_OPTS).append("}")
                            .append(" ");
                    sb.append("-D").append(tomcatInstanceProperty.getPropertyName()).append("=")
                            .append(getNIXValue(tomcatInstanceProperty.getPropertyValue())).append("\"");
                }
            }
            if (WIN) {
                sb.append(WIN_LS);
            } else {
                sb.append(NIX_LS);
            }
        }
        /**
         * Now perform Internal Replacement of any Properties Value which need to be replaced.
         */
        ResourceHelper.replace(sb, internalReplacementMap);
        /**
         * Save the Properties for Replacement ...
         */
        replacementMap.put(TOMCAT_INSTANCE_MANAGEMENT_PROPERTIES, sb.toString());
    }

    /**
     * get *NIX Value for a Property Setting.
     * If the Value is surrounded in quotes, escape them ...
     *
     * @param inValue Incoming String Value to Check Strings/
     * @return
     */
    protected static String getNIXValue(String inValue) {
        if (inValue.startsWith("\"") && inValue.endsWith("\"")) {
            return "\\\"" + inValue.substring(1, inValue.length() - 1) + "\\\"";
        } else {
            return inValue;
        }
    }

    /**
     * Get this Instances Default HostName by executing the 'hostname' command against this
     * operating system.
     *
     * @return String containing hostname or 'localhost' if system command fails.
     */
    public static final String getThisDefaultInstanceHostName() {
        try {
            return execReadToString("hostname");
        } catch (IOException ioe) {
            return "localhost";
        }
    }

    /**
     * Execute a Local System Command and pull in it's response.
     *
     * @param execCommand System command to be executed.
     * @return String containing contents of system command response.
     * @throws IOException thrown if IO Exception occurs.
     */
    protected static String execReadToString(String execCommand) throws IOException {
        Process proc = Runtime.getRuntime().exec(execCommand);
        try (InputStream stream = proc.getInputStream()) {
            try (Scanner s = new Scanner(stream).useDelimiter("\\A")) {
                return s.hasNext() ? s.next() : "";
            }
        }
    }

    /**
     * Get this Instances Default IP Address.
     *
     * @return String containing IP Address or 'localhost' if system command fails.
     */
    protected static final String getThisDefaultInetAddress() {
        try {
            InetAddress ipAddress = InetAddress.getLocalHost();
            return ipAddress.getHostAddress();
        } catch (Exception e) {
            return "localhost";
        }
    }

    /**
     * loadAvailableArchives
     *
     * @return TomcatInstance instantiate from persisted Configuraiton File.
     * @throws IOException Thrown if issues reading YAML File.
     */
    public static final TomcatAvailableArchives loadAvailableArchives() throws IOException {
        Yaml yaml = new Yaml(new Constructor(Map.class));
        /**
         * Check for an Available Archive Configuration File...
         */
        String propertyFileName = System.getProperty(DefaultDefinitions.TOMCAT_ARCHIVE_PROPERTY_NAME);
        if (propertyFileName == null || propertyFileName.isEmpty()) {
            /**
             * No remote Archives will be Available to pull down...
             */
            return new TomcatAvailableArchives();
        }
        /**
         * We have a specified Archive Configuration, so parse and have those archives available for selection.
         */
        InputStream input = new FileInputStream(new File(propertyFileName));
        Map<String, Object> mapFromYaml = (Map) yaml.load(input);
        return new TomcatAvailableArchives(mapFromYaml);
    }

    /**
     * resolvedExternalManagementProperties
     * Obtain any Externalized Management Properties.
     *
     * @return Map containing resolved Properties.
     */
    public static Map<String, String> resolvedExternalManagementProperties() {
        Map<String, String> resolvedProperties = new HashMap<>();
        /**
         * Get the ManageCat License Key Property
         */
        String value = System.getProperty(DefaultDefinitions.MANAGECAT_LICENSE_KEY_PROPERTY_NAME);
        if (value == null || value.isEmpty()) {
            value = DefaultDefinitions.DEFAULT_MANAGECAT_LICENSE_KEY_VALUE;
        }
        resolvedProperties.put(DefaultDefinitions.MANAGECAT_LICENSE_KEY_INTERNAL_PROPERTY_NAME, value);
        /**
         * return the Resolved Properties
         */
        return resolvedProperties;
    }

}