/* * 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.task; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Property; import org.apache.tools.ant.util.FileUtils; import org.rhq.bundle.ant.DeployPropertyNames; import org.rhq.bundle.ant.DeploymentPhase; import org.rhq.bundle.ant.type.DeploymentUnitType; import org.rhq.bundle.ant.type.InputPropertyType; /** * The rhq:bundle task defines the metadata needed to deploy, redeploy, or undeploy an RHQ bundle. * * @author Ian Springer */ public class BundleTask extends AbstractBundleTask { private String name; private String version; private String description; private Map<String, DeploymentUnitType> deploymentUnits = new HashMap<String, DeploymentUnitType>(); private List<Property> properties = new ArrayList<Property>(); private List<String> localPropertyFiles = new ArrayList<String>(); @Override public void maybeConfigure() throws BuildException { // The below call will init the attribute fields. super.maybeConfigure(); validateAttributes(); // TODO: Figure out why the Ant parse() method is not initializing the child Type objects. //validateTypes(); getProject().setBundleName(this.name); getProject().setBundleVersion(this.version); getProject().setBundleDescription(this.description); } /** * The following Ant project properties must be defined with valid values prior to this method being invoked: * * rhq.deploy.id - the {@link org.rhq.core.domain.bundle.BundleDeployment deployment}'s unique id * (e.g. "10001") * rhq.deploy.dir - the {@link org.rhq.core.domain.bundle.BundleDeployment deployment} install dir * (e.g. "/opt/jbossas-petstore") * rhq.deploy.phase - the {@link org.rhq.bundle.ant.DeploymentPhase deployment phase} * * If the bundle recipe is being executed from the command line, the user must supply these properties, along * with any input properties required by the bundle recipe. * * @throws BuildException if an error occurs */ @Override public void execute() throws BuildException { Hashtable projectProps = getProject().getProperties(); // Make sure the requires System properties are defined and valid. String deployDir = (String) projectProps.get(DeployPropertyNames.DEPLOY_DIR); if (deployDir == null) { throw new BuildException("Required property [" + DeployPropertyNames.DEPLOY_DIR + "] was not specified."); } File deployDirFile = new File(deployDir); if (!deployDirFile.isAbsolute()) { // throw exception unless we are on windows and the path is a root dir without a drive letter - ignore missing drive letter if (!deployDirFile.getPath().startsWith(File.separator)) { throw new BuildException("Value of property [" + DeployPropertyNames.DEPLOY_DIR + "] (" + deployDirFile + ") is not an absolute path."); } } getProject().setDeployDir(deployDirFile); log(DeployPropertyNames.DEPLOY_DIR + "=\"" + deployDir + "\"", Project.MSG_DEBUG); String deploymentIdStr = (String) projectProps.get(DeployPropertyNames.DEPLOY_ID); if (deploymentIdStr == null) { throw new BuildException("Required property [" + DeployPropertyNames.DEPLOY_ID + "] was not specified."); } int deploymentId; try { deploymentId = Integer.parseInt(deploymentIdStr); } catch (Exception e) { throw new BuildException("Value of property [" + DeployPropertyNames.DEPLOY_ID + "] (" + deploymentIdStr + ") is not valid.", e); } getProject().setDeploymentId(deploymentId); log(DeployPropertyNames.DEPLOY_ID + "=\"" + deploymentId + "\"", Project.MSG_DEBUG); //k, now that we have the properties pushed to the project, let's init the subtasks for (Property p : properties) { p.execute(); } if (this.deploymentUnits.size() != 1) { throw new BuildException("The rhq:bundle task must contain exactly one rhq:deploymentUnit element."); } DeploymentUnitType deploymentUnit = this.deploymentUnits.values().iterator().next(); String deploymentPhaseName = (String) projectProps.get(DeployPropertyNames.DEPLOY_PHASE); if (deploymentPhaseName == null) { throw new BuildException("Required property [" + DeployPropertyNames.DEPLOY_PHASE + "] was not specified."); } DeploymentPhase deploymentPhase; try { deploymentPhase = DeploymentPhase.valueOf(deploymentPhaseName.toUpperCase()); } catch (IllegalArgumentException e) { DeploymentPhase[] phases = DeploymentPhase.values(); List<String> validPhaseNames = new ArrayList<String>(phases.length); for (DeploymentPhase phase : phases) { validPhaseNames.add(phase.name().toLowerCase()); } throw new BuildException("Value of property '" + DeployPropertyNames.DEPLOY_PHASE + "' (" + deploymentPhaseName + ") is not a valid deployment phase - the valid phases are " + validPhaseNames + "."); } getProject().setDeploymentPhase(deploymentPhase); String dryRunString = (String) projectProps.get(DeployPropertyNames.DEPLOY_DRY_RUN); boolean dryRun = Boolean.valueOf(dryRunString); getProject().setDryRun(dryRun); String revertString = (String) projectProps.get(DeployPropertyNames.DEPLOY_REVERT); boolean revert = Boolean.valueOf(revertString); String cleanString = (String) projectProps.get(DeployPropertyNames.DEPLOY_CLEAN); boolean clean = Boolean.valueOf(cleanString); log("Executing '" + deploymentPhase + "' phase for deployment with id [" + deploymentId + "] from bundle '" + this.name + "' version " + this.version + " using config " + getProject().getConfiguration().toString(true) + " [dryRun=" + dryRun + ", revert=" + revert + ", clean=" + clean + "]..."); deploymentUnit.init(); switch (deploymentPhase) { case INSTALL: // TODO: Revert doesn't really make sense for an initial install. deploymentUnit.install(revert, clean); break; case START: deploymentUnit.start(); break; case STOP: deploymentUnit.stop(); break; case UPGRADE: deploymentUnit.upgrade(revert, clean); break; case UNINSTALL: deploymentUnit.uninstall(); break; } } @Override public String getDescription() { return description; } @Override public void setDescription(String description) { this.description = description; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } @SuppressWarnings("unused") public void addConfigured(InputPropertyType inputProperty) { inputProperty.init(); } public void addConfigured(Property property) throws Exception { property.init(); properties.add(property); File propertyFile = property.getFile(); if (propertyFile != null && isSubPath(propertyFile, getProject().getBaseDir())) { localPropertyFiles.add(FileUtils.getRelativePath(getProject().getBaseDir(), propertyFile)); } } public void addConfigured(PropertyTask propertyTask) throws Exception { propertyTask.init(); properties.add(propertyTask); File propertyFile = propertyTask.getFile(); //relativeToDeployDir means that the property file is not part of the bundle but exists somewhere //in or "around" the deploy dir of this bundle. if (propertyFile != null && !propertyTask.isRelativeToDeployDir() && isSubPath(propertyFile, getProject().getBaseDir())) { localPropertyFiles.add(FileUtils.getRelativePath(getProject().getBaseDir(), propertyFile)); } } public List<String> getLocalPropertyFiles() { return localPropertyFiles; } public void add(DeploymentUnitType deployment) { this.deploymentUnits.put(deployment.getName(), deployment); } public Map<String, DeploymentUnitType> getDeploymentUnits() { return deploymentUnits; } /** * Ensure we have a consistent and legal set of attributes, and set * any internal flags necessary based on different combinations * of attributes. * * @throws BuildException if an error occurs */ protected void validateAttributes() throws BuildException { if (this.name == null) { throw new BuildException("The 'name' attribute is required."); } if (this.name.length() == 0) { throw new BuildException("The 'name' attribute must have a non-empty value."); } if (this.version == null) { throw new BuildException("The 'version' attribute is required."); } if (this.version.length() == 0) { throw new BuildException("The 'version' attribute must have a non-empty value."); } } /** * Ensure we have a legal set of child types. * * @throws BuildException if an error occurs */ protected void validateTypes() throws BuildException { if (this.deploymentUnits.isEmpty()) { throw new BuildException("At least one 'rhq:deploymentUnit' child element must be specified."); } } private static boolean isSubPath(File file, File possibleParent) { file = file.getAbsoluteFile(); possibleParent = possibleParent.getAbsoluteFile(); while (file != null) { if (file.equals(possibleParent)) { return true; } file = file.getParentFile(); } return false; } }