modules/enterprise/server/embeddedagent/pom.xml
| 28
modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentConfigurationSetup.java
| 305 ++++++++++
modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentService.java
| 52 +
modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentSubsystemRestart.java
| 15
modules/enterprise/server/embeddedagent/src/main/resources/module/main/module.xml
| 1
modules/enterprise/server/embeddedagent/src/main/scripts/module-assembly.xml
| 7
6 files changed, 393 insertions(+), 15 deletions(-)
New commits:
commit ef729cd9b8c989ef0f37c35bb5734e8c698482da
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 30 14:05:38 2014 -0500
1) preconfigure the agent prior to starting it
2) get log4j.xml to work but yet not put log files under bin/
3) don't need to put module.xml in the extension jar, just needs to be in module
diff --git a/modules/enterprise/server/embeddedagent/pom.xml
b/modules/enterprise/server/embeddedagent/pom.xml
index c6118a6..0196223 100644
--- a/modules/enterprise/server/embeddedagent/pom.xml
+++ b/modules/enterprise/server/embeddedagent/pom.xml
@@ -26,6 +26,10 @@
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
+ <excludes>
+ <!-- this doesn't need to be in the jar, just in our module
.zip -->
+ <exclude>module/main/module.xml</exclude>
+ </excludes>
</resource>
</resources>
@@ -89,16 +93,28 @@
<copy
tofile="${module.lib}/macosx-x86_64/libsigar.dylib"
file="${agent.lib}/libsigar-universal64-macosx.dylib"
preservelastmodified="true"/>
<echo>Adjust default configuration</echo>
- <property name="config.xml"
location="${project.build.directory}/rhq-agent/conf/agent-configuration.xml"/>
+ <property name="agent.config.dir"
location="${project.build.directory}/rhq-agent/conf"/>
+ <property name="agent.config.xml"
location="${agent.config.dir}/agent-configuration.xml"/>
<!-- because we are embedded, as can't have a
prompt and ask user, so we will ensure the agent is always fully setup -->
- <replaceregexp file="${config.xml}"
flags="s"
+ <replaceregexp file="${agent.config.xml}"
flags="s"
match='<!--(\s*)<entry
key="rhq.agent.configuration-setup-flag" value="false"
/>(\s*)-->'
- replace='<entry
key="rhq.agent.configuration-setup-flag" value="true" />
BOOO' />
+ replace='<entry
key="rhq.agent.configuration-setup-flag" value="true" />'
/>
<!-- we don't support agent auto-update while the
agent is embedded -->
- <replaceregexp file="${config.xml}"
+ <replaceregexp file="${agent.config.xml}"
match='<entry
key="rhq.agent.agent-update.enabled" value="true" />'
- replace='<entry
key="rhq.agent.agent-update.enabled" value="false" /> BOOO'
/>
-
+ replace='<entry
key="rhq.agent.agent-update.enabled" value="false" />'
/>
+ <!-- because we don't want log4j writing files in
places we don't want, don't use file appenders. -->
+ <!-- our WildFly/EAP subsystem extension will turn
this back on at runtime after log4j is properly configured. -->
+ <replaceregexp
file="${agent.config.dir}/log4j.xml" flags="g"
+ match='<appender-ref
ref="FILE".*/>'
+ replace='<!--
<appender-ref ref="FILE"/> -->' />
+ <replaceregexp
file="${agent.config.dir}/log4j.xml" flags="g"
+ match='<appender-ref
ref="COMMANDTRACE".*/>'
+ replace='<!--
<appender-ref ref="COMMANDTRACE"/> -->' />
+ <jar
destfile="${agent.lib}/rhq-enterprise-agent-${project.version}.jar"
+ basedir="${agent.config.dir}"
+ includes="log4j.xml"
+ update="true" />
</target>
</configuration>
<goals>
diff --git
a/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentConfigurationSetup.java
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentConfigurationSetup.java
new file mode 100644
index 0000000..187527b
--- /dev/null
+++
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentConfigurationSetup.java
@@ -0,0 +1,305 @@
+package org.rhq.embeddedagent.extension;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Properties;
+import java.util.prefs.Preferences;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.xml.DOMConfigurator;
+
+import org.jboss.as.server.ServerEnvironment;
+import org.jboss.logging.Logger;
+import org.jboss.modules.Resource;
+import org.jboss.util.StringPropertyReplacer;
+
+import org.rhq.core.util.stream.StreamUtil;
+import org.rhq.enterprise.agent.AgentConfiguration;
+import org.rhq.enterprise.agent.AgentConfigurationConstants;
+import org.rhq.enterprise.agent.AgentConfigurationUpgrade;
+import org.rhq.enterprise.communications.ServiceContainerConfigurationConstants;
+
+public class AgentConfigurationSetup {
+
+ private final Logger log = Logger.getLogger(AgentConfigurationSetup.class);
+
+ private static final String DATA_DIRECTORY_NAME = "embeddedagent";
+
+ /**
+ * The location of the configuration file - can be a file path or path within
classloader.
+ */
+ private final Resource configFile;
+
+ /**
+ * Properties that will be used to override preferences found in the preferences node
and the configuration
+ * preferences file.
+ */
+ private final Properties configurationOverrides;
+
+ /**
+ * If <code>true</code>, will revert the agent's configuration back
to the original configuration file.
+ * Otherwise, the configuration will be that which is currently persisted in the
preferences store.
+ */
+ private final boolean resetConfigurationAtStartup;
+
+ /**
+ * The preferences node name that identifies the configuration set used to configure
the services.
+ */
+ private final String preferencesNodeName;
+
+ /**
+ * Provides environment information about the server in which we are embedded.
+ */
+ private final ServerEnvironment serverEnvironment;
+
+ public AgentConfigurationSetup(Resource configFile, boolean
resetConfigurationAtStartup,
+ Properties configurationOverrides, ServerEnvironment serverEnv) {
+
+ this.configFile = configFile;
+ this.resetConfigurationAtStartup = resetConfigurationAtStartup;
+ this.serverEnvironment = serverEnv;
+ this.configurationOverrides =
prepareConfigurationOverrides(configurationOverrides);
+
+ String agentName =
configurationOverrides.getProperty(AgentConfigurationConstants.NAME,
"embeddedagent");
+ preferencesNodeName = agentName;
+ System.setProperty("rhq.agent.preferences-node", preferencesNodeName);
+ }
+
+ public String getPreferencesNodeName() {
+ return this.preferencesNodeName;
+ }
+
+ private Properties prepareConfigurationOverrides(Properties overrides) {
+ // perform some checking to setup defaults if need be
+ String agentName = overrides.getProperty(AgentConfigurationConstants.NAME,
"");
+ if (agentName.trim().length() == 0 || "-".equals(agentName)) {
+ agentName = "embeddedagent-" + serverEnvironment.getNodeName();
+ }
+
+ agentName = StringPropertyReplacer.replaceProperties(agentName);
+ overrides.put(AgentConfigurationConstants.NAME, agentName);
+
+ File dataDir = getAgentDataDirectory();
+ File pluginsDir = new File(serverEnvironment.getServerDataDir(),
"embeddedagent-plugins");
+ overrides.put(AgentConfigurationConstants.DATA_DIRECTORY,
dataDir.getAbsolutePath());
+ overrides.put(AgentConfigurationConstants.PLUGINS_DIRECTORY,
pluginsDir.getAbsolutePath());
+ overrides.put(ServiceContainerConfigurationConstants.DATA_DIRECTORY,
dataDir.getAbsolutePath());
+
+ return overrides;
+ }
+
+ private File getAgentDataDirectory() {
+ File dir = new File(serverEnvironment.getServerDataDir(), DATA_DIRECTORY_NAME);
+ dir.mkdirs();
+ return dir;
+ }
+
+ public void preConfigureAgent() throws Exception {
+
+ // we need to store the preferences prior to starting the agent
+ if (resetConfigurationAtStartup) {
+ log.debug("Resetting the embedded agent's configuration back to its
original settings");
+ reloadAgentConfiguration();
+ cleanDataDirectory();
+ } else {
+ log.debug("Loading the embedded agent's pre-existing configuration
from preferences");
+ prepareConfigurationPreferences();
+ }
+
+ return;
+ }
+
+ /**
+ * Prepares the log config file so it writes the logs to the server's log
directory.
+ * This is needed if we call or use any agent class because it wants to use log4j.
+ * This MUST be called prior to using any class that logs via log4j.
+ *
+ * @param logConfigFile the agent's out-of-box log config file
+ * @return the new log config file that the agent should use
+ * @throws Exception
+ */
+ public void prepareLogConfigFile(Resource logConfigFile) throws Exception {
+ try {
+ File logDir = this.serverEnvironment.getServerLogDir();
+ String agentLogFile = new File(logDir,
"embedded-agent.log").getAbsolutePath();
+ String cmdTraceLogFile = new File(logDir,
"embedded-agent-command-trace.log").getAbsolutePath();
+
+ String logConfig = new String(StreamUtil.slurp(logConfigFile.openStream()));
+ logConfig = logConfig.replace("\"logs/agent.log\"",
"\"" + agentLogFile + "\"");
+ logConfig =
logConfig.replace("\"logs/command-trace.log\"", "\"" +
cmdTraceLogFile + "\"");
+ for (String app : new String[] { "ref=\"FILE\"",
"ref=\"COMMANDTRACE\"" }) {
+ logConfig = logConfig.replace("<!-- <appender-ref " + app
+ "/> -->", "<appender-ref " + app + "/>");
+ }
+
+ File runtimeLogConfigFile = new File(getAgentDataDirectory(),
"/log4j.xml");
+ ByteArrayInputStream in = new ByteArrayInputStream(logConfig.getBytes());
+ StreamUtil.copy(in, new FileOutputStream(runtimeLogConfigFile));
+
+ // this hot deploys the log4j.xml into log4j which is what the agent wants to
use
+ LogManager.resetConfiguration();
+ DOMConfigurator.configure(runtimeLogConfigFile.toURI().toURL());
+ } catch (Exception e) {
+ log.error("Cannot tell the agent to put its logs in the logs directory -
look elsewhere for the log files");
+ }
+ }
+
+ private Properties getAgentConfigurationProperties() {
+ try {
+ Properties properties = new Properties();
+ Preferences prefs = getPreferencesNode();
+
+ for (String key : prefs.keys()) {
+ properties.setProperty(key, prefs.get(key, "?"));
+ }
+
+ return properties;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void reloadAgentConfiguration() throws Exception {
+ getPreferencesNode().clear();
+ prepareConfigurationPreferences();
+ }
+
+ private void cleanDataDirectory() {
+ AgentConfiguration config = new AgentConfiguration(getPreferencesNode());
+ File dataDir = config.getDataDirectory();
+
+ cleanDataFile(dataDir);
+
+ // it is conceivable the comm services data directory was configured in a
different
+ // place than where the agent's data directory is - make sure we clean out
that other data dir
+ File commDataDir = config.getServiceContainerPreferences().getDataDirectory();
+ if (!commDataDir.getAbsolutePath().equals(dataDir.getAbsolutePath())) {
+ cleanDataFile(commDataDir);
+ }
+
+ return;
+ }
+
+ /**
+ * This will ensure the agent's configuration preferences are populated. If need
be, the configuration file is
+ * loaded and all overrides are overlaid on top of the preferences. The preferences
are also upgraded to ensure they
+ * conform to the latest configuration schema version.
+ *
+ * @return the agent configuration
+ *
+ * @throws Exception
+ */
+ private AgentConfiguration prepareConfigurationPreferences() throws Exception {
+ Preferences prefNode = getPreferencesNode();
+ AgentConfiguration config = new AgentConfiguration(prefNode);
+
+ if (config.getAgentConfigurationVersion() == 0) {
+ config = loadConfigurationFile();
+ }
+
+ // now that the configuration preferences are loaded, we need to override them
with any bootstrap override properties
+ Properties overrides = configurationOverrides;
+ if (overrides != null) {
+ for (Map.Entry<Object, Object> entry : overrides.entrySet()) {
+ String key = entry.getKey().toString();
+ String value = entry.getValue().toString();
+
+ // allow ${var} notation in the values so we can provide variable
replacements in the values
+ value = StringPropertyReplacer.replaceProperties(value);
+
+ prefNode.put(key, value);
+ }
+ }
+
+ // let's make sure our configuration is upgraded to the latest schema
+ AgentConfigurationUpgrade.upgradeToLatest(config.getPreferences());
+
+ return config;
+ }
+
+ /**
+ * Loads the configuration file.
+ *
+ * @return the configuration that was loaded
+ *
+ * @throws Exception on failure
+ */
+ private AgentConfiguration loadConfigurationFile() throws Exception {
+ // We need to clear out any previous configuration in case the current config
file doesn't specify a preference
+ // that already exists in the preferences node. In this case, the configuration
file wants to fall back on the
+ // default value and if we don't clear the preferences, we aren't
guaranteed the value stored in the backing
+ // store is the default value.
+ // But first we need to backup these original preferences in case the config file
fails to load -
+ // we'll restore the original values in that case.
+
+ Preferences prefNode = getPreferencesNode();
+ ByteArrayOutputStream backup = new ByteArrayOutputStream();
+ prefNode.exportSubtree(backup);
+ prefNode.clear();
+
+ // now load in the preferences
+ try {
+ ByteArrayOutputStream rawConfigFile = new ByteArrayOutputStream();
+ InputStream rawConfigInputStream = configFile.openStream();
+ StreamUtil.copy(rawConfigInputStream, rawConfigFile, true);
+ String newConfig =
StringPropertyReplacer.replaceProperties(rawConfigFile.toString());
+ ByteArrayInputStream newConfigInputStream = new
ByteArrayInputStream(newConfig.getBytes());
+ Preferences.importPreferences(newConfigInputStream);
+
+ if (new AgentConfiguration(prefNode).getAgentConfigurationVersion() == 0) {
+ throw new IllegalArgumentException("Bad preferences node");
+ }
+ } catch (Exception e) {
+ // a problem occurred importing the config file; let's restore our
original values
+ try {
+ Preferences.importPreferences(new
ByteArrayInputStream(backup.toByteArray()));
+ } catch (Exception e1) {
+ // its conceivable the same problem occurred here as with the original
exception (backing store problem?)
+ // let's throw the original exception, not this one
+ }
+ throw e;
+ }
+
+ AgentConfiguration agentConfig = new AgentConfiguration(prefNode);
+ return agentConfig;
+ }
+
+ /**
+ * Returns the preferences for this agent. The node returned is where all preferences
are to be stored.
+ *
+ * @return the agent preferences
+ */
+ private Preferences getPreferencesNode() {
+ Preferences topNode =
Preferences.userRoot().node(AgentConfigurationConstants.PREFERENCE_NODE_PARENT);
+ Preferences prefNode = topNode.node(preferencesNodeName);
+ return prefNode;
+ }
+
+ /**
+ * This will delete the given file and if its a directory, will recursively delete
its contents and its
+ * subdirectories.
+ *
+ * @param file the file/directory to delete
+ */
+ private void cleanDataFile(File file) {
+ boolean deleted;
+
+ File[] doomedFiles = file.listFiles();
+ if (doomedFiles != null) {
+ for (File doomedFile : doomedFiles) {
+ cleanDataFile(doomedFile); // call this method recursively
+ }
+ }
+
+ deleted = file.delete();
+
+ if (!deleted) {
+ log.warn("Cannot clean data file [" + file + "]");
+ }
+
+ return;
+ }
+}
\ No newline at end of file
diff --git
a/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentService.java
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentService.java
index 7860479..f4644e1 100644
---
a/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentService.java
+++
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentService.java
@@ -1,8 +1,10 @@
package org.rhq.embeddedagent.extension;
+import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.as.server.ServerEnvironment;
@@ -16,6 +18,8 @@ import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
+import org.rhq.enterprise.agent.AgentMain;
+
public class AgentService implements Service<AgentService> {
public static final ServiceName SERVICE_NAME =
ServiceName.of("org.rhq").append(
@@ -39,6 +43,17 @@ public class AgentService implements Service<AgentService> {
private Map<String, Boolean> plugins = Collections.synchronizedMap(new
HashMap<String, Boolean>());
/**
+ * Provides a mechanism to pre-configure the agent.
+ */
+ private AgentConfigurationSetup configSetup;
+
+ /**
+ * This is the actual embedded agent. This is what handles the plugin container
lifecycle
+ * and communication to/from the server.
+ */
+ private AgentMain theAgent;
+
+ /**
* Provides the status flag of the embedded agent itself (not of this service).
*/
private AtomicBoolean agentStarted = new AtomicBoolean(false);
@@ -96,12 +111,45 @@ public class AgentService implements Service<AgentService> {
return agentStarted.get();
}
- protected void startAgent() {
+ protected void startAgent() throws StartException {
+ if (isAgentStarted()) {
+ log.info("Embedded agent is already started.");
+ return;
+ }
+
log.info("Starting the embedded agent now");
- agentStarted.set(true);
+ try {
+ // make sure we pre-configure the agent with some settings taken from our
runtime environment
+ ServerEnvironment env = envServiceValue.getValue();
+ Properties overrides = new Properties();
+ boolean resetConfigurationAtStartup = true;
+ AgentConfigurationSetup configSetup = new AgentConfigurationSetup(
+ getExportedResource("conf/agent-configuration.xml"),
resetConfigurationAtStartup, overrides, env);
+ // prepare the agent logging first thing so the agent logs messages using
this config
+
configSetup.prepareLogConfigFile(getExportedResource("conf/log4j.xml"));
+ configSetup.preConfigureAgent();
+
+ // build the startup command line arguments to pass to the agent
+ String[] args = new String[3];
+ args[0] = "--daemon";
+ args[1] = "--pref=" + configSetup.getPreferencesNodeName();
+ args[2] = "--output=" + new File(env.getServerLogDir(),
"embedded-agent.out").getAbsolutePath();
+
+ theAgent = new AgentMain(args);
+ theAgent.start();
+
+ agentStarted.set(true);
+ } catch (Exception e) {
+ throw new StartException(e);
+ }
}
protected void stopAgent() {
+ if (!isAgentStarted()) {
+ log.info("Embedded agent is already stopped.");
+ return;
+ }
+
log.info("Stopping the embedded agent now");
agentStarted.set(false);
}
diff --git
a/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentSubsystemRestart.java
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentSubsystemRestart.java
index cdb14fd..e4080df 100644
---
a/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentSubsystemRestart.java
+++
b/modules/enterprise/server/embeddedagent/src/main/java/org/rhq/embeddedagent/extension/AgentSubsystemRestart.java
@@ -7,14 +7,15 @@ import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceNotFoundException;
+import org.jboss.msc.service.StartException;
class AgentSubsystemRestart implements OperationStepHandler {
- static final AgentSubsystemRestart INSTANCE = new AgentSubsystemRestart();
+ static final AgentSubsystemRestart INSTANCE = new AgentSubsystemRestart();
- private final Logger log = Logger.getLogger(AgentSubsystemRestart.class);
+ private final Logger log = Logger.getLogger(AgentSubsystemRestart.class);
- private AgentSubsystemRestart() {
+ private AgentSubsystemRestart() {
}
@Override
@@ -22,15 +23,17 @@ class AgentSubsystemRestart implements OperationStepHandler {
try {
ServiceName name = AgentService.SERVICE_NAME;
AgentService service = (AgentService)
opContext.getServiceRegistry(true).getRequiredService(name)
- .getValue();
+ .getValue();
log.info("Asked to restart the embedded agent");
service.stopAgent();
service.startAgent();
} catch (ServiceNotFoundException snfe) {
throw new OperationFailedException("Cannot restart embedded agent - the
agent is disabled", snfe);
- }
+ } catch (StartException se) {
+ throw new OperationFailedException("Cannot restart embedded agent",
se);
+ }
opContext.completeStep();
return;
- }
+ }
}
diff --git
a/modules/enterprise/server/embeddedagent/src/main/resources/module/main/module.xml
b/modules/enterprise/server/embeddedagent/src/main/resources/module/main/module.xml
index 2cc72ef..c818032 100644
--- a/modules/enterprise/server/embeddedagent/src/main/resources/module/main/module.xml
+++ b/modules/enterprise/server/embeddedagent/src/main/resources/module/main/module.xml
@@ -5,6 +5,7 @@
<resources>
<resource-root path="${project.build.finalName}.jar"/>
<resource-root path="rhq-agent"/>
+ <resource-root path="rhq-agent/conf"/> <!-- so we pick up the
agent's log4j.xml from here -->
<resource-root path="rhq-agent/lib/commons-httpclient-2.0.2.jar"
/> <!-- agent seems to want this specific version -->
<resource-root
path="rhq-agent/lib/commons-io-${commons-io.version}.jar" />
<resource-root
path="rhq-agent/lib/commons-logging-${commons-logging.version}.jar" />
diff --git a/modules/enterprise/server/embeddedagent/src/main/scripts/module-assembly.xml
b/modules/enterprise/server/embeddedagent/src/main/scripts/module-assembly.xml
index b9310c4..70d6642 100644
--- a/modules/enterprise/server/embeddedagent/src/main/scripts/module-assembly.xml
+++ b/modules/enterprise/server/embeddedagent/src/main/scripts/module-assembly.xml
@@ -8,15 +8,18 @@
<includeBaseDirectory>false</includeBaseDirectory>
<baseDirectory>${project.build.finalName}-module</baseDirectory>
<fileSets>
+ <!-- the module.xml which only does in the zip, it is not in the jar -->
<fileSet>
- <directory>${project.build.outputDirectory}/module</directory>
+ <directory>${basedir}/src/main/resources/module</directory>
<outputDirectory>/org/rhq/${artifactId}</outputDirectory>
<includes>
<include>main/module.xml</include>
</includes>
+ <filtered>true</filtered>
<fileMode>0644</fileMode>
<directoryMode>0755</directoryMode>
</fileSet>
+ <!-- the extension subsystem jar - this is just the extension classes, not the
agent itself -->
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/org/rhq/${artifactId}/main</outputDirectory>
@@ -26,6 +29,7 @@
<fileMode>0644</fileMode>
<directoryMode>0755</directoryMode>
</fileSet>
+ <!-- the agent distro itself, inside the module's rhq-agent subdirectory
-->
<fileSet>
<directory>${project.build.directory}/rhq-agent</directory>
<outputDirectory>/org/rhq/${artifactId}/main/rhq-agent</outputDirectory>
@@ -35,6 +39,7 @@
<fileMode>0644</fileMode>
<directoryMode>0755</directoryMode>
</fileSet>
+ <!-- the agent's native libraries which has to be specially organized for
JBoss Modules to find them -->
<fileSet>
<directory>${project.build.directory}/module-lib</directory>
<outputDirectory>/org/rhq/${artifactId}/main/lib</outputDirectory>