/* * 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 as published by * the Free Software Foundation version 2 of the License. * * 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 for more details. * * You should have received a copy of the GNU 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.type; import static org.rhq.core.util.file.FileUtil.copyFile; import static org.rhq.core.util.file.FileUtil.createTempDirectory; import static org.rhq.core.util.file.FileUtil.purge; import static org.rhq.core.util.file.FileUtil.writeFile; import static org.rhq.core.util.stream.StreamUtil.copy; import static org.rhq.core.util.stream.StreamUtil.safeClose; import static org.rhq.core.util.stream.StreamUtil.slurp; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Target; import org.rhq.bundle.ant.BundleAntProject.AuditStatus; import org.rhq.bundle.ant.DeployPropertyNames; import org.rhq.bundle.ant.HandoverTarget; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.system.SystemInfoFactory; import org.rhq.core.template.TemplateEngine; import org.rhq.core.util.ZipUtil; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.core.util.updater.DeployDifferences; import org.rhq.core.util.updater.Deployer; import org.rhq.core.util.updater.DeploymentData; import org.rhq.core.util.updater.DeploymentProperties; import org.rhq.core.util.updater.DestinationComplianceMode; /** * An Ant task for deploying a bundle or previewing the deployment. * * @author Ian Springer */ public class DeploymentUnitType extends AbstractBundleType { private String name; private DestinationComplianceMode compliance; // -------- Containers for file and archives to lay down private Map<File, File> files = new LinkedHashMap<File, File>(); private Map<URL, File> urlFiles = new LinkedHashMap<URL, File>(); private Set<File> rawFilesToReplace = new LinkedHashSet<File>(); private Set<URL> rawUrlFilesToReplace = new LinkedHashSet<URL>(); private Map<File, String> localFileNames = new LinkedHashMap<File, String>(); private Map<File, File> archives = new LinkedHashMap<File, File>(); private Map<URL, File> urlArchives = new LinkedHashMap<URL, File>(); private Map<File, Pattern> archiveReplacePatterns = new HashMap<File, Pattern>(); private Map<URL, Pattern> urlArchiveReplacePatterns = new HashMap<URL, Pattern>(); private Map<File, Boolean> archivesExploded = new HashMap<File, Boolean>(); private Map<URL, Boolean> urlArchivesExploded = new HashMap<URL, Boolean>(); private Map<File, String> localArchiveNames = new LinkedHashMap<File, String>(); // ------- Container for files and archives to handover (ordered) private List<HasHandover> contentToHandover = new ArrayList<HasHandover>(); private SystemServiceType systemService; private Pattern ignorePattern; private String preinstallTarget; private String postinstallTarget; public void init() throws BuildException { if (this.systemService != null) { this.systemService.init(); } } public void install(boolean revert, boolean clean) throws BuildException { if (clean) { getProject().auditLog( AuditStatus.INFO, "Clean Requested", "A clean deployment has been requested. Files will be deleted!", "A clean deployment has been requested. Files will be deleted" + " from the destination directory prior to the new deployment files getting written", null); } if (revert) { getProject().auditLog( AuditStatus.INFO, "Revert Requested", "The previous deployment will be reverted!", "The previous deployment will be reverted. An attempt to restore" + " backed up files and the old deployment content will be made", null); } File baseDir = getProject().getBaseDir(); File handoverRemoteContentDownloadDirectory = null; try { boolean dryRun = getProject().isDryRun(); DestinationComplianceMode complianceToUse = DestinationComplianceMode.instanceOrDefault(this.compliance); File deployDir = getProject().getDeployDir(); @SuppressWarnings("unchecked") TemplateEngine templateEngine = createTemplateEngine(getProject().getProperties()); int deploymentId = getProject().getDeploymentId(); DeploymentProperties deploymentProps = new DeploymentProperties(deploymentId, getProject().getBundleName(), getProject().getBundleVersion(), getProject().getBundleDescription(), complianceToUse); if (this.preinstallTarget != null) { getProject().auditLog(AuditStatus.SUCCESS, "Pre-Install Started", "The pre install target will start", "The pre install target named [" + this.preinstallTarget + "] will start", null); Target target = (Target) getProject().getTargets().get(this.preinstallTarget); if (target == null) { try { getProject().auditLog( AuditStatus.FAILURE, "Pre-Install Failure", "The pre install target does not exist", "The pre install target specified in the recipe [" + this.preinstallTarget + "] does not exist.", null); } catch (Throwable ignore) { // swallow any errors that occur here, we want to throw the real build exception } throw new BuildException("Specified preinstall target (" + this.preinstallTarget + ") does not exist."); } target.performTasks(); getProject().auditLog(AuditStatus.SUCCESS, "Pre-Install Finished", "The pre install target has finished", null, null); } boolean haveSomethingToDo = false; if (!this.files.isEmpty()) { haveSomethingToDo = true; log("Deploying files " + this.files + "...", Project.MSG_VERBOSE); } if (!this.urlFiles.isEmpty()) { haveSomethingToDo = true; log("Deploying files from URL " + this.urlFiles + "...", Project.MSG_VERBOSE); } if (!this.archives.isEmpty()) { haveSomethingToDo = true; log("Deploying archives " + this.archives + "...", Project.MSG_VERBOSE); } if (!this.urlArchives.isEmpty()) { haveSomethingToDo = true; log("Deploying archives from URL " + this.urlArchives + "...", Project.MSG_VERBOSE); } if (!this.contentToHandover.isEmpty()) { haveSomethingToDo = true; log("Handing over " + this.contentToHandover.size() + " file(s)/archive(s)...", Project.MSG_VERBOSE); } if (!haveSomethingToDo) { throw new BuildException( "You must specify at least one file to deploy via nested file, archive, url-file, url-archive types in your recipe"); } log("Destination compliance set to '" + complianceToUse + "'.", Project.MSG_VERBOSE); switch (complianceToUse) { case full: if (!dryRun) { getProject() .auditLog( AuditStatus.INFO, "Managing Top Level Deployment Directory", "The top level deployment directory will be managed - files found there will be backed up and removed!", "The bundle recipe has requested that the top level deployment directory be fully managed by RHQ." + "This means any files currently located in the top level deployment directory will be removed and backed up", null); } break; case filesAndDirectories: log("Files and directories in the destination directory not contained in the bundle will be kept intact.\n" + "Note that the contents of the directories that ARE contained in the bundle will be synced with " + "the contents as specified in the bundle. I.e. the subdirectories in the destination that are also " + "contained in the bundle are made compliant with the bundle.", Project.MSG_VERBOSE); break; default: throw new IllegalStateException("Unhandled destination compliance mode: " + complianceToUse.toString()); } Map<File, File> allArchives = new HashMap<File, File>(this.archives); Map<File, File> allFiles = new HashMap<File, File>(this.files); Map<File, Pattern> allArchiveReplacePatterns = new HashMap<File, Pattern>(this.archiveReplacePatterns); Set<File> allRawFilesToReplace = new HashSet<File>(this.rawFilesToReplace); Map<File, Boolean> allArchivesExploded = new HashMap<File, Boolean>(this.archivesExploded); downloadFilesFromUrlEndpoints(allArchives, allFiles, allArchiveReplacePatterns, allRawFilesToReplace, allArchivesExploded); handoverRemoteContentDownloadDirectory = createTempDirectory("handover-", ".download", baseDir); Map<HasHandover, File> downloadedFilesToHandover = downloadRemoteContentToHandover(handoverRemoteContentDownloadDirectory); try { DeploymentData deploymentData = new DeploymentData(deploymentProps, baseDir, deployDir, allFiles, allRawFilesToReplace, allArchives, allArchiveReplacePatterns, templateEngine, this.ignorePattern, allArchivesExploded); Deployer deployer = new Deployer(deploymentData); DeployDifferences diffs = getProject().getDeployDifferences(); // we only want to emit audit trail when something is really going to happen on disk; don't log if doing a dry run if (!dryRun) { getProject().auditLog(AuditStatus.SUCCESS, "Deployer Started", "The deployer has started its work", null, null); } if (revert) { deployer.redeployAndRestoreBackupFiles(diffs, clean, dryRun); } else { deployer.deploy(diffs, clean, dryRun); } HandoverTarget handoverTarget = getProject().getHandoverTarget(); if (handoverTarget != null) { for (HasHandover hasHandoverReference : contentToHandover) { Handover handoverTag = hasHandoverReference.getHandover(); File source = getFileSource(hasHandoverReference, downloadedFilesToHandover, templateEngine); FileInputStream contentStream = new FileInputStream(source); HandoverInfo.Builder builder = new HandoverInfo.Builder(); builder.setContent(contentStream); builder.setFilename(source.getName()); builder.setAction(handoverTag.getAction()); builder.setParams(handoverTag.getHandoverParams()); builder.setRevert(revert); HandoverInfo handoverInfo = builder.createHandoverInfo(); if (!dryRun) { try { boolean handoverSuccess = handoverTarget.handoverContent(handoverInfo); String informationMessage = "Source: " + source.getName() + ", " + handoverTag; if (handoverSuccess) { getProject().auditLog(AuditStatus.INFO, "Handover", "Handover target reported success", informationMessage, null); } else { if (handoverTag.isFailonerror()) { getProject().auditLog(AuditStatus.FAILURE, "Handover", "Handover target reported a failure", informationMessage, null); throw new Exception("Handover failed: " + handoverTag); } else { getProject().auditLog(AuditStatus.WARN, "Handover", "Handover target reported a failure", informationMessage, null); } } } finally { safeClose(contentStream); } } } } // we only want to emit audit trail when something is really going to happen on disk; don't log if doing a dry run if (!dryRun) { getProject().auditLog(AuditStatus.SUCCESS, "Deployer Finished", "The deployer has finished its work", null, diffs.toString()); } } catch (Throwable t) { try { getProject().auditLog(AuditStatus.FAILURE, "Deployer Failed", "The deployer encountered an error and could not finished", ThrowableUtil.getAllMessages(t), ThrowableUtil.getStackAsString(t)); } catch (Throwable ignore) { // swallow any errors that occur here, we want to throw the real build exception } throw new BuildException("Failed to deploy bundle [" + getProject().getBundleName() + "] version [" + getProject().getBundleVersion() + "]: " + t, t); } if (this.systemService != null) { this.systemService.install(); } if (this.postinstallTarget != null) { getProject().auditLog(AuditStatus.SUCCESS, "Post-Install Started", "The post install target will start", "The post install target named [" + this.postinstallTarget + "] will start", null); Target target = (Target) getProject().getTargets().get(this.postinstallTarget); if (target == null) { try { getProject().auditLog( AuditStatus.FAILURE, "Post-Install Failure", "The post install target does not exist", "The post install target specified in the recipe [" + this.postinstallTarget + "] does not exist.", null); } catch (Throwable ignore) { // swallow any errors that occur here, we want to throw the real build exception } throw new BuildException("Specified postinstall target (" + this.postinstallTarget + ") does not exist."); } target.performTasks(); getProject().auditLog(AuditStatus.SUCCESS, "Post-Install Finished", "The post install target has finished", null, null); } } catch (Throwable t) { try { getProject().auditLog(AuditStatus.FAILURE, "Error Occurred", "The deployment could not complete successfully.", ThrowableUtil.getAllMessages(t), ThrowableUtil.getStackAsString(t)); } catch (Throwable ignore) { // swallow any errors that occur here, we want to throw the real build exception } if (t instanceof BuildException) { throw (BuildException) t; } else { throw new BuildException(t); } } finally { purge(handoverRemoteContentDownloadDirectory, true); } } private File getFileSource(HasHandover hasHandoverReference, Map<HasHandover, File> downloadedFilesToHandover, TemplateEngine templateEngine) throws Exception { File source; if (hasHandoverReference instanceof UrlFileType) { UrlFileType urlFileType = (UrlFileType) hasHandoverReference; source = downloadedFilesToHandover.get(urlFileType); if (urlFileType.isReplace()) { processFileWithTemplateEngine(source, templateEngine); } } else if (hasHandoverReference instanceof UrlArchiveType) { UrlArchiveType urlArchiveType = (UrlArchiveType) hasHandoverReference; source = downloadedFilesToHandover.get(urlArchiveType); if (urlArchiveType.getReplacePattern() != null) { processArchiveWithTemplateEngine(source, urlArchiveType.getReplacePattern(), templateEngine); } } else if (hasHandoverReference instanceof FileType) { FileType fileType = (FileType) hasHandoverReference; source = fileType.getSource(); if (fileType.isReplace()) { processFileWithTemplateEngine(source, templateEngine); } } else if (hasHandoverReference instanceof ArchiveType) { ArchiveType archiveType = (ArchiveType) hasHandoverReference; source = archiveType.getSource(); if (archiveType.getReplacePattern() != null) { processArchiveWithTemplateEngine(source, archiveType.getReplacePattern(), templateEngine); } } else { throw new RuntimeException("Unsupported type: " + hasHandoverReference.getClass().getName()); } return source; } /** * This will download any files/archives that are found at URL endpoints as declared in the ant recipe. * * @param allArchives when a new archive is downloaded, its information is added to this * @param allFiles when a new raw file is downloaded, its information is added to this * @param allArchiveReplacePatterns when a new archive is downloaded, its information is added to this * @param allRawFilesToReplace when a new raw file is downloaded, its information is added to this * @param allArchivesExploded when a new archive is downloaded, its information is added to this */ private void downloadFilesFromUrlEndpoints(Map<File, File> allArchives, Map<File, File> allFiles, Map<File, Pattern> allArchiveReplacePatterns, Set<File> allRawFilesToReplace, Map<File, Boolean> allArchivesExploded) throws Exception { // check to see if we even need to download anything, if not, do nothing and return immediately if (this.urlFiles.isEmpty() && this.urlArchives.isEmpty()) { return; } // download all our files in the base dir, as if they came with the bundle like normal files File downloadDir = getProject().getBaseDir(); Set<File> downloadedFiles = getProject().getDownloadedFiles(); try { // do the raw files first for (Map.Entry<URL, File> fileEntry : this.urlFiles.entrySet()) { URL url = fileEntry.getKey(); File destFile = fileEntry.getValue(); File tmpFile = new File(downloadDir, destFile.getPath()); // use getPath in case they have 2+ raw files with the same name download(url, tmpFile); downloadedFiles.add(tmpFile); allFiles.put(tmpFile, destFile); if (this.rawUrlFilesToReplace.contains(url)) { allRawFilesToReplace.add(tmpFile); } } // do the archives next for (Map.Entry<URL, File> archiveEntry : this.urlArchives.entrySet()) { // determine what the base filename should be of our downloaded tmp archive file URL url = archiveEntry.getKey(); File destinationDirectory = archiveEntry.getValue(); String baseFileName = url.getPath(); if (baseFileName.endsWith("/")) { baseFileName = baseFileName.substring(0, baseFileName.length()); } int lastSlash = baseFileName.lastIndexOf('/'); if (lastSlash != -1) { baseFileName = baseFileName.substring(lastSlash + 1); } if (baseFileName.length() == 0) { baseFileName = url.getHost(); } File tmpFile = new File(downloadDir, baseFileName); download(url, tmpFile); downloadedFiles.add(tmpFile); allArchives.put(tmpFile, destinationDirectory); if (this.urlArchiveReplacePatterns.containsKey(url)) { allArchiveReplacePatterns.put(tmpFile, this.urlArchiveReplacePatterns.get(url)); } if (this.urlArchivesExploded.containsKey(url)) { allArchivesExploded.put(tmpFile, this.urlArchivesExploded.get(url)); } } } catch (Exception e) { // can't do anything with any files we did download - be nice and clean up try { for (File doomed : downloadedFiles) { doomed.delete(); } } catch (Exception ignore) { // ignore this, we just can't delete them - but we want to throw our original exception } throw e; } } private Map<HasHandover, File> downloadRemoteContentToHandover(File handoverDownloadDirectory) throws Exception { Map<HasHandover, File> result = new HashMap<HasHandover, File>(); for (HasHandover hasHandoverReference : contentToHandover) { URL source; String baseName; if (hasHandoverReference instanceof UrlFileType) { UrlFileType urlFileType = (UrlFileType) hasHandoverReference; source = urlFileType.getSource(); baseName = urlFileType.getBaseName(); } else if (hasHandoverReference instanceof UrlArchiveType) { UrlArchiveType urlArchiveType = (UrlArchiveType) hasHandoverReference; source = urlArchiveType.getSource(); baseName = urlArchiveType.getBaseName(); } else { continue; } Set<File> downloadedFiles = getProject().getDownloadedFiles(); try { File tmpFile = new File(handoverDownloadDirectory, baseName); download(source, tmpFile); downloadedFiles.add(tmpFile); result.put(hasHandoverReference, tmpFile); } catch (Exception e) { // can't do anything with any files we did download - be nice and clean up for (File file : downloadedFiles) { file.delete(); } throw e; } } return result; } private void download(URL url, File tmpFile) throws Exception { getProject().auditLog(AuditStatus.SUCCESS, "File Download Started", "Downloading file from URL", "Downloading file from URL: " + url, null); long size; try { InputStream in = url.openStream(); tmpFile.getParentFile().mkdirs(); // if this fails, our next line will throw a file-not-found error and we'll abort OutputStream out = new FileOutputStream(tmpFile); size = copy(in, out); } catch (Exception e) { getProject().auditLog(AuditStatus.FAILURE, "File Download Failed", "Failed to download content from a remote server", "Failed to download file from: " + url, ThrowableUtil.getStackAsString(e)); throw e; } getProject().auditLog(AuditStatus.SUCCESS, "File Download Finished", "Successfully downloaded file from URL", "Downloaded file of size [" + size + "] bytes from URL: " + url, null); } public void start() throws BuildException { if (this.systemService != null) { this.systemService.start(); } } public void stop() throws BuildException { if (this.systemService != null) { this.systemService.stop(); } } public void upgrade(boolean revert, boolean clean) throws BuildException { install(revert, clean); } public void uninstall() throws BuildException { if (this.systemService != null) { this.systemService.uninstall(); } } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * @deprecated since RHQ 4.9.0, use {@link #getCompliance()} */ @Deprecated public String getManageRootDir() { return Boolean.toString(getCompliance() == DestinationComplianceMode.full); } /** * @deprecated since RHQ 4.9.0, use {@link #setCompliance(org.rhq.core.util.updater.DestinationComplianceMode)} */ @Deprecated public void setManageRootDir(String booleanString) { if (!Boolean.TRUE.toString().equalsIgnoreCase(booleanString) && !Boolean.FALSE.toString().equalsIgnoreCase(booleanString)) { throw new BuildException("manageRootDir attribute must be 'true' or 'false': " + booleanString); } log("The deprecated 'manageRootDir' attribute was detected. Please consider replacing it with the 'compliance' attribute.", Project.MSG_INFO); boolean val = Boolean.parseBoolean(booleanString); setCompliance(val ? DestinationComplianceMode.full : DestinationComplianceMode.filesAndDirectories); } /** * @since 4.9.0 */ public DestinationComplianceMode getCompliance() { return compliance; } /** * @since 4.9.0 */ public void setCompliance(DestinationComplianceMode value) { this.compliance = value; } /** * Returns a map of all raw files. The key is the full absolute path * to the file as it does or would appear on the file system. The value * is a path that is either absolute or relative - it is the destination * where the file is to be placed when being deployed on the destination file system; * if the value is relative, then it is relative to the root destination directory. * * @return map of raw files */ public Map<File, File> getFiles() { return files; } /** * Returns a map of all raw files. The key is the full absolute path * to the file as it does or would appear on the file system (the same key * as the keys in map {@link #getFiles()}). * The value is a path relative to the file as it is found in the bundle distro (this * is the "name" attribute of the "file" type tag). * * @return map of local file names */ public Map<File, String> getLocalFileNames() { return localFileNames; } /** * Returns a map of all archive files. The key is the full absolute path * to the file as it does or would appear on the file system. The value is * the destination directory for the archive. If null the deployment's root * destination directory is assumed. * * @return map of raw files */ public Map<File, File> getArchives() { return archives; } /** * Returns a map of all archive files. The key is the full absolute path * to the archive as it does or would appear on the file system (the same key * as the keys in map {@link #getArchives()}). * The value is a path relative to the file as it is found in the bundle distro (this * is the "name" attribute of the "archive" type tag). * * @return map of local file names */ public Map<File, String> getLocalArchiveNames() { return localArchiveNames; } /** * Returns a map keyed on {@link #getArchives() archive names} whose values * are either true or false, where true means the archive is to be deployed exploded * and false means the archive should be deployed in compressed form. * * @return map showing how an archive should be deployed in its final form */ public Map<File, Boolean> getArchivesExploded() { return archivesExploded; } public String getPreinstallTarget() { return preinstallTarget; } public void setPreinstallTarget(String preinstallTarget) { this.preinstallTarget = preinstallTarget; } public String getPostinstallTarget() { return postinstallTarget; } public void setPostinstallTarget(String postinstallTarget) { this.postinstallTarget = postinstallTarget; } @SuppressWarnings("unused") public void addConfigured(SystemServiceType systemService) { if (this.systemService != null) { throw new IllegalStateException( "A rhq:deployment-unit element can only have one rhq:system-service child element."); } this.systemService = systemService; this.systemService.validate(); // Add the init script and its config file to the list of bundle files. this.files.put(this.systemService.getScriptFile(), this.systemService.getScriptDestFile()); this.localFileNames.put(this.systemService.getScriptFile(), this.systemService.getScriptFileName()); if (this.systemService.getConfigFile() != null) { this.files.put(this.systemService.getConfigFile(), this.systemService.getConfigDestFile()); this.localFileNames.put(this.systemService.getConfigFile(), this.systemService.getConfigFileName()); this.rawFilesToReplace.add(this.systemService.getConfigFile()); } } @SuppressWarnings("unused") public void addConfigured(FileType file) { this.localFileNames.put(file.getSource(), file.getName()); if (file.getHandover() != null) { contentToHandover.add(file); return; } File destFile = file.getDestinationFile(); if (destFile == null) { File destDir = file.getDestinationDir(); destFile = new File(destDir, file.getSource().getName()); } this.files.put(file.getSource(), destFile); // key=full absolute path, value=could be relative or absolute if (file.isReplace()) { this.rawFilesToReplace.add(file.getSource()); } } @SuppressWarnings("unused") public void addConfigured(UrlFileType file) { if (file.getHandover() != null) { contentToHandover.add(file); return; } File destFile = file.getDestinationFile(); if (destFile == null) { File destDir = file.getDestinationDir(); destFile = new File(destDir, file.getBaseName()); } this.urlFiles.put(file.getSource(), destFile); // key=full absolute path, value=could be relative or absolute if (file.isReplace()) { this.rawUrlFilesToReplace.add(file.getSource()); } } @SuppressWarnings("unused") public void addConfigured(ArchiveType archive) { this.localArchiveNames.put(archive.getSource(), archive.getName()); if (archive.getHandover() != null) { contentToHandover.add(archive); return; } this.archives.put(archive.getSource(), archive.getDestinationDir()); Pattern replacePattern = archive.getReplacePattern(); if (replacePattern != null) { this.archiveReplacePatterns.put(archive.getSource(), replacePattern); } Boolean exploded = Boolean.valueOf(archive.getExploded()); this.archivesExploded.put(archive.getSource(), exploded); } @SuppressWarnings("unused") public void addConfigured(UrlArchiveType archive) { if (archive.getHandover() != null) { contentToHandover.add(archive); return; } this.urlArchives.put(archive.getSource(), archive.getDestinationDir()); Pattern replacePattern = archive.getReplacePattern(); if (replacePattern != null) { this.urlArchiveReplacePatterns.put(archive.getSource(), replacePattern); } Boolean exploded = Boolean.valueOf(archive.getExploded()); this.urlArchivesExploded.put(archive.getSource(), exploded); } @SuppressWarnings("unused") public void addConfigured(IgnoreType ignore) { List<FileSet> fileSets = ignore.getFileSets(); this.ignorePattern = getPattern(fileSets); } private TemplateEngine createTemplateEngine(Hashtable<String, String> properties) { TemplateEngine templateEngine = SystemInfoFactory.fetchTemplateEngine(); // add properties to Template Engine tokens if (properties != null) { for (Map.Entry<String, String> e : properties.entrySet()) { templateEngine.getTokens().put(e.getKey(), e.getValue()); } } // Add the deployment props to the template engine's tokens. Configuration config = getProject().getConfiguration(); for (PropertySimple prop : config.getSimpleProperties().values()) { templateEngine.getTokens().put(prop.getName(), prop.getStringValue()); } // And add the special rhq.deploy.dir prop. templateEngine.getTokens().put(DeployPropertyNames.DEPLOY_DIR, getProject().getProperty(DeployPropertyNames.DEPLOY_DIR)); return templateEngine; } private void processFileWithTemplateEngine(File file, TemplateEngine templateEngine) throws Exception { byte[] contentBytes = slurp(new FileInputStream(file)); String processedContent = templateEngine.replaceTokens(new String(contentBytes)); writeFile(new ByteArrayInputStream(processedContent.getBytes()), file); } private void processArchiveWithTemplateEngine(File archive, Pattern realizeRegex, TemplateEngine templateEngine) throws Exception { File baseDir = getProject().getBaseDir(); File tempFile = null; ZipOutputStream zipOutputStream = null; try { tempFile = File.createTempFile("handover-archive-processing-", ".tmp", baseDir); zipOutputStream = new ZipOutputStream(new FileOutputStream(tempFile)); ZipUtil.walkZipFile(archive, new ArchiveEntryVisitor(zipOutputStream, realizeRegex, templateEngine)); safeClose(zipOutputStream); copyFile(tempFile, archive); } finally { safeClose(zipOutputStream); if (tempFile != null) { tempFile.delete(); } } } private static class ArchiveEntryVisitor implements ZipUtil.ZipEntryVisitor { private final ZipOutputStream zipOutputStream; private final Pattern realizeRegex; private final TemplateEngine templateEngine; public ArchiveEntryVisitor(ZipOutputStream zipOutputStream, Pattern realizeRegex, TemplateEngine templateEngine) { this.zipOutputStream = zipOutputStream; this.realizeRegex = realizeRegex; this.templateEngine = templateEngine; } @Override public boolean visit(ZipEntry entry, ZipInputStream stream) throws Exception { String pathName = entry.getName(); zipOutputStream.putNextEntry(new ZipEntry(pathName)); if (entry.isDirectory()) { return true; } if (!realizeRegex.matcher(pathName).matches()) { copy(stream, zipOutputStream, false); return true; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); copy(stream, baos, false); String processedContent = templateEngine.replaceTokens(baos.toString()); copy(new ByteArrayInputStream(processedContent.getBytes()), zipOutputStream, false); return true; } } }