modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
| 76 +++++++
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
| 105 ++++++++++
modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058DiscoveryComponent.java
| 51 ++++
modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058ResourceComponent.java
| 84 ++++++++
modules/core/plugin-container-itest/src/test/resources/arquillian.xml
| 9
modules/core/plugin-container-itest/src/test/resources/bz821058-rhq-plugin.xml
| 29 ++
modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementCollectorRunner.java
| 3
modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementManager.java
| 2
8 files changed, 356 insertions(+), 3 deletions(-)
New commits:
commit 2af89d1a98d2a699590be823b3bb2d20d7ad2dea
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Mon Jun 25 15:39:44 2012 -0400
[BZ] protect measurement facet from mucking with the plugin container's metric
schedules. the bulk of this commit is adding a unit test to the arquillian test framework
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
index 37e4ff2..b1ac175 100644
---
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
@@ -32,6 +32,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,6 +44,12 @@ import org.rhq.core.clientapi.agent.upgrade.ResourceUpgradeResponse;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.discovery.MergeResourceResponse;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
+import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.DisplayType;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
+import org.rhq.core.domain.measurement.NumericType;
+import org.rhq.core.domain.measurement.ResourceMeasurementScheduleRequest;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceError;
@@ -61,7 +68,6 @@ import org.rhq.core.domain.resource.ResourceType;
*
* @author Lukas Krejci
*/
-// TODO (ips): Why don't we just make this implement DiscoveryServerService and use
it as the actual server service?
public class FakeServerInventory {
private static final Log LOG = LogFactory.getLog(FakeServerInventory.class);
@@ -143,6 +149,7 @@ public class FakeServerInventory {
private Resource platform;
private Map<String, Resource> resourceStore = new HashMap<String,
Resource>();
private int counter;
+ private AtomicInteger metricScheduleCounter = new AtomicInteger(1); // used when
creating measurement schedules
private boolean failing;
private boolean failUpgrade;
@@ -364,6 +371,73 @@ public class FakeServerInventory {
};
}
+ // this expects the mock invocation to have two parameters - Set<Integer>
resourceIds, Boolean getChildSchedules
+ public synchronized Answer<Set<ResourceMeasurementScheduleRequest>>
getLatestSchedulesForResourceIds() {
+ return new Answer<Set<ResourceMeasurementScheduleRequest>>() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public Set<ResourceMeasurementScheduleRequest> answer(InvocationOnMock
invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ Set<Integer> resourceIds = (Set<Integer>) args[0];
+ Boolean getChildSchedules = (Boolean) args[1];
+
+ Set<Resource> resources = getResources(resourceIds,
getChildSchedules);
+ Set<ResourceMeasurementScheduleRequest> allSchedules = new
HashSet<ResourceMeasurementScheduleRequest>();
+ for (Resource resource : resources) {
+ ResourceMeasurementScheduleRequest resourceSchedules =
getDefaultMeasurementSchedules(resource);
+ if (resourceSchedules != null) {
+ allSchedules.add(resourceSchedules);
+ }
+ }
+ return allSchedules;
+ }
+ };
+ }
+
+ // this expects the mock invocation to have one parameter - Set<Integer>
resourceIds
+ public synchronized Answer<Set<ResourceMeasurementScheduleRequest>>
postProcessNewlyCommittedResources() {
+ return new Answer<Set<ResourceMeasurementScheduleRequest>>() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public Set<ResourceMeasurementScheduleRequest> answer(InvocationOnMock
invocation) throws Throwable {
+ Object[] args = invocation.getArguments();
+ Set<Integer> resourceIds = (Set<Integer>) args[0];
+
+ Set<Resource> resources = getResources(resourceIds, false);
+ Set<ResourceMeasurementScheduleRequest> allSchedules = new
HashSet<ResourceMeasurementScheduleRequest>();
+ for (Resource resource : resources) {
+ ResourceMeasurementScheduleRequest resourceSchedules =
getDefaultMeasurementSchedules(resource);
+ if (resourceSchedules != null) {
+ allSchedules.add(resourceSchedules);
+ }
+ }
+ return allSchedules;
+ }
+ };
+ }
+
+ private ResourceMeasurementScheduleRequest getDefaultMeasurementSchedules(Resource
resource) {
+ ResourceType rt = resource.getResourceType();
+ Set<MeasurementDefinition> metrics = rt.getMetricDefinitions();
+ if (metrics == null || metrics.isEmpty()) {
+ return null;
+ }
+
+ ResourceMeasurementScheduleRequest resourceSchedules = new
ResourceMeasurementScheduleRequest(resource.getId());
+ for (MeasurementDefinition metric : metrics) {
+ int id = this.metricScheduleCounter.getAndIncrement();
+ String name = metric.getName();
+ long interval = metric.getDefaultInterval();
+ boolean enabled = metric.isDefaultOn() || metric.getDisplayType() ==
DisplayType.SUMMARY;
+ DataType dataType = metric.getDataType();
+ NumericType nDataType = metric.getNumericType();
+ MeasurementScheduleRequest schedule = new MeasurementScheduleRequest(id,
name, interval, enabled, dataType,
+ nDataType);
+ resourceSchedules.addMeasurementScheduleRequest(schedule);
+ }
+ return resourceSchedules;
+ }
+
public synchronized boolean isFailing() {
return failing;
}
diff --git
a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
new file mode 100644
index 0000000..8435114
--- /dev/null
+++
b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
@@ -0,0 +1,105 @@
+package org.rhq.core.pc.measurement;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.TargetsContainer;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.arquillian.testng.Arquillian;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+
+import org.rhq.core.clientapi.server.discovery.InventoryReport;
+import org.rhq.core.domain.resource.InventoryStatus;
+import org.rhq.core.pc.PluginContainer;
+import org.rhq.core.pc.inventory.ResourceContainer;
+import org.rhq.plugins.test.measurement.BZ821058DiscoveryComponent;
+import org.rhq.plugins.test.measurement.BZ821058ResourceComponent;
+import org.rhq.test.arquillian.AfterDiscovery;
+import org.rhq.test.arquillian.BeforeDiscovery;
+import org.rhq.test.arquillian.FakeServerInventory;
+import org.rhq.test.arquillian.MockingServerServices;
+import org.rhq.test.arquillian.ResourceComponentInstances;
+import org.rhq.test.arquillian.ResourceContainers;
+import org.rhq.test.arquillian.RunDiscovery;
+import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
+
+/**
+ * Test for BZ 821058
+ */
+@RunDiscovery
+public class ReadOnlyScheduleSetTest extends Arquillian {
+
+ @Deployment(name = "bz821058Plugin")
+ @TargetsContainer("connected-pc-with-metric-collection")
+ public static RhqAgentPluginArchive getTestPlugin() {
+ RhqAgentPluginArchive pluginJar = ShrinkWrap.create(RhqAgentPluginArchive.class,
"bz821058-plugin-1.0.jar");
+ return
pluginJar.setPluginDescriptor("bz821058-rhq-plugin.xml").addClasses(BZ821058DiscoveryComponent.class,
+ BZ821058ResourceComponent.class);
+ }
+
+ @ArquillianResource
+ private PluginContainer pluginContainer;
+
+ @ArquillianResource
+ public MockingServerServices serverServices;
+
+ private FakeServerInventory fakeServerInventory;
+ private FakeServerInventory.CompleteDiscoveryChecker discoveryCompleteChecker;
+
+ @ResourceContainers(plugin = "bz821058Plugin", resourceType =
"BZ821058Server")
+ private Set<ResourceContainer> containers;
+
+ @ResourceComponentInstances(plugin = "bz821058Plugin", resourceType =
"BZ821058Server")
+ private Set<BZ821058ResourceComponent> components;
+
+ @BeforeDiscovery(testMethods = "testBZ821058")
+ public void resetServerServices() throws Exception {
+ serverServices.resetMocks();
+ fakeServerInventory = new FakeServerInventory();
+ discoveryCompleteChecker =
fakeServerInventory.createAsyncDiscoveryCompletionChecker(1);
+
+ // autoimport everything
+
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
+ fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+
+ // set up the metric schedules using the metric metadata to determine default
intervals and enablement
+
when(serverServices.getDiscoveryServerService().postProcessNewlyCommittedResources(any(Set.class))).then(
+ fakeServerInventory.postProcessNewlyCommittedResources());
+ }
+
+ @AfterDiscovery
+ public void waitForAsyncDiscoveries() throws Exception {
+ if (discoveryCompleteChecker != null) {
+ discoveryCompleteChecker.waitForDiscoveryComplete(10000);
+ }
+ }
+
+ @Test(groups = "pc.itest.bz821058", priority = 20)
+ public void testBZ821058() throws Exception {
+ Assert.assertNotNull(pluginContainer);
+ Assert.assertTrue(pluginContainer.isStarted());
+
+ // make sure we have the resource container
+ Assert.assertEquals(containers.size(), 1, "missing container");
+
+ // make sure we have the resource component
+ Assert.assertEquals(components.size(), 1, "missing component");
+
+ assert containers.iterator().next().getResource().getInventoryStatus() ==
InventoryStatus.COMMITTED;
+
+ BZ821058ResourceComponent server = this.components.iterator().next();
+ // collection interval is set to 30s, and our container
"connected-pc-with-metric-collection"
+ // is configured to start collecting metrics after an initial delay of 10s (see
arquillian.xml).
+ // So let's give the test some time so the measurement facet can be called.
+ server.getValuesLatch.await(45, TimeUnit.SECONDS);
+
+ assert !server.errors.isEmpty() : "there should have been exceptions that
occurred in the getValues method";
+ }
+}
diff --git
a/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058DiscoveryComponent.java
b/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058DiscoveryComponent.java
new file mode 100644
index 0000000..b9f49a4
--- /dev/null
+++
b/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058DiscoveryComponent.java
@@ -0,0 +1,51 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-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 as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.plugins.test.measurement;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
+import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
+
+public class BZ821058DiscoveryComponent implements
ResourceDiscoveryComponent<ResourceComponent<?>> {
+
+ @Override
+ public Set<DiscoveredResourceDetails>
discoverResources(ResourceDiscoveryContext<ResourceComponent<?>> context)
+ throws InvalidPluginConfigurationException, Exception {
+
+ HashSet<DiscoveredResourceDetails> details = new
HashSet<DiscoveredResourceDetails>(1);
+ ResourceType rt = context.getResourceType();
+ String key = "bz821058";
+ String name = "bz821058";
+ String version = "1";
+ Configuration pc = context.getDefaultPluginConfiguration();
+ DiscoveredResourceDetails resource = new DiscoveredResourceDetails(rt, key, name,
version, null, pc, null);
+ details.add(resource);
+
+ return details;
+ }
+
+}
diff --git
a/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058ResourceComponent.java
b/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058ResourceComponent.java
new file mode 100644
index 0000000..1ccadd6
--- /dev/null
+++
b/modules/core/plugin-container-itest/src/test/java/org/rhq/plugins/test/measurement/BZ821058ResourceComponent.java
@@ -0,0 +1,84 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-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 as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.plugins.test.measurement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import org.rhq.core.domain.measurement.AvailabilityType;
+import org.rhq.core.domain.measurement.MeasurementDataNumeric;
+import org.rhq.core.domain.measurement.MeasurementReport;
+import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+import org.rhq.core.pluginapi.inventory.ResourceContext;
+import org.rhq.core.pluginapi.measurement.MeasurementFacet;
+
+public class BZ821058ResourceComponent implements
ResourceComponent<ResourceComponent<?>>, MeasurementFacet {
+
+ public List<Throwable> errors = Collections.synchronizedList(new
ArrayList<Throwable>());
+ public CountDownLatch getValuesLatch = new CountDownLatch(1);
+
+ @Override
+ public AvailabilityType getAvailability() {
+ return AvailabilityType.UP;
+ }
+
+ @Override
+ public void start(ResourceContext<ResourceComponent<?>> context) throws
Exception {
+ }
+
+ @Override
+ public void stop() {
+ }
+
+ /**
+ * To test BZ 821058, we just need to make sure we get exceptions if we try to
+ * manipulate the metrics set when this method is called by the PC.
+ */
+ @Override
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest>
metrics) throws Exception {
+ try {
+ for (Iterator<MeasurementScheduleRequest> i = metrics.iterator();
i.hasNext();) {
+ MeasurementScheduleRequest metric = i.next();
+ report.addData(new MeasurementDataNumeric(metric, new Double(1.0)));
+ System.out.println("===============PRE REMOVE");
+ i.remove(); // this should not be allowed
+ System.out.println("===============POST REMOVE");
+ }
+ } catch (Throwable e) {
+ errors.add(e);
+ }
+
+ try {
+ System.out.println("===============PRE CLEAR");
+ metrics.clear(); // this should not be allowed
+ System.out.println("===============POST CLEAR");
+ } catch (Throwable e) {
+ errors.add(e);
+ }
+
+ getValuesLatch.countDown();
+ return;
+ }
+}
diff --git a/modules/core/plugin-container-itest/src/test/resources/arquillian.xml
b/modules/core/plugin-container-itest/src/test/resources/arquillian.xml
index b8b9271..92968b2 100644
--- a/modules/core/plugin-container-itest/src/test/resources/arquillian.xml
+++ b/modules/core/plugin-container-itest/src/test/resources/arquillian.xml
@@ -4,6 +4,15 @@
xsi:schemaLocation="http://jboss.org/schema/arquillian
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<group qualifier="pcs">
+ <container qualifier="connected-pc-with-metric-collection">
+ <configuration>
+ <property
name="serverServicesImplementationClassName">org.rhq.test.arquillian.MockingServerServices</property>
+ <property name="insideAgent">true</property>
+ <property name="startManagementBean">false</property>
+ <property
name="measurementCollectionInitialDelay">10</property> <!-- start
collecting metrics after 10s -->
+ </configuration>
+ </container>
+
<container qualifier="connected-pc" default="true">
<configuration>
<property
name="serverServicesImplementationClassName">org.rhq.test.arquillian.MockingServerServices</property>
diff --git
a/modules/core/plugin-container-itest/src/test/resources/bz821058-rhq-plugin.xml
b/modules/core/plugin-container-itest/src/test/resources/bz821058-rhq-plugin.xml
new file mode 100644
index 0000000..730a656
--- /dev/null
+++ b/modules/core/plugin-container-itest/src/test/resources/bz821058-rhq-plugin.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<plugin name="bz821058Plugin"
+ displayName="Plugin to test BZ 821058"
+ description="This will help test that measurement facet is given a read-only
schedule set."
+ package="org.rhq.plugins.test.measurement"
+ version="1.0"
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="urn:xmlns:rhq-plugin"
+ xmlns:c="urn:xmlns:rhq-configuration">
+
+ <server name="BZ821058Server"
+ discovery="BZ821058DiscoveryComponent"
+ class="BZ821058ResourceComponent">
+
+ <metric property="metric1"
+ dataType="measurement"
+ defaultOn="true"
+ displayType="summary"
+ defaultInterval="30000" />
+
+ <metric property="metric2"
+ dataType="measurement"
+ defaultOn="true"
+ displayType="summary"
+ defaultInterval="30000" />
+ </server>
+</plugin>
+
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementCollectorRunner.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementCollectorRunner.java
index 3714ad9..ac13b06 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementCollectorRunner.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementCollectorRunner.java
@@ -23,6 +23,7 @@
package org.rhq.core.pc.measurement;
import java.util.ArrayDeque;
+import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -130,7 +131,7 @@ public class MeasurementCollectorRunner implements
Callable<MeasurementReport>,
Set<? extends MeasurementScheduleRequest> requests, Resource resource) {
try {
long start = System.currentTimeMillis();
- measurementComponent.getValues(report,
(Set<MeasurementScheduleRequest>) requests);
+ measurementComponent.getValues(report,
Collections.unmodifiableSet(requests));
long duration = (System.currentTimeMillis() - start);
if (duration > 2000L) {
String message = "[PERF] Collection of measurements for [" +
resource + "] (component=["
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementManager.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementManager.java
index 9ab82cb..dc7d0ca 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementManager.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/measurement/MeasurementManager.java
@@ -469,7 +469,7 @@ public class MeasurementManager extends AgentService implements
MeasurementAgent
}
try {
- measurementFacet.getValues(report, requests);
+ measurementFacet.getValues(report, Collections.unmodifiableSet(requests));
} catch (Throwable t) {
LOG.error("Could not get measurement values", t);
return Collections.emptySet();