modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginDependencyGraph.java | 51 ++
modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataManager.java | 11
modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataParser.java | 4
modules/core/client-api/src/test/java/org/rhq/core/clientapi/agent/metadata/test/PluginDependencyGraphTest.java | 60 +++
modules/core/domain/src/main/java/org/rhq/core/domain/plugin/Plugin.java | 27 +
modules/core/plugin-container/src/main/java/org/rhq/core/pc/PluginContainerConfiguration.java | 52 +-
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryFile.java | 65 +++
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java | 46 +-
modules/core/plugin-container/src/main/java/org/rhq/core/pc/plugin/PluginManager.java | 97 +----
modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java | 181 +++++-----
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfiguration.java | 12
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfigurationConstants.java | 5
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentMain.java | 5
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/PluginUpdate.java | 66 ++-
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java | 38 +-
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/promptcmd/PluginsPromptCommand.java | 26 +
modules/enterprise/agent/src/main/resources/agent-configuration.xml | 26 +
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/admin/plugin/InstalledPluginsUIBean.java | 64 +++
modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-details.xhtml | 2
modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-list.xhtml | 16
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerBean.java | 122 ++++++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerLocal.java | 9
modules/enterprise/server/sars/agent-sar/src/main/resources/META-INF/embedded-agent-configuration.xml | 26 +
modules/plugins/rhq-agent/src/main/resources/META-INF/rhq-plugin.xml | 1
24 files changed, 781 insertions(+), 231 deletions(-)
New commits:
commit ce7eb3d0afc94d75274b1a5a751c30653bfedba6
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Fri Feb 5 00:59:04 2010 -0500
a solution to the infamous RHQ-2 (now BZ 535894)
you can disable an agent plugin such that all agents will no longer download/deploy them
if a resource exists in inventory of a type from a disabled plugin, the agent will remove it from its internal inventory.
any attempt by the server to access a disabled resource will result in an error.
the user must eventually uninventory resources that are disabled. the plugin and its metadata
will remain in the database, but they will essentially be ignored by the agents.
in addition, the agent has a configuration preference rhq.agent.plugins.disabled that lets
you have an individual agent disable a plugin(s) while having other agents still have those
plugins enabled.
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginDependencyGraph.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginDependencyGraph.java
index 2acecfc..8f7de5d 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginDependencyGraph.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginDependencyGraph.java
@@ -102,8 +102,8 @@ public class PluginDependencyGraph {
/**
* Given a plugin that is in this dependency graph, this will return the list of its direct dependencies (the
- * plugins this plugin explicitly depends on). The list will be empty if there are no dependencies. <code>
- * null</code> will be returned if the plugin does not exist in this graph.
+ * plugins this plugin explicitly depends on). The list will be empty if there are no dependencies or the
+ * plugin does not exist in this graph.
*
* Note that if a dependency is not required to exist, and it does not exist, it will not be returned
* in the list. This is to say that if a plugin was configured to depend on another plugin, but that
@@ -112,7 +112,7 @@ public class PluginDependencyGraph {
*
* @param pluginName the plugin name
*
- * @return list of plugin dependencies (<code>null</code> if the plugin doesn't exist yet in the graph)
+ * @return list of plugin dependencies
*/
public List<String> getPluginDependencies(String pluginName) {
List<String> dependencies = new ArrayList<String>();
@@ -154,6 +154,51 @@ public class PluginDependencyGraph {
}
/**
+ * Given a plugin that is in this dependency graph, this will return all those plugins
+ * the plugin either directly or indirectly depends on it. This is different
+ * than {@link #getPluginDependencies(String)} because this method does a deep
+ * dive and returns all direct dependencies and dependencies of those dependencies.
+ *
+ * @param pluginName the plugin whose dependencies are to be returned
+ *
+ * @return list of all plugins that the given plugin depends on
+ */
+ public Collection<String> getAllDependencies(String pluginName) {
+ if (this.dependencyMap.containsKey(pluginName)) {
+ return getDeepDependencies(pluginName, new ArrayList<String>(), true);
+ } else {
+ return new HashSet<String>();
+ }
+ }
+
+ /**
+ * Given a plugin that is in this dependency graph, this will return all those plugins
+ * that either directly or indirectly depend on it (both optional and required dependencies).
+ *
+ * @param pluginName the plugin whose dependents are to be returned
+ *
+ * @return list of all plugins that depend on the given plugin
+ */
+ public Collection<String> getAllDependents(String pluginName) {
+ Set<String> dependents = new HashSet<String>();
+ for (Map.Entry<String, List<PluginDependency>> entry : this.dependencyMap.entrySet()) {
+ if (entry.getKey().equals(pluginName)) {
+ continue; // don't bother examining the plugin itself
+ }
+
+ // see if current plugin depends on the given pluginName, if so, add it to the list
+ for (PluginDependency dependency : entry.getValue()) {
+ if (dependency.name.equals(pluginName)) {
+ dependents.addAll(getAllDependents(entry.getKey()));
+ dependents.add(entry.getKey());
+ break;
+ }
+ }
+ }
+ return dependents;
+ }
+
+ /**
* Returns <code>true</code> if the dependency graph has no missing required plugins.
* That is to say, all required dependencies of all plugins can be found in this graph. If this returns <code>true</code>,
* you can safely call {@link #getDeploymentOrder()} and expect it to return an ordered list of plugins.
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataManager.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataManager.java
index 425c4da..63072db 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataManager.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataManager.java
@@ -32,6 +32,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
+import org.rhq.core.clientapi.descriptor.AgentPluginDescriptorUtil;
import org.rhq.core.clientapi.descriptor.plugin.PluginDescriptor;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
@@ -211,4 +212,14 @@ public class PluginMetadataManager {
public Set<String> getPluginNames() {
return this.parsersByPlugin.keySet();
}
+
+ public PluginDependencyGraph buildDependencyGraph() {
+ PluginDependencyGraph dependencyGraph = new PluginDependencyGraph();
+ for (String pluginName : getPluginNames()) {
+ PluginDescriptor descriptor = this.parsersByPlugin.get(pluginName).getDescriptor();
+ AgentPluginDescriptorUtil.addPluginToDependencyGraph(dependencyGraph, descriptor);
+ }
+ return dependencyGraph;
+ }
+
}
\ No newline at end of file
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataParser.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataParser.java
index 27d7c34..5d38963 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataParser.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/metadata/PluginMetadataParser.java
@@ -92,6 +92,10 @@ public class PluginMetadataParser {
parseDescriptor();
}
+ public PluginDescriptor getDescriptor() {
+ return this.pluginDescriptor;
+ }
+
public String getPluginLifecycleListenerClass() {
String pkg = this.pluginDescriptor.getPackage();
String clazz = this.pluginDescriptor.getPluginLifecycleListener();
diff --git a/modules/core/client-api/src/test/java/org/rhq/core/clientapi/agent/metadata/test/PluginDependencyGraphTest.java b/modules/core/client-api/src/test/java/org/rhq/core/clientapi/agent/metadata/test/PluginDependencyGraphTest.java
index 097940c..3b8462a 100644
--- a/modules/core/client-api/src/test/java/org/rhq/core/clientapi/agent/metadata/test/PluginDependencyGraphTest.java
+++ b/modules/core/client-api/src/test/java/org/rhq/core/clientapi/agent/metadata/test/PluginDependencyGraphTest.java
@@ -23,6 +23,7 @@
package org.rhq.core.clientapi.agent.metadata.test;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import org.testng.annotations.Test;
@@ -81,6 +82,41 @@ public class PluginDependencyGraphTest {
assert dependents.contains("D") : dependents;
assert dependents.size() == 2 : dependents;
+ Collection<String> dependents2 = graph.getAllDependents("F");
+ assert dependents2.contains("A") : dependents2;
+ assert dependents2.contains("B") : dependents2;
+ assert dependents2.contains("C") : dependents2;
+ assert dependents2.contains("D") : dependents2;
+ assert dependents2.contains("G") : dependents2;
+ assert dependents2.size() == 5 : dependents2;
+
+ // plugin A depends on plugin B (required)
+ // plugin B depends on plugin D and C (required)
+ // plugin C depends on plugin E and F (optional)
+
+ Collection<String> dependencies3 = graph.getAllDependencies("F");
+ assert dependencies3.isEmpty() : dependencies3;
+
+ dependencies3 = graph.getAllDependencies("A");
+ assert dependencies3.contains("B") : dependencies3;
+ assert dependencies3.contains("D") : dependencies3;
+ assert dependencies3.contains("C") : dependencies3;
+ assert dependencies3.contains("E") : dependencies3;
+ assert dependencies3.contains("F") : dependencies3;
+ assert dependencies3.size() == 5 : dependencies3;
+
+ dependencies3 = graph.getAllDependencies("B");
+ assert dependencies3.contains("D") : dependencies3;
+ assert dependencies3.contains("C") : dependencies3;
+ assert dependencies3.contains("E") : dependencies3;
+ assert dependencies3.contains("F") : dependencies3;
+ assert dependencies3.size() == 4 : dependencies3;
+
+ dependencies3 = graph.getAllDependencies("C");
+ assert dependencies3.contains("E") : dependencies3;
+ assert dependencies3.contains("F") : dependencies3;
+ assert dependencies3.size() == 2 : dependencies3;
+
// Use the same dependency graph, but do not deploy plugin F.
// With F missing, G will fail because it required F
graph = new PluginDependencyGraph();
@@ -255,6 +291,30 @@ public class PluginDependencyGraphTest {
assert order.get(0).equals("C") : order;
assert order.get(1).equals("B") : order;
assert order.get(2).equals("A") : order;
+
+ Collection<String> dependents = graph.getAllDependents("unknownPlugin");
+ assert dependents.isEmpty() : dependents;
+ dependents = graph.getAllDependents("A");
+ assert dependents.isEmpty() : dependents;
+ dependents = graph.getAllDependents("B");
+ assert dependents.contains("A") : dependents;
+ assert dependents.size() == 1 : dependents;
+ dependents = graph.getAllDependents("C");
+ assert dependents.contains("A") : dependents;
+ assert dependents.contains("B") : dependents;
+ assert dependents.size() == 2 : dependents;
+
+ Collection<String> dependencies = graph.getAllDependencies("unknownPlugin");
+ assert dependencies.isEmpty() : dependencies;
+ dependencies = graph.getAllDependencies("C");
+ assert dependencies.isEmpty() : dependencies;
+ dependencies = graph.getAllDependencies("B");
+ assert dependencies.contains("C") : dependencies;
+ assert dependencies.size() == 1 : dependencies;
+ dependencies = graph.getAllDependencies("A");
+ assert dependencies.contains("C") : dependencies;
+ assert dependencies.contains("B") : dependencies;
+ assert dependencies.size() == 2 : dependencies;
}
public void testComplexDependencyGraph() {
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/plugin/Plugin.java b/modules/core/domain/src/main/java/org/rhq/core/domain/plugin/Plugin.java
index cdd3254..126738c 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/plugin/Plugin.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/plugin/Plugin.java
@@ -36,6 +36,12 @@ import javax.persistence.NamedQuery;
@DiscriminatorValue("AGENT")
@NamedQueries( {
//
+ // this query is how you enable and disable plugins
+ @NamedQuery(name = Plugin.UPDATE_PLUGIN_ENABLED_BY_ID, query = "" //
+ + "UPDATE Plugin p " //
+ + " SET p.enabled = :enabled " //
+ + " WHERE p.id = :id)"),
+
// helps you determine if a plugin is installed or was deleted
@NamedQuery(name = Plugin.QUERY_GET_STATUS_BY_NAME, query = "" //
+ " SELECT p.status " //
@@ -70,6 +76,25 @@ import javax.persistence.NamedQuery;
+ " AND p.status = 'INSTALLED' "), //
// this query does not load the content blob, but loads everything else
+ @NamedQuery(name = Plugin.QUERY_FIND_ALL_BY_IDS, query = "" //
+ + " SELECT new org.rhq.core.domain.plugin.Plugin( " //
+ + " p.id, " //
+ + " p.name, " //
+ + " p.path, " //
+ + " p.displayName, " //
+ + " p.enabled, " //
+ + " p.status, " //
+ + " p.description, " //
+ + " p.help, " //
+ + " p.md5, " //
+ + " p.version, " //
+ + " p.ampsVersion, " //
+ + " p.ctime, " //
+ + " p.mtime) " //
+ + " FROM Plugin AS p " //
+ + " WHERE p.id IN (:ids) "), //
+
+ // this query does not load the content blob, but loads everything else
@NamedQuery(name = Plugin.QUERY_FIND_BY_NAME, query = "" //
+ " SELECT new org.rhq.core.domain.plugin.Plugin( " //
+ " p.id, " //
@@ -167,11 +192,13 @@ public class Plugin extends AbstractPlugin {
public static final String QUERY_GET_STATUS_BY_NAME = "Plugin.queryGetStatusByName";
public static final String QUERY_GET_NAMES_BY_ENABLED = "Plugin.queryGetNamesByEnabled";
public static final String QUERY_FIND_BY_IDS = "Plugin.findByIds";
+ public static final String QUERY_FIND_ALL_BY_IDS = "Plugin.findAllByIds";
public static final String QUERY_FIND_BY_NAME = "Plugin.findByName";
public static final String QUERY_FIND_ANY_BY_NAME = "Plugin.findAnyByName";
public static final String QUERY_FIND_ALL_INSTALLED = "Plugin.findAllInstalled";
public static final String UPDATE_PLUGINS_ENABLED_BY_IDS = "Plugin.updatePluginsEnabledByIds";
public static final String QUERY_FIND_BY_RESOURCE_TYPE_AND_CATEGORY = "Plugin.findByResourceType";
+ public static final String UPDATE_PLUGIN_ENABLED_BY_ID = "Plugin.updatePluginEnabledById";
public Plugin() {
super();
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/PluginContainerConfiguration.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/PluginContainerConfiguration.java
index 563eb47..f39fa13 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/PluginContainerConfiguration.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/PluginContainerConfiguration.java
@@ -24,15 +24,14 @@ package org.rhq.core.pc;
import java.io.File;
import java.net.InetAddress;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.HashSet;
import org.rhq.core.pc.plugin.PluginFinder;
import org.rhq.core.pc.plugin.RootPluginClassLoader;
import org.rhq.core.pluginapi.inventory.PluginContainerDeployment;
-import org.rhq.core.domain.plugin.Plugin;
/**
* Configuration properties for the plugin container and all its internal managers.
@@ -47,6 +46,7 @@ public class PluginContainerConfiguration {
private static final String CONTAINER_NAME_PROP = PROP_PREFIX + "container-name";
private static final String DATA_DIRECTORY_PROP = PROP_PREFIX + "data-directory";
private static final String TEMP_DIRECTORY_PROP = PROP_PREFIX + "temp-directory";
+ private static final String DISABLED_PLUGINS = PROP_PREFIX + "disabled-plugins";
private static final String ROOT_PLUGIN_CLASSLOADER_REGEX_PROP = PROP_PREFIX + "root-plugin-classloader-regex";
private static final String CREATE_RESOURCE_CLASSLOADERS = PROP_PREFIX + "create-resource-classloaders";
@@ -127,11 +127,6 @@ public class PluginContainerConfiguration {
private ServerServices serverServices = null;
/**
- * The plugins that reside on the server which are sent down to the agent.
- */
- private Set<Plugin> pluginsOnServer = new HashSet<Plugin>();
-
- /**
* This is our hash map that contains the actual properties. We use a map (as opposed to individual data member
* variables) to support a future enhancement by which our plugins can squirrel away their own custom global
* properties here.
@@ -195,6 +190,39 @@ public class PluginContainerConfiguration {
}
/**
+ * If any plugins are to be disabled (i.e. not loaded by the plugin container), the plugin
+ * names will be returned in a list. If no plugins are to be disabled, and empty list is
+ * returned. The returned list is a copy, not the actual list used internally.
+ * Note that the plugin name is the name found in the plugin .xml descriptor in the plugin root element.
+ *
+ * @return list of plugin names identifying plugins to be disabled
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getDisabledPlugins() {
+ List<String> list = (List<String>) configuration.get(DISABLED_PLUGINS);
+ if (list == null) {
+ return new ArrayList<String>(0);
+ } else {
+ return new ArrayList<String>(list);
+ }
+ }
+
+ /**
+ * If one or more plugins are not to be loaded by the plugin container, the given
+ * list should be the names of the plugins to be disabled. Note that the plugin name
+ * is the name found in the plugin .xml descriptor in the plugin root element.
+ *
+ * @param disabledPlugins
+ */
+ public void setDisabledPlugins(List<String> disabledPlugins) {
+ if (disabledPlugins != null) {
+ configuration.put(DISABLED_PLUGINS, disabledPlugins);
+ } else {
+ configuration.remove(DISABLED_PLUGINS);
+ }
+ }
+
+ /**
* Returns the regex that defines what classes the plugin container can provide to its
* plugins from its own classloader and its parents. If not <code>null</code>, any classes
* found in the plugin container's classloader (and its parent classloaders) that do
@@ -712,14 +740,6 @@ public class PluginContainerConfiguration {
this.serverServices = serverServices;
}
- public Set<Plugin> getPluginsOnServer() {
- return pluginsOnServer;
- }
-
- public void setPluginsOnServer(Set<Plugin> plugins) {
- pluginsOnServer = plugins;
- }
-
/**
* Returns whether or not the plugin container is running inside an agent, which means it is running external to any
* managed product.
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryFile.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryFile.java
index 839b8a2..7a8e1e5 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryFile.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryFile.java
@@ -28,6 +28,8 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -50,7 +52,7 @@ public class InventoryFile {
private final File inventoryFile;
private Resource platform;
- private Map<String, ResourceContainer> resourceContainers;
+ private Map<String, ResourceContainer> resourceContainers; // keyed on UUID
/**
* Constructor for {@link InventoryFile} that will read and write inventory data to the given file.
@@ -105,35 +107,82 @@ public class InventoryFile {
FileInputStream fis = new FileInputStream(inventoryFile);
ObjectInputStream ois = new ObjectInputStream(fis);
+ // this list will contain UUIDs of resources that we should ignore usually due to disabled plugins
+ Set<String> uuidsToIgnore = new HashSet<String>();
+
this.platform = (Resource) ois.readObject();
- connectTypes(this.platform);
+ connectTypes(this.platform, uuidsToIgnore);
this.resourceContainers = (Map<String, ResourceContainer>) ois.readObject();
for (ResourceContainer resourceContainer : this.resourceContainers.values()) {
- connectTypes(resourceContainer.getResource());
+ connectTypes(resourceContainer.getResource(), uuidsToIgnore);
+ }
+
+ for (String uuidToIgnore : uuidsToIgnore) {
+ this.resourceContainers.remove(uuidToIgnore);
}
+
+ // purge all resources from disabled plugins - after this call, uuidsToIgnore should be empty
+ removeIgnoredResourcesFromChildren(this.platform, uuidsToIgnore);
+ return;
} catch (Exception e) {
throw new PluginContainerException("Cannot load inventory file: " + inventoryFile, e);
}
}
- private void connectTypes(Resource resource) {
+ private void removeIgnoredResourcesFromChildren(Resource resource, Set<String> uuidsToIgnore) {
+ Set<Resource> children = resource.getChildResources();
+ if (children != null && !children.isEmpty() && !uuidsToIgnore.isEmpty()) {
+ Iterator<Resource> iterator = children.iterator();
+ while (iterator.hasNext() && !uuidsToIgnore.isEmpty()) {
+ Resource child = iterator.next();
+ removeIgnoredResourcesFromChildren(child, uuidsToIgnore);
+ if (uuidsToIgnore.contains(child.getUuid())) {
+ iterator.remove();
+ uuidsToIgnore.remove(child.getUuid());
+ }
+ }
+ }
+ return;
+ }
+
+ private void connectTypes(Resource resource, Set<String> uuidsToIgnore) {
PluginMetadataManager metadataManager = PluginContainer.getInstance().getPluginManager().getMetadataManager();
ResourceType resourceType = resource.getResourceType();
if (resourceType != null) {
ResourceType fullResourceType = metadataManager.getType(resourceType);
- resource.setResourceType(fullResourceType);
+ if (fullResourceType != null) {
+ resource.setResourceType(fullResourceType);
+
+ // now reconnect all its children's types
+ Set<Resource> children = resource.getChildResources();
+ if (children != null) {
+ for (Resource child : children) {
+ connectTypes(child, uuidsToIgnore);
+ }
+ }
+ } else {
+ log.info("Persisted resource [" + resource + "] has a disabled resource type - will not reconnect it");
+ addAllUUIDsToList(resource, uuidsToIgnore);
+ }
} else {
- log.error("Resource [" + resource + "] has a null resource type - cannot reconnect the type");
- // I guess keep going, reconnect the children's types - but we are probably no good now
+ log.error("Persisted resource [" + resource
+ + "] does not have a resource type - cannot reconnect its type or its children types");
+ addAllUUIDsToList(resource, uuidsToIgnore);
}
+ return;
+ }
+
+ private void addAllUUIDsToList(Resource resource, Set<String> list) {
+ list.add(resource.getUuid());
Set<Resource> children = resource.getChildResources();
if (children != null) {
for (Resource child : children) {
- connectTypes(child);
+ addAllUUIDsToList(child, list);
}
}
+ return;
}
/**
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index 524d2de..db115d5 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -372,7 +372,9 @@ public class InventoryManager extends AgentService implements ContainerService,
@Nullable
public ResourceContainer getResourceContainer(CanonicalResourceKey canonicalId) {
ResourceContainer resourceContainer = null;
- for (Map.Entry<String, ResourceContainer> entry : this.resourceContainers.entrySet()) {
+ Map<String, ResourceContainer> copy = new HashMap<String, ResourceContainer>(this.resourceContainers); // avoids concurrent mod exception, kinda
+
+ for (Map.Entry<String, ResourceContainer> entry : copy.entrySet()) {
ResourceContainer container = entry.getValue();
Resource resource = container.getResource();
if (resource != null) {
@@ -390,6 +392,8 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
}
+
+ copy.clear(); // help GC
return resourceContainer;
}
@@ -415,13 +419,15 @@ public class InventoryManager extends AgentService implements ContainerService,
}
List<ResourceContainer> containers = new ArrayList<ResourceContainer>(this.resourceContainers.values()); // avoids concurrent mod exception
+ ResourceContainer retContainer = null;
for (ResourceContainer container : containers) {
if (resourceId.equals(container.getResource().getId())) {
- return container;
+ retContainer = container;
+ break;
}
}
-
- return null;
+ containers.clear(); // help GC
+ return retContainer;
}
void executePlatformScan() {
@@ -2173,14 +2179,32 @@ public class InventoryManager extends AgentService implements ContainerService,
log.debug("Merging [" + unknownResourceIds.size()
+ "] unknown Resources and their descendants into local inventory...");
}
- Set<Resource> unknownResources = configuration.getServerServices().getDiscoveryServerService().getResources(
- unknownResourceIds, true);
- for (Resource unknownResource : unknownResources) {
- mergeResource(unknownResource);
- syncSchedulesRecursively(unknownResource);
- }
- //print(this.platform, 0);
+ if (!unknownResourceIds.isEmpty()) {
+ PluginMetadataManager pmm = this.pluginManager.getMetadataManager();
+
+ Set<Resource> unknownResources = configuration.getServerServices().getDiscoveryServerService()
+ .getResources(unknownResourceIds, true);
+
+ Set<Resource> toBeIgnored = new HashSet<Resource>();
+
+ for (Resource unknownResource : unknownResources) {
+ ResourceType resourceType = pmm.getType(unknownResource.getResourceType());
+ if (resourceType != null) {
+ mergeResource(unknownResource);
+ syncSchedulesRecursively(unknownResource);
+ } else {
+ toBeIgnored.add(unknownResource);
+ if (log.isDebugEnabled()) {
+ log.debug("During an inventory sync, the server gave us resource [" + unknownResource
+ + "] but its type is disabled in the agent; ignoring it");
+ }
+ }
+ }
+
+ unknownResources.removeAll(toBeIgnored);
+ }
+ return;
}
private void print(Resource resourceTreeNode, int level) {
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/plugin/PluginManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/plugin/PluginManager.java
index 8ec3fe0..2c64ff1 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/plugin/PluginManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/plugin/PluginManager.java
@@ -22,9 +22,20 @@
*/
package org.rhq.core.pc.plugin;
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.agent.metadata.PluginDependencyGraph;
import org.rhq.core.clientapi.agent.metadata.PluginMetadataManager;
@@ -39,18 +50,6 @@ import org.rhq.core.pluginapi.plugin.PluginLifecycleListener;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.system.SystemInfoFactory;
import org.rhq.core.util.exception.ThrowableUtil;
-import org.rhq.core.util.MessageDigestGenerator;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* This container service will load in all plugins that can be found and will maintain the complete set of
@@ -108,6 +107,7 @@ public class PluginManager implements ContainerService {
PluginFinder finder = configuration.getPluginFinder();
File tmpDir = configuration.getTemporaryDirectory();
+ List<String> disabledPlugins = configuration.getDisabledPlugins();
// The root classloader for all plugins will have all classes hidden except for those configured in the regex.
// Notice this root classloader has no jar URLs - it will provide no classes except for what it will allow
@@ -131,8 +131,12 @@ public class PluginManager implements ContainerService {
log.debug("Plugin found at: " + url);
try {
PluginDescriptor descriptor = AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(url);
- AgentPluginDescriptorUtil.addPluginToDependencyGraph(graph, descriptor);
- pluginNamesUrls.put(descriptor.getName(), url);
+ if (!disabledPlugins.contains(descriptor.getName())) {
+ AgentPluginDescriptorUtil.addPluginToDependencyGraph(graph, descriptor);
+ pluginNamesUrls.put(descriptor.getName(), url);
+ } else {
+ log.info("Not loading disabled plugin: " + url);
+ }
} catch (Throwable t) {
// probably due to invalid XML syntax in the deployment descriptor - the plugin will be ignored
log.error("Plugin at [" + url + "] could not be loaded and will therefore not be deployed.", t);
@@ -173,23 +177,6 @@ public class PluginManager implements ContainerService {
}
private void initUpdateLoadedPluginsCallback() {
-// if (configuration.isInsideAgent()) {
-// updateLoadedPlugins = new UpdateLoadedPlugins() {
-// public void execute(PluginDescriptor pluginDescriptor, URL pluginURL) {
-// loadPluginIfFoundInListFromServer(pluginDescriptor);
-// }
-// };
-// }
-// else {
-// updateLoadedPlugins = new UpdateLoadedPlugins() {
-// PluginTransformer transformer = new PluginTransformer();
-//
-// public void execute(PluginDescriptor pluginDescriptor, URL pluginURL) {
-// Plugin plugin = transformer.toPlugin(pluginDescriptor, pluginURL);
-// loadedPlugins.add(plugin);
-// }
-// };
-// }
updateLoadedPlugins = new UpdateLoadedPlugins() {
PluginTransformer transformer = new PluginTransformer();
@@ -200,15 +187,6 @@ public class PluginManager implements ContainerService {
};
}
- private void loadPluginIfFoundInListFromServer(PluginDescriptor pluginDescriptor) {
- for (Plugin plugin : configuration.getPluginsOnServer()) {
- if (pluginDescriptor.getName().equals(plugin.getName())) {
- loadedPlugins.add(plugin);
- return;
- }
- }
- }
-
/**
* @see ContainerService#shutdown()
*/
@@ -247,7 +225,7 @@ public class PluginManager implements ContainerService {
this.loadedPlugins.clear();
this.loadedPlugins = null;
- pluginLifecycleListenerMgr.shutdown();;
+ pluginLifecycleListenerMgr.shutdown();
this.metadataManager = null;
this.classLoaderManager = null;
@@ -385,17 +363,6 @@ public class PluginManager implements ContainerService {
return pluginLifecycleListenerMgr.loadListener(pluginDescriptor, pluginEnvironment);
}
- private String getPluginLifecycleListenerClass(PluginDescriptor pluginDescriptor) {
- String className = pluginDescriptor.getPluginLifecycleListener();
- if (className != null) {
- String pkg = pluginDescriptor.getPackage();
- if ((className.indexOf('.') == -1) && (pkg != null)) {
- className = pkg + '.' + className;
- }
- }
- return className;
- }
-
private PluginContext createPluginContext(String pluginName) {
SystemInfo sysInfo = SystemInfoFactory.createSystemInfo();
File dataDir = new File(this.configuration.getDataDirectory(), pluginName);
@@ -406,32 +373,6 @@ public class PluginManager implements ContainerService {
return context;
}
- private Object instantiatePluginClass(PluginEnvironment environment, String className)
- throws PluginContainerException {
-
- ClassLoader loader = environment.getPluginClassLoader();
-
- log.debug("Loading class [" + className + "]...");
-
- try {
- Class<?> clazz = Class.forName(className, true, loader);
- log.debug("Loaded class [" + clazz + "].");
- return clazz.newInstance();
- } catch (InstantiationException e) {
- throw new PluginContainerException("Could not instantiate plugin class [" + className
- + "] from plugin environment [" + environment + "]", e);
- } catch (IllegalAccessException e) {
- throw new PluginContainerException("Could not access plugin class " + className
- + "] from plugin environment [" + environment + "]", e);
- } catch (ClassNotFoundException e) {
- throw new PluginContainerException("Could not find plugin class " + className
- + "] from plugin environment [" + environment + "]", e);
- } catch (NullPointerException npe) {
- throw new PluginContainerException("Plugin class was 'null' in plugin environment [" + environment + "]",
- npe);
- }
- }
-
/**
* Given a plugin name, this will get all of its dependencies' plugin URLs and add them to <code>allUrls</code>.
* This recursively travels N levels deep in the dependency graph.
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java
index 533675b..e02cd34 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java
@@ -43,6 +43,7 @@ import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.inventory.InventoryFile;
import org.rhq.core.pc.inventory.InventoryManager;
import org.rhq.core.pc.inventory.ResourceContainer;
+import org.rhq.core.pc.plugin.PluginManager;
/**
* @author John Mazzitelli
@@ -147,21 +148,23 @@ public class InventoryPrinter {
public static void outputInventory(PrintWriter exportWriter, boolean recurseChildren, boolean xml,
ResourceContainer resourceContainer) {
- outputInventory(exportWriter, recurseChildren, xml, resourceContainer, 0, PluginContainer.getInstance()
+ PluginContainer pc = PluginContainer.getInstance();
+ outputInventory(exportWriter, recurseChildren, xml, resourceContainer, 0, pc.getPluginManager(), pc
.getInventoryManager(), null, null);
exportWriter.flush();
}
public static void outputInventory(PrintWriter exportWriter, boolean recurseChildren, boolean xml,
InventoryFile file) {
- outputInventory(exportWriter, recurseChildren, xml, null, 0, PluginContainer.getInstance()
- .getInventoryManager(), file, null);
+ PluginContainer pc = PluginContainer.getInstance();
+ outputInventory(exportWriter, recurseChildren, xml, null, 0, pc.getPluginManager(), pc.getInventoryManager(),
+ file, null);
exportWriter.flush();
}
private static void outputInventory(PrintWriter exportWriter, boolean recurseChildren, boolean dumpXml,
- ResourceContainer resourceContainer, int descendantDepth, InventoryManager inventoryManager,
- InventoryFile inventoryFile, InventoryPrinter.ResourceCounter resourceCounter) {
+ ResourceContainer resourceContainer, int descendantDepth, PluginManager pluginManager,
+ InventoryManager inventoryManager, InventoryFile inventoryFile, InventoryPrinter.ResourceCounter resourceCounter) {
StringBuffer indent = new StringBuffer();
for (int i = 0; i < descendantDepth; i++) {
indent.append(" ");
@@ -202,100 +205,108 @@ public class InventoryPrinter {
exportWriter.printf("!!!RESOURCE IS NULL!!!");
return;
}
- resourceCounter.tallyResource(resource);
-
- Availability avail = resourceContainer.getAvailability();
- AvailabilityType availType = null;
- if (avail != null) {
- availType = avail.getAvailabilityType();
+ boolean disabledResource = false; // will be true if the plugin was disabled
+ if (pluginManager != null) {
+ disabledResource = pluginManager.getMetadataManager().getType(resource.getResourceType()) == null;
}
- String availString = (availType == null) ? "UNKNOWN" : availType.toString();
- int installedPackageCount = 0;
- if (resourceContainer.getInstalledPackages() != null) {
- installedPackageCount = resourceContainer.getInstalledPackages().size();
- }
+ if (!disabledResource) {
+ resourceCounter.tallyResource(resource);
- if (dumpXml) {
- exportWriter.printf("%s<resource>\n", indent);
- exportWriter.printf("%s <id>%d</id>\n", indent, resource.getId());
- exportWriter.printf("%s <key>%s</key>\n", indent, resource.getResourceKey());
- exportWriter.printf("%s <name>%s</name>\n", indent, resource.getName());
- exportWriter.printf("%s <version>%s</version>\n", indent, resource.getVersion());
- exportWriter.printf("%s <uuid>%s</uuid>\n", indent, resource.getUuid());
- exportWriter.printf("%s <mtime>%s</mtime>\n", indent, resource.getMtime());
- exportWriter.printf("%s <mtime-date>%s</mtime-date>\n", indent, new Date(resource.getMtime()));
- exportWriter.printf("%s <description>%s</description>\n", indent, resource.getDescription());
- exportWriter
- .printf("%s <inventory-status>%s</inventory-status>\n", indent, resource.getInventoryStatus());
- exportWriter.printf("%s <type>%s</type>\n", indent, (resource.getResourceType() != null) ? resource
- .getResourceType().getName() : "null");
- exportWriter.printf("%s <availabilityType>%s</availabilityType>\n", indent, availString);
- exportWriter.printf("%s <category>%s</category>\n", indent,
- (resource.getResourceType() != null) ? resource.getResourceType().getCategory() : "null");
- exportWriter.printf("%s <container>\n", indent);
- exportWriter.printf("%s <availability>%s</availability>\n", indent, avail);
- exportWriter.printf("%s <state>%s</state>\n", indent, resourceContainer.getResourceComponentState());
- exportWriter.printf("%s <installedPackageCount>%d</installedPackageCount>\n", indent,
- installedPackageCount);
- exportWriter.printf("%s <schedules>\n", indent);
-
- Set<MeasurementScheduleRequest> schedules = resourceContainer.getMeasurementSchedule();
- if (schedules != null) {
- for (MeasurementScheduleRequest schedule : schedules) {
- exportWriter.printf("%s <schedule>\n", indent);
- exportWriter.printf("%s <schedule-id>%d</schedule-id>\n", indent, schedule
- .getScheduleId());
- exportWriter.printf("%s <name>%s</name>\n", indent, schedule.getName());
- exportWriter.printf("%s <enabled>%s</enabled>\n", indent, schedule.isEnabled());
- exportWriter.printf("%s <interval>%d</interval>\n", indent, schedule.getInterval());
- exportWriter.printf("%s </schedule>\n", indent);
- }
+ Availability avail = resourceContainer.getAvailability();
+ AvailabilityType availType = null;
+ if (avail != null) {
+ availType = avail.getAvailabilityType();
}
+ String availString = (availType == null) ? "UNKNOWN" : availType.toString();
- exportWriter.printf("%s </schedules>\n", indent);
- exportWriter.printf("%s </container>\n", indent);
- if (recurseChildren) {
- exportWriter.printf("%s <children>\n", indent);
- }
- } else {
- Set<MeasurementScheduleRequest> schedules = resourceContainer.getMeasurementSchedule();
- int enabled = 0;
- if (schedules != null) {
- for (MeasurementScheduleRequest schedule : schedules) {
- enabled += (schedule.isEnabled()) ? 1 : 0;
- }
+ int installedPackageCount = 0;
+ if (resourceContainer.getInstalledPackages() != null) {
+ installedPackageCount = resourceContainer.getInstalledPackages().size();
}
- exportWriter.printf("%s+ %s (sync=%s, state=%s, avail=%s, sched=%d/%d)\n", indent, resource,
- resourceContainer.getSynchronizationState(), resourceContainer.getResourceComponentState(),
- availString, enabled, (schedules != null) ? schedules.size() : 0);
- }
- if (recurseChildren) {
- Set<Resource> children = new HashSet(resource.getChildResources()); // wrap in new HashSet to avoid CCMEs
- for (Resource child : children) {
- ResourceContainer childContainer;
-
- if (inventoryFile == null) {
- // get the child from the in-memory inventory
- childContainer = inventoryManager.getResourceContainer(child);
- } else {
- // get the child from the inventory found in the file
- childContainer = getResourceContainer(child.getId(), inventoryFile.getResourceContainers());
+ if (dumpXml) {
+ exportWriter.printf("%s<resource>\n", indent);
+ exportWriter.printf("%s <id>%d</id>\n", indent, resource.getId());
+ exportWriter.printf("%s <key>%s</key>\n", indent, resource.getResourceKey());
+ exportWriter.printf("%s <name>%s</name>\n", indent, resource.getName());
+ exportWriter.printf("%s <version>%s</version>\n", indent, resource.getVersion());
+ exportWriter.printf("%s <uuid>%s</uuid>\n", indent, resource.getUuid());
+ exportWriter.printf("%s <mtime>%s</mtime>\n", indent, resource.getMtime());
+ exportWriter.printf("%s <mtime-date>%s</mtime-date>\n", indent, new Date(resource.getMtime()));
+ exportWriter.printf("%s <description>%s</description>\n", indent, resource.getDescription());
+ exportWriter.printf("%s <inventory-status>%s</inventory-status>\n", indent, resource
+ .getInventoryStatus());
+ exportWriter.printf("%s <type>%s</type>\n", indent, (resource.getResourceType() != null) ? resource
+ .getResourceType().getName() : "null");
+ exportWriter.printf("%s <availabilityType>%s</availabilityType>\n", indent, availString);
+ exportWriter.printf("%s <category>%s</category>\n", indent,
+ (resource.getResourceType() != null) ? resource.getResourceType().getCategory() : "null");
+ exportWriter.printf("%s <container>\n", indent);
+ exportWriter.printf("%s <availability>%s</availability>\n", indent, avail);
+ exportWriter.printf("%s <state>%s</state>\n", indent, resourceContainer
+ .getResourceComponentState());
+ exportWriter.printf("%s <installedPackageCount>%d</installedPackageCount>\n", indent,
+ installedPackageCount);
+ exportWriter.printf("%s <schedules>\n", indent);
+
+ Set<MeasurementScheduleRequest> schedules = resourceContainer.getMeasurementSchedule();
+ if (schedules != null) {
+ for (MeasurementScheduleRequest schedule : schedules) {
+ exportWriter.printf("%s <schedule>\n", indent);
+ exportWriter.printf("%s <schedule-id>%d</schedule-id>\n", indent, schedule
+ .getScheduleId());
+ exportWriter.printf("%s <name>%s</name>\n", indent, schedule.getName());
+ exportWriter.printf("%s <enabled>%s</enabled>\n", indent, schedule.isEnabled());
+ exportWriter.printf("%s <interval>%d</interval>\n", indent, schedule.getInterval());
+ exportWriter.printf("%s </schedule>\n", indent);
+ }
}
- // recursively call ourselves
- outputInventory(exportWriter, recurseChildren, dumpXml, childContainer, descendantDepth + 1,
- inventoryManager, inventoryFile, resourceCounter);
+ exportWriter.printf("%s </schedules>\n", indent);
+ exportWriter.printf("%s </container>\n", indent);
+ if (recurseChildren) {
+ exportWriter.printf("%s <children>\n", indent);
+ }
+ } else {
+ Set<MeasurementScheduleRequest> schedules = resourceContainer.getMeasurementSchedule();
+ int enabled = 0;
+ if (schedules != null) {
+ for (MeasurementScheduleRequest schedule : schedules) {
+ enabled += (schedule.isEnabled()) ? 1 : 0;
+ }
+ }
+ exportWriter.printf("%s+ %s (sync=%s, state=%s, avail=%s, sched=%d/%d)\n", indent, resource,
+ resourceContainer.getSynchronizationState(), resourceContainer.getResourceComponentState(),
+ availString, enabled, (schedules != null) ? schedules.size() : 0);
}
- }
- if (dumpXml) {
if (recurseChildren) {
- exportWriter.printf("%s </children>\n", indent);
+ Set<Resource> children = new HashSet(resource.getChildResources()); // wrap in new HashSet to avoid CCMEs
+ for (Resource child : children) {
+ ResourceContainer childContainer;
+
+ if (inventoryFile == null) {
+ // get the child from the in-memory inventory
+ childContainer = inventoryManager.getResourceContainer(child);
+ } else {
+ // get the child from the inventory found in the file
+ childContainer = getResourceContainer(child.getId(), inventoryFile.getResourceContainers());
+ }
+
+ // recursively call ourselves
+ outputInventory(exportWriter, recurseChildren, dumpXml, childContainer, descendantDepth + 1,
+ pluginManager, inventoryManager, inventoryFile, resourceCounter);
+ }
}
- exportWriter.printf("%s</resource>\n", indent);
+ if (dumpXml) {
+ if (recurseChildren) {
+ exportWriter.printf("%s </children>\n", indent);
+ }
+
+ exportWriter.printf("%s</resource>\n", indent);
+ }
}
if (descendantDepth == 0) {
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfiguration.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfiguration.java
index 4eb21d8..ae41e6f 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfiguration.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfiguration.java
@@ -21,6 +21,9 @@ package org.rhq.enterprise.agent;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
@@ -1167,6 +1170,14 @@ public class AgentConfiguration {
tmp_directory.mkdir();
}
+ // determine what, if any, plugins are to be disabled
+ String disabled_pref = m_preferences.get(AgentConfigurationConstants.PLUGINS_DISABLED, null);
+ List<String> disabled_plugins = null;
+ if (disabled_pref != null) {
+ String[] array = disabled_pref.split(",");
+ disabled_plugins = new ArrayList<String>(Arrays.asList(array));
+ }
+
// Define what plugin container/agent classes are to be hidden from our plugins.
String clRegex = m_preferences.get(AgentConfigurationConstants.PLUGINS_ROOT_PLUGIN_CLASSLOADER_REGEX, null);
if (clRegex == null) {
@@ -1249,6 +1260,7 @@ public class AgentConfiguration {
config.setPluginDirectory(plugin_dir);
config.setDataDirectory(data_directory);
config.setTemporaryDirectory(tmp_directory);
+ config.setDisabledPlugins(disabled_plugins);
config.setRootPluginClassLoaderRegex(clRegex);
config.setServerDiscoveryInitialDelay(server_discovery_initial_delay);
config.setServerDiscoveryPeriod(server_discovery_period);
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfigurationConstants.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfigurationConstants.java
index c4a8182..c32d04b 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfigurationConstants.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentConfigurationConstants.java
@@ -529,6 +529,11 @@ public interface AgentConfigurationConstants {
String PLUGINS_ROOT_PLUGIN_CLASSLOADER_REGEX = PROPERTY_NAME_PREFIX + "plugins.root-plugin-classloader-regex";
/**
+ * The comma separated list of names of plugins that are to be disabled at startup
+ */
+ String PLUGINS_DISABLED = PROPERTY_NAME_PREFIX + "plugins.disabled";
+
+ /**
* Defines, in seconds, the initial delay before the first server discovery scan is run.
*/
String PLUGINS_SERVER_DISCOVERY_INITIAL_DELAY = PROPERTY_NAME_PREFIX
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentMain.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentMain.java
index 2baf5f5..0a790ba 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentMain.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/AgentMain.java
@@ -1053,8 +1053,12 @@ public class AgentMain {
try {
m_output.flush();
input_string = m_input.readLine();
+ if (input_string == null) {
+ LOG.debug(AgentI18NResourceKeys.INPUT_EOF);
+ }
} catch (Exception e) {
input_string = null;
+ LOG.debug(AgentI18NResourceKeys.INPUT_EXCEPTION, ThrowableUtil.getAllMessages(e));
}
}
@@ -1076,6 +1080,7 @@ public class AgentMain {
m_input = AgentInputReaderFactory.create(this);
} catch (IOException e1) {
m_input = null;
+ LOG.debug(e1, AgentI18NResourceKeys.INPUT_FACTORY_EXCEPTION);
}
m_stdinInput = true;
input_string = "";
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/PluginUpdate.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/PluginUpdate.java
index 4e4a07b..5e812c2 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/PluginUpdate.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/PluginUpdate.java
@@ -26,7 +26,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.HashSet;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReadWriteLock;
@@ -129,6 +128,10 @@ public class PluginUpdate {
this(null, config);
}
+ public PluginContainerConfiguration getPluginContainerConfiguration() {
+ return this.config;
+ }
+
/**
* This will compare the current set of plugins that exist locally with the set of latest plugins that are available
* from the core server service. If we need to upgrade to one or more latest plugins, this method will download them
@@ -157,20 +160,20 @@ public class PluginUpdate {
createMarkerFile();
try {
+ List<String> disabled_plugin_names = this.config.getDisabledPlugins();
+
// find out what plugins we already have locally
Map<String, Plugin> current_plugins = getCurrentPlugins();
// find out what the latest plugins are available to us
List<Plugin> latest_plugins = coreServerService.getLatestPlugins();
- config.setPluginsOnServer(new HashSet(latest_plugins));
-
if (LOG.isDebugEnabled()) {
LOG.debug(AgentI18NResourceKeys.LATEST_PLUGINS_COUNT, latest_plugins.size());
for (Plugin latest_plugin : latest_plugins) {
LOG.debug(AgentI18NResourceKeys.LATEST_PLUGIN, latest_plugin.getId(), latest_plugin.getName(),
latest_plugin.getDisplayName(), latest_plugin.getVersion(), latest_plugin.getPath(),
- latest_plugin.getMd5(), latest_plugin.getDescription());
+ latest_plugin.getMd5(), latest_plugin.isEnabled(), latest_plugin.getDescription());
}
}
@@ -186,15 +189,25 @@ public class PluginUpdate {
updated_plugins.add(latest_plugin); // we don't have any version of this plugin, we'll need to get it
LOG.debug(AgentI18NResourceKeys.NEED_MISSING_PLUGIN, plugin_filename);
} else {
- String latest_md5 = latest_plugin.getMD5();
- String current_md5 = current_plugin.getMD5();
-
- if (!current_md5.equals(latest_md5)) {
- updated_plugins.add(latest_plugin);
- LOG.debug(AgentI18NResourceKeys.PLUGIN_NEEDS_TO_BE_UPDATED, plugin_filename, current_md5,
- latest_md5);
+ if (latest_plugin.isEnabled() && !disabled_plugin_names.contains(latest_plugin.getName())) {
+ String latest_md5 = latest_plugin.getMD5();
+ String current_md5 = current_plugin.getMD5();
+
+ if (!current_md5.equals(latest_md5)) {
+ updated_plugins.add(latest_plugin);
+ LOG.debug(AgentI18NResourceKeys.PLUGIN_NEEDS_TO_BE_UPDATED, plugin_filename,
+ current_md5, latest_md5);
+ } else {
+ LOG.debug(AgentI18NResourceKeys.PLUGIN_ALREADY_AT_LATEST, plugin_filename);
+ }
} else {
- LOG.debug(AgentI18NResourceKeys.PLUGIN_ALREADY_AT_LATEST, plugin_filename);
+ // we have a plugin file locally, but it is to be disabled, so delete the plugin .jar
+ File disabled_file = getPluginFile(latest_plugin);
+ if (disabled_file.delete()) {
+ LOG.info(AgentI18NResourceKeys.PLUGIN_DISABLED_PLUGIN_DELETED, disabled_file);
+ } else {
+ LOG.error(AgentI18NResourceKeys.PLUGIN_DISABLED_PLUGIN_DELETE_FAILED, disabled_file);
+ }
}
}
}
@@ -204,13 +217,20 @@ public class PluginUpdate {
// Let's go ahead and download all the plugins that we need.
// Try to update all plugins, even if one or more fails to update. At the end,
// if an exception was thrown, we'll rethrow it but only after all update attempts were made
+ // NOTE: we do not download any plugins that are to be disabled
Exception last_error = null;
for (Plugin updated_plugin : updated_plugins) {
- try {
- downloadPluginWithRetries(updated_plugin); // tries our very best to get it
- } catch (Exception e) {
- last_error = e;
+ String name = updated_plugin.getName();
+ if (updated_plugin.isEnabled() && !disabled_plugin_names.contains(name)) {
+ try {
+ downloadPluginWithRetries(updated_plugin); // tries our very best to get it
+ } catch (Exception e) {
+ last_error = e;
+ }
+ } else {
+ LOG.info(AgentI18NResourceKeys.PLUGIN_DISABLED_PLUGIN_DOWNLOAD_SKIPPED, name);
+ updated_plugin.setEnabled(false);
}
}
@@ -424,10 +444,10 @@ public class PluginUpdate {
private void deleteIllegitimatePlugins(Map<String, Plugin> current_plugins, Map<String, Plugin> latest_plugins_map) {
for (Plugin current_plugin : current_plugins.values()) {
if (!latest_plugins_map.containsKey(current_plugin.getPath())) {
- File plugin_dir = this.config.getPluginDirectory();
- String plugin_filename = current_plugin.getPath();
- File plugin = new File(plugin_dir, plugin_filename);
+ File plugin = getPluginFile(current_plugin);
if (plugin.exists()) {
+ File plugin_dir = this.config.getPluginDirectory();
+ String plugin_filename = plugin.getPath();
File plugin_backup = new File(plugin_dir, plugin_filename + ".REJECTED");
LOG.warn(AgentI18NResourceKeys.PLUGIN_NOT_ON_SERVER, plugin_filename, plugin_backup.getName());
try {
@@ -437,6 +457,7 @@ public class PluginUpdate {
if (!renamed) {
LOG.error(AgentI18NResourceKeys.PLUGIN_RENAME_FAILED, plugin_filename, plugin_backup
.getName());
+ plugin.delete(); // give it one last try to remove it - otherwise, the PC will still deploy it
}
} catch (RuntimeException e) {
LOG.error(e, AgentI18NResourceKeys.PLUGIN_RENAME_FAILED, plugin_filename, plugin_backup
@@ -446,4 +467,11 @@ public class PluginUpdate {
}
}
}
+
+ private File getPluginFile(Plugin plugin) {
+ File plugin_dir = this.config.getPluginDirectory();
+ String plugin_filename = plugin.getPath();
+ File file = new File(plugin_dir, plugin_filename);
+ return file;
+ }
}
\ No newline at end of file
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
index 68da9a6..e098b2b 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
@@ -236,6 +236,15 @@ public interface AgentI18NResourceKeys {
@I18NMessage("Failed to download an updated server failover list. Cause: {0}")
String FAILOVER_LIST_DOWNLOAD_FAILURE = "AgentMain.failover-list-download-failure";
+ @I18NMessage("The prompt input reader returned null. EOF?")
+ String INPUT_EOF = "AgentMain.input-eof";
+
+ @I18NMessage("The prompt input reader stopped providing input due to an exception. EOF? Cause: {0}")
+ String INPUT_EXCEPTION = "AgentMain.input-exception";
+
+ @I18NMessage("Failed to create prompt input reader.")
+ String INPUT_FACTORY_EXCEPTION = "AgentMain.input-factory-exception";
+
@I18NMessage("(type it again to confirm) ")
String PROMPT_CONFIRM = "AgentNativePromptInfo.prompt-confirm";
@@ -299,7 +308,7 @@ public interface AgentI18NResourceKeys {
@I18NMessage("The server has [{0}] plugins available for download")
String LATEST_PLUGINS_COUNT = "PluginUpdate.latest-plugins-count";
- @I18NMessage("Plugin available for download: id=[{0}], name=[{1}], displayName=[{2}], version=[{3}], path=[{4}], md5=[{5}], description=[{6}]")
+ @I18NMessage("Plugin available for download: id=[{0}], name=[{1}], displayName=[{2}], version=[{3}], path=[{4}], md5=[{5}], enabled=[{6}], description=[{7}]")
String LATEST_PLUGIN = "PluginUpdate.latest-plugin";
@I18NMessage("Updating plugins to their latest versions.")
@@ -317,6 +326,15 @@ public interface AgentI18NResourceKeys {
@I18NMessage("The plugin [{0}] is current and does not need to be updated.")
String PLUGIN_ALREADY_AT_LATEST = "PluginUpdate.already-at-latest";
+ @I18NMessage("The plugin [{0}] is disabled and will not be downloaded.")
+ String PLUGIN_DISABLED_PLUGIN_DOWNLOAD_SKIPPED = "PluginUpdate.disabled-plugin-download-skipped";
+
+ @I18NMessage("The disabled plugin file [{0}] is deleted,")
+ String PLUGIN_DISABLED_PLUGIN_DELETED = "PluginUpdate.disabled-plugin-deleted";
+
+ @I18NMessage("The disabled plugin file [{0}] failed to be deleted.")
+ String PLUGIN_DISABLED_PLUGIN_DELETE_FAILED = "PluginUpdate.disabled-plugin-delete-failed";
+
@I18NMessage("The plugin [{0}] does not exist on the Server - renaming it to [{1}] so it will not get deployed by the Plugin Container.")
String PLUGIN_NOT_ON_SERVER = "PluginUpdate.plugin-not-on-server";
@@ -344,9 +362,12 @@ public interface AgentI18NResourceKeys {
@I18NMessage("Failed to download the plugin [{0}]. This was attempt #[{1}] - will no longer retry. This plugin will not be deployed in the agent. Cause: {2}")
String DOWNLOAD_PLUGIN_FAILURE_WILL_NOT_RETRY = "PluginUpdate.download-failure-will-not-retry";
- @I18NMessage("The plugin [{0}] has been updated.")
+ @I18NMessage("The plugin [{0}] has been updated at [{1}].")
String DOWNLOADING_PLUGIN_COMPLETE = "PluginUpdate.downloading-complete";
+ @I18NMessage("The plugin [{0}] is disabled and will not be updated.")
+ String DOWNLOADING_PLUGIN_SKIPPED = "PluginUpdate.downloading-skipped";
+
@I18NMessage("All plugins are already up-to-date.")
String UPDATING_PLUGINS_ALREADY_UPTODATE = "PluginUpdate.already-uptodate";
@@ -1483,8 +1504,14 @@ public interface AgentI18NResourceKeys {
+ "Perform an update to download the latest plugins from the server.")
String PLUGINS_NO_CURRENT_PLUGINS = "PromptCommand.plugins.no-current-plugins";
- @I18NMessage("Plugins that are currently installed:")
- String PLUGINS_LISTING_PLUGINS = "PromptCommand.plugins.listing-plugins";
+ @I18NMessage("Summary of installed plugins:\\n{0}")
+ String PLUGINS_LISTING_PLUGINS_SUMMARY = "PromptCommand.plugins.listing-plugins-summary";
+
+ @I18NMessage("The following plugins will be disabled:\\n{0}")
+ String PLUGINS_LISTING_PLUGINS_DISABLED = "PromptCommand.plugins.listing-plugins-disabled";
+
+ @I18NMessage("Details of the plugins that are currently installed:")
+ String PLUGINS_LISTING_PLUGINS_DETAILS = "PromptCommand.plugins.listing-plugins-details";
@I18NMessage("Total number of plugins currently installed: [{0}]")
String PLUGINS_NUM_CURRENT_PLUGINS = "PromptCommand.plugins.num-current-plugins";
@@ -1495,6 +1522,9 @@ public interface AgentI18NResourceKeys {
@I18NMessage("Plugin Name: {0}")
String PLUGINS_PLUGINS_INFO_NAME = "PromptCommand.plugins.plugin-info.name";
+ @I18NMessage("Display Name: {0}")
+ String PLUGINS_PLUGINS_INFO_DISPLAY_NAME = "PromptCommand.plugins.plugin-info.display-name";
+
@I18NMessage("File Size: {0,number} bytes")
String PLUGINS_PLUGINS_INFO_FILESIZE = "PromptCommand.plugins.plugin-info.filesize";
diff --git a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/promptcmd/PluginsPromptCommand.java b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/promptcmd/PluginsPromptCommand.java
index 5181bbe..5f512a6 100644
--- a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/promptcmd/PluginsPromptCommand.java
+++ b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/promptcmd/PluginsPromptCommand.java
@@ -21,6 +21,7 @@ package org.rhq.enterprise.agent.promptcmd;
import java.io.File;
import java.io.PrintWriter;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -113,18 +114,24 @@ public class PluginsPromptCommand implements AgentPromptCommand {
PrintWriter out = agent.getOut();
PluginUpdate plugin_update = getPluginUpdateObject(agent);
List<File> current_plugins = plugin_update.getCurrentPluginFiles();
+ List<String> disabled_plugins = plugin_update.getPluginContainerConfiguration().getDisabledPlugins();
+ List<String> installed_plugins = new ArrayList<String>();
if (current_plugins.size() > 0) {
- out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_LISTING_PLUGINS));
+ out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_LISTING_PLUGINS_DETAILS));
for (File current_plugin : current_plugins) {
String plugin_name;
+ String plugin_display_name;
try {
URL url = current_plugin.toURI().toURL();
PluginDescriptor descriptor = AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(url);
plugin_name = descriptor.getName();
+ plugin_display_name = descriptor.getDisplayName();
+ installed_plugins.add(plugin_name);
} catch (Throwable t) {
plugin_name = "?cannot-parse-descriptor?";
+ plugin_display_name = "?cannot-parse-descriptor?";
}
String filename = current_plugin.getName();
@@ -137,6 +144,8 @@ public class PluginsPromptCommand implements AgentPromptCommand {
out.print('\t');
out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_PLUGINS_INFO_NAME, plugin_name));
out.print('\t');
+ out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_PLUGINS_INFO_DISPLAY_NAME, plugin_display_name));
+ out.print('\t');
out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_PLUGINS_INFO_LASTMOD, last_mod));
out.print('\t');
out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_PLUGINS_INFO_FILESIZE, filesize));
@@ -145,6 +154,14 @@ public class PluginsPromptCommand implements AgentPromptCommand {
}
out.println();
+ out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_LISTING_PLUGINS_SUMMARY, installed_plugins));
+
+ if (!disabled_plugins.isEmpty()) {
+ out.println();
+ out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_LISTING_PLUGINS_DISABLED, disabled_plugins));
+ }
+
+ out.println();
out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_NUM_CURRENT_PLUGINS, current_plugins.size()));
} else {
out.println(MSG.getMsg(AgentI18NResourceKeys.PLUGINS_NO_CURRENT_PLUGINS));
@@ -187,7 +204,12 @@ public class PluginsPromptCommand implements AgentPromptCommand {
if ((updated_plugins != null) && (updated_plugins.size() > 0)) {
for (Plugin plugin : updated_plugins) {
- out.println(MSG.getMsg(AgentI18NResourceKeys.DOWNLOADING_PLUGIN_COMPLETE, plugin.getPath()));
+ if (plugin.isEnabled()) {
+ out.println(MSG.getMsg(AgentI18NResourceKeys.DOWNLOADING_PLUGIN_COMPLETE, plugin.getName(),
+ plugin.getPath()));
+ } else {
+ out.println(MSG.getMsg(AgentI18NResourceKeys.DOWNLOADING_PLUGIN_SKIPPED, plugin.getName()));
+ }
}
} else {
out.println(MSG.getMsg(AgentI18NResourceKeys.UPDATING_PLUGINS_ALREADY_UPTODATE));
diff --git a/modules/enterprise/agent/src/main/resources/agent-configuration.xml b/modules/enterprise/agent/src/main/resources/agent-configuration.xml
index 452d1b2..b087a76 100644
--- a/modules/enterprise/agent/src/main/resources/agent-configuration.xml
+++ b/modules/enterprise/agent/src/main/resources/agent-configuration.xml
@@ -750,6 +750,32 @@ commands named "config", "setconfig" and "setup" and the command line options
<!--
_______________________________________________________________
+ rhq.agent.plugins.disabled
+
+ Defines the names of the plugins that are to be disabled.
+ This is a comma-separated list of plugin names, where a
+ plugin name is found in the name attribute in the root XML
+ element in the plugin descriptor. A disabled plugin will
+ simply not be loaded in the plugin container.
+
+ By default, all plugins are enabled. If a plugin was marked
+ as disabled by the server, the agent will not download it and
+ will not load it, regardless of the value of this preference.
+ If a plugin is enabled on the server, this
+ preference will override that enable setting (in other words,
+ an agent is able to disable a plugin, effectively overriding
+ the server setting, by placing the plugin name in this
+ preference). If the agent already has a plugin jar in
+ its local plugins directory, but that plugin is disabled
+ via this preference, that local plugin jar file will be
+ deleted and the plugin will not be loaded.
+ -->
+ <!--
+ <entry key="rhq.agent.plugins.disabled" value=""/>
+ -->
+
+ <!--
+ _______________________________________________________________
rhq.communications.configuration-schema-version
Defines what version of the agent configuration schema this
diff --git a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/admin/plugin/InstalledPluginsUIBean.java b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/admin/plugin/InstalledPluginsUIBean.java
index 1fef765..f1f8f1a 100644
--- a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/admin/plugin/InstalledPluginsUIBean.java
+++ b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/admin/plugin/InstalledPluginsUIBean.java
@@ -152,6 +152,63 @@ public class InstalledPluginsUIBean {
return;
}
+ public void enableAgentPlugins() {
+ List<Plugin> allSelectedPlugins = getSelectedAgentPlugins();
+ List<String> selectedPluginNames = new ArrayList<String>();
+ List<Plugin> pluginsToEnable = new ArrayList<Plugin>();
+
+ for (Plugin selectedPlugin : allSelectedPlugins) {
+ if (!selectedPlugin.isEnabled() && selectedPlugin.getStatus() == PluginStatusType.INSTALLED) {
+ selectedPluginNames.add(selectedPlugin.getDisplayName());
+ pluginsToEnable.add(selectedPlugin);
+ }
+ }
+
+ if (selectedPluginNames.isEmpty()) {
+ FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO,
+ "No disabled plugins were selected. Nothing to enable");
+ return;
+ }
+
+ try {
+ Subject subject = EnterpriseFacesContextUtility.getSubject();
+ resourceMetadataManagerBean.enablePlugins(subject, getIds(pluginsToEnable));
+ FacesContextUtility
+ .addMessage(FacesMessage.SEVERITY_INFO, "Enabled server plugins: " + selectedPluginNames);
+ } catch (Exception e) {
+ processException("Failed to enable agent plugins", e);
+ }
+ return;
+ }
+
+ public void disableAgentPlugins() {
+ List<Plugin> allSelectedPlugins = getSelectedAgentPlugins();
+ List<String> selectedPluginNames = new ArrayList<String>();
+ List<Plugin> pluginsToDisable = new ArrayList<Plugin>();
+
+ for (Plugin selectedPlugin : allSelectedPlugins) {
+ if (selectedPlugin.isEnabled()) {
+ selectedPluginNames.add(selectedPlugin.getDisplayName());
+ pluginsToDisable.add(selectedPlugin);
+ }
+ }
+
+ if (selectedPluginNames.isEmpty()) {
+ FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO,
+ "No enabled plugins were selected. Nothing to disable");
+ return;
+ }
+
+ try {
+ Subject subject = EnterpriseFacesContextUtility.getSubject();
+ resourceMetadataManagerBean.disablePlugins(subject, getIds(pluginsToDisable));
+ FacesContextUtility.addMessage(FacesMessage.SEVERITY_INFO, "Disabled plugins: " + selectedPluginNames);
+ } catch (Exception e) {
+ processException("Failed to disable agent plugins", e);
+ }
+ return;
+ }
+
public void enableServerPlugins() {
List<ServerPlugin> allSelectedPlugins = getSelectedServerPlugins();
List<String> selectedPluginNames = new ArrayList<String>();
@@ -290,6 +347,13 @@ public class InstalledPluginsUIBean {
return ids;
}
+ private List<Plugin> getSelectedAgentPlugins() {
+ Integer[] integerItems = getSelectedPluginIds();
+ List<Integer> ids = Arrays.asList(integerItems);
+ List<Plugin> plugins = resourceMetadataManagerBean.getAllPluginsById(ids);
+ return plugins;
+ }
+
private List<ServerPlugin> getSelectedServerPlugins() {
Integer[] integerItems = getSelectedPluginIds();
List<Integer> ids = Arrays.asList(integerItems);
diff --git a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-details.xhtml b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-details.xhtml
index b9fd6f3..f2065cc 100644
--- a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-details.xhtml
+++ b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-details.xhtml
@@ -18,7 +18,7 @@
</h:outputLink>
>
<h:outputLink value="#" >
- <h:outputText value="#{plugin.deployment == 'SERVER' ? 'Server Plugin - ' : 'Agent Plugin -'}"/>
+ <h:outputText value="#{plugin.deployment == 'SERVER' ? 'Server Plugin - ' : 'Agent Plugin - '}"/>
<h:outputText value="#{plugin.displayName}"/>
</h:outputLink>
diff --git a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-list.xhtml b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-list.xhtml
index 9613295..fb60605 100644
--- a/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-list.xhtml
+++ b/modules/enterprise/gui/portal-war/src/main/webapp/rhq/admin/plugin/plugin-list.xhtml
@@ -49,20 +49,20 @@
value="#{InstalledPluginsUIBean.installedAgentPlugins}"
var="agentPlugin"
width="100%"
- columnsWidth="20%, 50%, 20%, 10%"
+ columnsWidth="3%, 20%, 52%, 15%, 10%"
headerClass="tableRowHeader"
footerClass="on-pager-footer"
onRowMouseOver="this.style.backgroundColor='#E7E7E7'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'">
- <!--<rich:column>
+ <rich:column>
<f:facet name="header">
<onc:allSelect target="selectedPlugin"/>
</f:facet>
<onc:select name="selectedPlugin" value="#{agentPlugin.id}"/>
- </rich:column>-->
+ </rich:column>
<rich:column rendered="#{param.debug}">
<f:facet name="header">
@@ -114,7 +114,15 @@
<f:facet name="footer">
<rich:columnGroup>
- <rich:column colspan="4" width="100%">
+ <rich:column colspan="5" width="100%">
+ <onc:selectCommandButton action="#{InstalledPluginsUIBean.enableAgentPlugins}"
+ value="ENABLE" target="selectedPlugin" styleClass="on-pager-button buttonsmall">
+ </onc:selectCommandButton>
+ <onc:selectCommandButton action="#{InstalledPluginsUIBean.disableAgentPlugins}"
+ value="DISABLE" target="selectedPlugin" styleClass="on-pager-button buttonsmall">
+ </onc:selectCommandButton>
+ </rich:column>
+ <rich:column colspan="5" width="100%" breakBefore="true">
<h:commandButton action="#{InstalledPluginsUIBean.scan}"
value="SCAN FOR UPDATES" styleClass="on-pager-button buttonsmall">
</h:commandButton>
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerBean.java
index 8c4b0ec..c1f46fc 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerBean.java
@@ -25,6 +25,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -34,6 +35,8 @@ import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
@@ -44,6 +47,7 @@ import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.rhq.core.clientapi.agent.metadata.PluginDependencyGraph;
import org.rhq.core.clientapi.agent.metadata.PluginMetadataManager;
import org.rhq.core.clientapi.agent.metadata.SubCategoriesMetadataParser;
import org.rhq.core.clientapi.descriptor.AgentPluginDescriptorUtil;
@@ -105,6 +109,123 @@ public class ResourceMetadataManagerBean implements ResourceMetadataManagerLocal
private ResourceManagerLocal resourceManager;
@EJB
private EventManagerLocal eventManager;
+ @EJB
+ private ResourceMetadataManagerLocal resourceMetadataManager; // self
+
+ @SuppressWarnings("unchecked")
+ public List<Plugin> getAllPluginsById(List<Integer> pluginIds) {
+ if (pluginIds == null || pluginIds.size() == 0) {
+ return new ArrayList<Plugin>(); // nothing to do
+ }
+ Query query = entityManager.createNamedQuery(Plugin.QUERY_FIND_ALL_BY_IDS);
+ query.setParameter("ids", pluginIds);
+ return query.getResultList();
+ }
+
+ @RequiredPermission(Permission.MANAGE_SETTINGS)
+ public void enablePlugins(Subject subject, List<Integer> pluginIds) throws Exception {
+ if (pluginIds == null || pluginIds.size() == 0) {
+ return; // nothing to do
+ }
+
+ // we need to make sure that if a plugin is enabled, all of its dependencies are enabled
+ PluginDependencyGraph graph = PLUGIN_METADATA_MANAGER.buildDependencyGraph();
+ List<Plugin> allPlugins = getPlugins();
+ Set<String> pluginsThatNeedToBeEnabled = new HashSet<String>();
+
+ for (Integer pluginId : pluginIds) {
+ Plugin plugin = getPluginFromListById(allPlugins, pluginId.intValue());
+ if (plugin != null) {
+ Collection<String> dependencyNames = graph.getAllDependencies(plugin.getName());
+ for (String dependencyName : dependencyNames) {
+ Plugin dependencyPlugin = getPluginFromListByName(allPlugins, dependencyName);
+ if (dependencyPlugin != null && !dependencyPlugin.isEnabled()
+ && !pluginIds.contains(Integer.valueOf(dependencyPlugin.getId()))) {
+ pluginsThatNeedToBeEnabled.add(dependencyPlugin.getDisplayName()); // this isn't enabled and isn't getting enabled, but it needs to be
+ }
+ }
+ }
+ }
+
+ if (!pluginsThatNeedToBeEnabled.isEmpty()) {
+ throw new IllegalArgumentException("You must enable the following plugin dependencies also: "
+ + pluginsThatNeedToBeEnabled);
+ }
+
+ // everything is OK, we can enable them
+ for (Integer pluginId : pluginIds) {
+ resourceMetadataManager.setPluginEnabledFlag(subject, pluginId, true);
+ }
+
+ return;
+ }
+
+ @RequiredPermission(Permission.MANAGE_SETTINGS)
+ public void disablePlugins(Subject subject, List<Integer> pluginIds) throws Exception {
+ if (pluginIds == null || pluginIds.size() == 0) {
+ return; // nothing to do
+ }
+
+ // we need to make sure that if a plugin is disabled, no other plugins that depend on it are enabled
+ PluginDependencyGraph graph = PLUGIN_METADATA_MANAGER.buildDependencyGraph();
+ List<Plugin> allPlugins = getPlugins();
+ Set<String> pluginsThatNeedToBeDisabled = new HashSet<String>();
+
+ for (Integer pluginId : pluginIds) {
+ Plugin plugin = getPluginFromListById(allPlugins, pluginId.intValue());
+ if (plugin != null) {
+ Collection<String> dependentNames = graph.getAllDependents(plugin.getName());
+ for (String dependentName : dependentNames) {
+ Plugin dependentPlugin = getPluginFromListByName(allPlugins, dependentName);
+ if (dependentPlugin != null && dependentPlugin.isEnabled()
+ && !pluginIds.contains(Integer.valueOf(dependentPlugin.getId()))) {
+ pluginsThatNeedToBeDisabled.add(dependentPlugin.getDisplayName()); // this isn't disabled and isn't getting disabled, but it needs to be
+ }
+ }
+ }
+ }
+
+ if (!pluginsThatNeedToBeDisabled.isEmpty()) {
+ throw new IllegalArgumentException("You must disable the following dependent plugins also: "
+ + pluginsThatNeedToBeDisabled);
+ }
+
+ // everything is OK, we can disable them
+ for (Integer pluginId : pluginIds) {
+ resourceMetadataManager.setPluginEnabledFlag(subject, pluginId, false);
+ }
+
+ return;
+ }
+
+ private Plugin getPluginFromListByName(List<Plugin> plugins, String name) {
+ for (Plugin plugin : plugins) {
+ if (name.equals(plugin.getName())) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ private Plugin getPluginFromListById(List<Plugin> plugins, int id) {
+ for (Plugin plugin : plugins) {
+ if (id == plugin.getId()) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ @RequiredPermission(Permission.MANAGE_SETTINGS)
+ @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+ public void setPluginEnabledFlag(Subject subject, int pluginId, boolean enabled) throws Exception {
+ Query q = entityManager.createNamedQuery(Plugin.UPDATE_PLUGIN_ENABLED_BY_ID);
+ q.setParameter("id", pluginId);
+ q.setParameter("enabled", Boolean.valueOf(enabled));
+ q.executeUpdate();
+ log.info((enabled ? "Enabling" : "Disabling") + " plugin [" + pluginId + "]");
+ return;
+ }
/**
* Returns the information on the given plugin as found in the database.
@@ -157,6 +278,7 @@ public class ResourceMetadataManagerBean implements ResourceMetadataManagerLocal
newOrUpdated = true;
}
plugin.setId(existingPlugin.getId());
+ plugin.setEnabled(existingPlugin.isEnabled());
}
// If this is a brand new plugin, it gets "updated" too - which ends up being a simple persist.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerLocal.java
index 2be6319..973c855 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/metadata/ResourceMetadataManagerLocal.java
@@ -34,6 +34,15 @@ import org.rhq.core.domain.resource.ResourceCategory;
*/
@Local
public interface ResourceMetadataManagerLocal {
+
+ List<Plugin> getAllPluginsById(List<Integer> pluginIds);
+
+ void enablePlugins(Subject subject, List<Integer> pluginIds) throws Exception;
+
+ void disablePlugins(Subject subject, List<Integer> pluginIds) throws Exception;
+
+ void setPluginEnabledFlag(Subject subject, int pluginId, boolean enabled) throws Exception;
+
/**
* For server-side registration of plugin archives. At server startup or as new plugins are runtime deployed the jar
* will have its descriptor read and parsed and the metadata for the plugin will be updated in the db.
diff --git a/modules/enterprise/server/sars/agent-sar/src/main/resources/META-INF/embedded-agent-configuration.xml b/modules/enterprise/server/sars/agent-sar/src/main/resources/META-INF/embedded-agent-configuration.xml
index 211bb7c..b2702cd 100644
--- a/modules/enterprise/server/sars/agent-sar/src/main/resources/META-INF/embedded-agent-configuration.xml
+++ b/modules/enterprise/server/sars/agent-sar/src/main/resources/META-INF/embedded-agent-configuration.xml
@@ -745,6 +745,32 @@ Embedded RHQ Agent Configuration
<!--
_______________________________________________________________
+ rhq.agent.plugins.disabled
+
+ Defines the names of the plugins that are to be disabled.
+ This is a comma-separated list of plugin names, where a
+ plugin name is found in the name attribute in the root XML
+ element in the plugin descriptor. A disabled plugin will
+ simply not be loaded in the plugin container.
+
+ By default, all plugins are enabled. If a plugin was marked
+ as disabled by the server, the agent will not download it and
+ will not load it, regardless of the value of this preference.
+ If a plugin is enabled on the server, this
+ preference will override that enable setting (in other words,
+ an agent is able to disable a plugin, effectively overriding
+ the server setting, by placing the plugin name in this
+ preference). If the agent already has a plugin jar in
+ its local plugins directory, but that plugin is disabled
+ via this preference, that local plugin jar file will be
+ deleted and the plugin will not be loaded.
+ -->
+ <!--
+ <entry key="rhq.agent.plugins.disabled" value=""/>
+ -->
+
+ <!--
+ _______________________________________________________________
rhq.communications.configuration-schema-version
Defines what version of the agent configuration schema this
diff --git a/modules/plugins/rhq-agent/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/rhq-agent/src/main/resources/META-INF/rhq-plugin.xml
index 9ab0eab..ab76cb6 100644
--- a/modules/plugins/rhq-agent/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/rhq-agent/src/main/resources/META-INF/rhq-plugin.xml
@@ -352,6 +352,7 @@
<c:simple-property name="rhq.agent.plugins.event-sender.period-secs" type="integer" units="seconds" activationPolicy="restart" required="false" default="30" displayName="Event Sender Period" description="Defines how often event reports get sent to the server" />
<c:simple-property name="rhq.agent.plugins.event-report.max-per-source" type="integer" activationPolicy="restart" required="false" default="200" displayName="Event Report Max Per Source" description="Defines the maximum number of events for any given event source that can be placed in a single event report that is sent up to the server. If this number is larger than the max-total setting, then this setting is ignored" />
<c:simple-property name="rhq.agent.plugins.event-report.max-total" type="integer" activationPolicy="restart" required="false" default="400" displayName="Event Report Max Total" description="Defines the total maximum number of events that can be placed in a single event report that is sent up to the server" />
+ <c:simple-property name="rhq.agent.plugins.disabled" type="string" activationPolicy="restart" required="false" default="" displayName="Disabled Plugins" description="Defines the plugins that should be disabled and not loaded by the plugin container. This is a comma-separated list of plugin names." />
</c:group>
<c:group name="comm-endpoints" displayName="Communication Endpoints" hiddenByDefault="false">
<c:description>Communication settings that define the endpoints of this RHQ Agent and its RHQ Server</c:description>