modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/configuration/MapPropertySimpleWrapper.java | 11
modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/util/StartScriptConfiguration.java | 35 ++
modules/core/plugin-api/src/test/java/org/rhq/core/pluginapi/util/StartScriptConfigurationTest.java | 136 ++++++++++
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AutoDiscoveryExecutor.java | 11
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java | 112 ++++----
modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml | 30 +-
modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml | 80 +++--
7 files changed, 305 insertions(+), 110 deletions(-)
New commits:
commit b1f434d25de060b809df7c971ca554512ea73b77
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon May 21 11:20:29 2012 -0400
[772223] related work
Further update startScript prop descriptions with feedback from field.
Conflicts:
modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
diff --git a/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
index b066b94..0fdc2be 100644
--- a/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
@@ -957,19 +957,29 @@
<c:simple-property name="startScriptEnv" displayName="Start Script Environment Variables"
required="false"
type="longString"
- default=""
- description="The variables that the Start and Restart operations will add to the environment
- of the server start script. Each name=value pair should be on a new line. Values
- should *not* be enclosed in quotes.
- For example: JAVA_OPTS=-Xms=128M" />
+ default="" >
+ <c:description>
+ The variables that the Start and Restart operations will add to the environment of the server start script.
+ Each name=value pair should be on a new line. Variable values should *not* be enclosed in quotes (e.g.
+ JAVA_OPTS=-Xms512M -Xmx1024M). On UNIX systems, the typical minimum set of environment variables is:
+ PATH=/usr/bin:/bin . And on Windows the typical minimum set is: PATH=C:\Windows\System32;C:\Windows ,
+ OS=Windows_NT , SYSTEMROOT=C:\Windows . In addition, it is good practice to set JAVA_HOME to the absolute
+ path of the install directory of the JRE or JDK you wish to use to run the AS7 instance. However, if
+ JAVA_HOME is not specified, the start script will attempt to find java in the PATH. There is currently
+ a 2000 character limit for this value.
+ </c:description>
+ </c:simple-property>
<c:simple-property name="startScriptArgs" displayName="Start Script Arguments"
required="false"
type="longString"
- default=""
- description="The arguments that the Start and Restart operations will pass to the server
- start script. Each argument should be on a new line. For example: --host=127.0.0.2 .
- As an exception, a space delimited option can have its value on the same line, or
- on a new line. For example: -b 127.0.0.2" />
+ default="" >
+ <c:description>
+ The arguments that the Start and Restart operations will pass to the server
+ start script. Each argument should be on a new line - for example: --server-config=standalone-ha-full.xml.
+ As an exception, the value of a space-delimited option can optionally be on the same line as the option -
+ for example: -c default. There is currently a 2000 character limit for this value.
+ </c:description>
+ </c:simple-property>
<c:simple-property name="shutdownScript" displayName="Shutdown Script" type="file" required="false"
description="The path to the script used by the Shut Down operation
to shut down this JBossAS server
diff --git a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
index ac629b6..0e2a1e3 100644
--- a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
@@ -5,45 +5,47 @@
<!ENTITY pluginName 'JBossAS7'>
<!ENTITY startScriptPluginConfigGroup '
- <c:group name="operations">
- <c:simple-property name="startScript" displayName="Start Script Path" type="file" required="false">
- <c:description>
- The path to the script that the Start and Restart operations should use to start the server. If the path is
- not absolute, it is resolved relative to the server home directory
- (e.g. "bin/standalone.sh", "bin/domain.sh").
- </c:description>
- </c:simple-property>
- <c:simple-property name="startScriptPrefix" type="string" required="false">
- <c:description>
- A prefix command line that should be prepended to the start script command line by the Start and Restart
- operations; the prefix command must be an absolute path (e.g. "/usr/bin/sudo"). This property is
- most commonly used to run the AS7 process as a different user than the RHQ Agent (e.g. "sudo -u jboss
- -g jboss" could be used to run AS7 as user "jboss" and group "jboss". It is also
- possible to chain prefix commands (e.g. "nohup sudo -u jboss" could be used to make AS7 ignore HUP
- signals and to run as user "jboss").
- </c:description>
- </c:simple-property>
- <c:simple-property name="startScriptEnv" displayName="Start Script Environment Variables" type="longString" required="false">
- <c:description>
- The variables that the Start and Restart operations will add to the environment of the server start script.
- Each name=value pair should be on a new line. Variable values should *not* be enclosed in quotes (e.g.
- JAVA_OPTS=-Xms512M -Xmx1024M). On UNIX systems, the typical minimum set of environment variables is:
- PATH=/usr/bin:/bin . And on Windows the typical minimum set is: PATH=C:\Windows\System32;C:\Windows ,
- OS=Windows_NT , SYSTEMROOT=C:\Windows . In addition, it is good practice to set JAVA_HOME to the absolute
- path of the install directory of the JRE or JDK you wish to use to run the AS7 instance. However, if
- JAVA_HOME is not specified, the start script will attempt to find java in the PATH.
- </c:description>
- </c:simple-property>
- <c:simple-property name="startScriptArgs" displayName="Start Script Arguments" type="longString" required="false">
- <c:description>
- The arguments that the Start and Restart operations will pass to the server
- start script. Each argument should be on a new line - for example: --server-config=standalone-ha-full.xml.
- As an exception, the value of a space-delimited option can optionally be on the same line as the option -
- for example: -c standalone-ha-full.xml.
- </c:description>
- </c:simple-property>
- </c:group>
- '>
+ <c:group name="operations">
+ <c:simple-property name="startScript" displayName="Start Script Path" type="file" required="false">
+ <c:description>
+ The path to the script that the Start and Restart operations should use to start the server. If the path is
+ not absolute, it is resolved relative to the server home directory
+ (e.g. "bin/standalone.sh", "bin/domain.sh").
+ </c:description>
+ </c:simple-property>
+ <c:simple-property name="startScriptPrefix" type="string" required="false">
+ <c:description>
+ A prefix command line that should be prepended to the start script command line by the Start and Restart
+ operations; the prefix command must be an absolute path (e.g. "/usr/bin/sudo"). This property is
+ most commonly used to run the AS7 process as a different user than the RHQ Agent (e.g. "sudo -u jboss
+ -g jboss" could be used to run AS7 as user "jboss" and group "jboss". It is also
+ possible to chain prefix commands (e.g. "nohup sudo -u jboss" could be used to make AS7 ignore HUP
+ signals and to run as user "jboss").
+ </c:description>
+ </c:simple-property>
+ <c:simple-property name="startScriptEnv" displayName="Start Script Environment Variables" type="longString" required="false">
+ <c:description>
+ The variables that the Start and Restart operations will add to the environment of the server start script.
+ Each name=value pair should be on a new line. Variable values should *not* be enclosed in quotes (e.g.
+ JAVA_OPTS=-Xms512M -Xmx1024M). On UNIX systems, the typical minimum set of environment variables is:
+ PATH=/usr/bin:/bin . And on Windows the typical minimum set is: PATH=C:\Windows\System32;C:\Windows ,
+ OS=Windows_NT , SYSTEMROOT=C:\Windows . In addition, it is good practice to set JAVA_HOME to the absolute
+ path of the install directory of the JRE or JDK you wish to use to run the AS7 instance. However, if
+ JAVA_HOME is not specified, the start script will attempt to find java in the PATH. There is currently
+ a 2000 character limit for this value.
+ </c:description>
+ </c:simple-property>
+ <c:simple-property name="startScriptArgs" displayName="Start Script Arguments" type="longString" required="false">
+ <c:description>
+ The arguments that the Start and Restart operations will pass to the server
+ start script. Each argument should be on a new line - for example: --server-config=standalone-ha-full.xml.
+ As an exception, the value of a space-delimited option can optionally be on the same line as the option -
+ for example: -c standalone-ha-full.xml. There is currently a 2000 character limit for this value.
+ </c:description>
+ </c:simple-property>
+ </c:group>
+'>
+
<!ENTITY logSources '
<c:group name="events">
commit 2eb3800f0b2367144fcd9f33baf7aed789bdf6d3
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue May 15 13:18:40 2012 -0400
[772223] related work
- Add tests for StartScriptConfiguration
- basic util test
- long (invalid) value handling for startScriptEnv or startScriptArg
Solution is to not update the startScript property as a truncated
value will provide an invalid value (bad map) and/or incomplete env.
Instead, we report a config prop error and log the full value in the
agent log for inspection. Note the GUI already prevents values that are
too long from being entered, so this protection is for discovery or
CLI based change.
- Fixed bug in StartScriptConfiguration setters when the prop was not yet
in the plugin config (mainly a unit test scenario)
diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/configuration/MapPropertySimpleWrapper.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/configuration/MapPropertySimpleWrapper.java
index 863279a..2bc8371 100644
--- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/configuration/MapPropertySimpleWrapper.java
+++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/configuration/MapPropertySimpleWrapper.java
@@ -37,6 +37,11 @@ public class MapPropertySimpleWrapper {
this.prop = prop;
}
+ /**
+ * @param map
+ * @throws IllegalArgumentException if the map values can not be translated to a storable string. Typically this
+ * means max property length is exceeded.
+ */
public void setValue(Map<String, String> map) {
String stringValue;
if (map != null) {
@@ -49,6 +54,12 @@ public class MapPropertySimpleWrapper {
} else {
stringValue = null;
}
+
+ // If the value is too long then don't store it, because it will likely invalidate the Map on the way back out.
+ if (null != stringValue && stringValue.length() > PropertySimple.MAX_VALUE_LENGTH) {
+ throw new IllegalArgumentException(stringValue);
+ }
+
this.prop.setStringValue(stringValue);
}
diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/util/StartScriptConfiguration.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/util/StartScriptConfiguration.java
index fd633dc..4e66c77 100644
--- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/util/StartScriptConfiguration.java
+++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/util/StartScriptConfiguration.java
@@ -28,6 +28,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -46,6 +48,8 @@ public class StartScriptConfiguration {
public static final String START_SCRIPT_ENV_PROP = "startScriptEnv";
public static final String START_SCRIPT_ARGS_PROP = "startScriptArgs";
+ private final Log log = LogFactory.getLog(this.getClass());
+
private Configuration pluginConfig;
public StartScriptConfiguration(Configuration pluginConfig) {
@@ -65,6 +69,7 @@ public class StartScriptConfiguration {
PropertySimple prop = this.pluginConfig.getSimple(START_SCRIPT_PROP);
if (prop == null) {
prop = new PropertySimple(START_SCRIPT_PROP, null);
+ this.pluginConfig.put(prop);
}
prop.setValue(startScript);
}
@@ -90,8 +95,14 @@ public class StartScriptConfiguration {
PropertySimple prop = this.pluginConfig.getSimple(START_SCRIPT_ENV_PROP);
if (prop == null) {
prop = new PropertySimple(START_SCRIPT_ENV_PROP, null);
+ this.pluginConfig.put(prop);
+ }
+
+ try {
+ new MapPropertySimpleWrapper(prop).setValue(startScriptEnv);
+ } catch (IllegalArgumentException e) {
+ setPropertyError(startScriptEnv, prop);
}
- new MapPropertySimpleWrapper(prop).setValue(startScriptEnv);
}
@NotNull
@@ -105,8 +116,22 @@ public class StartScriptConfiguration {
PropertySimple prop = this.pluginConfig.getSimple(START_SCRIPT_ARGS_PROP);
if (prop == null) {
prop = new PropertySimple(START_SCRIPT_ARGS_PROP, null);
+ this.pluginConfig.put(prop);
+ }
+
+ try {
+ new ArgsPropertySimpleWrapper(prop).setValue(startScriptArgs);
+ } catch (IllegalArgumentException e) {
+ setPropertyError(startScriptArgs, prop);
}
- new ArgsPropertySimpleWrapper(prop).setValue(startScriptArgs);
+ }
+
+ private void setPropertyError(Object rawPropValue, PropertySimple prop) {
+ prop.setErrorMessage("The discovered value of this property was longer than "
+ + PropertySimple.MAX_VALUE_LENGTH
+ + " characters (the maximum length of an RHQ property). It has been left unset. Please see the Agent log for the full value.");
+ log.warn("The discovered value for property [" + prop.getName()
+ + "] was too long and was left unset. It is logged here for reference: " + rawPropValue);
}
public Configuration getPluginConfig() {
@@ -139,6 +164,12 @@ public class StartScriptConfiguration {
} else {
stringValue = null;
}
+
+ // If the value is too long then don't store it, because it will likely invalidate the Map on the way back out.
+ if (null != stringValue && stringValue.length() > PropertySimple.MAX_VALUE_LENGTH) {
+ throw new IllegalArgumentException(stringValue);
+ }
+
this.prop.setStringValue(stringValue);
}
diff --git a/modules/core/plugin-api/src/test/java/org/rhq/core/pluginapi/util/StartScriptConfigurationTest.java b/modules/core/plugin-api/src/test/java/org/rhq/core/pluginapi/util/StartScriptConfigurationTest.java
new file mode 100644
index 0000000..18aad70
--- /dev/null
+++ b/modules/core/plugin-api/src/test/java/org/rhq/core/pluginapi/util/StartScriptConfigurationTest.java
@@ -0,0 +1,136 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2012 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.core.pluginapi.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.PropertySimple;
+
+/**
+ * Unit Tests.
+ *
+ * @author Jay Shaughnessy
+ */
+@Test
+public class StartScriptConfigurationTest {
+
+ @Test
+ public void testBasic() throws Exception {
+ Configuration config = new Configuration();
+
+ StartScriptConfiguration ssc = new StartScriptConfiguration(config);
+ assert config.equals(ssc.getPluginConfig());
+
+ File startScript = new File("run.sh");
+ ssc.setStartScript(startScript);
+ Assert.assertEquals(ssc.getStartScript(), startScript);
+
+ Map<String, String> startScriptEnv = new HashMap<String, String>();
+ startScriptEnv.put("JAVA_HOME", "/java");
+ startScriptEnv.put("JAVA_OPTS", "-XmX1024");
+ ssc.setStartScriptEnv(startScriptEnv);
+ Assert.assertEquals(ssc.getStartScriptEnv(), startScriptEnv);
+ Assert.assertTrue(ssc.getPluginConfig().getSimpleValue(StartScriptConfiguration.START_SCRIPT_ENV_PROP, "")
+ .contains("JAVA_HOME"));
+
+ List<String> startScriptArgs = new ArrayList<String>();
+ startScriptArgs.add("-c");
+ startScriptArgs.add("default");
+ ssc.setStartScriptArgs(startScriptArgs);
+ Assert.assertEquals(ssc.getStartScriptArgs(), startScriptArgs);
+ Assert.assertTrue(ssc.getPluginConfig().getSimpleValue(StartScriptConfiguration.START_SCRIPT_ARGS_PROP, "")
+ .contains("default"));
+ }
+
+ @Test
+ public void testLongEnvorArgs() throws Exception {
+ Configuration config = new Configuration();
+
+ StartScriptConfiguration ssc = new StartScriptConfiguration(config);
+ assert config.equals(ssc.getPluginConfig());
+
+ Map<String, String> startScriptEnv = new HashMap<String, String>();
+ char[] maxArr = new char[PropertySimple.MAX_VALUE_LENGTH];
+ Arrays.fill(maxArr, 'J');
+ String maxString = new String(maxArr);
+ char[] longArr = new char[500];
+ Arrays.fill(longArr, 'S');
+ String longString = new String(longArr);
+
+ // First ensure we can store the MAX VALUE
+ String maxVal = maxString.substring(0, PropertySimple.MAX_VALUE_LENGTH - 5); // MAX= and a line terminator
+ startScriptEnv.put("MAX", maxVal);
+ ssc.setStartScriptEnv(startScriptEnv);
+ Assert.assertNotNull(ssc.getStartScriptEnv());
+ Assert.assertEquals(ssc.getStartScriptEnv().get("MAX"), maxVal);
+ PropertySimple prop = ssc.getPluginConfig().getSimple(StartScriptConfiguration.START_SCRIPT_ENV_PROP);
+ Assert.assertNotNull(prop);
+ Assert.assertNotNull(prop.getStringValue());
+ Assert.assertEquals(prop.getStringValue().length(), PropertySimple.MAX_VALUE_LENGTH);
+ Assert.assertNull(prop.getErrorMessage());
+
+ List<String> startScriptArgs = new ArrayList<String>();
+ startScriptArgs.add(maxString);
+ ssc.setStartScriptArgs(startScriptArgs);
+ Assert.assertNotNull(ssc.getStartScriptArgs());
+ Assert.assertEquals(ssc.getStartScriptArgs().size(), 1);
+ Assert.assertEquals(ssc.getStartScriptArgs().get(0), maxString);
+ prop = ssc.getPluginConfig().getSimple(StartScriptConfiguration.START_SCRIPT_ARGS_PROP);
+ Assert.assertNotNull(prop);
+ Assert.assertNotNull(prop.getStringValue());
+ Assert.assertNull(prop.getErrorMessage());
+
+ // Now, break the camel
+ config = new Configuration();
+ ssc = new StartScriptConfiguration(config);
+ assert config.equals(ssc.getPluginConfig());
+
+ startScriptEnv.put("LONG2", longString);
+ ssc.setStartScriptEnv(startScriptEnv);
+ Assert.assertTrue(ssc.getStartScriptEnv().isEmpty());
+ prop = ssc.getPluginConfig().getSimple(StartScriptConfiguration.START_SCRIPT_ENV_PROP);
+ Assert.assertNotNull(prop);
+ Assert.assertNull(prop.getStringValue());
+ Assert.assertNotNull(prop.getErrorMessage());
+ assert prop.getErrorMessage().contains("long");
+
+ startScriptArgs.add(longString);
+ ssc.setStartScriptArgs(startScriptArgs);
+ Assert.assertTrue(ssc.getStartScriptArgs().isEmpty());
+ prop = ssc.getPluginConfig().getSimple(StartScriptConfiguration.START_SCRIPT_ARGS_PROP);
+ Assert.assertNotNull(prop);
+ Assert.assertNull(prop.getStringValue());
+ Assert.assertNotNull(prop.getErrorMessage());
+ assert prop.getErrorMessage().contains("long");
+ }
+
+}
commit ac6a8cb6370a423e286f99b6bffda3b547a85615
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu May 17 16:19:58 2012 -0400
[Bug 820709 - [plugin-container] with two or more AS7 host controllers in inventory, a full discovery scan takes an unacceptably long time]
- Working on discovery algorithm. Trying to make it more efficient recursion.
- remove unnecessary/invalid avail checking
- add a little jdoc
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AutoDiscoveryExecutor.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AutoDiscoveryExecutor.java
index 67094b5..6ae4d53 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AutoDiscoveryExecutor.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AutoDiscoveryExecutor.java
@@ -34,8 +34,8 @@ import java.util.concurrent.Callable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.jetbrains.annotations.NotNull;
+
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.server.discovery.AutoDiscoveryRequest;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
@@ -60,9 +60,12 @@ import org.rhq.core.util.exception.ExceptionPackage;
import org.rhq.core.util.exception.Severity;
/**
- * Standard platform/server inventory detection execution. This is typically called in a non-blocking fashion, and the
- * report is returned asynchronously to the server. It is available for direct execution via a Future when running in an
- * embedded mode.
+ * Standard platform/server inventory detection execution. This looks for top level servers, typically doing
+ * process-based discovery. It should probably be renamed to ServerDiscoveryExecutor as "AutoDiscovery" is
+ * pretty much redundant and non-descriptive. This is typically called in a non-blocking fashion, and the report
+ * is returned asynchronously to the server. It is available for direct execution via a Future when running in
+ * an embedded mode. This is complemented by {@link RuntimeDiscoveryExecutor} for discovering new child resources
+ * in the existing inventory hierarchy.
*
* @author Greg Hinkle
* @author John Mazzitelli
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
index cf5b86e..f69bdfb 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2012 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -32,8 +32,8 @@ import java.util.concurrent.Callable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.jetbrains.annotations.NotNull;
+
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.measurement.AvailabilityType;
@@ -50,9 +50,14 @@ import org.rhq.core.util.exception.ExceptionPackage;
import org.rhq.core.util.exception.Severity;
/**
-* @author Greg Hinkle
-* @author Ian Springer
-*/
+ * This should probably be renamed to ServiceDiscoveryExecutor or maybe ChildDiscoveryExecutor. It is responsible for
+ * discovering children of existing resources. It recursively walks the hierarchy looking for new resources, which
+ * are typically services (but could be non-top-level servers). It is complemented by {@link AutoDiscoveryExecutor}
+ * which looks for new top level servers.
+ *
+ * @author Greg Hinkle
+ * @author Ian Springer
+ */
public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryReport> {
private Log log = LogFactory.getLog(RuntimeDiscoveryExecutor.class);
@@ -62,7 +67,7 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
/**
* Resource to scan. If null, the entire platform will be scanned.
*/
- private Resource resource;
+ private Resource rootResource;
public RuntimeDiscoveryExecutor(InventoryManager inventoryManager,
PluginContainerConfiguration pluginContainerConfiguration) {
@@ -76,12 +81,12 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
*
* @param inventoryManager hook back to the inventory manager
* @param pluginContainerConfiguration configuration of this executor
- * @param resource scopes the runtime scan to a particular resource
+ * @param rootResource scopes the runtime scan to a particular resource
*/
public RuntimeDiscoveryExecutor(InventoryManager inventoryManager,
- PluginContainerConfiguration pluginContainerConfiguration, Resource resource) {
+ PluginContainerConfiguration pluginContainerConfiguration, Resource rootResource) {
this(inventoryManager, pluginContainerConfiguration);
- this.resource = resource;
+ this.rootResource = rootResource;
}
public void run() {
@@ -90,7 +95,7 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
@NotNull
public InventoryReport call() {
- String target = (resource != null) ? this.resource.toString() : "platform";
+ String target = (rootResource != null) ? this.rootResource.toString() : "platform";
log.info("Executing runtime discovery scan rooted at [" + target + "]...");
InventoryReport report = new InventoryReport(inventoryManager.getAgent());
@@ -101,8 +106,8 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
report.setEndTime(System.currentTimeMillis());
if (log.isDebugEnabled()) {
- log.debug(String.format("Runtime discovery scan took %d ms.", (report.getEndTime() - report
- .getStartTime())));
+ log.debug(String.format("Runtime discovery scan took %d ms.",
+ (report.getEndTime() - report.getStartTime())));
}
// TODO: This is always zero for embedded because we don't populate the report.
@@ -128,38 +133,17 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
}
private void runtimeDiscover(InventoryReport report) throws PluginContainerException {
- // Always start out by refreshing availabilities, since we will only scan servers that are available.
- this.inventoryManager.executeAvailabilityScanImmediately(true);
- if (this.resource == null) {
+
+ if (this.rootResource == null) {
// Run a full scan for all resources in the inventory
Resource platform = this.inventoryManager.getPlatform();
// Discover platform services here
discoverForResource(platform, report, false);
- // Next discover all other services and non-top-level servers, recursively down the hierarchy
- discoverForResourceRecursive(platform, report);
} else {
// Run a single scan for just a resource and its descendants
- discoverForResource(resource, report, false);
- }
-
- return;
- }
-
- private void discoverForResourceRecursive(Resource parent, InventoryReport report) throws PluginContainerException {
- for (Resource child : parent.getChildResources()) {
- // See if the child has new children itself. Then we check those children to see if there are grandchildren.
- // Note that if the child has already been added to the report, there is no need to process it again, so skip it.
- boolean alreadyProcessed = report.getAddedRoots().contains(child);
- if (!alreadyProcessed) {
- discoverForResource(child, report, alreadyProcessed);
- // We need to recurse here even though discoverForResource recurses over child, too.
- // This is because that discovery above only goes over newly discovered resources.
- // It is possible this child has already existing children (e.g. previously manually added)
- // that they themselves might have additional new children that need discovering.
- discoverForResourceRecursive(child, report);
- }
+ discoverForResource(rootResource, report, false);
}
return;
@@ -175,8 +159,6 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
*/
private void discoverForResource(Resource parent, InventoryReport report, boolean parentReported)
throws PluginContainerException {
- // TODO GH: If resource.isRuntimeDiscoveryEnabled
- // TODO GH: If resource.isInventoryStatusCommitted
log.debug("Discovering child Resources for " + parent + "...");
@@ -210,10 +192,23 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
return;
}
+ // For each child resource type of the server, do a discovery for resources of that type
+ Set<ResourceType> childResourceTypes = parent.getResourceType().getChildResourceTypes();
+ if (null == childResourceTypes || childResourceTypes.isEmpty()) {
+ // I'm not sure it's possible, but just in case, make sure it doesn't have children. If it does, keep going
+ if (parent.getChildResources().isEmpty()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parent resource type [" + parent + "] has no child types; cannot perform service scan.");
+ }
+ return;
+ }
+ }
+
// Do a live check of availability here. This won't set the availability anywhere but will allow us
// to find nested resources, i.e. children of resources we've found during our recursive call
// to discoverForResource(). Without this live check, the availability of these newly discovered
- // resources would be null, so we would just return without checking for their children.
+ // resources would be null, so we would just return without checking for their children. Also, we don't
+ // want to do discovery for a NOT UP parent.
AvailabilityType availability;
ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
try {
@@ -232,11 +227,11 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
return;
}
- // For each child resource type of the server, do a discovery for resources of that type
PluginComponentFactory factory = PluginContainer.getInstance().getPluginComponentFactory();
- Set<ResourceType> childResourceTypes = parent.getResourceType().getChildResourceTypes();
- for (ResourceType childResourceType : childResourceTypes) {
- try {
+
+ try {
+
+ for (ResourceType childResourceType : childResourceTypes) {
// Make sure we have a discovery component for that type, otherwise there is nothing to do
ResourceDiscoveryComponent discoveryComponent = null;
try {
@@ -257,29 +252,36 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
log.debug("Running service scan on parent resource [" + parent + "] looking for children of type ["
+ childResourceType + "]");
}
- Set<Resource> childResources = this.inventoryManager.executeComponentDiscovery(childResourceType,
- discoveryComponent, parentContainer, Collections.<ProcessScanResult> emptyList());
+ Set<Resource> discoveredChildResources = this.inventoryManager
+ .executeComponentDiscovery(childResourceType, discoveryComponent, parentContainer,
+ Collections.<ProcessScanResult> emptyList());
- // For each discovered resource, update it in the inventory manager and recursively discover its child resources
+ // For each discovered child resource, update it in the inventory manager
Map<String, Resource> mergedResources = new HashMap<String, Resource>();
-
- for (Resource childResource : childResources) {
- boolean thisInReport = false;
+ for (Resource discoveredChildResource : discoveredChildResources) {
Resource mergedResource;
- mergedResource = this.inventoryManager.mergeResourceFromDiscovery(childResource, parent);
+ mergedResource = this.inventoryManager.mergeResourceFromDiscovery(discoveredChildResource, parent);
mergedResources.put(mergedResource.getUuid(), mergedResource);
if ((mergedResource.getId() == 0) && !parentReported) {
report.addAddedRoot(parent);
- thisInReport = true;
parentReported = true;
}
- discoverForResource(mergedResource, report, thisInReport);
}
+
+ // get rid of any child resources of this type that were not yet committed and are now gone
removeStaleResources(parent, childResourceType, mergedResources);
- } catch (Throwable t) {
- report.getErrors().add(new ExceptionPackage(Severity.Severe, t));
- log.error("Error in runtime discovery", t);
+
}
+
+ // now, recursively perform discovery on all of the parent's children, which includes the newly
+ // merged children as well as previously existing children.
+ for (Resource childResource : parent.getChildResources()) {
+ discoverForResource(childResource, report, parentReported);
+ }
+
+ } catch (Throwable t) {
+ report.getErrors().add(new ExceptionPackage(Severity.Severe, t));
+ log.error("Error in runtime discovery", t);
}
return;