modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/ClearPersistedData.java
| 70 ++++
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
| 16
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/When.java
| 31 +
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/DataCleanupExecutor.java
| 170 ++++++++++
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
| 25 +
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerConfiguration.java
| 12
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerExtension.java
| 3
modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
| 154 +++++++++
modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/TestResourceComponent.java
| 24 +
modules/core/arquillian-integration/container/src/test/resources/arquillian.xml
| 8
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
| 42 +-
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
| 10
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
| 62 +++
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
| 24 -
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
| 12
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java
| 3
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/client/security/test/ScriptingAPIServerTest.java
| 133 +++++++
pom.xml
| 2
18 files changed, 753 insertions(+), 48 deletions(-)
New commits:
commit 0683f6b070d499e59507b73007db91f71b0ad278
Merge: bcc35b6 bd0c492
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Mon Apr 1 15:14:30 2013 -0400
Merge remote-tracking branch 'origin/master' into bug/rhq-1
commit bd0c4929b07ef9d481a3031aa01434cd38e9f8a5
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Mar 29 18:04:50 2013 +0100
@ClearPersistedData annotation handling in Arq plugin container tests.
The tests can declare the @ClearPersistedData annotation on the test to
define what of the data that plugin container and its plugins store,
and when should be cleared prior or after the test execution.
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/ClearPersistedData.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/ClearPersistedData.java
new file mode 100644
index 0000000..c37eca7
--- /dev/null
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/ClearPersistedData.java
@@ -0,0 +1,70 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.test.arquillian;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * When a test method is annotated with this annotation, the data persisted by the plugin
container during its lifetime
+ * can be automatically cleaned up.
+ *
+ * @author Lukas Krejci
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target(ElementType.METHOD)
+public @interface ClearPersistedData {
+
+ /**
+ * Special marker string to express the wish to affect all the plugins.
+ */
+ final String ALL_PLUGINS = "__ALL__";
+
+ /**
+ * The time when the clearing of the data should occur.
+ * Defaults to before the invocation of the test method but can also be after or
both.
+ */
+ When[] when() default { When.BEFORE_TEST };
+
+ /**
+ * Whether to clear the inventory.dat file (i.e. the persisted inventory).
+ * Defaults to true.
+ */
+ boolean ofInventory() default true;
+
+ /**
+ * Whether to clear the "changesets" directory storing the drift data.
+ * Defaults to true.
+ */
+ boolean ofDrift() default true;
+
+ /**
+ * The list of the names of the plugins to clear the data of.
+ * Defaults to list containing the special {@link #ALL_PLUGINS} string that means to
delete
+ * the data of all plugins.
+ */
+ String[] ofPlugins() default { ALL_PLUGINS };
+}
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 b1ac175..96b86e1 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
@@ -82,12 +82,14 @@ public class FakeServerInventory {
* @author Lukas Krejci
*/
public static class CompleteDiscoveryChecker {
- private boolean depthReached;
+ private volatile boolean depthReached;
private final int expectedDepth;
private final Object sync = new Object();
+ private volatile boolean finished;
public CompleteDiscoveryChecker(int expectedDepth) {
this.expectedDepth = expectedDepth;
+ this.depthReached = expectedDepth == 0;
}
public void waitForDiscoveryComplete() throws InterruptedException {
@@ -106,6 +108,8 @@ public class FakeServerInventory {
LOG.debug("Discovery already complete... no need to wait on
" + this);
}
}
+
+ finished = true;
}
}
@@ -132,11 +136,13 @@ public class FakeServerInventory {
} catch (InterruptedException e) {
//well, we are going to finish in a few anyway
} finally {
- if (LOG.isDebugEnabled()) {
- LOG.debug("Notifying about discovery complete on
" + CompleteDiscoveryChecker.this);
- }
synchronized (sync) {
- sync.notifyAll();
+ if (!finished) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Notifying about discovery
complete on " + CompleteDiscoveryChecker.this);
+ }
+ sync.notifyAll();
+ }
}
}
}
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/When.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/When.java
new file mode 100644
index 0000000..1910a66
--- /dev/null
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/When.java
@@ -0,0 +1,31 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.test.arquillian;
+
+/**
+* @author Lukas Krejci
+*/
+public enum When {
+ BEFORE_TEST, AFTER_TEST
+}
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/DataCleanupExecutor.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/DataCleanupExecutor.java
new file mode 100644
index 0000000..a823e23
--- /dev/null
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/DataCleanupExecutor.java
@@ -0,0 +1,170 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.test.arquillian.impl;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jboss.arquillian.core.api.Instance;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.test.spi.event.suite.After;
+
+import org.rhq.core.util.file.FileUtil;
+import org.rhq.test.arquillian.ClearPersistedData;
+import org.rhq.test.arquillian.When;
+import org.rhq.test.arquillian.spi.events.PluginContainerDiscovered;
+
+/**
+ * @author Lukas Krejci
+ */
+public class DataCleanupExecutor {
+
+ private static final String INVENTORY_DAT = "inventory.dat";
+ private static final Log LOG = LogFactory.getLog(DataCleanupExecutor.class);
+
+ @Inject
+ private Instance<RhqAgentPluginContainer> pcContainer;
+
+ public void process(@Observes PluginContainerDiscovered pcDiscovered) {
+ doCleanup(pcDiscovered.getTestMethod(), true);
+ }
+
+ public void process(@Observes After test) {
+ doCleanup(test.getTestMethod(), false);
+ }
+
+ private void doCleanup(Method testMethod, boolean isBefore) {
+ ClearPersistedData clearData =
testMethod.getAnnotation(ClearPersistedData.class);
+ if (clearData != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Clean up for: " + testMethod);
+ }
+
+ //b a x y
+ //0 0 0 nogo
+ //0 0 1 nogo
+ //0 1 0 go
+ //0 1 1 nogo
+ //1 0 0 nogo
+ //1 0 1 go
+ //1 1 0 go
+ //1 1 1 go
+ EnumSet<When> when = toEnumSet(When.class, clearData.when());
+ boolean hasBefore = when.contains(When.BEFORE_TEST);
+ boolean hasAfter = when.contains(When.AFTER_TEST);
+
+ if ((!hasBefore && !hasAfter) ||
+ (!hasBefore && isBefore) ||
+ (hasBefore && !hasAfter && !isBefore)) {
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Skipping clean up. Currently " + (isBefore ?
"before" : "after") + " test but scheduled to run:" +
when);
+ }
+ return;
+ }
+
+ LOG.info("Stopping Plugin Container to clean up data");
+ pcContainer.get().stopPc();
+
+ File dataDir = pcContainer.get().getConfiguration().getDataDirectory();
+ File tmpDir = pcContainer.get().getConfiguration().getTemporaryDirectory();
+
+ FileUtil.purge(tmpDir, false);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Purged temp dir");
+ }
+
+ if (clearData.ofInventory()) {
+ File inventoryDat = new File(dataDir, INVENTORY_DAT);
+ if (inventoryDat.exists()) {
+ inventoryDat.delete();
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Purged inventory dat");
+ }
+ }
+
+ if (clearData.ofDrift()) {
+ FileUtil.purge(new File(dataDir, "changesets"), false);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Purged drift changesets");
+ }
+ }
+
+ List<String> plugins = Arrays.asList(clearData.ofPlugins());
+ if (plugins.contains(ClearPersistedData.ALL_PLUGINS)) {
+ removeAllDataBut(dataDir, new String[] {"inventory.dat",
"changesets"});
+ } else {
+ for(String n : plugins) {
+ FileUtil.purge(new File(dataDir, n), true);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Purged plugin dir: " + n);
+ }
+ }
+ }
+
+ LOG.info("Starting Plugin Container after data cleanup.");
+ pcContainer.get().startPc();
+ }
+ }
+
+ private void removeAllDataBut(File dataDir, final String[] excludedNames) {
+ File[] files = dataDir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String name) {
+ for(String n : excludedNames) {
+ if (name.equals(n)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ });
+
+ for(File f : files) {
+ FileUtil.purge(f, true);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Purged dir: " + f.getName());
+ }
+ }
+ }
+
+ private <T extends Enum<T>> EnumSet<T> toEnumSet(Class<T> c,
T... values) {
+ EnumSet<T> ret = EnumSet.noneOf(c);
+ for(T e : values) {
+ ret.add(e);
+ }
+
+ return ret;
+ }
+}
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
index 917fc67..0950865 100644
---
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
@@ -47,6 +47,7 @@ import
org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription;
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.test.spi.annotation.TestScoped;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ArchivePath;
import org.jboss.shrinkwrap.api.ArchivePaths;
@@ -342,9 +343,12 @@ public class RhqAgentPluginContainer implements
DeployableContainer<RhqAgentPlug
/**
* Starts the plugin container.
+ * This method is package private so that other instances can start/stop the PC the
same way as the container
+ * itself even outside the default lifecycle of the container.
+ *
* @return true if the plugin container needed to be started (i.e. was not running
before), false otherwise.
*/
- private boolean startPc() {
+ boolean startPc() {
LOG.debug("Starting PluginContainer on demand...");
PluginContainer pc = PluginContainer.getInstance();
@@ -367,9 +371,12 @@ public class RhqAgentPluginContainer implements
DeployableContainer<RhqAgentPlug
/**
* Stops the plugin container.
+ * This method is package private so that other instances can start/stop the PC the
same way as the container
+ * itself even outside the default lifecycle of the container.
+ *
* @return true if PC was running before this call, false otherwise
*/
- private boolean stopPc() {
+ boolean stopPc() {
LOG.debug("Stopping PluginContainer on demand...");
PluginContainer pc = PluginContainer.getInstance();
boolean wasStarted = pc.isStarted();
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerExtension.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerExtension.java
index c5f23c9..342727c 100644
---
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerExtension.java
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerExtension.java
@@ -49,7 +49,8 @@ public class RhqAgentPluginContainerExtension implements
LoadableExtension {
.observer(PluginContainerPreparatorExecutor.class)
.observer(PluginContainerOperationExecutor.class)
.observer(PluginContainerRemedyExecutor.class)
- .observer(PostPrepareEnricherExecutor.class);
+ .observer(PostPrepareEnricherExecutor.class)
+ .observer(DataCleanupExecutor.class);
builder.service(DeployableContainer.class, RhqAgentPluginContainer.class)
.service(ResourceProvider.class, PluginContainerProvider.class)
diff --git
a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
new file mode 100644
index 0000000..4483cba
--- /dev/null
+++
b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
@@ -0,0 +1,154 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.test.arquillian;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
+
+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.PluginContainerConfiguration;
+import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
+
+/**
+ * @author Lukas Krejci
+ */
+public class CleanUpTest extends Arquillian {
+
+ @Deployment(name = "1")
+ @TargetsContainer("cleaned-up-pc")
+ public static RhqAgentPluginArchive getDeepTestPlugin() {
+ return ShrinkWrap.create(RhqAgentPluginArchive.class,
"test-cleanup-deep-plugin-1.0.0.jar")
+ .addClasses(TestDiscoveryComponent.class, TestResourceComponent.class)
+ .setPluginDescriptor("test-deep-rhq-plugin.xml");
+ }
+
+ @Deployment(name = "2")
+ @TargetsContainer("cleaned-up-pc")
+ public static RhqAgentPluginArchive getTestPlugin() {
+ return ShrinkWrap.create(RhqAgentPluginArchive.class,
"test-cleanup-plugin-1.0.0.jar")
+ .addClasses(TestDiscoveryComponent.class, TestResourceComponent.class)
+ .setPluginDescriptor("test-rhq-plugin.xml");
+ }
+
+ @ArquillianResource
+ private PluginContainerConfiguration pluginContainerConfig;
+
+ @ArquillianResource
+ private MockingServerServices serverServices;
+
+ private FakeServerInventory fakeServerInventory;
+ private FakeServerInventory.CompleteDiscoveryChecker discoveryCompleteChecker;
+
+ private void setupDiscoveryServerMocks(int expectedDiscoveryDepth) throws Exception
{
+ serverServices.resetMocks();
+ fakeServerInventory = new FakeServerInventory();
+ discoveryCompleteChecker =
fakeServerInventory.createAsyncDiscoveryCompletionChecker(expectedDiscoveryDepth);
+ //autoimport everything
+
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
+ fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ }
+
+ @BeforeDiscovery(testMethods = {"testCleanAll",
"testClearingAfterTest",
"checkDiscoveryCanRunFullBecauseInventoryClear"})
+ public void setupDiscoveryMocksWithCleanInventory() throws Exception {
+ setupDiscoveryServerMocks(3);
+ }
+
+ @BeforeDiscovery(testMethods = "testCleanAllButInventoryDat")
+ public void setupDiscoveryMocksWithInventory() throws Exception {
+ //do nothing here... we want the faked server to pretend the stuff was left in
it.
+ //we don't want the agent to think that it has obsolete resources which would
cause it
+ //to stop the resources from the persisted inventory, which would cause our
marker files
+ //to be put on the filesystem and fail the test.
+ }
+
+ @AfterDiscovery(testMethods = {"testCleanAll",
"testClearingAfterTest",
"checkDiscoveryCanRunFullBecauseInventoryClear"})
+ public void waitForAsyncDiscoveries() throws Exception {
+ if (discoveryCompleteChecker != null) {
+ discoveryCompleteChecker.waitForDiscoveryComplete();
+ }
+ }
+
+ @RunDiscovery
+ @ClearPersistedData
+ @Test
+ public void testCleanAll() {
+ String[] files = pluginContainerConfig.getDataDirectory().list();
+ assertExpected(files, new String[]{}, "No other files should be in the data
dir.");
+ }
+
+ @RunDiscovery
+ @ClearPersistedData(ofInventory = false)
+ @Test(dependsOnMethods = "testCleanAll")
+ public void testCleanAllButInventoryDat() {
+ String[] files = pluginContainerConfig.getDataDirectory().list();
+ assertExpected(files, new String[]{"inventory.dat"}, "No other
files should be in the data dir.");
+ }
+
+ @RunDiscovery
+ @ClearPersistedData(ofPlugins = {})
+ @Test(dependsOnMethods = "testCleanAllButInventoryDat")
+ public void testCleanJustInventoryDat() {
+ String[] files = pluginContainerConfig.getDataDirectory().list();
+
+ assertExpected(files, new String[]{"testDeepPlugin",
"testPlugin"},
+ "No other files should be in the data dir.");
+ }
+
+ @ClearPersistedData(when = {When.AFTER_TEST})
+ @Test(dependsOnMethods = "testCleanJustInventoryDat")
+ public void testClearingAfterTest() {
+ //nothing to be done here... we just want stuff to be cleared after this test
+ //and actually check in the next test that it was successful
+ }
+
+ @RunDiscovery
+ @Test(dependsOnMethods = "testClearingAfterTest")
+ public void checkClearAfterTestWorked() {
+ String[] files = pluginContainerConfig.getDataDirectory().list();
+ assertExpected(files, new String[]{}, "No other files should be in the data
dir.");
+ }
+
+ private void assertExpected(String[] actualFiles, String[] expectedFiles, String
message) {
+ HashSet<Object> sActual = new
HashSet<Object>(Arrays.asList(actualFiles));
+ HashSet<Object> sExpected = new
HashSet<Object>(Arrays.asList(expectedFiles));
+
+ //the "changesets" directory is going to always be there because it is
eagerly created during PC startup.
+ sExpected.add("changesets");
+
+ assertEquals(sActual, sExpected, message);
+ }
+}
diff --git
a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/TestResourceComponent.java
b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/TestResourceComponent.java
index cd19c25..3fe50d5 100644
---
a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/TestResourceComponent.java
+++
b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/TestResourceComponent.java
@@ -19,6 +19,12 @@
package org.rhq.test.arquillian;
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
@@ -31,6 +37,10 @@ import org.rhq.core.pluginapi.inventory.ResourceContext;
*/
public class TestResourceComponent implements
ResourceComponent<ResourceComponent<?>> {
+ private static final Log LOG = LogFactory.getLog(TestResourceComponent.class);
+
+ private ResourceContext<?> context;
+
@Override
public AvailabilityType getAvailability() {
return AvailabilityType.UP;
@@ -39,9 +49,23 @@ public class TestResourceComponent implements
ResourceComponent<ResourceComponen
@Override
public void start(ResourceContext<ResourceComponent<?>> context) throws
InvalidPluginConfigurationException,
Exception {
+
+ this.context = context;
}
@Override
public void stop() {
+ context.getDataDirectory().mkdirs();
+ try {
+ File f = new File(context.getDataDirectory(), "test-plugin.data");
+ if (!f.exists()) {
+ f.createNewFile();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Create file " + f);
+ }
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not touch a marker file.");
+ }
}
}
diff --git
a/modules/core/arquillian-integration/container/src/test/resources/arquillian.xml
b/modules/core/arquillian-integration/container/src/test/resources/arquillian.xml
index 3bf9e46..134cac4 100644
--- a/modules/core/arquillian-integration/container/src/test/resources/arquillian.xml
+++ b/modules/core/arquillian-integration/container/src/test/resources/arquillian.xml
@@ -25,6 +25,14 @@
<property
name="nativeSystemInfoEnabled">true</property>
</configuration>
</container>
+
+ <container qualifier="cleaned-up-pc">
+ <configuration>
+ <property name="startManagementBean">false</property>
+ <property name="insideAgent">true</property>
+ <property
name="serverServicesImplementationClassName">org.rhq.test.arquillian.MockingServerServices</property>
+ </configuration>
+ </container>
</group>
<!-- We actually have to exclude all the classes from instrumentation by Arquillian
Jacoco extension, because our
commit 8cfcd136621c1d5241cea4156154bf0e07712c75
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Mar 27 09:58:53 2013 +0100
Arq support for cleaning PC's data dir on shutdown.
It can now be done using the "clearDataOnShutdown" configuration
property in the container configuration in arquillian.xml.
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
index 203e2bb..917fc67 100644
---
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainer.java
@@ -95,8 +95,6 @@ public class RhqAgentPluginContainer implements
DeployableContainer<RhqAgentPlug
sigar = new File(root, "sigar");
sigar.mkdir();
} catch (IOException e) {
- root = null;
- deployments = null;
throw new IllegalStateException(
"Could not create the root directory for RHQ plugin container test
deployments");
}
@@ -374,17 +372,23 @@ public class RhqAgentPluginContainer implements
DeployableContainer<RhqAgentPlug
private boolean stopPc() {
LOG.debug("Stopping PluginContainer on demand...");
PluginContainer pc = PluginContainer.getInstance();
- if (pc.isStarted()) {
+ boolean wasStarted = pc.isStarted();
+ if (wasStarted) {
boolean shutdownGracefully = pc.shutdown();
if (shutdownGracefully) {
LOG.debug("Stopped PluginContainer gracefully.");
} else {
LOG.debug("Stopped PluginContainer.");
}
- return true;
}
- return false;
+ FileUtil.purge(configuration.getTemporaryDirectory(), false);
+
+ if (configuration.isClearDataOnShutdown()) {
+ FileUtil.purge(configuration.getDataDirectory(), false);
+ }
+
+ return wasStarted;
}
private void deployPlugin(RhqAgentPluginArchive plugin) {
diff --git
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerConfiguration.java
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerConfiguration.java
index cdc697d..3fc34eb 100644
---
a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerConfiguration.java
+++
b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/impl/RhqAgentPluginContainerConfiguration.java
@@ -18,7 +18,8 @@ public class RhqAgentPluginContainerConfiguration extends
PluginContainerConfigu
private String serverServicesImplementationClassName;
private boolean nativeSystemInfoEnabled;
private String additionalPackagesForRootPluginClassLoaderToExclude;
-
+ private boolean clearDataOnShutdown;
+
private static final long HUNDRED_YEARS = 100L * 365 * 24 * 60 * 60;
public RhqAgentPluginContainerConfiguration() {
@@ -95,6 +96,14 @@ public class RhqAgentPluginContainerConfiguration extends
PluginContainerConfigu
setRootPluginClassLoaderRegex(newRegex.toString());
}
+ public boolean isClearDataOnShutdown() {
+ return clearDataOnShutdown;
+ }
+
+ public void setClearDataOnShutdown(boolean clearDataOnShutdown) {
+ this.clearDataOnShutdown = clearDataOnShutdown;
+ }
+
@Override
public void validate() throws ConfigurationException {
RhqAgentPluginContainer.init();
@@ -113,5 +122,4 @@ public class RhqAgentPluginContainerConfiguration extends
PluginContainerConfigu
+ getServerServicesImplementationClassName() + "].", e);
}
}
-
}
commit 99bbd52d13b16fe57c80c6dab7230942cfd49560
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Mar 29 17:04:21 2013 +0100
[BZ 928971] - ClassPoolFactory is now resilient against context classloader changes
The instance must not be cached because it could be using a wrong context
classloader if that changed between invocations.
Also made changes to lower the numbder of instances of the ClassPool
in the created while using the ConfigurationClassBuilder.
This, in addition to commit fe8952cf9631c2d839f9e37d53f8c1aa1bb04b83
(which I failed to annotate with the BZ number), should fix the bug.
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
index 2e6ce5c..63c96f0 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
@@ -4,7 +4,6 @@ import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -153,7 +152,7 @@ public class ResourceClientFactory {
private Class<?> defineCustomInterface(ResourceClientProxy proxy) {
try {
// define the dynamic class - do not put it in any known rhq package in case
our jars are signed (see BZ-794503)
- ClassPool pool = ClassPoolFactory.get();
+ ClassPool pool = ClassPoolFactory.newInstance();
CtClass customClass =
pool.makeInterface("org.rhq.bindings.client.dynamic."
+ ResourceClientProxy.class.getSimpleName() + proxy.fingerprint);
@@ -173,8 +172,8 @@ public class ResourceClientFactory {
} else if (prop instanceof ResourceClientProxy.Operation) {
ResourceClientProxy.Operation o = (ResourceClientProxy.Operation)
prop;
- LinkedHashMap<String, CtClass> types =
ConfigurationClassBuilder.translateParameters(o
- .getDefinition().getParametersConfigurationDefinition());
+ LinkedHashMap<String, CtClass> types =
ConfigurationClassBuilder.translateParameters(pool, o
+ .getDefinition().getParametersConfigurationDefinition());
CtClass[] params = new CtClass[types.size()];
int x = 0;
@@ -182,8 +181,10 @@ public class ResourceClientFactory {
params[x++] = types.get(param);
}
- CtMethod method =
CtNewMethod.abstractMethod(ConfigurationClassBuilder.translateConfiguration(o
- .getDefinition().getResultsConfigurationDefinition()),
ResourceClientProxy.simpleName(key), params,
+ CtMethod method = CtNewMethod
+
.abstractMethod(ConfigurationClassBuilder.translateConfiguration(pool, o
+ .getDefinition().getResultsConfigurationDefinition()),
ResourceClientProxy.simpleName(key),
+ params,
new javassist.CtClass[0], customClass);
// Setup @WebParam annotations so the signatures have the config prop
names
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
index 9b20aef..fc5e610 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
@@ -29,11 +29,13 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import javassist.ClassPool;
import javassist.util.proxy.MethodHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.rhq.bindings.util.ClassPoolFactory;
import org.rhq.bindings.util.ConfigurationClassBuilder;
import org.rhq.bindings.util.LazyLoadScenario;
import org.rhq.bindings.util.ResourceTypeFingerprint;
@@ -410,7 +412,9 @@ public class ResourceClientProxy {
if (!LazyLoadScenario.isShouldLoad())
return null;
- Configuration parameters =
ConfigurationClassBuilder.translateParametersToConfig(definition
+ ClassPool pool = ClassPoolFactory.newInstance();
+
+ Configuration parameters =
ConfigurationClassBuilder.translateParametersToConfig(pool, definition
.getParametersConfigurationDefinition(), args);
OperationManagerRemote operationManager =
remoteClient.getProxy(OperationManagerRemote.class);
@@ -441,7 +445,7 @@ public class ResourceClientProxy {
Configuration result = (history != null ? history.getResults() : null);
- Object returnResults = ConfigurationClassBuilder.translateResults(definition
+ Object returnResults = ConfigurationClassBuilder.translateResults(pool,
definition
.getResultsConfigurationDefinition(), result);
return returnResults;
@@ -525,7 +529,7 @@ public class ResourceClientProxy {
/**
* @deprecated Superseded by ({@link #updateBackingContent(String, String)}
*
- * @param fileName file name
+ * @param filename file name
*/
@Deprecated
public void updateBackingContent(String filename) {
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
index 50673e8..38397db 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
@@ -28,24 +28,22 @@ import javassist.ClassPool;
import javassist.LoaderClassPath;
/**
- * This class is used to create Javassist's classpools usable in RHQ on both client
and server side.
- * This only exists to centralize the initialization code for the pools.
- * <p>
- * This class is similar to how the <code>ClassPool.getDefault()</code>
method operates (in that it only ever returns
- * a single instance of the pool) but uses a "wider" class path, looking for
classes using the context class loader
- * and the using the default resource lookup of the <code>Class</code> class,
in addition to just the system class path
- * as detected by Javassist (which is the only one used in the class pool returned by
- * <code>ClassPool.getDefault()</code>).
- * <p>
- * This is to ensure that Javassist can locate the classes in various classloading
"schemes" - the traditional
+ * This class is used to create Javassist's classpools usable in RHQ on both client
and server side. This only exists to
+ * centralize the initialization code for the pools.
+ *
+ * <p> Unlike the <code>ClassPool.getDefault()</code> method that only
ever returns a single instance of the pool, this
+ * factory returns a <b>new instance</b> every time. This is because it uses
a "wider" class path, looking for classes
+ * using the current thread context class loader but also using the default resource
lookup of the <code>Class</code>
+ * class, in addition to just the system class path as detected by Javassist (which is
the only one used in the class
+ * pool returned by <code>ClassPool.getDefault()</code>).
+ *
+ * <p> This is to ensure that Javassist can locate the classes in various
classloading "schemes" - the traditional
* application classloader used in the CLI client and the JBoss Modules classloading used
on the server.
*
* @author Lukas Krejci
*/
public class ClassPoolFactory {
- private static ClassPool pool;
-
private ClassPoolFactory() {
}
@@ -53,13 +51,11 @@ public class ClassPoolFactory {
/**
* @return the singleton class pool instance initialized according the {@link
ClassPoolFactory rules}.
*/
- public static synchronized ClassPool get() {
- if (pool == null) {
- pool = new ClassPool(null);
- pool.appendClassPath(new
LoaderClassPath(Thread.currentThread().getContextClassLoader()));
- pool.appendClassPath(new ClassClassPath(ClassPoolFactory.class));
- pool.appendSystemPath();
- }
+ public static ClassPool newInstance() {
+ ClassPool pool = new ClassPool(null);
+ pool.appendClassPath(new
LoaderClassPath(Thread.currentThread().getContextClassLoader()));
+ pool.appendClassPath(new ClassClassPath(ClassPoolFactory.class));
+ pool.appendSystemPath();
return pool;
}
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
index fd72443..d01ce14 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
@@ -40,7 +40,7 @@ public class ConfigurationClassBuilder {
* @return Map of propertyName to types for the config def
* @throws NotFoundException
*/
- public static LinkedHashMap<String, CtClass>
translateParameters(ConfigurationDefinition def)
+ public static LinkedHashMap<String, CtClass> translateParameters(ClassPool cp,
ConfigurationDefinition def)
throws NotFoundException {
LinkedHashMap<String, CtClass> result = new LinkedHashMap<String,
CtClass>();
if (def == null || def.getPropertyDefinitions() == null) {
@@ -51,14 +51,14 @@ public class ConfigurationClassBuilder {
if (pd instanceof PropertyDefinitionSimple) {
PropertyDefinitionSimple simple = (PropertyDefinitionSimple) pd;
String name = pd.getName();
- CtClass paramType = getSimpleTypeClass(simple);
+ CtClass paramType = getSimpleTypeClass(cp, simple);
result.put(name, paramType);
}
}
return result;
}
- private static CtClass getSimpleTypeClass(PropertyDefinitionSimple simple) throws
NotFoundException {
+ private static CtClass getSimpleTypeClass(ClassPool cp, PropertyDefinitionSimple
simple) throws NotFoundException {
Class<?> paramType = null;
switch (simple.getType()) {
case STRING:
@@ -84,19 +84,19 @@ public class ConfigurationClassBuilder {
paramType = Double.TYPE;
break;
}
- return ClassPoolFactory.get().get(paramType.getName());
+ return cp.get(paramType.getName());
}
- public static CtClass translateConfiguration(ConfigurationDefinition def) throws
NotFoundException {
+ public static CtClass translateConfiguration(ClassPool cp, ConfigurationDefinition
def) throws NotFoundException {
if (def == null) {
return CtClass.voidType;
} else if (def.getPropertyDefinitionSimple("operationResult") != null)
{
// Its a simple type
- return
getSimpleTypeClass(def.getPropertyDefinitionSimple("operationResult"));
+ return getSimpleTypeClass(cp,
def.getPropertyDefinitionSimple("operationResult"));
} else {
// TODO GH: Build a custom type?
- return ClassPoolFactory.get().get(Configuration.class.getName());
+ return cp.get(Configuration.class.getName());
}
}
@@ -108,9 +108,9 @@ public class ConfigurationClassBuilder {
return Character.toLowerCase(name.charAt(0)) + name.substring(1, name.length());
}
- public static Configuration translateParametersToConfig(ConfigurationDefinition
parametersConfigurationDefinition,
+ public static Configuration translateParametersToConfig(ClassPool cp,
ConfigurationDefinition parametersConfigurationDefinition,
Object[] args) throws NotFoundException {
- LinkedHashMap<String, CtClass> translateParameters =
translateParameters(parametersConfigurationDefinition);
+ LinkedHashMap<String, CtClass> translateParameters =
translateParameters(cp, parametersConfigurationDefinition);
Configuration config = new Configuration();
int index = 0;
@@ -122,12 +122,12 @@ public class ConfigurationClassBuilder {
}
- public static Object translateResults(ConfigurationDefinition
resultsConfigurationDefinition, Configuration result)
+ public static Object translateResults(ClassPool cp, ConfigurationDefinition
resultsConfigurationDefinition, Configuration result)
throws NotFoundException {
- CtClass expectedReturn = translateConfiguration(resultsConfigurationDefinition);
+ CtClass expectedReturn = translateConfiguration(cp,
resultsConfigurationDefinition);
- if
(expectedReturn.equals(ClassPoolFactory.get().get(Configuration.class.getName()))) {
+ if
(expectedReturn.equals(ClassPoolFactory.newInstance().get(Configuration.class.getName())))
{
return result;
} else {
//bail on translation if Configuration passed in is null
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
index 1e13179..640f8bf 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
@@ -22,7 +22,6 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
-import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
@@ -143,7 +142,7 @@ public class InterfaceSimplifier {
public static Class<?> simplify(Class<?> intf) {
try {
- ClassPool classPool = ClassPoolFactory.get();
+ ClassPool classPool = ClassPoolFactory.newInstance();
String simplifiedName = getSimplifiedName(intf);
LOG.debug("Simplifying " + intf + " (simplified interface
name: " + simplifiedName + ")...");
commit fe8952cf9631c2d839f9e37d53f8c1aa1bb04b83
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Mar 29 16:06:45 2013 +0100
Fix classloading issues when defining objects in the std script context.
It seems that the change of the container to AS7 has caused a regression
in the CLI alert scripts that were no longer able to properly define the
standard context provided to scripts by RHQ.
This is fixed by using a differently configured Javassist's ClassPool
during the various class modifications we use to prepare the *Manager
objects and when generating a resource proxy in ProxyFactory.
Also, the errors that can happen during the various stages of this setup
are now thrown up the chain instead of merely logged so that errors like
this are apparent - it is not something the user should be expected to
deal with.
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
index 3bf4729..2e6ce5c 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientFactory.java
@@ -2,6 +2,9 @@ package org.rhq.bindings.client;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -26,6 +29,7 @@ import javax.jws.WebParam;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.rhq.bindings.util.ClassPoolFactory;
import org.rhq.bindings.util.ConfigurationClassBuilder;
import org.rhq.bindings.util.ResourceTypeFingerprint;
import org.rhq.core.domain.resource.ResourceCreationDataType;
@@ -109,12 +113,16 @@ public class ResourceClientFactory {
instantiateMethodHandler(proxy, interfaces, rhqFacade));
} catch (InstantiationException e) {
LOG.error("Could not instantiate a ResourceClientProxy, this is a
bug.", e);
+ throw new IllegalStateException("Could not instantiate a
ResourceClientProxy, this is a bug.", e);
} catch (IllegalAccessException e) {
LOG.error("Could not instantiate a ResourceClientProxy, this is a
bug.", e);
+ throw new IllegalStateException("Could not instantiate a
ResourceClientProxy, this is a bug.", e);
} catch (NoSuchMethodException e) {
LOG.error("Could not instantiate a ResourceClientProxy, this is a
bug.", e);
+ throw new IllegalStateException("Could not instantiate a
ResourceClientProxy, this is a bug.", e);
} catch (InvocationTargetException e) {
LOG.error("Could not instantiate a ResourceClientProxy, this is a
bug.", e);
+ throw new IllegalStateException("Could not instantiate a
ResourceClientProxy, this is a bug.", e);
}
return proxied;
}
@@ -145,7 +153,7 @@ public class ResourceClientFactory {
private Class<?> defineCustomInterface(ResourceClientProxy proxy) {
try {
// define the dynamic class - do not put it in any known rhq package in case
our jars are signed (see BZ-794503)
- ClassPool pool = ClassPool.getDefault();
+ ClassPool pool = ClassPoolFactory.get();
CtClass customClass =
pool.makeInterface("org.rhq.bindings.client.dynamic."
+ ResourceClientProxy.class.getSimpleName() + proxy.fingerprint);
@@ -202,19 +210,34 @@ public class ResourceClientFactory {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
-
Thread.currentThread().setContextClassLoader(ResourceClientProxy.class.getClassLoader());
+ setContextClassLoader(ResourceClientProxy.class.getClassLoader());
return customClass.toClass();
} finally {
- Thread.currentThread().setContextClassLoader(cl);
+ setContextClassLoader(cl);
}
} catch (NotFoundException e) {
LOG.error("Could not create custom interface for resource with id "
+ proxy.getId(), e);
+ throw new IllegalStateException("Could not create custom interface for
resource with id " + proxy.getId(), e);
} catch (CannotCompileException e) {
LOG.error("Could not create custom interface for resource with id "
+ proxy.getId(), e);
+ throw new IllegalStateException("Could not create custom interface for
resource with id " + proxy.getId(), e);
} catch (Exception e) {
LOG.error("Could not create custom interface for resource with id "
+ proxy.getId(), e);
+ throw new IllegalStateException("Could not create custom interface for
resource with id " + proxy.getId(), e);
+ }
+ }
+
+ private void setContextClassLoader(final ClassLoader cl) {
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ Thread.currentThread().setContextClassLoader(cl);
+ return null;
+ }
+ });
+ } else {
+ Thread.currentThread().setContextClassLoader(cl);
}
-
- return null;
}
}
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
new file mode 100644
index 0000000..50673e8
--- /dev/null
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ClassPoolFactory.java
@@ -0,0 +1,66 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.bindings.util;
+
+import javassist.ClassClassPath;
+import javassist.ClassPool;
+import javassist.LoaderClassPath;
+
+/**
+ * This class is used to create Javassist's classpools usable in RHQ on both client
and server side.
+ * This only exists to centralize the initialization code for the pools.
+ * <p>
+ * This class is similar to how the <code>ClassPool.getDefault()</code>
method operates (in that it only ever returns
+ * a single instance of the pool) but uses a "wider" class path, looking for
classes using the context class loader
+ * and the using the default resource lookup of the <code>Class</code> class,
in addition to just the system class path
+ * as detected by Javassist (which is the only one used in the class pool returned by
+ * <code>ClassPool.getDefault()</code>).
+ * <p>
+ * This is to ensure that Javassist can locate the classes in various classloading
"schemes" - the traditional
+ * application classloader used in the CLI client and the JBoss Modules classloading used
on the server.
+ *
+ * @author Lukas Krejci
+ */
+public class ClassPoolFactory {
+
+ private static ClassPool pool;
+
+ private ClassPoolFactory() {
+
+ }
+
+ /**
+ * @return the singleton class pool instance initialized according the {@link
ClassPoolFactory rules}.
+ */
+ public static synchronized ClassPool get() {
+ if (pool == null) {
+ pool = new ClassPool(null);
+ pool.appendClassPath(new
LoaderClassPath(Thread.currentThread().getContextClassLoader()));
+ pool.appendClassPath(new ClassClassPath(ClassPoolFactory.class));
+ pool.appendSystemPath();
+ }
+
+ return pool;
+ }
+}
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
index b739581..fd72443 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ConfigurationClassBuilder.java
@@ -84,7 +84,7 @@ public class ConfigurationClassBuilder {
paramType = Double.TYPE;
break;
}
- return ClassPool.getDefault().get(paramType.getName());
+ return ClassPoolFactory.get().get(paramType.getName());
}
public static CtClass translateConfiguration(ConfigurationDefinition def) throws
NotFoundException {
@@ -96,7 +96,7 @@ public class ConfigurationClassBuilder {
} else {
// TODO GH: Build a custom type?
- return ClassPool.getDefault().get(Configuration.class.getName());
+ return ClassPoolFactory.get().get(Configuration.class.getName());
}
}
@@ -127,7 +127,7 @@ public class ConfigurationClassBuilder {
CtClass expectedReturn = translateConfiguration(resultsConfigurationDefinition);
- if
(expectedReturn.equals(ClassPool.getDefault().get(Configuration.class.getName()))) {
+ if
(expectedReturn.equals(ClassPoolFactory.get().get(Configuration.class.getName()))) {
return result;
} else {
//bail on translation if Configuration passed in is null
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
index 8ce7bc6..1e13179 100644
---
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
+++
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java
@@ -143,7 +143,7 @@ public class InterfaceSimplifier {
public static Class<?> simplify(Class<?> intf) {
try {
- ClassPool classPool = ClassPool.getDefault();
+ ClassPool classPool = ClassPoolFactory.get();
String simplifiedName = getSimplifiedName(intf);
LOG.debug("Simplifying " + intf + " (simplified interface
name: " + simplifiedName + ")...");
@@ -275,12 +275,11 @@ public class InterfaceSimplifier {
return newClass.toClass();
- } catch (NotFoundException e) {
- LOG.debug("Failed to simplify " + intf + " - cause: " +
e);
- } catch (CannotCompileException e) {
- LOG.error("Failed to simplify " + intf + ".", e);
+ } catch (Exception e) {
+ String msg = "Failed to simplify " + intf + ".";
+ LOG.error(msg, e);
+ throw new IllegalStateException(msg, e);
}
- return intf;
}
private static String getSimplifiedName(Class<?> interfaceClass) {
diff --git
a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java
b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java
index a918389..df0eab4 100644
---
a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java
+++
b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java
@@ -87,7 +87,8 @@ public class LocalClient implements RhqFacade {
managers.put(manager, proxy);
} catch (Throwable e) {
- LOG.error("Failed to load manager " + manager + "
due to missing class.", e);
+ LOG.error("Failed to load manager " + manager +
".", e);
+ throw new IllegalStateException("Failed to load manager
" + manager + ".", e);
}
}
}
diff --git
a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/client/security/test/ScriptingAPIServerTest.java
b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/client/security/test/ScriptingAPIServerTest.java
new file mode 100644
index 0000000..0195603
--- /dev/null
+++
b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/client/security/test/ScriptingAPIServerTest.java
@@ -0,0 +1,133 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.rhq.enterprise.client.security.test;
+
+import java.util.UUID;
+
+import javax.script.ScriptEngine;
+
+import org.testng.annotations.Test;
+
+import org.rhq.bindings.client.RhqManager;
+import org.rhq.bindings.util.SimplifiedClass;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
+import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple;
+import org.rhq.core.domain.configuration.definition.PropertySimpleType;
+import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.DisplayType;
+import org.rhq.core.domain.measurement.MeasurementCategory;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementUnits;
+import org.rhq.core.domain.operation.OperationDefinition;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceCategory;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.enterprise.client.ScriptableAbstractEJB3Test;
+import org.rhq.enterprise.server.test.TransactionCallback;
+import org.rhq.enterprise.server.util.LookupUtil;
+
+import static org.testng.Assert.assertNotEquals;
+
+/**
+ * @author Lukas Krejci
+ */
+public class ScriptingAPIServerTest extends ScriptableAbstractEJB3Test {
+
+ @Test
+ public void testProxyFactoryCorrectlyDefinesMethodsAndProperties() throws Exception
{
+ executeInTransaction(new TransactionCallback() {
+ @Override
+ public void execute() throws Exception {
+ ResourceType rt = createResourceType();
+ Resource r = createResource(rt);
+
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+ ScriptEngine engine = getEngine(overlord);
+
+ engine.eval("var r = ProxyFactory.getResource(" + r.getId() +
");");
+
+ String detectedMeasurementType = (String)
engine.eval("typeof(r.measurement);");
+ String detectedOperationType = (String)
engine.eval("typeof(r.operation);");
+
+ assertEquals("object", detectedMeasurementType);
+ assertEquals("function", detectedOperationType);
+ }
+ });
+ }
+
+ @Test
+ public void testManagersHaveSimplifiedSignatures() throws Exception {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+ ScriptEngine engine = getEngine(overlord);
+
+ //we know that interface simplification worked if the full class name is
different from the original
+ //Here, we want to test the simplification worked within the classloading
environment of the RHQ server.
+ //Simplification itself is unit tested separately.
+
+ for (RhqManager m : RhqManager.values()) {
+ String name = m.name();
+ Object scriptedManager = engine.eval(name);
+ assertNotNull(scriptedManager);
+
+ Class<?> implementedIface =
scriptedManager.getClass().getInterfaces()[0];
+
+ assertNotEquals(m.remote(), implementedIface);
+ assertNotNull(implementedIface.getAnnotation(SimplifiedClass.class));
+ }
+ }
+
+ private ResourceType createResourceType() {
+ ResourceType resourceType = new ResourceType("ScriptingAPITestType",
"dummy", ResourceCategory.PLATFORM, null);
+
+ MeasurementDefinition measurement = new
MeasurementDefinition("measurement", MeasurementCategory.PERFORMANCE,
+ MeasurementUnits.BYTES,
+ DataType.MEASUREMENT, true, 1, DisplayType.DETAIL);
+ measurement.setDisplayName("measurement");
+
+ resourceType.addMetricDefinition(measurement);
+
+ ConfigurationDefinition params = new ConfigurationDefinition("dummy",
null);
+ params.put(new PropertyDefinitionSimple("parameter", null, true,
PropertySimpleType.BOOLEAN));
+
+ OperationDefinition operation = new OperationDefinition(resourceType,
"operation");
+ operation.setDisplayName("operation");
+ operation.setParametersConfigurationDefinition(params);
+
+ resourceType.addOperationDefinition(operation);
+
+ getEntityManager().persist(resourceType);
+
+ return resourceType;
+ }
+
+ private Resource createResource(ResourceType resourceType) {
+ Resource resource = new Resource("key", "resource",
resourceType);
+ resource.setUuid(UUID.randomUUID().toString());
+
+ getEntityManager().persist(resource);
+
+ return resource;
+ }
+}
commit 652deea1954bd3132456c95d669e58e1519c582a
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Fri Mar 29 13:46:34 2013 +0100
[BZ 923458] - Versioning for dependencies and plugins not consistent - version of
liquibase set to 2.0.3 (there was a CNF exception when running itests-2)
diff --git a/pom.xml b/pom.xml
index 511142f..32dd41a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -158,7 +158,7 @@
<commons-configuration.version>1.6</commons-configuration.version>
<junit.version>4.10</junit.version>
- <liquibase-core.version>1.9.5</liquibase-core.version>
+ <liquibase-core.version>2.0.3</liquibase-core.version>
<jbpm.version>3.1.1</jbpm.version>
<servlet-api.version>2.4</servlet-api.version>