modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/AntLauncher.java | 42 -
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java | 64 +
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java | 3
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java | 47 -
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/AbstractUrlFileType.java | 79 ++
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/DeploymentUnitType.java | 356 ++++++++--
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlArchiveType.java | 63 +
modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlFileType.java | 78 ++
modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml | 2
modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java | 98 ++
modules/common/ant-bundle/src/test/resources/test-bundle-url-input.properties | 1
modules/common/ant-bundle/src/test/resources/test-bundle-url.xml | 26
modules/enterprise/server/plugins/ant-bundle/src/main/java/org/rhq/enterprise/server/plugins/ant/AntBundleServerPluginComponent.java | 2
13 files changed, 748 insertions(+), 113 deletions(-)
New commits:
commit 9f09183904e6c03dc81b058f715a12ce02bc662c
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Fri Dec 24 00:09:50 2010 -0500
BZ 609154 - be able to specify archives and raw files via remote URLs rather than bundle them directly in the bundle distro. The agents must have access to this URL for the bundle to be provisioned.
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/AntLauncher.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/AntLauncher.java
index f5d32eb..e26a7da 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/AntLauncher.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/AntLauncher.java
@@ -85,9 +85,9 @@ public class AntLauncher {
public BundleAntProject executeBundleDeployFile(File buildFile, Properties buildProperties,
List<BuildListener> buildListeners) throws InvalidBuildFileException {
// Parse and validate the build file before even attempting to execute it.
- BundleAntProject parsedProject = parseBundleDeployFile(buildFile);
+ BundleAntProject parsedProject = parseBundleDeployFile(buildFile, buildProperties);
- BundleAntProject project = createProject(buildFile, false);
+ BundleAntProject project = createProject(buildFile, false, buildProperties);
// The parse above got us all the bundle files names. The rest of this method
// will be able to re-determine everything else for 'project' but these filenames.
@@ -96,17 +96,6 @@ public class AntLauncher {
project.getBundleFileNames().addAll(parsedProject.getBundleFileNames());
try {
- if (buildProperties != null) {
- for (Map.Entry<Object, Object> property : buildProperties.entrySet()) {
- // On the assumption that these properties will be slurped in via Properties.load we
- // need to escape backslashes to have them treated as literals
- project.setUserProperty(property.getKey().toString(), property.getValue().toString().replace("\\",
- "\\\\"));
- }
- }
- project.setUserProperty(MagicNames.ANT_FILE, buildFile.getAbsolutePath());
- project.setUserProperty(MagicNames.ANT_FILE_TYPE, MagicNames.ANT_FILE_TYPE_FILE);
-
if (buildListeners != null) {
for (BuildListener buildListener : buildListeners) {
project.addBuildListener(buildListener);
@@ -131,8 +120,9 @@ public class AntLauncher {
}
}
- public BundleAntProject parseBundleDeployFile(File buildFile) throws InvalidBuildFileException {
- BundleAntProject project = createProject(buildFile, true);
+ public BundleAntProject parseBundleDeployFile(File buildFile, Properties buildProperties)
+ throws InvalidBuildFileException {
+ BundleAntProject project = createProject(buildFile, true, buildProperties);
ProjectHelper2 projectHelper = new ProjectHelper2();
try {
@@ -157,10 +147,23 @@ public class AntLauncher {
return project;
}
- private BundleAntProject createProject(File buildFile, boolean parseOnly) {
+ private BundleAntProject createProject(File buildFile, boolean parseOnly, Properties buildProperties) {
+
ClassLoader classLoader = getClass().getClassLoader();
BundleAntProject project = new BundleAntProject(parseOnly);
+
+ if (buildProperties != null) {
+ for (Map.Entry<Object, Object> property : buildProperties.entrySet()) {
+ // On the assumption that these properties will be slurped in via Properties.load we
+ // need to escape backslashes to have them treated as literals
+ project.setUserProperty(property.getKey().toString(), property.getValue().toString().replace("\\",
+ "\\\\"));
+ }
+ }
+ project.setUserProperty(MagicNames.ANT_FILE, buildFile.getAbsolutePath());
+ project.setUserProperty(MagicNames.ANT_FILE_TYPE, MagicNames.ANT_FILE_TYPE_FILE);
+
project.setCoreLoader(classLoader);
project.init();
project.setBaseDir(buildFile.getParentFile());
@@ -246,6 +249,13 @@ public class AntLauncher {
for (String archive : archives.values()) {
project.getBundleFileNames().add(archive);
}
+
+ // note that we do NOT add url-files and url-archives to the BundleFileNames because those are
+ // not true "bundle files" that are stored with the bundle version in the database. Those will
+ // be downloaded by the agents at the time the recipe is invoked. There is nothing server side
+ // that need to be known about the files/archives from URLs.
+
+ return;
}
private void abortIfTaskWithinTarget(Target target, Task task) throws InvalidBuildFileException {
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
index 6fab4ba..1beb540 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
@@ -69,9 +69,12 @@ public class BundleAntProject extends Project {
private final Set<String> bundleFileNames = new HashSet<String>();
private int deploymentId;
private DeploymentPhase deploymentPhase;
- private DeployDifferences deployDiffs = new DeployDifferences();
private boolean dryRun;
+ // results of project execution
+ private DeployDifferences deployDiffs = new DeployDifferences();
+ private Set<File> downloadedFiles = new HashSet<File>();
+
public BundleAntProject() {
this(false);
}
@@ -150,10 +153,6 @@ public class BundleAntProject extends Project {
this.deploymentPhase = deploymentPhase;
}
- public DeployDifferences getDeployDifferences() {
- return deployDiffs;
- }
-
public void setDryRun(boolean dryRun) {
this.dryRun = dryRun;
}
@@ -162,6 +161,20 @@ public class BundleAntProject extends Project {
return dryRun;
}
+ public DeployDifferences getDeployDifferences() {
+ return deployDiffs;
+ }
+
+ /**
+ * If there were url-file or url-archives, this returns the set of files
+ * that were downloaded from the URLs.
+ *
+ * @return downloaded files from remote URLs that contain our bundle content
+ */
+ public Set<File> getDownloadedFiles() {
+ return downloadedFiles;
+ }
+
/**
* Logs a message in a format that our audit task/agent-side audit log listener knows about.
* When running in the agent, this audit log will be sent to the server.
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/AbstractUrlFileType.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/AbstractUrlFileType.java
new file mode 100644
index 0000000..8eecee4
--- /dev/null
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/AbstractUrlFileType.java
@@ -0,0 +1,79 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2010 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.type;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * A base class for the functionality shared by {@link UrlFileType} and {@link UrlArchiveType}.
+ *
+ * @author Ian Springer
+ * @author John Mazzitelli
+ */
+public abstract class AbstractUrlFileType extends AbstractBundleType {
+ private String url;
+ private URL source;
+
+ /**
+ * Returns the base filename of the URL (no parent paths will be in the returned name).
+ * If there is no path, the hostname of the URL is used.
+ *
+ * @return base filename of the source file
+ */
+ public String getBaseName() {
+ String path = this.source.getPath();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length());
+ }
+ int lastSlash = path.lastIndexOf('/');
+ if (lastSlash != -1) {
+ path = path.substring(lastSlash + 1);
+ }
+ if (path.length() == 0) {
+ path = this.source.getHost();
+ }
+ return path;
+ }
+
+ public URL getSource() {
+ return this.source;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ public void setUrl(String urlString) {
+ this.url = urlString;
+ try {
+ this.source = new URL(urlString);
+ } catch (MalformedURLException e) {
+ throw new BuildException("URL specified by 'url' attribute [" + urlString
+ + "] is malformed - it must be a valid URL.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/DeploymentUnitType.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/DeploymentUnitType.java
index 8d00a96..dfcbcab 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/DeploymentUnitType.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/DeploymentUnitType.java
@@ -18,7 +18,12 @@
package org.rhq.bundle.ant.type;
import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -31,10 +36,13 @@ import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.rhq.bundle.ant.DeployPropertyNames;
+import org.rhq.bundle.ant.BundleAntProject.AuditStatus;
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.exception.ThrowableUtil;
+import org.rhq.core.util.stream.StreamUtil;
import org.rhq.core.util.updater.DeployDifferences;
import org.rhq.core.util.updater.Deployer;
import org.rhq.core.util.updater.DeploymentData;
@@ -48,13 +56,21 @@ import org.rhq.core.util.updater.DeploymentProperties;
public class DeploymentUnitType extends AbstractBundleType {
private String name;
private String manageRootDir = Boolean.TRUE.toString();
+
private Map<File, File> files = new LinkedHashMap<File, File>();
- private Map<File, String> localFileNames = new LinkedHashMap<File, String>();
+ 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 Set<File> archives = new LinkedHashSet<File>();
- private Map<File, String> localArchiveNames = new LinkedHashMap<File, String>();
- private Map<File, Boolean> archivesExploded = new HashMap<File, Boolean>();
+ private Set<URL> urlArchives = new LinkedHashSet<URL>();
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>();
+
private SystemServiceType systemService;
private Pattern ignorePattern;
private String preinstallTarget;
@@ -67,72 +83,282 @@ public class DeploymentUnitType extends AbstractBundleType {
}
public void install(boolean revert, boolean clean) throws BuildException {
- if (this.preinstallTarget != null) {
- Target target = (Target) getProject().getTargets().get(this.preinstallTarget);
- if (target == null) {
- throw new BuildException("Specified preinstall target (" + this.preinstallTarget + ") does not exist.");
- }
- target.performTasks();
+ if (clean) {
+ getProject().auditLog(
+ AuditStatus.WARN,
+ "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.WARN,
+ "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);
}
- int deploymentId = getProject().getDeploymentId();
- DeploymentProperties deploymentProps = new DeploymentProperties(deploymentId, getProject().getBundleName(),
- getProject().getBundleVersion(), getProject().getBundleDescription());
- File deployDir = getProject().getDeployDir();
- TemplateEngine templateEngine = createTemplateEngine();
+ try {
+ boolean dryRun = getProject().isDryRun();
- if (this.files.isEmpty() && this.archives.isEmpty()) {
- throw new BuildException(
- "You must specify at least one file to deploy via nested rhq:file, rhq:archive, and/or rhq:system-service elements.");
- }
- if (!this.files.isEmpty()) {
- log("Deploying files " + this.files + "...", Project.MSG_VERBOSE);
- }
- if (!this.archives.isEmpty()) {
- log("Deploying archives " + this.archives + "...", Project.MSG_VERBOSE);
+ 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);
+ }
+
+ int deploymentId = getProject().getDeploymentId();
+ DeploymentProperties deploymentProps = new DeploymentProperties(deploymentId, getProject().getBundleName(),
+ getProject().getBundleVersion(), getProject().getBundleDescription());
+ File deployDir = getProject().getDeployDir();
+ TemplateEngine templateEngine = createTemplateEngine();
+
+ 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 (!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");
+ }
+
+ boolean willManageRootDir = Boolean.parseBoolean(this.manageRootDir);
+ if (willManageRootDir) {
+ log("Managing the root directory of this deployment unit - unrelated files found will be removed",
+ Project.MSG_VERBOSE);
+ // don't send an audit message on this unless we are really going to move files out of the way (i.e. !dryrun)
+ if (!dryRun) {
+ getProject()
+ .auditLog(
+ AuditStatus.WARN,
+ "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);
+ }
+ } else {
+ log("Not managing the root directory of this deployment unit - unrelated files will remain intact",
+ Project.MSG_VERBOSE);
+ }
+
+ Set<File> allArchives = new HashSet<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);
+
+ try {
+ DeploymentData deploymentData = new DeploymentData(deploymentProps, allArchives, allFiles, getProject()
+ .getBaseDir(), deployDir, allArchiveReplacePatterns, allRawFilesToReplace, templateEngine,
+ this.ignorePattern, willManageRootDir, 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);
+ }
+
+ // 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);
+ }
}
+ return;
+ }
+
+ /**
+ * 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(Set<File> allArchives, Map<File, File> allFiles,
+ Map<File, Pattern> allArchiveReplacePatterns, Set<File> allRawFilesToReplace,
+ Map<File, Boolean> allArchivesExploded) throws Exception {
- boolean willManageRootDir = Boolean.parseBoolean(this.manageRootDir);
- if (willManageRootDir) {
- log("Managing the root directory of this deployment unit - unrelated files found will be removed",
- Project.MSG_VERBOSE);
- } else {
- log("Not managing the root directory of this deployment unit - unrelated files will remain intact",
- Project.MSG_VERBOSE);
+ // 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;
}
- DeploymentData deploymentData = new DeploymentData(deploymentProps, this.archives, this.files, getProject()
- .getBaseDir(), deployDir, this.archiveReplacePatterns, this.rawFilesToReplace, templateEngine,
- this.ignorePattern, willManageRootDir, this.archivesExploded);
- Deployer deployer = new Deployer(deploymentData);
+ // 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 {
- DeployDifferences diffs = getProject().getDeployDifferences();
- boolean dryRun = getProject().isDryRun();
- if (revert) {
- deployer.redeployAndRestoreBackupFiles(diffs, clean, dryRun);
- } else {
- deployer.deploy(diffs, clean, dryRun);
+ // 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);
+ }
}
- getProject().log("Results:\n" + diffs + "\n");
+
+ // do the archives next
+ for (URL url : this.urlArchives) {
+ // determine what the base filename should be of our downloaded tmp archive file
+ 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.add(tmpFile);
+ if (this.urlArchiveReplacePatterns.containsKey(url)) {
+ allArchiveReplacePatterns.put(tmpFile, this.urlArchiveReplacePatterns.get(url));
+ }
+ if (this.urlArchivesExploded.containsKey(url)) {
+ allArchivesExploded.put(tmpFile, this.urlArchivesExploded.get(url));
+ }
+ }
+
+ return;
+
} catch (Exception e) {
- throw new BuildException("Failed to deploy bundle '" + getProject().getBundleName() + "' version "
- + getProject().getBundleVersion() + ": " + e, 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;
}
+ }
- if (this.systemService != null) {
- this.systemService.install();
- }
+ 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);
- if (this.postinstallTarget != null) {
- Target target = (Target) getProject().getTargets().get(this.postinstallTarget);
- if (target == null) {
- throw new BuildException("Specified postinstall target (" + this.postinstallTarget
- + ") does not exist.");
- }
- target.performTasks();
+ 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 = StreamUtil.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;
}
- return;
+ 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 {
@@ -289,6 +515,28 @@ public class DeploymentUnitType extends AbstractBundleType {
this.archivesExploded.put(archive.getSource(), exploded);
}
+ public void addConfigured(UrlFileType file) {
+ 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());
+ }
+ }
+
+ public void addConfigured(UrlArchiveType archive) {
+ this.urlArchives.add(archive.getSource());
+ 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);
+ }
+
public void addConfigured(IgnoreType ignore) {
List<FileSet> fileSets = ignore.getFileSets();
this.ignorePattern = getPattern(fileSets);
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlArchiveType.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlArchiveType.java
new file mode 100644
index 0000000..af664aa
--- /dev/null
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlArchiveType.java
@@ -0,0 +1,63 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2010 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.type;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * An archive file to be exploded during the bundle deployment (it could remain compressed if exploded="false" is specified)
+ * Can optionally contain a rhq:replace child element that specifies the set of files that contain
+ * template variables (e.g. @@http.port@@) which need to be replaced with the value of the corresponding property.
+ *
+ * This archive file is located at a remote location specified by a URL.
+ *
+ * @author Ian Springer
+ * @author John Mazzitelli
+ */
+public class UrlArchiveType extends AbstractUrlFileType {
+ private Pattern replacePattern;
+ private String exploded = Boolean.TRUE.toString();
+
+ public void addConfigured(ReplaceType replace) {
+ List<FileSet> fileSets = replace.getFileSets();
+ this.replacePattern = getPattern(fileSets);
+ }
+
+ public Pattern getReplacePattern() {
+ return replacePattern;
+ }
+
+ public String getExploded() {
+ return exploded;
+ }
+
+ public void setExploded(String exploded) {
+ if (!Boolean.TRUE.toString().equalsIgnoreCase(exploded) && !Boolean.FALSE.toString().equalsIgnoreCase(exploded)) {
+ throw new BuildException("'exploded' attribute must be 'true' or 'false': " + exploded);
+ }
+ this.exploded = exploded;
+ }
+}
\ No newline at end of file
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlFileType.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlFileType.java
new file mode 100644
index 0000000..b4c20b0
--- /dev/null
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/type/UrlFileType.java
@@ -0,0 +1,78 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2010 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.type;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * A file to be copied during the bundle deployment. If the replace attribute is set to true, any template variables
+ * (e.g. @@http.port@@) inside the file will be replaced with the value of the corresponding property.
+ *
+ * This file is located at a remote location specified by a URL.
+ *
+ * @author Ian Springer
+ * @author John Mazzitelli
+ */
+public class UrlFileType extends AbstractUrlFileType {
+ private File destinationDir;
+ private File destinationFile;
+ private boolean replace;
+
+ public File getDestinationDir() {
+ return this.destinationDir;
+ }
+
+ // Pass in a String, rather than a File, since we don't want Ant to resolve the path relative to basedir if it's relative.
+ public void setDestinationDir(String destinationDir) {
+ if (this.destinationFile != null) {
+ throw new BuildException(
+ "Both 'destinationDir' and 'destinationFile' attributes are defined - only one or the other may be specified.");
+ }
+ this.destinationDir = new File(destinationDir);
+ }
+
+ public File getDestinationFile() {
+ if (this.destinationDir == null && this.destinationFile == null) {
+ return new File(getBaseName()); // the default destination is the same relative path as that of its local name
+ }
+ return this.destinationFile;
+ }
+
+ public void setDestinationFile(String destinationFile) {
+ if (this.destinationDir != null) {
+ throw new BuildException(
+ "Both 'destinationDir' and 'destinationFile' attributes are defined - only one or the other may be specified.");
+ }
+ this.destinationFile = new File(destinationFile);
+ }
+
+ public boolean isReplace() {
+ return replace;
+ }
+
+ public void setReplace(boolean replace) {
+ this.replace = replace;
+ }
+}
\ No newline at end of file
diff --git a/modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml b/modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml
index 010077e..d5cf418 100644
--- a/modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml
+++ b/modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml
@@ -13,6 +13,8 @@
<!-- deployment type's child types -->
<typedef name="file" classname="org.rhq.bundle.ant.type.FileType"/>
<typedef name="archive" classname="org.rhq.bundle.ant.type.ArchiveType"/>
+ <typedef name="url-file" classname="org.rhq.bundle.ant.type.UrlFileType"/>
+ <typedef name="url-archive" classname="org.rhq.bundle.ant.type.UrlArchiveType"/>
<typedef name="replace" classname="org.rhq.bundle.ant.type.ReplaceType"/>
<typedef name="ignore" classname="org.rhq.bundle.ant.type.IgnoreType"/>
<typedef name="fileset" classname="org.rhq.bundle.ant.type.FileSet"/>
diff --git a/modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java b/modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java
index 4eb4369..92f43b4 100644
--- a/modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java
+++ b/modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java
@@ -78,7 +78,7 @@ public class AntLauncherTest {
AntLauncher ant = new AntLauncher();
- BundleAntProject project = ant.parseBundleDeployFile(getBuildXml("test-bundle-v1.xml"));
+ BundleAntProject project = ant.parseBundleDeployFile(getBuildXml("test-bundle-v1.xml"), null);
assert project != null;
Set<String> bundleFiles = project.getBundleFileNames();
assert bundleFiles != null;
@@ -518,8 +518,8 @@ public class AntLauncherTest {
assert new File(DEPLOY_DIR, "subdir/test0.txt").exists() : "missing raw file from default destination location";
assert new File(DEPLOY_DIR, "another/foo.txt").exists() : "missing raw file from the destinationFile";
assert new File(DEPLOY_DIR, "second.dir/test2.txt").exists() : "missing raw file from the destinationDir";
- assert !new File(DEPLOY_DIR, "subdir/test1.zip").exists() : "should not be here because destinationFile was specified";
- assert !new File(DEPLOY_DIR, "subdir/test2.zip").exists() : "should not be here because destinationFile was specified";
+ assert !new File(DEPLOY_DIR, "subdir/test1.txt").exists() : "should not be here because destinationFile was specified";
+ assert !new File(DEPLOY_DIR, "subdir/test2.txt").exists() : "should not be here because destinationFile was specified";
assert new File(DEPLOY_DIR, "subdir/test.zip").exists() : "missing unexploded zip file";
assert new File(DEPLOY_DIR, "subdir/test-replace.zip").exists() : "missing unexploded zip file";
assert !new File(DEPLOY_DIR, "subdir/test-explode.zip").exists() : "should have been exploded";
@@ -544,6 +544,98 @@ public class AntLauncherTest {
}
}
+ public void testUrlFilesAndArchives() throws Exception {
+ // We want to test a fresh install, so make sure the deploy dir doesn't pre-exist.
+ FileUtil.purge(DEPLOY_DIR, true);
+
+ // 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
+ 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
+
+ AntLauncher ant = new AntLauncher();
+ 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(getBuildXml("test-bundle-url.xml"), inputProps,
+ buildListeners);
+ assert project != null;
+
+ Set<String> bundleFiles = project.getBundleFileNames();
+ assert bundleFiles != null;
+ assert bundleFiles.size() == 0 : "we don't have any bundle files - only downloaded files from URLs: "
+ + bundleFiles;
+
+ downloadedFiles = project.getDownloadedFiles();
+ assert downloadedFiles != null;
+ assert downloadedFiles.size() == 6 : 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("test.zip");
+ expectedDownloadedFileNames.add("test-explode.zip");
+ expectedDownloadedFileNames.add("test-replace.zip");
+ for (File downloadedFile : downloadedFiles) {
+ assert expectedDownloadedFileNames.contains(downloadedFile.getName()) : "We downloaded a file but its not in the project's list: "
+ + downloadedFile;
+ }
+
+ assert new File(DEPLOY_DIR, "test0.txt").exists() : "missing raw file from default destination location";
+ assert new File(DEPLOY_DIR, "another/foo.txt").exists() : "missing raw file from the destinationFile";
+ assert new File(DEPLOY_DIR, "second.dir/test2.txt").exists() : "missing raw file from the destinationDir";
+ assert !new File(DEPLOY_DIR, "test1.txt").exists() : "should not be here because destinationFile was specified";
+ assert !new File(DEPLOY_DIR, "test2.txt").exists() : "should not be here because destinationFile was specified";
+ assert new File(DEPLOY_DIR, "test.zip").exists() : "missing unexploded zip file";
+ assert new File(DEPLOY_DIR, "test-replace.zip").exists() : "missing unexploded zip file";
+ assert !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;
+ }
+ });
+ assert templateVarValue[0] != null && templateVarValue[0].equals("9876") : templateVarValue[0];
+
+ // 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);
+ assert props.getProperty("X", "<unset>").equals("9876");
+ } finally {
+ inStream.close();
+ }
+ } finally {
+ FileUtil.purge(tmpUrlLocation, true);
+ if (downloadedFiles != null) {
+ for (File doomed : downloadedFiles) {
+ doomed.delete();
+ }
+ }
+ }
+ }
+
private List<BuildListener> createBuildListeners() {
List<BuildListener> buildListeners = new ArrayList<BuildListener>();
DefaultLogger logger = new DefaultLogger();
diff --git a/modules/common/ant-bundle/src/test/resources/test-bundle-url-input.properties b/modules/common/ant-bundle/src/test/resources/test-bundle-url-input.properties
new file mode 100644
index 0000000..6f7b9ba
--- /dev/null
+++ b/modules/common/ant-bundle/src/test/resources/test-bundle-url-input.properties
@@ -0,0 +1 @@
+X=9876
diff --git a/modules/common/ant-bundle/src/test/resources/test-bundle-url.xml b/modules/common/ant-bundle/src/test/resources/test-bundle-url.xml
new file mode 100644
index 0000000..ebfdd81
--- /dev/null
+++ b/modules/common/ant-bundle/src/test/resources/test-bundle-url.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+
+<project name="test-bundle" default="main" xmlns:rhq="antlib:org.rhq.bundle">
+
+ <rhq:bundle name="test" version="1">
+
+ <rhq:input-property name="X" />
+
+ <rhq:deployment-unit name="appserver">
+ <rhq:url-file url="${rhq.test.url.dir}/subdir/test0.txt" replace="false" />
+ <rhq:url-file url="${rhq.test.url.dir}/subdir/test1.txt" destinationFile="another/foo.txt" replace="false" />
+ <rhq:url-file url="${rhq.test.url.dir}/subdir/test2.txt" destinationDir="second.dir" replace="true" />
+ <rhq:url-archive url="${rhq.test.url.dir}/subdir/test.zip" exploded="false" />
+ <rhq:url-archive url="${rhq.test.url.dir}/subdir/test-explode.zip" exploded="true" />
+ <rhq:url-archive url="${rhq.test.url.dir}/subdir/test-replace.zip" exploded="false">
+ <rhq:replace>
+ <rhq:fileset includes="template.txt"/>
+ </rhq:replace>
+ </rhq:url-archive>
+ </rhq:deployment-unit>
+
+ </rhq:bundle>
+
+ <target name="main"/>
+
+</project>
\ No newline at end of file
diff --git a/modules/enterprise/server/plugins/ant-bundle/src/main/java/org/rhq/enterprise/server/plugins/ant/AntBundleServerPluginComponent.java b/modules/enterprise/server/plugins/ant-bundle/src/main/java/org/rhq/enterprise/server/plugins/ant/AntBundleServerPluginComponent.java
index 07fcad5..82354b6 100644
--- a/modules/enterprise/server/plugins/ant-bundle/src/main/java/org/rhq/enterprise/server/plugins/ant/AntBundleServerPluginComponent.java
+++ b/modules/enterprise/server/plugins/ant-bundle/src/main/java/org/rhq/enterprise/server/plugins/ant/AntBundleServerPluginComponent.java
@@ -106,7 +106,7 @@ public class AntBundleServerPluginComponent implements ServerPluginComponent, Bu
// parse, but do not execute, the Ant script
AntLauncher antLauncher = new AntLauncher();
- BundleAntProject project = antLauncher.parseBundleDeployFile(recipeFile);
+ BundleAntProject project = antLauncher.parseBundleDeployFile(recipeFile, null);
// obtain the parse results
deploymentProps = new DeploymentProperties(0, project.getBundleName(), project.getBundleVersion(), project
commit 33f4acf7c1b1c118bc0e3996cb6f573ac0e636c2
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Dec 23 14:40:47 2010 -0500
move the auditLog method over to the project so both our tasks and types can use it
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
index a80e61e..6fab4ba 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/BundleAntProject.java
@@ -23,6 +23,7 @@
package org.rhq.bundle.ant;
import java.io.File;
+import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@@ -42,11 +43,18 @@ import org.rhq.core.util.updater.DeployDifferences;
* This project object is to be used by either the bundle {@link AntLauncher} or custom
* bundle Ant tasks. The launcher or tasks can inform this project object of things that
* are happening as the Ant script is being parsed and/or executed.
+ *
+ * Also provides a common method for any task to invoke to send an audit message.
*
* @author John Mazzitelli
* @author Ian Springer
*/
public class BundleAntProject extends Project {
+ // these statuses should match those of see BundleResourceDeploymentHistory.Status
+ public enum AuditStatus {
+ SUCCESS, FAILURE, WARN
+ };
+
// Bundle-level attributes
private boolean parseOnly;
@@ -153,4 +161,37 @@ public class BundleAntProject extends Project {
public boolean isDryRun() {
return dryRun;
}
+
+ /**
+ * Logs a message in a format that our audit task/agent-side audit log listener knows about.
+ * When running in the agent, this audit log will be sent to the server.
+ * It is always logged at part of the normal Ant logger mechanism.
+ *
+ * @param status SUCCESS, FAILURE or WARN
+ * @param action audit action, a short summary easily displayed (e.g "File Download")
+ * @param info information about the action target, easily displayed (e.g. "myfile.zip")
+ * @param message Optional, brief (one or two lines) information message
+ * @param details Optional, verbose data, such as full file text or long error messages/stack traces
+ */
+ public void auditLog(AuditStatus status, String action, String info, String message, String details) {
+ if (status == null) {
+ status = AuditStatus.SUCCESS;
+ }
+
+ // this will log a message with a very specific format that is understood
+ // by the agent-side build listener's messageLogged method:
+ // org.rhq.plugins.ant.DeploymentAuditorBuildListener.messageLogged(BuildEvent)
+ // RHQ_AUDIT_MESSAGE___<status>___<action>___<info>___<message>___<details>
+ StringBuilder str = new StringBuilder("RHQ_AUDIT_MESSAGE___");
+ str.append(status.name());
+ str.append("___");
+ str.append((action != null) ? action : "Audit Message");
+ str.append("___");
+ str.append((info != null) ? info : "Timestamp: " + new Date().toString());
+ str.append("___");
+ str.append((message != null) ? message : "");
+ str.append("___");
+ str.append((details != null) ? details : "");
+ this.log(str.toString(), Project.MSG_INFO);
+ }
}
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
index a95724a..f6d819a 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
@@ -18,9 +18,6 @@
*/
package org.rhq.bundle.ant.task;
-import java.util.Date;
-
-import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.rhq.bundle.ant.BundleAntProject;
@@ -33,15 +30,9 @@ import org.rhq.bundle.ant.BundleAntProject;
* As new tasks are created by extending this task object, developers must make sure
* they add the new tasks to the bundle-ant-tasks.properties file.
*
- * Also provides a common method for any task to invoke to send an audit message.
- *
* @author John Mazzitelli
*/
public abstract class AbstractBundleTask extends Task {
- // these statuses should match those of see BundleResourceDeploymentHistory.Status
- enum AuditStatus {
- SUCCESS, FAILURE, WARN
- };
/**
* Returns the specific {@link BundleAntProject} object that is invoking this task.
@@ -54,37 +45,4 @@ public abstract class AbstractBundleTask extends Task {
public BundleAntProject getProject() {
return (BundleAntProject) super.getProject();
}
-
- /**
- * Logs a message in a format that our audit task/agent-side audit log listener knows about.
- * When running in the agent, this audit log will be sent to the server.
- * It is always logged at part of the normal Ant logger mechanism.
- *
- * @param status SUCCESS, FAILURE or WARN
- * @param action audit action, a short summary easily displayed (e.g "File Download")
- * @param info information about the action target, easily displayed (e.g. "myfile.zip")
- * @param message Optional, brief (one or two lines) information message
- * @param details Optional, verbose data, such as full file text or long error messages/stack traces
- */
- protected void auditLog(AuditStatus status, String action, String info, String message, String details) {
- if (status == null) {
- status = AuditStatus.SUCCESS;
- }
-
- // this will log a message with a very specific format that is understood
- // by the agent-side build listener's messageLogged method:
- // org.rhq.plugins.ant.DeploymentAuditorBuildListener.messageLogged(BuildEvent)
- // RHQ_AUDIT_MESSAGE___<status>___<action>___<info>___<message>___<details>
- StringBuilder str = new StringBuilder("RHQ_AUDIT_MESSAGE___");
- str.append(status.name());
- str.append("___");
- str.append((action != null) ? action : "Audit Message");
- str.append("___");
- str.append((info != null) ? info : "Timestamp: " + new Date().toString());
- str.append("___");
- str.append((message != null) ? message : "");
- str.append("___");
- str.append((details != null) ? details : "");
- getProject().log(str.toString(), Project.MSG_INFO);
- }
}
\ No newline at end of file
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
index a74b77c..19f284b 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
@@ -20,6 +20,8 @@ package org.rhq.bundle.ant.task;
import org.apache.tools.ant.BuildException;
+import org.rhq.bundle.ant.BundleAntProject.AuditStatus;
+
/**
* The rhq:audit task is a way recipe authors can add their own audit messages to the stream
* of audit messages that the server gets to see how the progress went with the provisioning of a bundle.
@@ -44,7 +46,7 @@ public class AuditTask extends AbstractBundleTask {
@Override
public void execute() throws BuildException {
- auditLog(status, action, info, message, details);
+ getProject().auditLog(status, action, info, message, details);
}
public AuditStatus getStatus() {
commit da2d86a9cc49ac57c5a0caa91b3339b8cb89d24c
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Dec 23 14:25:47 2010 -0500
pull up the audit code so all tasks can emit audit messages (on the agent, this sends them to the server)
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
index eabad70..a95724a 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AbstractBundleTask.java
@@ -18,6 +18,9 @@
*/
package org.rhq.bundle.ant.task;
+import java.util.Date;
+
+import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.rhq.bundle.ant.BundleAntProject;
@@ -29,10 +32,17 @@ import org.rhq.bundle.ant.BundleAntProject;
*
* As new tasks are created by extending this task object, developers must make sure
* they add the new tasks to the bundle-ant-tasks.properties file.
- *
+ *
+ * Also provides a common method for any task to invoke to send an audit message.
+ *
* @author John Mazzitelli
*/
public abstract class AbstractBundleTask extends Task {
+ // these statuses should match those of see BundleResourceDeploymentHistory.Status
+ enum AuditStatus {
+ SUCCESS, FAILURE, WARN
+ };
+
/**
* Returns the specific {@link BundleAntProject} object that is invoking this task.
* This task can call methods on the returned project object to inform the project
@@ -44,4 +54,37 @@ public abstract class AbstractBundleTask extends Task {
public BundleAntProject getProject() {
return (BundleAntProject) super.getProject();
}
+
+ /**
+ * Logs a message in a format that our audit task/agent-side audit log listener knows about.
+ * When running in the agent, this audit log will be sent to the server.
+ * It is always logged at part of the normal Ant logger mechanism.
+ *
+ * @param status SUCCESS, FAILURE or WARN
+ * @param action audit action, a short summary easily displayed (e.g "File Download")
+ * @param info information about the action target, easily displayed (e.g. "myfile.zip")
+ * @param message Optional, brief (one or two lines) information message
+ * @param details Optional, verbose data, such as full file text or long error messages/stack traces
+ */
+ protected void auditLog(AuditStatus status, String action, String info, String message, String details) {
+ if (status == null) {
+ status = AuditStatus.SUCCESS;
+ }
+
+ // this will log a message with a very specific format that is understood
+ // by the agent-side build listener's messageLogged method:
+ // org.rhq.plugins.ant.DeploymentAuditorBuildListener.messageLogged(BuildEvent)
+ // RHQ_AUDIT_MESSAGE___<status>___<action>___<info>___<message>___<details>
+ StringBuilder str = new StringBuilder("RHQ_AUDIT_MESSAGE___");
+ str.append(status.name());
+ str.append("___");
+ str.append((action != null) ? action : "Audit Message");
+ str.append("___");
+ str.append((info != null) ? info : "Timestamp: " + new Date().toString());
+ str.append("___");
+ str.append((message != null) ? message : "");
+ str.append("___");
+ str.append((details != null) ? details : "");
+ getProject().log(str.toString(), Project.MSG_INFO);
+ }
}
\ No newline at end of file
diff --git a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
index af0e60b..a74b77c 100644
--- a/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
@@ -18,10 +18,7 @@
*/
package org.rhq.bundle.ant.task;
-import java.util.Date;
-
import org.apache.tools.ant.BuildException;
-import org.apache.tools.ant.Project;
/**
* The rhq:audit task is a way recipe authors can add their own audit messages to the stream
@@ -34,7 +31,7 @@ import org.apache.tools.ant.Project;
* @author John Mazzitelli
*/
public class AuditTask extends AbstractBundleTask {
- private String status = "SUCCESS"; // must match one of SUCCESS, WARN, or FAILURE (see BundleResourceDeploymentHistory.Status)
+ private AuditStatus status = AuditStatus.SUCCESS; // see BundleResourceDeploymentHistory.Status
private String action = null;
private String info = null;
private String message = "";
@@ -43,34 +40,27 @@ public class AuditTask extends AbstractBundleTask {
@Override
public void maybeConfigure() throws BuildException {
super.maybeConfigure(); // inits the attribute fields
- validateAttributes();
}
@Override
public void execute() throws BuildException {
- // this will log a message with a very specific format that is understood
- // by the agent-side build listener's messageLogged method:
- // org.rhq.plugins.ant.DeploymentAuditorBuildListener.messageLogged(BuildEvent)
- // RHQ_AUDIT_MESSAGE___<status>___<action>___<info>___<message>___<details>
- StringBuilder str = new StringBuilder("RHQ_AUDIT_MESSAGE___");
- str.append(this.status);
- str.append("___");
- str.append((this.action != null) ? this.action : "Audit Message");
- str.append("___");
- str.append((this.info != null) ? this.info : "Timestamp: " + new Date().toString());
- str.append("___");
- str.append(this.message);
- str.append("___");
- str.append(this.details);
- getProject().log(str.toString(), Project.MSG_INFO);
+ auditLog(status, action, info, message, details);
}
- public String getStatus() {
+ public AuditStatus getStatus() {
return status;
}
public void setStatus(String status) {
- this.status = status;
+ if (this.status == null) {
+ this.status = AuditStatus.SUCCESS;
+ } else {
+ try {
+ this.status = AuditStatus.valueOf(status.toUpperCase());
+ } catch (Exception e) {
+ throw new BuildException("The 'status' attribute must be either 'SUCCESS', 'WARN' or 'FAILURE'");
+ }
+ }
}
public String getAction() {
@@ -106,15 +96,4 @@ public class AuditTask extends AbstractBundleTask {
this.details += getProject().replaceProperties(msg);
}
}
-
- protected void validateAttributes() throws BuildException {
- if (this.status == null) {
- this.status = "SUCCESS";
- } else if (!this.status.equalsIgnoreCase("SUCCESS") && !this.status.equalsIgnoreCase("FAILURE")
- && !this.status.equalsIgnoreCase("WARN")) {
- throw new BuildException("The 'result' attribute must be either 'SUCCESS', 'WARN' or 'FAILURE'");
- }
- this.status = this.status.toUpperCase();
- }
-
}
\ No newline at end of file