modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
| 120 ++++++++++
modules/common/ant-bundle/src/main/resources/org/rhq/bundle/antlib.xml
| 4
modules/common/ant-bundle/src/test/java/org/rhq/bundle/ant/AntLauncherTest.java
| 45 +++
modules/common/ant-bundle/src/test/resources/test-audit-input.properties
| 1
modules/common/ant-bundle/src/test/resources/test-audit.properties
| 3
modules/common/ant-bundle/src/test/resources/test-bundle-audit.xml
| 39 +++
modules/core/domain/src/main/java/org/rhq/core/domain/bundle/BundleResourceDeploymentHistory.java
| 3
modules/plugins/ant-bundle/src/main/java/org/rhq/plugins/ant/DeploymentAuditorBuildListener.java
| 37 +++
8 files changed, 249 insertions(+), 3 deletions(-)
New commits:
commit 65c741a60042218f7ab290b4795b563c9be2c77e
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Dec 23 06:03:04 2010 -0500
BZ 649318 add new rhq:audit task that lets the recipe author be able to send up custom
audit trail entries
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
new file mode 100644
index 0000000..af0e60b
--- /dev/null
+++ b/modules/common/ant-bundle/src/main/java/org/rhq/bundle/ant/task/AuditTask.java
@@ -0,0 +1,120 @@
+/*
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+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
+ * of audit messages that the server gets to see how the progress went with the
provisioning of a bundle.
+ *
+ * <rhq:audit status='SUCCESS|WARN|FAILURE' action="My Step"
info="summary info" message="intermediate details">
+ * long details here
+ * </rhq:audit>
+ *
+ * @author John Mazzitelli
+ */
+public class AuditTask extends AbstractBundleTask {
+ private String status = "SUCCESS"; // must match one of SUCCESS, WARN, or
FAILURE (see BundleResourceDeploymentHistory.Status)
+ private String action = null;
+ private String info = null;
+ private String message = "";
+ private String details = "";
+
+ @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);
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+
+ public String getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = info;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = (message != null) ? message : "";
+ }
+
+ /**
+ * Set a multiline message.
+ * @param msg the CDATA text to append to the message text
+ */
+ public void addText(String msg) {
+ if (msg != null) {
+ 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
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 fee46ef..010077e 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
@@ -17,5 +17,7 @@
<typedef name="ignore"
classname="org.rhq.bundle.ant.type.IgnoreType"/>
<typedef name="fileset"
classname="org.rhq.bundle.ant.type.FileSet"/>
<typedef name="system-service"
classname="org.rhq.bundle.ant.type.SystemServiceType"/>
-
+
+ <!-- audit task used to send custom audit messages to server -->
+ <taskdef name="audit"
classname="org.rhq.bundle.ant.task.AuditTask"/>
</antlib>
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 2c41874..772c3e5 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
@@ -140,6 +140,7 @@ public class AntLauncherTest {
assert propDef.getDescription().equals("This is where the product will
listen for incoming messages");
assert propDef.isRequired();
+ // make sure our test infrastruction setup the input properties correctly
Configuration config = project.getConfiguration();
assert config.getProperties().size() == 1 : config.getProperties();
assert "10000".equals(config.getSimpleValue("listener.port",
null)) : config.getProperties();
@@ -193,6 +194,7 @@ public class AntLauncherTest {
assert propDef.getDescription().equals("This is where the product will
listen for incoming messages");
assert propDef.isRequired();
+ // make sure our test infrastruction setup the input properties correctly
Configuration config = project.getConfiguration();
assert config.getProperties().size() == 1;
assert "20000".equals(config.getSimpleValue("listener.port",
null)) : config.getProperties();
@@ -249,6 +251,7 @@ public class AntLauncherTest {
assert propDef.getDescription().equals("This is where the product will
listen for incoming messages");
assert propDef.isRequired();
+ // make sure our test infrastruction setup the input properties correctly
Configuration config = project.getConfiguration();
assert config.getProperties().size() == 1;
assert "20000".equals(config.getSimpleValue("listener.port",
null)) : config.getProperties();
@@ -431,6 +434,48 @@ public class AntLauncherTest {
}
}
+ // this doesn't verify the audit messages getting emitted are correct
+ // but it does verify the audit tag getting processed correctly.
+ // you have to look at the test logs to see the audit messages
+ // TODO: write a ant build listener to listen for this messages, parse them and
verify they are correct
+ // this test should then ask the listener at the end if everything was OK and
assert false if not
+ public void testAuditMessages() throws Exception {
+ // We want to test a fresh install, so make sure the deploy dir doesn't
pre-exist.
+ FileUtil.purge(DEPLOY_DIR, true);
+
+ AntLauncher ant = new AntLauncher();
+ Properties inputProps =
createInputProperties("/test-audit-input.properties");
+ List<BuildListener> buildListeners = createBuildListeners();
+
+ BundleAntProject project =
ant.executeBundleDeployFile(getBuildXml("test-bundle-audit.xml"), inputProps,
+ buildListeners);
+ assert project != null;
+ Set<String> bundleFiles = project.getBundleFileNames();
+ assert bundleFiles != null;
+ assert bundleFiles.size() == 1 : bundleFiles;
+ assert bundleFiles.contains("test-audit.properties") : bundleFiles;
+
+ // sanity check - make sure our recipe defined this property
+ ConfigurationDefinition configDef = project.getConfigurationDefinition();
+ assert configDef.getPropertyDefinitions().size() == 1 :
configDef.getPropertyDefinitions();
+ PropertyDefinitionSimple propDef =
configDef.getPropertyDefinitionSimple("listener.port");
+ assert propDef != null;
+
+ // make sure our test infrastruction setup the input properties correctly
+ Configuration config = project.getConfiguration();
+ assert config.getProperties().size() == 1 : config.getProperties();
+ assert "777".equals(config.getSimpleValue("listener.port",
null)) : config.getProperties();
+
+ String preinstallTargetExecuted = (String)
project.getProperties().get("preinstallTargetExecuted");
+ assert preinstallTargetExecuted.equals("1a");
+ String postinstallTargetExecuted = (String)
project.getProperties().get("postinstallTargetExecuted");
+ assert postinstallTargetExecuted.equals("1b");
+
+ assert new File(DEPLOY_DIR, "test-audit.properties").exists() :
"missing file";
+ assert readPropsFile(new File(DEPLOY_DIR,
"test-audit.properties")).getProperty("my.listener.port").equals(
+ "777");
+ }
+
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-audit-input.properties
b/modules/common/ant-bundle/src/test/resources/test-audit-input.properties
new file mode 100644
index 0000000..aaa7499
--- /dev/null
+++ b/modules/common/ant-bundle/src/test/resources/test-audit-input.properties
@@ -0,0 +1 @@
+listener.port=777
diff --git a/modules/common/ant-bundle/src/test/resources/test-audit.properties
b/modules/common/ant-bundle/src/test/resources/test-audit.properties
new file mode 100644
index 0000000..4dc4374
--- /dev/null
+++ b/modules/common/ant-bundle/src/test/resources/test-audit.properties
@@ -0,0 +1,3 @@
+my.listener.port=@@listener.port(a)@
+one.property=wot
+two.property=gorilla?
diff --git a/modules/common/ant-bundle/src/test/resources/test-bundle-audit.xml
b/modules/common/ant-bundle/src/test/resources/test-bundle-audit.xml
new file mode 100644
index 0000000..687c4ba
--- /dev/null
+++ b/modules/common/ant-bundle/src/test/resources/test-bundle-audit.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+
+<project name="test-bundle" default="main"
xmlns:rhq="antlib:org.rhq.bundle">
+
+ <rhq:bundle name="audit-test" version="1">
+
+ <rhq:input-property name="listener.port"
type="integer"/>
+
+ <rhq:deployment-unit name="test"
preinstallTarget="preinstall" postinstallTarget="postinstall">
+ <rhq:file name="test-audit.properties"
destinationFile="test-audit.properties" replace="true"/>
+ </rhq:deployment-unit>
+
+ </rhq:bundle>
+
+ <target name="main"/>
+
+ <target name="preinstall">
+ <property name="preinstallTargetExecuted" value="1a"/>
+ <rhq:audit status="success" action="action1"
info="info1" message="message1">
+ preinstall successful message with port "${listener.port}" !
+ </rhq:audit>
+ <rhq:audit status="failure" action="action2"
info="info2" message="message2">
+ preinstall failure message here!
+ </rhq:audit>
+ <rhq:audit status="warn" action="action3"
info="info3" message="message3">
+ preinstall warn message here!
+ </rhq:audit>
+ </target>
+
+ <target name="postinstall">
+ <property name="postinstallTargetExecuted"
value="1b"/>
+ <rhq:audit />
+ <rhq:audit status="SUCCESS" />
+ <rhq:audit status="WARN" action="actionA" />
+ <rhq:audit status="FAILURE" action="actionB"
info="infoB" />
+ <rhq:audit status="SUCCESS" action="actionC"
info="infoC" message="messageC" />
+ </target>
+
+</project>
\ No newline at end of file
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/bundle/BundleResourceDeploymentHistory.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/bundle/BundleResourceDeploymentHistory.java
index eb00b26..b7f28fc 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/bundle/BundleResourceDeploymentHistory.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/bundle/BundleResourceDeploymentHistory.java
@@ -228,7 +228,8 @@ public class BundleResourceDeploymentHistory implements Serializable
{
FILE_ADD("File Add"), //
FILE_CHANGE("File Change"), //
FILE_DOWNLOAD("File Download"), //
- FILE_REMOVE("File Remove");
+ FILE_REMOVE("File Remove"), //
+ AUDIT_MESSAGE("Audit Message");
private String displayName;
diff --git
a/modules/plugins/ant-bundle/src/main/java/org/rhq/plugins/ant/DeploymentAuditorBuildListener.java
b/modules/plugins/ant-bundle/src/main/java/org/rhq/plugins/ant/DeploymentAuditorBuildListener.java
index 7f41de8..b8476b9 100644
---
a/modules/plugins/ant-bundle/src/main/java/org/rhq/plugins/ant/DeploymentAuditorBuildListener.java
+++
b/modules/plugins/ant-bundle/src/main/java/org/rhq/plugins/ant/DeploymentAuditorBuildListener.java
@@ -22,12 +22,16 @@
*/
package org.rhq.plugins.ant;
+import java.util.Date;
+
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.util.StringUtils;
import org.rhq.core.domain.bundle.BundleResourceDeployment;
import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory;
+import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory.Category;
+import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory.Status;
import org.rhq.core.pluginapi.bundle.BundleManagerProvider;
/**
@@ -71,6 +75,38 @@ public class DeploymentAuditorBuildListener implements BuildListener {
}
public void messageLogged(BuildEvent event) {
+ // this will see if this is an audit message (e.g. <rhq:audit>) and if so,
send it up to the server
+ // see org.rhq.bundle.ant.task.AuditTask.execute()
+ //
RHQ_AUDIT_MESSAGE___<status>___<action>___<info>___<message>___<details>
+
+ try {
+ Status status = Status.SUCCESS;
+ String action = "Audit Message";
+ String info = "Recipe Audit Message";
+ String message = new Date().toString();
+ String details = null;
+ BundleResourceDeployment deployment = this.bundleResourceDeployment;
+ Category category = Category.AUDIT_MESSAGE;
+
+ String[] eventStrings = event.getMessage().split("___");
+ int index = 0;
+ if (eventStrings[index++].equals("RHQ_AUDIT_MESSAGE")) {
+ try {
+ String statusStr = eventStrings[index++];
+ status = Status.valueOf(statusStr.toUpperCase());
+ action = eventStrings[index++];
+ info = eventStrings[index++];
+ message = eventStrings[index++];
+ details = eventStrings[index++];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // the message didn't have all the info, just skip looking for
the rest and log what we have
+ }
+ }
+
+ this.bundleManagerProvider.auditDeployment(deployment, action, info,
category, status, message, details);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
return;
}
@@ -101,7 +137,6 @@ public class DeploymentAuditorBuildListener implements BuildListener
{
return msg.toString();
}
- @SuppressWarnings( { "ThrowableResultOfMethodCallIgnored" })
private static String createMessage(String action, BuildEvent event) {
StringBuilder msg = new StringBuilder(action);