[rhq] modules/enterprise
by lkrejci
modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java | 2 ++
1 file changed, 2 insertions(+)
New commits:
commit 9af5936fe96183531a393f68dd638ef79cc895cd
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 19:26:51 2012 +0200
make sure CLI scripts on the server redirect standard and error output to
the tracked writer.
diff --git a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
index 98246a0..0bca2fa 100644
--- a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
+++ b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
@@ -274,6 +274,8 @@ public class CliSender extends AlertSender<CliComponent> {
bindings.put("alert", alert);
ScriptEngine engine = takeEngine(bindings);
+ engine.getContext().setWriter(output);
+ engine.getContext().setErrorWriter(output);
return engine;
}
11 years, 10 months
[rhq] 34 commits - modules/enterprise
by lkrejci
modules/enterprise/binding/pom.xml | 28
modules/enterprise/binding/src/main/java/org/rhq/bindings/SandboxedScriptEngine.java | 204 --
modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java | 192 +-
modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java | 55
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java | 53
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java | 59
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java | 102 -
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManager.java | 133 +
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManagers.java | 133 -
modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/JsEngineInitializer.java | 104 -
modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/ScriptEngineInitializer.java | 82 -
modules/enterprise/binding/src/main/java/org/rhq/bindings/script/BaseRhqSchemeScriptSourceProvider.java | 63
modules/enterprise/binding/src/main/java/org/rhq/bindings/script/RepoScriptSourceProvider.java | 127 +
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java | 524 ++++++-
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/MultiScriptSourceProvider.java | 66
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptAssert.java | 36
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java | 5
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java | 38
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java | 41
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SummaryFilter.java | 8
modules/enterprise/binding/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider | 1
modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java | 152 --
modules/enterprise/binding/src/test/java/org/rhq/bindings/ScriptEngineTest.java | 6
modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java | 185 ++
modules/enterprise/binding/src/test/java/org/rhq/bindings/script/RepoScriptSourceProviderTest.java | 160 ++
modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java | 351 +++++
modules/enterprise/binding/src/test/java/org/rhq/bindings/util/ScriptAssertTest.java | 3
modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java | 4
modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java | 2
modules/enterprise/pom.xml | 2
modules/enterprise/remoting/cli/pom.xml | 13
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java | 104 +
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java | 602 --------
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java | 15
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java | 77 -
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java | 72 +
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/SamplesScriptSourceProvider.java | 67
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java | 93 +
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ChangeRegisteringPrintWriter.java | 233 +++
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java | 48
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/DummyCodeCompletion.java | 50
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ReflectionUtility.java | 27
modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider | 2
modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml | 3
modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java | 98 -
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java | 256 +--
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java | 47
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RhqDownloadsScriptSourceProvider.java | 89 +
modules/enterprise/remoting/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider | 1
modules/enterprise/scripting/api/pom.xml | 173 ++
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java | 70 +
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java | 84 +
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java | 104 +
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java | 62
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java | 44
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProviderFactory.java | 61
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/util/SandboxedScriptEngine.java | 224 +++
modules/enterprise/scripting/javascript/pom.xml | 229 +++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java | 586 ++++++++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java | 163 ++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java | 48
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/ExternalScriptable.java | 496 +++++++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/JSAdapter.java | 361 +++++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoCompiledScript.java | 91 +
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java | 695 ++++++++++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngineFactory.java | 219 +++
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ExtendedScriptException.java | 76 +
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/InterfaceImplementor.java | 94 +
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ScriptEngineFactoryBase.java | 62
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/util/ScriptSourceToModuleSourceProviderAdapter.java | 75 +
modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory | 1
modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider | 1
modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/EngineTest.java | 98 +
modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java | 190 ++
modules/enterprise/scripting/javascript/src/test/resources/allow-all.policy | 3
modules/enterprise/scripting/javascript/src/test/resources/test-module1.js | 7
modules/enterprise/scripting/javascript/src/test/resources/test-module2.js | 7
modules/enterprise/scripting/pom.xml | 33
modules/enterprise/scripting/python/pom.xml | 216 +++
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java | 41
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java | 132 +
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineProvider.java | 49
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java | 136 +
modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory | 1
modules/enterprise/scripting/python/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider | 1
modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java | 178 ++
modules/enterprise/scripting/python/src/test/resources/allow-all.policy | 3
modules/enterprise/server/client-api/pom.xml | 10
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClient.java | 457 +-----
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java | 13
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/RhqDownloadsScriptSourceProvider.java | 80 +
modules/enterprise/server/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider | 1
modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java | 55
modules/enterprise/server/ear/pom.xml | 6
modules/enterprise/server/itests/pom.xml | 7
modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/client/security/test/JndiAccessTest.java | 3
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerRemote.java | 1
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerBean.java | 31
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerLocal.java | 3
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerRemote.java | 14
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java | 4
modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java | 11
103 files changed, 8285 insertions(+), 2343 deletions(-)
New commits:
commit 12762b55b0add61b33ff861047acf2e7eac1cff5
Merge: 7095212 a49e075
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 18:00:02 2012 +0200
Merge lkrejci/modular-scripting into master
commit 709521269eea8fa16f7c672e34ad90c0b748c0ee
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 16:39:49 2012 +0200
Fixing the last javascript specific thing in the CLI - no more assumptions
about the format of the error message.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index cfe1686..c7f6bbf 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -58,6 +58,7 @@ import org.rhq.enterprise.client.utility.CodeCompletionCompletorWrapper;
import org.rhq.enterprise.client.utility.DummyCodeCompletion;
import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.ScriptEngineInitializer;
/**
* @author Greg Hinkle
@@ -108,6 +109,7 @@ public class ClientMain {
private Recorder recorder = new NoOpRecorder();
private ScriptEngine engine;
+ private ScriptEngineInitializer scriptEngineInitializer;
private class StartupConfiguration {
public boolean askForPassword;
@@ -675,6 +677,12 @@ public class ClientMain {
try {
engine = ScriptEngineFactory.getScriptEngine(getLanguage(),
new PackageFinder(Arrays.asList(getLibDir())), null);
+
+ if (engine == null) {
+ throw new IllegalStateException("The scripting language '" + getLanguage()
+ + "' could not be loaded.");
+ }
+ scriptEngineInitializer = ScriptEngineFactory.getInitializer(getLanguage());
} catch (ScriptException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -685,6 +693,10 @@ public class ClientMain {
return engine;
}
+ public String getUsefulErrorMessage(ScriptException e) {
+ return scriptEngineInitializer.extractUserFriendlyErrorMessage(e);
+ }
+
public Map<String, ClientCommand> getCommands() {
return commands;
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
index a511310..42137af 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
@@ -151,9 +151,7 @@ public class ScriptCommand implements ClientCommand {
}
} catch (ScriptException e) {
- String message = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
- message = message.replace("sun.org.mozilla.javascript.internal.EcmaError: ", "");
- message = message.replace("(<Unknown source>#1) in <Unknown source> at line number 1", "");
+ String message = client.getUsefulErrorMessage(e);
client.getPrintWriter().println(message);
client.getPrintWriter().println(script);
commit 0520b77135d2be31dea003cb1b99bf75aac49a9b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 16:35:16 2012 +0200
Finishing up the python support for CLI.
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
index c91b706..e65f793 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
@@ -34,5 +34,11 @@ import java.net.URI;
*/
public interface ScriptSourceProvider {
+ /**
+ * Returns the reader of the source of the script specified by given location.
+ *
+ * @param location the location of the script
+ * @return the reader of the script source or null if it could not be found
+ */
Reader getScriptSource(URI location);
}
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index 6009b1b..06bed89 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -23,7 +23,6 @@
<artifactId>rhino</artifactId>
<version>1.7R4</version>
</dependency>
-
</dependencies>
<build>
diff --git a/modules/enterprise/scripting/python/pom.xml b/modules/enterprise/scripting/python/pom.xml
index 4b48e52..b657dd2 100644
--- a/modules/enterprise/scripting/python/pom.xml
+++ b/modules/enterprise/scripting/python/pom.xml
@@ -27,10 +27,39 @@
<build>
<plugins>
<plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
- </configuration>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>jarjar-maven-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jarjar</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>org.python:jython-standalone</include>
+ </includes>
+ <rules>
+ <keep>
+ <pattern>*.**</pattern>
+ </keep>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ <argLine>-Djava.security.manager
+ -Djava.security.policy==${project.build.testOutputDirectory}/allow-all.policy</argLine>
+ <!-- This is important, because some of the tests try to exit
+ the JVM. -->
+ <failIfNoTests>true</failIfNoTests>
+ </configuration>
</plugin>
</plugins>
</build>
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java
new file mode 100644
index 0000000..2d1ef65
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java
@@ -0,0 +1,41 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import javax.script.ScriptEngine;
+
+import org.python.jsr223.PyScriptEngineFactory;
+
+/**
+ * @author Lukas Krejci
+ *
+ */
+public class PythonScriptEngineFactory extends PyScriptEngineFactory {
+
+ @Override
+ public Object getParameter(String key) {
+ if (ScriptEngine.NAME.equals(key)) {
+ return "python";
+ } else {
+ return super.getParameter(key);
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
index 07ed8eb..e868204 100644
--- a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
@@ -23,12 +23,19 @@ import java.lang.reflect.Method;
import java.security.PermissionCollection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Properties;
import java.util.Set;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.python.core.Py;
+import org.python.core.PySystemState;
+import org.python.util.PythonInterpreter;
+
import org.rhq.scripting.ScriptEngineInitializer;
import org.rhq.scripting.ScriptSourceProvider;
import org.rhq.scripting.util.SandboxedScriptEngine;
@@ -40,6 +47,16 @@ import org.rhq.scripting.util.SandboxedScriptEngine;
*/
public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
+ private static final Log LOG = LogFactory.getLog(PythonScriptEngineInitializer.class);
+
+ static {
+ Properties props = new Properties();
+ props.put("python.packages.paths", "java.class.path,sun.boot.class.path");
+ props.put("python.packages.directories", "java.ext.dirs");
+ props.put("python.cachedir.skip", false);
+ PythonInterpreter.initialize(System.getProperties(), props, null);
+ }
+
private ScriptEngineManager engineManager = new ScriptEngineManager();
@Override
@@ -47,17 +64,29 @@ public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
ScriptEngine eng = engineManager.getEngineByName("python");
+ //XXX this might not work perfectly in jython
+ //but we can't make it work perfectly either, so let's just
+ //keep our fingers crossed..
+ //http://www.jython.org/jythonbook/en/1.0/ModulesPackages.html#from-import-statements
for (String pkg : packages) {
- eng.eval("from " + pkg + " import *\n");
+ try {
+ eng.eval("from " + pkg + " import *\n");
+ } catch (ScriptException e) {
+ //well, let's just keep things going, this is not fatal...
+ LOG.info("Python script engine could not pre-import members of package '" + pkg + "'.");
+ }
}
//fingers crossed we can secure jython like this
- return new SandboxedScriptEngine(eng, permissions);
+ return permissions == null ? eng : new SandboxedScriptEngine(eng, permissions);
}
@Override
public void installScriptSourceProvider(ScriptEngine scriptEngine, ScriptSourceProvider provider) {
- //TODO add support for script source providers... possibly using http://www.python.org/dev/peps/pep-0302/
+ PySystemState sys = Py.getSystemState();
+ if (sys != null) {
+ sys.path_hooks.append(new PythonSourceProvider(provider));
+ }
}
@Override
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java
new file mode 100644
index 0000000..70688d4
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java
@@ -0,0 +1,136 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.python.core.Py;
+import org.python.core.PyObject;
+import org.python.core.imp;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * This class translates the requests for modules in python using the import
+ * statement into calls to RHQ's script source providers.
+ * <p>
+ * For a script to be downloadable using RHQ, one must add a path prefix to
+ * <code>sys.path</code> so that RHQ is aware of the available locations it should
+ * look into.
+ * <p>
+ * For example, if you have the RHQ repository script source provider available on
+ * the classpath of the CLI, you can add the following to the <code>sys.path</code>:
+ * <pre>
+ * <code>
+ * import sys
+ * sys.path.append("__rhq__:rhq:/repositories/my_repository")
+ * </code>
+ * </pre>
+ * and then you can import a module from that repository by the ordinary import statement:
+ * <pre>
+ * <code>
+ * import my_module
+ * </code>
+ * </pre>
+ * This will translate into a download of the script from the following location:
+ * <code>rhq://repositories/my_repository/my_module.py</code>.
+ *
+ * @author Lukas Krejci
+ */
+public class PythonSourceProvider extends PyObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final String RHQ_PATH_EXTENSION_PREFIX = "__rhq__:";
+
+ private ScriptSourceProvider scriptSourceProvider;
+ private String currentPathPrefix;
+
+ public PyObject __call__(PyObject args[], String keywords[]) {
+ if (args[0].toString().startsWith(RHQ_PATH_EXTENSION_PREFIX)) {
+ currentPathPrefix = args[0].toString().substring(RHQ_PATH_EXTENSION_PREFIX.length());
+ return this;
+ }
+ throw Py.ImportError("unable to handle");
+ }
+
+ private static class ReaderInputStream extends InputStream {
+ private Reader rdr;
+
+ public ReaderInputStream(Reader rdr) {
+ this.rdr = rdr;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return rdr.read();
+ }
+
+ }
+
+ public class Loader extends PyObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private String prefix;
+
+ public Loader(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public PyObject load_module(String name) {
+ try {
+ URI uri = new URI(prefix + name + ".py");
+ Reader rdr = scriptSourceProvider.getScriptSource(uri);
+ return imp.createFromSource(name, new ReaderInputStream(rdr), uri.toString());
+ } catch (URISyntaxException e) {
+ return Py.None;
+ }
+ }
+ }
+
+ public PythonSourceProvider(ScriptSourceProvider scriptSourceProvider) {
+ this.scriptSourceProvider = scriptSourceProvider;
+ }
+
+ public PyObject find_module(String name) {
+ return find_module(name, Py.None);
+ }
+
+ public PyObject find_module(String name, PyObject path) {
+ try {
+ URI uri = new URI(currentPathPrefix + name + ".py");
+
+ return scriptSourceProvider.getScriptSource(uri) == null ? Py.None : new Loader(currentPathPrefix);
+ } catch (URISyntaxException e) {
+ return Py.None;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getType().toString();
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 0000000..f2c5a71
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1 @@
+org.rhq.scripting.python.PythonScriptEngineFactory
diff --git a/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java b/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java
new file mode 100644
index 0000000..b1d4a65
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java
@@ -0,0 +1,178 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import java.io.FilePermission;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.security.AccessControlException;
+import java.security.Permissions;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class PythonScriptEngineInitializerTest {
+
+ public static class Tester {
+ private int cnt = 0;
+
+ public void increment() {
+ ++cnt;
+ }
+
+ public int getInvocationCoung() {
+ return cnt;
+ }
+ }
+
+ private static final String EXPECTED_OUTPUT = "kachny";
+
+ public static class SourceProvider implements ScriptSourceProvider {
+ @Override
+ public Reader getScriptSource(URI scriptUri) {
+ if (scriptUri.toString().equals("test/test_module.py")) {
+ return new StringReader("print '" + EXPECTED_OUTPUT + "'");
+ }
+ return null;
+ }
+ }
+
+ public void testEngineInitialization() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ //just some code to test out this is python
+ engine.eval("from java.util import HashMap\nHashMap()");
+ }
+
+ public void testMethodIndirection() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ Bindings bindings = engine.createBindings();
+ Tester tester = new Tester();
+ bindings.put("tester", tester);
+
+ engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+
+ engine.eval("tester.increment()");
+
+ Assert.assertEquals(tester.getInvocationCoung(), 1, "Unexpected number of tester invocations.");
+
+ Map<String, Set<Method>> methods = getMethodsByName(Tester.class);
+ for (Set<Method> ms : methods.values()) {
+ Set<String> fns = initializer.generateIndirectionMethods("tester", ms);
+ for (String fn : fns) {
+ engine.eval(fn);
+ }
+ }
+
+ engine.eval("increment()");
+ Assert.assertEquals(tester.getInvocationCoung(), 2,
+ "Unexpected number of tester invocations after calling an indirected method.");
+ }
+
+ public void testSecuredEngine() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+
+ //jython seems to need these two..
+ Permissions perms = new Permissions();
+ perms.add(new RuntimePermission("createClassLoader"));
+ perms.add(new RuntimePermission("getProtectionDomain"));
+
+ //add permission to read files so that modules can be loaded, but writing should fail
+ perms.add(new FilePermission("<<ALL FILES>>", "read"));
+
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), perms);
+
+ try {
+ engine.eval("import os\nfp = open('pom.xml', 'w')");
+ Assert.fail("Opening a file for writing should have failed with a security exception.");
+ } catch (ScriptException e) {
+ checkIsCausedByAccessControlException(e);
+ }
+ }
+
+ public void testSourceProvider() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ StringWriter wrt = new StringWriter();
+
+ engine.getContext().setWriter(wrt);
+
+ initializer.installScriptSourceProvider(engine, new SourceProvider());
+
+ engine
+ .eval("import sys\nsys.path.append('__rhq__:test-unsupported/')\nsys.path.append('__rhq__:test/')\nimport test_module");
+
+ Assert.assertEquals(wrt.toString(), EXPECTED_OUTPUT + "\n", "Unexpected output from a custom module.");
+ }
+
+ private void checkIsCausedByAccessControlException(Throwable e) {
+ Throwable ex = e;
+ while (ex != null) {
+ if (ex instanceof AccessControlException) {
+ return;
+ }
+
+ ex = ex.getCause();
+ }
+
+ Assert.fail("Expected an AccessControlException but the exception doesn't seem to be caused by it.", e);
+ }
+
+ private static Map<String, Set<Method>> getMethodsByName(Class<?> cls) {
+ Map<String, Set<Method>> ret = new HashMap<String, Set<Method>>();
+
+ for (Method m : cls.getDeclaredMethods()) {
+ Set<Method> methods = ret.get(m.getName());
+ if (methods == null) {
+ methods = new HashSet<Method>();
+ ret.put(m.getName(), methods);
+ }
+
+ methods.add(m);
+ }
+
+ return ret;
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/test/resources/allow-all.policy b/modules/enterprise/scripting/python/src/test/resources/allow-all.policy
new file mode 100644
index 0000000..cb9dbed
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/test/resources/allow-all.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
commit 1e102dc66868f2a07d572e56a560e8c60ac93933
Merge: cdd639d fce34e6
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 15:16:16 2012 +0200
Merge remote-tracking branch 'origin/master' into lkrejci/modular-scripting
commit cdd639d3a1324412aded642a31d8eb4d88c73115
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:54:35 2012 +0200
Bumping Rhino to 1.7R4.
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index 11f3784..6009b1b 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -21,7 +21,7 @@
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
- <version>1.7R3</version>
+ <version>1.7R4</version>
</dependency>
</dependencies>
commit 5d58a155a00362b6a5eb4e44ab69471ef577258b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:54:23 2012 +0200
Do not compile the scripts to new classes to save the permgen space.
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
index 0456e94..e8069a6 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
@@ -193,12 +193,19 @@ public class RhinoScriptEngine extends AbstractScriptEngine
}
}
+ protected Context makeContext() {
+ Context cx = super.makeContext();
+ cx.setOptimizationLevel(-1);
+ return cx;
+ }
+
private Object superDoTopCall(final Callable callable,
final Context cx, final Scriptable scope,
final Scriptable thisObj, final Object[] args) {
return super.doTopCall(callable, cx, scope, thisObj, args);
- }
+ }
+
});
}
commit 4344f843a8bdd16454def73be245ada673d259b9
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:53:34 2012 +0200
Setting the require function to NOT be sandboxed (which doesn't make much
difference) and make the require function use the correct scope so that it
can resolve all the variables defined by the script engine.
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
index a2dca22..0456e94 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
@@ -266,7 +266,8 @@ public class RhinoScriptEngine extends AbstractScriptEngine
requireBuilder = new RequireBuilder();
setModuleSourceProvider(moduleSourceProvider);
-
+ requireBuilder.setSandboxed(false);
+
new LazilyLoadedCtor(topLevel, "JSAdapter",
"org.rhq.scripting.javascript.engine.JSAdapter",
false);
@@ -484,7 +485,7 @@ public class RhinoScriptEngine extends AbstractScriptEngine
Context cx = enterContext();
try {
cx.evaluateString(newScope, printSource, "print", 1, null);
- requireBuilder.createRequire(cx, topLevel).install(newScope);
+ requireBuilder.createRequire(cx, newScope).install(newScope);
} finally {
Context.exit();
}
commit 35bad6163b91cc55d2bee67be67f10a6aabdd4bb
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:51:40 2012 +0200
Adding a script source provider able to handle the "file://" scheme.
This is for the scripts to be able to access the file system using absolute
paths.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java
new file mode 100644
index 0000000..531222b
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java
@@ -0,0 +1,72 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.script;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.Reader;
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public class FileSystemScriptSourceProvider implements ScriptSourceProvider {
+
+ private static final Log LOG = LogFactory.getLog(FileSystemScriptSourceProvider.class);
+ private static final String SCHEME = "file";
+
+ @Override
+ public Reader getScriptSource(URI location) {
+ String scheme = location.getScheme();
+
+ //return early if we can't handle this URI
+ if (scheme != null && !SCHEME.equals(scheme)) {
+ return null;
+ }
+
+ String path = location.getSchemeSpecificPart();
+
+ if (scheme != null) {
+ // leave out the leading '//';
+ path = path.substring(2);
+ }
+
+ File f = new File(path);
+
+ try {
+ if (f.exists() && f.isFile() && f.canRead()) {
+ return new FileReader(f);
+ }
+ } catch (FileNotFoundException e) {
+ LOG.debug("File '" + f.getAbsolutePath() + "' seems to have disappeared while we were trying to open it.",
+ e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
index 9722a1c..a935c58 100644
--- a/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
+++ b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -1 +1,2 @@
org.rhq.enterprise.client.script.SamplesScriptSourceProvider
+org.rhq.enterprise.client.script.FileSystemScriptSourceProvider
commit 5f651047a7d1a147de9ec3f3cabcdba195b42483
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:50:30 2012 +0200
Fixed the method resolution in the abstract rhq facade proxy.
The old impl assumed the simplified class implements the original interface
which is no longer true. I therefore added some auxiliary annotations on
the simplified class and methods to aid in recovering the original method
even though the simplified interface no longer implements the original
interface.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
index bd37131..5a6d1c5 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
@@ -22,7 +22,6 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.rhq.bindings.util.InterfaceSimplifier;
-import org.rhq.core.domain.auth.Subject;
/**
* An abstract {@link InvocationHandler} to help the script users create proxies to actually call the
@@ -50,38 +49,21 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
- Class<?> originalClass;
- if (interfaces != null && interfaces.length > 0) {
- originalClass = interfaces[0];
- } else {
- originalClass = method.getDeclaringClass();
- }
-
- try {
- // See if this method really exists or if its a simplified set of parameters
- originalClass.getMethod(method.getName(), method.getParameterTypes());
- } catch (NoSuchMethodException e) {
- // If this was not in the original interface it must've been added in the Simplifier... add back the subject argument
- Class<?>[] origParams = method.getParameterTypes();
- Class<?>[] params = new Class<?>[origParams.length + 1];
- params[0] = Subject.class;
- System.arraycopy(method.getParameterTypes(), 0, params, 1, origParams.length);
-
- try {
- method = originalClass.getMethod(method.getName(), params);
- } catch (NoSuchMethodException e2) {
- throw new IllegalArgumentException("Method " + method + " doesn't seem to be present on the interface "
- + originalClass + " neither in its original or simplified form.");
- }
+ Method origMethod = InterfaceSimplifier.getOriginalMethod(method);
+
+ if (origMethod != null) {
+ if (InterfaceSimplifier.isSimplified(method)) {
+ // If this was not in the original interface it must've been added in the Simplifier... add back the subject argument
+ int numArgs = (null == args) ? 0 : args.length;
+ Object[] newArgs = new Object[numArgs + 1];
+ if (numArgs > 0) {
+ System.arraycopy(args, 0, newArgs, 1, numArgs);
+ }
+ newArgs[0] = getRhqFacade().getSubject();
- int numArgs = (null == args) ? 0 : args.length;
- Object[] newArgs = new Object[numArgs + 1];
- if (numArgs > 0) {
- System.arraycopy(args, 0, newArgs, 1, numArgs);
+ args = newArgs;
}
- newArgs[0] = getRhqFacade().getSubject();
- args = newArgs;
+ method = origMethod;
}
return doInvoke(proxy, method, args);
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 dc07d4e..8ce7bc6 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
@@ -18,6 +18,7 @@
*/
package org.rhq.bindings.util;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -69,6 +70,77 @@ public class InterfaceSimplifier {
}
+ /**
+ * Returns the method on the original interface that the simplified interface with given method was generated from
+ * using the {@link #simplify(Class)} method (i.e. this method is kind of reverse to the {@link #simplify(Class)}
+ * method).
+ * <p>
+ * The returned method may or may not have different signature from the supplied method - that depends on whether
+ * the {@link #simplify(Class)} simplified the method or not.
+ *
+ * @param method the potentially simplified method
+ * @return null if the method doesn't come from a simplified class, otherwise a method on the original interface
+ * that the supplied method was generated from.
+ */
+ public static Method getOriginalMethod(Method method) {
+ SimplifiedClass simplifiedClass = method.getDeclaringClass().getAnnotation(SimplifiedClass.class);
+ if (simplifiedClass == null) {
+ return null;
+ } else {
+ SimplifiedMethod simplifiedMethod = method.getAnnotation(SimplifiedMethod.class);
+ Class<?> origClass = simplifiedClass.originalClass();
+
+ if (simplifiedMethod == null) {
+ try {
+ return origClass.getMethod(method.getName(), method.getParameterTypes());
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Inconsisten interface simplification. The non-simplified method "
+ + method + " should have had a counterpart with the exact signature on the interface "
+ + origClass + " but it could not be found.", e);
+ }
+ } else {
+
+ Class<?>[] paramTypes = new Class<?>[method.getParameterTypes().length + 1];
+ paramTypes[0] = Subject.class;
+ System.arraycopy(method.getParameterTypes(), 0, paramTypes, 1, paramTypes.length - 1);
+
+ try {
+ return origClass.getMethod(method.getName(), paramTypes);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Inconsistent interface simplification. The simplified method "
+ + method + " should have had a counterpart on the interface " + origClass
+ + " but it couldn't be found.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines whether given class is simplified or not. This method will return true for any class returned from
+ * {@link #simplify(Class)}.
+ *
+ * @param cls the class
+ * @return true if the class object was created by the {@link #simplify(Class)} method, false otherwise.
+ */
+ public static boolean isSimplified(Class<?> cls) {
+ return cls.getAnnotation(SimplifiedClass.class) != null;
+ }
+
+ /**
+ * Determines whether the method (declared on the simplified interface, i.e.
+ * <code>isSimplified(method.getDeclaringClass()</code> returns true) has been "tampered with" by the simplifier or
+ * has been left intact.
+ * <p>
+ * If you want to get the original method that the supplied method corresponds to, use
+ * the {@link #getOriginalMethod(Method)} method.
+ *
+ * @param method the potentially simplified method present on a simplified class
+ * @return true if the method's signature has been modified by the simplifier, false otherwise.
+ */
+ public static boolean isSimplified(Method method) {
+ return method.getAnnotation(SimplifiedMethod.class) != null;
+ }
+
public static Class<?> simplify(Class<?> intf) {
try {
ClassPool classPool = ClassPool.getDefault();
@@ -107,9 +179,10 @@ public class InterfaceSimplifier {
AnnotationsAttribute annotations = (AnnotationsAttribute) originalClassFile
.getAttribute(AnnotationsAttribute.visibleTag);
AnnotationsAttribute newAnnotations = copyAnnotations(annotations, constPool);
- if (newAnnotations != null) {
- newClassFile.addAttribute(newAnnotations);
- }
+
+ //add our @Simplified annotation to the new class
+ newAnnotations = addSimplifiedClassAnnotation(originalClass.getName(), newAnnotations, constPool);
+ newClassFile.addAttribute(newAnnotations);
//copy the generic signature of the class
SignatureAttribute signature = (SignatureAttribute) originalClassFile.getAttribute(SignatureAttribute.tag);
@@ -160,6 +233,9 @@ public class InterfaceSimplifier {
annotations = copyAnnotations(annotations, constPool);
if (simplify) {
+ //add the @SimplifiedMethod to the method annotations
+ annotations = addSimplifiedMethodAnnotation(annotations, constPool);
+
if (signature != null) {
//fun, we need to modify the signature, too, because we have left out the parameter
MethodSignature sig = MethodSignature.parse(signature.getSignature());
@@ -475,4 +551,31 @@ public class InterfaceSimplifier {
return bld.toString();
}
}
+
+ private static AnnotationsAttribute addSimplifiedClassAnnotation(String originalClassName,
+ AnnotationsAttribute annotations, ConstPool constPool) {
+
+ if (annotations == null) {
+ annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+ }
+
+ Annotation simplified = new Annotation(SimplifiedClass.class.getName(), constPool);
+ simplified.addMemberValue("originalClass", new ClassMemberValue(originalClassName, constPool));
+
+ annotations.addAnnotation(simplified);
+
+ return annotations;
+ }
+
+ private static AnnotationsAttribute addSimplifiedMethodAnnotation(AnnotationsAttribute annotations,
+ ConstPool constPool) {
+
+ if (annotations == null) {
+ annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+ }
+
+ annotations.addAnnotation(new Annotation(SimplifiedMethod.class.getName(), constPool));
+
+ return annotations;
+ }
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java
new file mode 100644
index 0000000..50edede
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java
@@ -0,0 +1,38 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to keep track of the original class on the simplified classes.
+ * Used by the {@link InterfaceSimplifier}.
+ *
+ * @author Lukas Krejci
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target(ElementType.TYPE)
+public @interface SimplifiedClass {
+ Class<?> originalClass();
+}
\ No newline at end of file
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java
new file mode 100644
index 0000000..290b807
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java
@@ -0,0 +1,41 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method on a simplified class as being simplified.
+ * This is to distinguish the simplified methods on a simplified class
+ * from the non-simplified methods on that class.
+ * <p>
+ * Used by the {@link InterfaceSimplifier}.
+ *
+ * @author Lukas Krejci
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target(ElementType.METHOD)
+public @interface SimplifiedMethod {
+
+}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java
new file mode 100644
index 0000000..d1042df
--- /dev/null
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java
@@ -0,0 +1,185 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.client;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.bindings.util.InterfaceSimplifier;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.server.resource.ResourceManagerRemote;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class AbstractRhqFacadeProxyTest {
+
+ public interface TestInterface {
+ void method();
+ }
+
+ public static class TestFacade implements RhqFacade {
+
+ private Subject subject;
+
+ @Override
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public void setSubject(Subject subject) {
+ this.subject = subject;
+ }
+
+ @Override
+ public Subject login(String user, String password) throws Exception {
+ return subject;
+ }
+
+ @Override
+ public void logout() {
+ subject = null;
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return subject != null;
+ }
+
+ @Override
+ public Map<RhqManager, Object> getScriptingAPI() {
+ EnumMap<RhqManager, Object> ret = new EnumMap<RhqManager, Object>(RhqManager.class);
+
+ for (RhqManager m : RhqManager.values()) {
+ Class<?> iface = InterfaceSimplifier.simplify(m.remote());
+ Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[] { iface },
+ new TestProxy(this, m));
+ ret.put(m, proxy);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public <T> T getProxy(Class<T> remoteApiIface) {
+ RhqManager m = RhqManager.forInterface(remoteApiIface);
+ if (m == null) {
+ throw new IllegalArgumentException();
+ }
+
+ return remoteApiIface.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(),
+ new Class<?>[] { remoteApiIface }, new TestProxy(this, m)));
+ }
+
+ }
+
+ public static class InvocationRecord {
+ public Method method;
+ public Object[] args;
+ }
+
+ public static class TestProxy extends AbstractRhqFacadeProxy<TestFacade> {
+
+ private static List<InvocationRecord> pastInvocations = new ArrayList<InvocationRecord>();
+
+ /**
+ * @param facade
+ * @param manager
+ */
+ public TestProxy(TestFacade facade, RhqManager manager) {
+ super(facade, manager);
+ }
+
+ @Override
+ protected Object doInvoke(Object proxy, Method originalMethod, Object[] args) throws Throwable {
+ InvocationRecord inv = new InvocationRecord();
+ inv.method = originalMethod;
+ inv.args = args;
+
+ pastInvocations.add(inv);
+
+ return null;
+ }
+
+ public static List<InvocationRecord> getPastInvocations() {
+ return pastInvocations;
+ }
+
+ public static void clearPastInvocations() {
+ pastInvocations.clear();
+ }
+ }
+
+ public void testInvocationOfSimplifiedMethods() throws Exception {
+ TestProxy.clearPastInvocations();
+
+ TestFacade facade = new TestFacade();
+ Subject subject = new Subject();
+
+ facade.setSubject(subject);
+
+ Object resourceManager = facade.getScriptingAPI().get(RhqManager.ResourceManager);
+
+ Method getResource = resourceManager.getClass().getMethod("getResource", int.class);
+
+ getResource.invoke(resourceManager, 1);
+
+ Assert.assertEquals(TestProxy.getPastInvocations().size(), 1, "Unexpected number of proxy invocations");
+
+ InvocationRecord inv = TestProxy.getPastInvocations().get(0);
+
+ Assert.assertEquals(inv.method, ResourceManagerRemote.class.getMethod("getResource", Subject.class, int.class),
+ "Unexpected method invoked.");
+
+ Assert.assertEquals(subject, inv.args[0], "Unexpected subject passed to the invocation.");
+ Assert.assertEquals(inv.args[1], new Integer(1), "Unexpected resource id passed to the invocation.");
+ }
+
+ public void testProxyRobustAgainstNonSimplifiedMethods() throws Exception {
+ TestProxy.clearPastInvocations();
+
+ Class<?> iface = InterfaceSimplifier.simplify(TestInterface.class);
+
+ Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { iface },
+ new TestProxy(null, null));
+
+ Method charAt = proxy.getClass().getMethod("method");
+
+ charAt.invoke(proxy);
+
+ Assert.assertEquals(TestProxy.getPastInvocations().size(), 1, "Unexpected number of proxy invocations");
+
+ InvocationRecord inv = TestProxy.getPastInvocations().get(0);
+
+ Assert.assertEquals(inv.method, TestInterface.class.getMethod("method"),
+ "Unexpected method invoked.");
+
+ Assert.assertNull(inv.args, "Unexpected number of arguments passed to the invocation.");
+ }
+}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
index 4312153..8c581ce 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
@@ -243,7 +243,9 @@ public class InterfaceSimplifierTest {
Class<?> iface = InterfaceSimplifier.simplify(Annotations.class);
Annotation[] annotations = iface.getAnnotations();
- Assert.assertEquals(annotations.length, 1, "UNexpected number of annotations on the 'Annotations' class.");
+ //we add the @SimplifiedClass annotation
+ Assert.assertEquals(annotations.length, Annotations.class.getAnnotations().length + 1,
+ "Unexpected number of annotations on the 'Annotations' class.");
Annotation annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the class 'Annotations");
@@ -284,8 +286,9 @@ public class InterfaceSimplifierTest {
method = iface.getMethod("methodSimplified", int.class);
annotations = method.getAnnotations();
+ //we add the @SimplifiedMethod on the method
Assert
- .assertEquals(annotations.length, 1, "Unexpected number of annotations on the 'methodSimplified' method.");
+ .assertEquals(annotations.length, 2, "Unexpected number of annotations on the 'methodSimplified' method.");
annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
@@ -314,4 +317,35 @@ public class InterfaceSimplifierTest {
.assertEquals(((MyAnnotation) annotation).parameter(), "d",
"Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
}
+
+ public void testOriginalMethodRetrieval() throws Exception {
+ Class<?> iface = InterfaceSimplifier.simplify(Generics.class);
+
+ Method simplifiedMethod = iface.getMethod("typeParametersSimplified", Type.class, int.class);
+ Method origMethod = InterfaceSimplifier.getOriginalMethod(simplifiedMethod);
+
+ Assert.assertTrue(InterfaceSimplifier.isSimplified(iface),
+ "Unable to determine that the simplified interface was simplified.");
+ Assert.assertTrue(InterfaceSimplifier.isSimplified(simplifiedMethod));
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(String.class), "String class is NOT simplified.");
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(Object.class.getMethod("toString")),
+ "Object.toString() is NOT simplified.");
+
+ Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
+ "Unexpected declaring class of the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes().length, simplifiedMethod.getParameterTypes().length + 1,
+ "Unexpected number of params on the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes()[0], Subject.class,
+ "Unexpected first param of the original method.");
+
+ Method nonSimplifiedMethod = iface.getMethod("typeParameters", Type.class, int.class);
+ origMethod = InterfaceSimplifier.getOriginalMethod(nonSimplifiedMethod);
+
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(nonSimplifiedMethod));
+
+ Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
+ "Unexpected declaring class of the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes(), nonSimplifiedMethod.getParameterTypes(),
+ "Unexpected params on the original method.");
+ }
}
commit 34c1b99a067c54c875b83a646c8cf309b32d0d34
Merge: 9287b7e e704b68
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 9 14:53:41 2012 +0200
Merge remote-tracking branch 'origin/master' into lkrejci/modular-scripting
commit 9287b7e475adc4a182270624913b7cc926ff0664
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 9 14:50:00 2012 +0200
Improvements to javascript code completion - we now are able to complete
the properties/functions of native javascript objects. This makes
code completion on required modules return the defined functions.
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
index f90969a..d4d68bc 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
@@ -20,6 +20,7 @@
package org.rhq.scripting.javascript;
import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
@@ -28,6 +29,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -40,6 +42,8 @@ import javax.script.ScriptContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.ScriptableObject;
import org.rhq.scripting.CodeCompletion;
import org.rhq.scripting.MetadataProvider;
@@ -231,8 +235,10 @@ public class JavascriptCompletor implements CodeCompletion {
}
if (matches.size() == 1 && matches.containsKey(call[0])) {
- if (matches.get(call[0]).get(0) instanceof Method) {
- list.add("(" + (((Method) matches.get(call[0]).get(0)).getParameterTypes().length == 0 ? ")" : ""));
+ Object obj = matches.get(call[0]).get(0);
+ if (isMethod(obj)) {
+ boolean close = obj instanceof Method && ((Method) obj).getParameterTypes().length == 0;
+ list.add("(" + (close ? ")" : ""));
}
return call[0].length() + 1;
}
@@ -446,53 +452,80 @@ public class JavascriptCompletor implements CodeCompletion {
}
try {
- if (baseObjectClass.equals(Void.TYPE))
+ if (baseObjectClass.equals(Void.TYPE)) {
return found;
-
- BeanInfo info = null;
- if (baseObjectClass.isInterface() || baseObjectClass.equals(Object.class)) {
- info = Introspector.getBeanInfo(baseObjectClass);
+ } else if (ScriptableObject.class.isAssignableFrom(baseObjectClass)) {
+ return findJavascriptContextMatches((ScriptableObject) baseObject, start);
} else {
- info = Introspector.getBeanInfo(baseObjectClass, Object.class);
+ return findJavaBeanContextMatches(baseObject, baseObjectClass, start);
}
- Set<Method> methodsCovered = new HashSet<Method>();
+ } catch (Exception e) {
+ LOG.info("Failure during code completion", e);
+ e.printStackTrace(output);
+ }
- PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
- for (PropertyDescriptor desc : descriptors) {
- if (desc.getName().startsWith(start) && (!IGNORED_METHODS.contains(desc.getName()))) {
+ return found;
+ }
- List<Object> list = found.get(desc.getName());
- if (list == null) {
- list = new ArrayList<Object>();
- found.put(desc.getName(), list);
- }
- list.add(desc);
+ private Map<String, List<Object>> findJavascriptContextMatches(ScriptableObject object, String start) {
+ HashMap<String, List<Object>> ret = new HashMap<String, List<Object>>();
+ for (Object o : object.getIds()) {
+ String key = o.toString();
- methodsCovered.add(desc.getReadMethod());
- methodsCovered.add(desc.getWriteMethod());
- }
+ if (start == null || start.isEmpty() || key.startsWith(start)) {
+ Object target = object.get(key);
+ ret.put(key, new ArrayList<Object>(Arrays.asList(target)));
}
+ }
- MethodDescriptor[] methods = info.getMethodDescriptors();
- for (MethodDescriptor desc : methods) {
- if (desc.getName().startsWith(start) && !methodsCovered.contains(desc.getMethod())
- && !desc.getName().startsWith("_d") && !IGNORED_METHODS.contains(desc.getName())) {
+ return ret;
+ }
- Method m = desc.getMethod();
+ private Map<String, List<Object>> findJavaBeanContextMatches(Object baseObject, Class<?> baseObjectClass,
+ String start) throws IntrospectionException {
- List<Object> list = found.get(desc.getName());
- if (list == null) {
- list = new ArrayList<Object>();
- found.put(desc.getName(), list);
- }
- list.add(m);
+ Map<String, List<Object>> found = new HashMap<String, List<Object>>();
+
+ BeanInfo info = null;
+ if (baseObjectClass.isInterface() || baseObjectClass.equals(Object.class)) {
+ info = Introspector.getBeanInfo(baseObjectClass);
+ } else {
+ info = Introspector.getBeanInfo(baseObjectClass, Object.class);
+ }
+
+ Set<Method> methodsCovered = new HashSet<Method>();
+
+ PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
+ for (PropertyDescriptor desc : descriptors) {
+ if (desc.getName().startsWith(start) && (!IGNORED_METHODS.contains(desc.getName()))) {
+
+ List<Object> list = found.get(desc.getName());
+ if (list == null) {
+ list = new ArrayList<Object>();
+ found.put(desc.getName(), list);
}
+ list.add(desc);
+
+ methodsCovered.add(desc.getReadMethod());
+ methodsCovered.add(desc.getWriteMethod());
}
+ }
- } catch (Exception e) {
- LOG.info("Failure during code completion", e);
- e.printStackTrace(output);
+ MethodDescriptor[] methods = info.getMethodDescriptors();
+ for (MethodDescriptor desc : methods) {
+ if (desc.getName().startsWith(start) && !methodsCovered.contains(desc.getMethod())
+ && !desc.getName().startsWith("_d") && !IGNORED_METHODS.contains(desc.getName())) {
+
+ Method m = desc.getMethod();
+
+ List<Object> list = found.get(desc.getName());
+ if (list == null) {
+ list = new ArrayList<Object>();
+ found.put(desc.getName(), list);
+ }
+ list.add(m);
+ }
}
return found;
@@ -546,4 +579,8 @@ public class JavascriptCompletor implements CodeCompletion {
m.setAccessible(access);
}
}
+
+ private static boolean isMethod(Object object) {
+ return object != null && object instanceof Method || object instanceof Function;
+ }
}
commit eb1ade1290dedc3c83ef12f8cdf6531df5433278
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 9 14:42:46 2012 +0200
Make the summary print all the "summary" properties of the object, including the inherited ones from the super classes.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SummaryFilter.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SummaryFilter.java
index 871484f..7015068 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SummaryFilter.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SummaryFilter.java
@@ -22,13 +22,11 @@
*/
package org.rhq.bindings.util;
-import org.rhq.core.domain.util.Summary;
-
import java.beans.BeanInfo;
+import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
-import java.beans.IndexedPropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -36,13 +34,15 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
+import org.rhq.core.domain.util.Summary;
+
public class SummaryFilter {
public PropertyDescriptor[] getPropertyDescriptors(Object object, boolean exportMode) throws IntrospectionException {
- BeanInfo info = Introspector.getBeanInfo(object.getClass(), object.getClass().getSuperclass());
+ BeanInfo info = Introspector.getBeanInfo(object.getClass(), Object.class);
final Map<PropertyDescriptor, Integer> indexes = new HashMap<PropertyDescriptor, Integer>();
commit 2e121e658b517b35ccb8752ff4a8c0489d14d90b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jul 4 18:44:51 2012 +0200
Use the correct language name for the javascript language.
diff --git a/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/client/security/test/JndiAccessTest.java b/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/client/security/test/JndiAccessTest.java
index f96b081..302fa10 100644
--- a/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/client/security/test/JndiAccessTest.java
+++ b/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/client/security/test/JndiAccessTest.java
@@ -196,7 +196,8 @@ public class JndiAccessTest extends AbstractEJB3Test {
PermissionCollection perms = new StandardScriptPermissions();
- return ScriptEngineFactory.getSecuredScriptEngine("JavaScript", new PackageFinder(Collections.<File>emptyList()), bindings, perms);
+ return ScriptEngineFactory.getSecuredScriptEngine("javascript",
+ new PackageFinder(Collections.<File> emptyList()), bindings, perms);
}
private static void checkIsDesiredSecurityException(ScriptException e) {
diff --git a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
index 8ca0b39..98246a0 100644
--- a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
+++ b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
@@ -71,7 +71,7 @@ import org.rhq.scripting.ScriptSourceProviderFactory;
*/
public class CliSender extends AlertSender<CliComponent> {
- private static final String ENGINE_NAME = "JavaScript";
+ private static final String ENGINE_NAME = "javascript";
private static final int MAX_RESULT_SIZE = 4000;
commit f94c0df7851ee1ae64cb0995abf75ed9ec93cf32
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jul 4 18:44:20 2012 +0200
LocalClient now generates proxies inside a privileged block to correctly
work in the restricted environment of the RHQ server where we disallow
the scripts to do "unattended" JNDI lookups against the RHQ server.
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 7f8efad..5cbed7e 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
@@ -97,19 +97,24 @@ public class LocalClient implements RhqFacade {
}
@Override
- public <T> T getProxy(Class<T> remoteApiIface) {
- RhqManager manager = RhqManager.forInterface(remoteApiIface);
+ public <T> T getProxy(final Class<T> remoteApiIface) {
+ final RhqManager manager = RhqManager.forInterface(remoteApiIface);
if (manager == null) {
throw new IllegalArgumentException("Unknown remote interface " + remoteApiIface);
}
- Object localSLSB = getLocalSLSB(manager);
+ return AccessController.doPrivileged(new PrivilegedAction<T>() {
+ @Override
+ public T run() {
+ Object localSLSB = getLocalSLSB(manager);
- Object proxy = Proxy.newProxyInstance(remoteApiIface.getClassLoader(), new Class<?>[] { remoteApiIface },
- new LocalClientProxy(localSLSB, this, manager));
+ Object proxy = Proxy.newProxyInstance(remoteApiIface.getClassLoader(),
+ new Class<?>[] { remoteApiIface }, new LocalClientProxy(localSLSB, LocalClient.this, manager));
- return remoteApiIface.cast(proxy);
+ return remoteApiIface.cast(proxy);
+ }
+ });
}
private Object getScriptingProxy(Object slsb, RhqManager manager) {
commit b63714a7093588f7688c196e649e5003b7cdf701
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jul 4 18:41:51 2012 +0200
Fixed a problem with copying annotations with no attributes.
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 dc29bc9..dc07d4e 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
@@ -229,15 +229,17 @@ public class InterfaceSimplifier {
Annotation ret = new Annotation(annotation.getTypeName(), constPool);
- for (Object m : annotation.getMemberNames()) {
- final String memberName = (String) m;
+ if (annotation.getMemberNames() != null) {
+ for (Object m : annotation.getMemberNames()) {
+ final String memberName = (String) m;
- MemberValue origValue = annotation.getMemberValue(memberName);
- final MemberValue[] newValue = new MemberValue[1];
+ MemberValue origValue = annotation.getMemberValue(memberName);
+ final MemberValue[] newValue = new MemberValue[1];
- origValue.accept(new ArrayIndexAssigningVisitor(newValue, 0, constPool));
+ origValue.accept(new ArrayIndexAssigningVisitor(newValue, 0, constPool));
- ret.addMemberValue(memberName, newValue[0]);
+ ret.addMemberValue(memberName, newValue[0]);
+ }
}
return ret;
commit fd22cb9644d17d43d876945f34689492bcf84c9b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jul 4 18:41:20 2012 +0200
Fixing the build. The RHQ's javascript language module now bundles
Rhino in the jar itself to ease the deployment, added the javascript module
as a test dep to a number of modules that have tests utilizing scripting.
diff --git a/modules/enterprise/binding/pom.xml b/modules/enterprise/binding/pom.xml
index 945b4bc..7d37053 100644
--- a/modules/enterprise/binding/pom.xml
+++ b/modules/enterprise/binding/pom.xml
@@ -209,6 +209,20 @@
<artifactId>javassist</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-testng</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
diff --git a/modules/enterprise/remoting/cli/pom.xml b/modules/enterprise/remoting/cli/pom.xml
index ae77f1e..3924ff0 100644
--- a/modules/enterprise/remoting/cli/pom.xml
+++ b/modules/enterprise/remoting/cli/pom.xml
@@ -163,19 +163,6 @@
</plugin>
<plugin>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <includes>
- <include>org/rhq/**</include>
- <include>client-messages*</include>
- </includes>
- <archive>
- <index>true</index>
- </archive>
- </configuration>
- </plugin>
-
- <plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
diff --git a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
index 202bef7..808bd82 100644
--- a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
+++ b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
@@ -70,7 +70,6 @@
<copy file="${settings.localRepository}/org/rhq/rhq-scripting-api/${project.version}/rhq-scripting-api-${project.version}.jar" tofile="${lib.home}/rhq-scripting-api-${project.version}.jar" verbose="true" />
<copy file="${settings.localRepository}/org/rhq/rhq-scripting-javascript/${project.version}/rhq-scripting-javascript-${project.version}.jar" tofile="${lib.home}/rhq-scripting-javascript-${project.version}.jar" verbose="true" />
- <copy file="${settings.localRepository}/org/mozilla/rhino/${rhino.version}/rhino-${rhino.version}.jar" tofile="${lib.home}/rhino-${rhino.version}.jar" verbose="true" />
</target>
<target name="prepare-samples-dir">
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index e813d3b..11f3784 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -1,205 +1,230 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>rhq-scripting-parent</artifactId>
- <groupId>org.rhq</groupId>
- <version>4.5.0-SNAPSHOT</version>
- </parent>
- <artifactId>rhq-scripting-javascript</artifactId>
- <version>4.5.0-SNAPSHOT</version>
- <name>RHQ Javascript support</name>
- <description>Provides RHQ scripting in Javascript using Rhino</description>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>rhq-scripting-parent</artifactId>
+ <groupId>org.rhq</groupId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rhq-scripting-javascript</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ <name>RHQ Javascript support</name>
+ <description>Provides RHQ scripting in Javascript using Rhino</description>
- <dependencies>
- <dependency>
- <groupId>${project.groupId}</groupId>
- <artifactId>rhq-scripting-api</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.mozilla</groupId>
- <artifactId>rhino</artifactId>
- <version>1.7R3</version>
- </dependency>
-
-<!-- we no longer depend on Phobos because we now bundle the Phobos script engine with
- our own modifications.
- <dependency>
- <groupId>com.sun.phobos</groupId>
- <artifactId>phobos-js</artifactId>
- <version>0.6.2</version>
- <exclusions>
- <exclusion>
- <groupId>com.sun.phobos</groupId>
- <artifactId>phobos-rhino</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- -->
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
- <argLine>-Djava.security.manager -Djava.security.policy==${project.build.testOutputDirectory}/allow-all.policy</argLine>
- <!-- This is important, because some of the tests try to exit the JVM. -->
- <failIfNoTests>true</failIfNoTests>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-scripting-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
- <profiles>
+ <dependency>
+ <groupId>org.mozilla</groupId>
+ <artifactId>rhino</artifactId>
+ <version>1.7R3</version>
+ </dependency>
- <profile>
- <id>dev</id>
+ </dependencies>
- <properties>
- <rhq.rootDir>../../..</rhq.rootDir>
- <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
- <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/lib</rhq.deploymentDir>
- </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>jarjar-maven-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jarjar</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>org.mozilla:rhino</include>
+ </includes>
+ <rules>
+ <keep>
+ <pattern>*.**</pattern>
+ </keep>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
- <build>
- <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ <argLine>-Djava.security.manager
+ -Djava.security.policy==${project.build.testOutputDirectory}/allow-all.policy</argLine>
+ <!-- This is important, because some of the tests try to exit
+ the JVM. -->
+ <failIfNoTests>true</failIfNoTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
- <plugin>
- <artifactId>maven-antrun-plugin</artifactId>
- <version>1.1</version>
- <executions>
+ <profiles>
- <execution>
- <id>deploy</id>
- <phase>compile</phase>
- <configuration>
- <tasks>
- <mkdir dir="${rhq.deploymentDir}" />
- <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
- <echo>*** Updating
- ${deployment.file}...</echo>
- <jar destfile="${deployment.file}" basedir="${project.build.outputDirectory}" />
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
+ <profile>
+ <id>dev</id>
- <execution>
- <id>undeploy</id>
- <phase>clean</phase>
- <configuration>
- <tasks>
- <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
- <echo>*** Deleting
- ${deployment.file}...</echo>
- <delete file="${deployment.file}" />
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
+ <properties>
+ <rhq.rootDir>../../..</rhq.rootDir>
+ <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
+ <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/lib</rhq.deploymentDir>
+ </properties>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
+ <build>
+ <plugins>
- <profile>
- <id>cobertura-plugins</id>
- <activation>
- <activeByDefault>false</activeByDefault>
- </activation>
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-antrun-plugin</artifactId>
- <dependencies>
- <dependency>
- <groupId>net.sourceforge.cobertura</groupId>
- <artifactId>cobertura</artifactId>
- <version>1.9.4.1</version>
- </dependency>
- </dependencies>
- <executions>
- <execution>
- <id>cobertura-instrument</id>
- <phase>pre-integration-test</phase>
- <configuration>
- <tasks>
- <!-- prepare directory structure
- for cobertura -->
- <mkdir dir="target/cobertura" />
- <mkdir dir="target/cobertura/backup" />
- <!-- backup all classes so that we
- can instrument the original classes -->
- <copy toDir="target/cobertura/backup" verbose="true" overwrite="true">
- <fileset dir="target/classes">
- <include name="**/*.class" />
- </fileset>
- </copy>
- <!-- create a properties file and
- save there location of cobertura data file -->
- <touch file="target/classes/cobertura.properties" />
- <echo file="target/classes/cobertura.properties">net.sourceforge.cobertura.datafile=${project.build.directory}/cobertura/cobertura.ser</echo>
- <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
- <!-- instrument all classes in target/classes
- directory -->
- <cobertura-instrument datafile="${project.build.directory}/cobertura/cobertura.ser" todir="${project.build.directory}/classes">
- <fileset dir="${project.build.directory}/classes">
- <include name="**/*.class" />
- </fileset>
- </cobertura-instrument>
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- <execution>
- <id>cobertura-report</id>
- <phase>post-integration-test</phase>
- <configuration>
- <tasks>
- <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
- <!-- prepare directory structure
- for cobertura -->
- <mkdir dir="target/cobertura" />
- <mkdir dir="target/site/cobertura" />
- <!-- restore classes from backup
- folder to classes folder -->
- <copy toDir="target/classes" verbose="true" overwrite="true">
- <fileset dir="target/cobertura/backup">
- <include name="**/*.class" />
- </fileset>
- </copy>
- <!-- delete backup folder -->
- <delete dir="target/cobertura/backup" />
- <!-- create a code coverage report -->
- <cobertura-report format="html" datafile="${project.build.directory}/cobertura/cobertura.ser" destdir="${project.build.directory}/site/cobertura">
- <fileset dir="${basedir}/src/main/java">
- <include name="**/*.java" />
- </fileset>
- </cobertura-report>
- <!-- delete cobertura.properties
- file -->
- <delete file="target/classes/cobertura.properties" />
- </tasks>
- </configuration>
- <goals>
- <goal>run</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+
+ <execution>
+ <id>deploy</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+ <mkdir dir="${rhq.deploymentDir}" />
+ <property name="deployment.file"
+ location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Updating
+ ${deployment.file}...</echo>
+ <jar destfile="${deployment.file}"
+ basedir="${project.build.outputDirectory}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <tasks>
+ <property name="deployment.file"
+ location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Deleting
+ ${deployment.file}...</echo>
+ <delete file="${deployment.file}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>cobertura-plugins</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.cobertura</groupId>
+ <artifactId>cobertura</artifactId>
+ <version>1.9.4.1</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>cobertura-instrument</id>
+ <phase>pre-integration-test</phase>
+ <configuration>
+ <tasks>
+ <!-- prepare directory structure for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/cobertura/backup" />
+ <!-- backup all classes so that we can instrument
+ the original classes -->
+ <copy toDir="target/cobertura/backup"
+ verbose="true" overwrite="true">
+ <fileset dir="target/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- create a properties file and save there
+ location of cobertura data file -->
+ <touch
+ file="target/classes/cobertura.properties" />
+ <echo
+ file="target/classes/cobertura.properties">net.sourceforge.cobertura.datafile=${project.build.directory}/cobertura/cobertura.ser</echo>
+ <taskdef classpathref="maven.plugin.classpath"
+ resource="tasks.properties" />
+ <!-- instrument all classes in target/classes
+ directory -->
+ <cobertura-instrument
+ datafile="${project.build.directory}/cobertura/cobertura.ser"
+ todir="${project.build.directory}/classes">
+ <fileset
+ dir="${project.build.directory}/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </cobertura-instrument>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>cobertura-report</id>
+ <phase>post-integration-test</phase>
+ <configuration>
+ <tasks>
+ <taskdef classpathref="maven.plugin.classpath"
+ resource="tasks.properties" />
+ <!-- prepare directory structure for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/site/cobertura" />
+ <!-- restore classes from backup folder to
+ classes folder -->
+ <copy toDir="target/classes" verbose="true"
+ overwrite="true">
+ <fileset dir="target/cobertura/backup">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- delete backup folder -->
+ <delete dir="target/cobertura/backup" />
+ <!-- create a code coverage report -->
+ <cobertura-report format="html"
+ datafile="${project.build.directory}/cobertura/cobertura.ser"
+ destdir="${project.build.directory}/site/cobertura">
+ <fileset dir="${basedir}/src/main/java">
+ <include name="**/*.java" />
+ </fileset>
+ </cobertura-report>
+ <!-- delete cobertura.properties file -->
+ <delete
+ file="target/classes/cobertura.properties" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
</project>
diff --git a/modules/enterprise/server/ear/pom.xml b/modules/enterprise/server/ear/pom.xml
index 46ec595..10b613e 100644
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@ -97,6 +97,12 @@
</dependency>
<dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-scripting-javascript</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>org.rhq</groupId>
<artifactId>rhq-server-client-api</artifactId>
<version>${project.version}</version>
diff --git a/modules/enterprise/server/itests/pom.xml b/modules/enterprise/server/itests/pom.xml
index 3f3f03f..43a14c8 100644
--- a/modules/enterprise/server/itests/pom.xml
+++ b/modules/enterprise/server/itests/pom.xml
@@ -99,6 +99,13 @@
<dependency>
<groupId>org.rhq</groupId>
+ <artifactId>rhq-scripting-javascript</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
<artifactId>rhq-server-client-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
commit 4412e1e77d7914f7712a4426cb36f86d769f53c6
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 3 21:27:49 2012 +0200
A couple of changes in this commit:
1) RhqFacade.getManagers() returns a Map<RhqManager, Object> instead of
a Map<String, Object>. Strong typing is good.
2) ScriptSourceProviders hooked up with the ScriptEngineFactory and the
StandardBindings so that they can be installed into the script engine
and use the standard bindings or the rhq facade to do weird and wonderful
things. A couple of basic impls of the ScriptSourceProviders were
sprinkled about the codebase.
3) ScriptSourceProviders are now loadable from META-INF/services so that
it is easy for third parties to add their own impls.
4) A new method added to RepoManagerRemote that enables download of an
arbitrary package version from a repository (this is used by one of the
script source providers to remotely download a script from the server).
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
index fbaf1a7..9887958 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
@@ -40,11 +40,14 @@ import javax.script.ScriptException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.rhq.bindings.client.RhqManager;
+import org.rhq.bindings.util.MultiScriptSourceProvider;
import org.rhq.bindings.util.NoTopLevelIndirection;
import org.rhq.bindings.util.PackageFinder;
import org.rhq.scripting.CodeCompletion;
import org.rhq.scripting.ScriptEngineInitializer;
import org.rhq.scripting.ScriptEngineProvider;
+import org.rhq.scripting.ScriptSourceProvider;
/**
* This is RHQ specific imitation of ScriptEngineFactory.
@@ -117,26 +120,26 @@ public class ScriptEngineFactory {
* @param language the language of the script to instantiate
* @param packageFinder the package finder to find the standard packages in user provided locations
* @param bindings the initial standard bindings or null if none required
- * @return the initialized engine or null if the engine for given language isn't known.
+ *
+ * @return the initialized engine or null if a {@link ScriptEngineProvider} for given language isn't known.
*
* @throws ScriptException on error during initialization of the script environment
* @throws IOException if the package finder fails to find the packages
*/
- public static ScriptEngine getScriptEngine(String language, PackageFinder packageFinder, StandardBindings bindings)
- throws ScriptException, IOException {
-
- return getSecuredScriptEngine(language, packageFinder, bindings, null);
+ public static ScriptEngine getScriptEngine(String language, PackageFinder packageFinder, StandardBindings bindings) throws ScriptException, IOException {
+
+ return getSecuredScriptEngine(language, packageFinder, bindings, null);
}
/**
- * This method is similar to the {@link #getScriptEngine(String, PackageFinder, StandardBindings)} method
+ * This method is similar to the {@link #getScriptEngine(String, PackageFinder, StandardBindings, ScriptSourceProvider...)} method
* but additionally applies a security wrapper on the returned script engine so that the scripts execute
* with the provided java permissions.
*
- * @see #getScriptEngine(String, PackageFinder, StandardBindings)
+ * @see #getScriptEngine(String, PackageFinder, StandardBindings, ScriptSourceProvider...)
*/
- public static ScriptEngine getSecuredScriptEngine(final String language, final PackageFinder packageFinder,
- final StandardBindings bindings, final PermissionCollection permissions) throws ScriptException, IOException {
+ public static ScriptEngine getSecuredScriptEngine(String language, PackageFinder packageFinder,
+ StandardBindings bindings, PermissionCollection permissions) throws ScriptException, IOException {
ScriptEngineInitializer initializer = getInitializer(language);
@@ -144,9 +147,8 @@ public class ScriptEngineFactory {
return null;
}
- //TODO change this so that we support supplying a custom module source provider so that callers
- //have control over the location of the loadable scripts.
- ScriptEngine engine = initializer.instantiate(packageFinder.findPackages("org.rhq.core.domain"), null, permissions);
+ ScriptEngine engine = initializer.instantiate(packageFinder.findPackages("org.rhq.core.domain"),
+ permissions);
if (bindings != null) {
injectStandardBindings(engine, bindings, true);
@@ -162,10 +164,15 @@ public class ScriptEngineFactory {
* @param engine the engine
* @param bindings the bindings
* @param deleteExistingBindings true if the existing bindings should be replaced by the provided ones, false
- * if the provided bindings should be added to the existing ones (possibly overwriting bindings with the same name).
+ * if the provided bindings should be added to the existing ones (possibly overwriting bindings with the same name).
+ * @param scriptSourceProviders the list of script source providers to be used by the script engine to locate the scripts.
+ * Note that the providers become associated with the script engine and its bindings and therefore should
+ * not be used with any other script engine. If the providers implement the
+ * {@link StandardBindings.RhqFacadeChangeListener} interface, they will be automatically hooked up with the
+ * <code>bindings</code> so that the providers will get notified whenever the rhq facade changes.
*/
public static void injectStandardBindings(ScriptEngine engine, StandardBindings bindings,
- boolean deleteExistingBindings) {
+ boolean deleteExistingBindings, ScriptSourceProvider... scriptSourceProviders) {
bindings.preInject(engine);
Bindings engineBindings = deleteExistingBindings ? engine.createBindings() : engine
@@ -175,6 +182,28 @@ public class ScriptEngineFactory {
engineBindings.put(entry.getKey(), entry.getValue());
}
+ if (scriptSourceProviders != null) {
+ //first figure out which initializer to use with this script engine
+ String language = (String) engine.getFactory().getParameter(ScriptEngine.NAME);
+ ScriptEngineProvider engineProvider = KNOWN_PROVIDERS.get(language);
+ if (engineProvider == null) {
+ throw new IllegalArgumentException("The supplied script engine [" + engine + "] is not supported.");
+ }
+
+ ScriptEngineInitializer initializer = engineProvider.getInitializer();
+
+ ScriptSourceProvider provider = null;
+
+ for (ScriptSourceProvider p : scriptSourceProviders) {
+ if (p instanceof StandardBindings.RhqFacadeChangeListener) {
+ bindings.addRhqFacadeChangeListener((StandardBindings.RhqFacadeChangeListener) p);
+ }
+ }
+ provider = new MultiScriptSourceProvider(scriptSourceProviders);
+
+ initializer.installScriptSourceProvider(engine, provider);
+ }
+
engine.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE);
bindings.postInject(engine);
@@ -186,12 +215,12 @@ public class ScriptEngineFactory {
* @param engine the engine
* @param keySet the binding keys to be removed
*/
- public static void removeBindings(ScriptEngine engine, Set<String> keySet) {
+ public static void removeBindings(ScriptEngine engine, Set<RhqManager> keySet) {
Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
- for (String key : keySet) {
- engineBindings.remove(key);
+ for (RhqManager key : keySet) {
+ engineBindings.remove(key.name());
}
engine.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE);
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
index 812b4ae..3e1e137 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
@@ -22,13 +22,15 @@ package org.rhq.bindings;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
-import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import org.rhq.bindings.client.ResourceClientFactory;
import org.rhq.bindings.client.RhqFacade;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.export.Exporter;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.bindings.util.ScriptAssert;
@@ -49,6 +51,14 @@ import org.rhq.core.domain.util.PageControl;
*/
public class StandardBindings extends HashMap<String, Object> {
+ /**
+ * A listener interface for objects that need to be aware of the fact that the
+ * RHQ facade associated with the bidings has changed.
+ */
+ public interface RhqFacadeChangeListener {
+ void rhqFacadeChanged(StandardBindings bindings);
+ }
+
private static final long serialVersionUID = 1L;
public static final String UNLIMITED_PC = "unlimitedPC";
@@ -60,7 +70,9 @@ public class StandardBindings extends HashMap<String, Object> {
public static final String PROXY_FACTORY = "ProxyFactory";
public static final String ASSERT = "Assert";
- private Map<String, Object> managers;
+ private Map<RhqManager, Object> managers;
+ private Set<RhqFacadeChangeListener> facadeChangeListeners;
+ private RhqFacade rhqFacade;
private static class CastingEntry<T> implements Map.Entry<String, T> {
@@ -90,6 +102,7 @@ public class StandardBindings extends HashMap<String, Object> {
}
public StandardBindings(PrintWriter output, RhqFacade rhqFacade) {
+ facadeChangeListeners = new HashSet<RhqFacadeChangeListener>();
PageControl pc = new PageControl();
pc.setPageNumber(-1);
@@ -110,8 +123,8 @@ public class StandardBindings extends HashMap<String, Object> {
public void setFacade(PrintWriter output, RhqFacade rhqFacade) {
// remove any existing managers
if (null != managers) {
- for (String manager : managers.keySet()) {
- remove(manager);
+ for (RhqManager manager : managers.keySet()) {
+ remove(manager.name());
}
managers.clear();
}
@@ -130,7 +143,26 @@ public class StandardBindings extends HashMap<String, Object> {
put(PROXY_FACTORY, null);
}
- putAll(managers);
+ for (Map.Entry<RhqManager, Object> entry : managers.entrySet()) {
+ put(entry.getKey().name(), entry.getValue());
+ }
+
+ this.rhqFacade = rhqFacade;
+
+ notifyFacadeChanged();
+ }
+
+ public RhqFacade getAssociatedRhqFacade() {
+ return rhqFacade;
+ }
+
+ public void addRhqFacadeChangeListener(RhqFacadeChangeListener listener) {
+ this.facadeChangeListeners.add(listener);
+ listener.rhqFacadeChanged(this);
+ }
+
+ public void removeRhqFacadeChangeListere(RhqFacadeChangeListener listener) {
+ this.facadeChangeListeners.remove(listener);
}
public void preInject(ScriptEngine scriptEngine) {
@@ -171,7 +203,7 @@ public class StandardBindings extends HashMap<String, Object> {
return castEntry(PROXY_FACTORY, ResourceClientFactory.class);
}
- public Map<String, Object> getManagers() {
+ public Map<RhqManager, Object> getManagers() {
//XXX ideally this should be a projection into our map
return (null == managers) ? managers = Collections.emptyMap() : managers;
}
@@ -196,4 +228,10 @@ public class StandardBindings extends HashMap<String, Object> {
return new CastingEntry<T>(entry, clazz);
}
+
+ private void notifyFacadeChanged() {
+ for (RhqFacadeChangeListener listener : facadeChangeListeners) {
+ listener.rhqFacadeChanged(this);
+ }
+ }
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
index b975436..2f80f0c 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
@@ -47,7 +47,7 @@ public interface RhqFacade {
*
* @return a map of all available proxied managers keyed by their names.
*/
- Map<String, Object> getScriptingAPI();
+ Map<RhqManager, Object> getScriptingAPI();
/**
* Unlike the {@link #getScriptingAPI()} method that returns objects with modified signatures
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/BaseRhqSchemeScriptSourceProvider.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/BaseRhqSchemeScriptSourceProvider.java
new file mode 100644
index 0000000..d90403d
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/BaseRhqSchemeScriptSourceProvider.java
@@ -0,0 +1,63 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.script;
+
+import java.io.Reader;
+import java.net.URI;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public abstract class BaseRhqSchemeScriptSourceProvider implements ScriptSourceProvider {
+
+ public static final String SCHEME = "rhq";
+
+ @Override
+ public Reader getScriptSource(URI scriptUri) {
+ if (scriptUri == null || !SCHEME.equals(scriptUri.getScheme())) {
+ return null;
+ }
+
+ String path = scriptUri.getSchemeSpecificPart();
+
+ if (!path.startsWith("//")) {
+ return null;
+ }
+
+ return doGetScriptSource(scriptUri);
+ }
+
+ /**
+ * Implement this method to provide the script source.
+ * The base implementation of the {@link #getScriptSource(URI)} method
+ * only checks that the scheme of the URI is "rhq" and that the scheme
+ * specific part starts with "//".
+ *
+ * @param scriptUri
+ * @return the reader of the script or null if the script could not be
+ * found using the URI
+ *
+ * @see #getScriptSource(URI)
+ */
+ protected abstract Reader doGetScriptSource(URI scriptUri);
+}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/RepoScriptSourceProvider.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/RepoScriptSourceProvider.java
new file mode 100644
index 0000000..b316dea
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/script/RepoScriptSourceProvider.java
@@ -0,0 +1,127 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.script;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.bindings.StandardBindings;
+import org.rhq.bindings.client.RhqFacade;
+import org.rhq.core.domain.content.Repo;
+import org.rhq.core.domain.content.composite.PackageAndLatestVersionComposite;
+import org.rhq.core.domain.criteria.PackageCriteria;
+import org.rhq.core.domain.criteria.RepoCriteria;
+import org.rhq.enterprise.server.content.ContentManagerRemote;
+import org.rhq.enterprise.server.content.RepoManagerRemote;
+
+/**
+ * The implementation of script source provider that is able to locate the script files
+ * in the repositories on the RHQ server.
+ * <p>
+ * The URI looks like:<br>
+ * <code>
+ * rhq://repositories/repositoryName/scriptFile
+ * </code>
+ *
+ * @author Lukas Krejci
+ */
+public class RepoScriptSourceProvider extends BaseRhqSchemeScriptSourceProvider implements
+ StandardBindings.RhqFacadeChangeListener {
+
+ private static final Log LOG = LogFactory.getLog(RepoScriptSourceProvider.class);
+
+ private static final String PREFIX = "//repositories/";
+
+ private RhqFacade rhqFacade;
+
+ @Override
+ public void rhqFacadeChanged(StandardBindings bindings) {
+ this.rhqFacade = bindings.getAssociatedRhqFacade();
+ }
+
+ @Override
+ protected Reader doGetScriptSource(URI scriptUri) {
+ if (rhqFacade == null) {
+ return null;
+ }
+
+ String path = scriptUri.getSchemeSpecificPart();
+
+ if (!path.startsWith(PREFIX)) {
+ return null;
+ }
+
+ path = path.substring(PREFIX.length());
+
+ int slashIdx = path.indexOf('/');
+
+ if (slashIdx == -1) {
+ return null;
+ }
+
+ String repoName = path.substring(0, slashIdx);
+ String scriptName = path.substring(slashIdx + 1);
+
+ try {
+ RepoManagerRemote repoManager = rhqFacade.getProxy(RepoManagerRemote.class);
+
+ RepoCriteria repoCrit = new RepoCriteria();
+ repoCrit.addFilterName(repoName);
+ List<Repo> repos = repoManager.findReposByCriteria(rhqFacade.getSubject(), repoCrit);
+
+ if (repos.isEmpty()) {
+ return null;
+ }
+
+ ContentManagerRemote contentManager = rhqFacade.getProxy(ContentManagerRemote.class);
+
+ for (Repo repo : repos) {
+ PackageCriteria pCrit = new PackageCriteria();
+ pCrit.addFilterName(scriptName);
+ pCrit.addFilterRepoId(repo.getId());
+
+ List<PackageAndLatestVersionComposite> pvs = contentManager.findPackagesWithLatestVersion(
+ rhqFacade.getSubject(), pCrit);
+
+ if (!pvs.isEmpty()) {
+ PackageAndLatestVersionComposite pv = pvs.get(0);
+
+ byte[] bytes = repoManager.getPackageVersionBytes(rhqFacade.getSubject(), repo.getId(), pv
+ .getLatestPackageVersion().getId());
+
+ return new InputStreamReader(new ByteArrayInputStream(bytes), Charset.forName("UTF-8"));
+ }
+ }
+ } catch (Exception e) {
+ LOG.debug("Failed to download bytes for the script: " + scriptUri.toString(), e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/MultiScriptSourceProvider.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/MultiScriptSourceProvider.java
new file mode 100644
index 0000000..072e70f
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/MultiScriptSourceProvider.java
@@ -0,0 +1,66 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.io.Reader;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * An implementation of the script source provider that uses a collection of another
+ * providers to locate the scripts.
+ * <p>
+ * The providers are tried in the order they were supplied to the constructor of this class
+ * and the first script source provider that manages to provide a non-null reader for
+ * given URI "wins".
+ *
+ * @author Lukas Krejci
+ */
+public class MultiScriptSourceProvider implements ScriptSourceProvider {
+
+ private Collection<ScriptSourceProvider> providers;
+
+ public MultiScriptSourceProvider(Collection<? extends ScriptSourceProvider> providers) {
+ this.providers = new ArrayList<ScriptSourceProvider>(providers);
+ }
+
+ public MultiScriptSourceProvider(ScriptSourceProvider... providers) {
+ this.providers = new ArrayList<ScriptSourceProvider>(Arrays.asList(providers));
+ }
+
+ @Override
+ public Reader getScriptSource(URI scriptUri) {
+
+ for (ScriptSourceProvider provider : providers) {
+ Reader rdr = provider.getScriptSource(scriptUri);
+ if (rdr != null) {
+ return rdr;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/binding/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/binding/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
new file mode 100644
index 0000000..4d0472e
--- /dev/null
+++ b/modules/enterprise/binding/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -0,0 +1 @@
+org.rhq.bindings.script.RepoScriptSourceProvider
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
index d42f3e1..39cb145 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Map;
import org.rhq.bindings.client.RhqFacade;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.core.domain.auth.Subject;
public class FakeRhqFacade implements RhqFacade {
@@ -43,7 +44,7 @@ public class FakeRhqFacade implements RhqFacade {
return false;
}
- public Map<String, Object> getScriptingAPI() {
+ public Map<RhqManager, Object> getScriptingAPI() {
return Collections.emptyMap();
}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/script/RepoScriptSourceProviderTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/script/RepoScriptSourceProviderTest.java
new file mode 100644
index 0000000..db3953a
--- /dev/null
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/script/RepoScriptSourceProviderTest.java
@@ -0,0 +1,160 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.script;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.Reader;
+import java.lang.reflect.Field;
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.bindings.StandardBindings;
+import org.rhq.bindings.client.RhqFacade;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.content.PackageVersion;
+import org.rhq.core.domain.content.Repo;
+import org.rhq.core.domain.content.composite.PackageAndLatestVersionComposite;
+import org.rhq.core.domain.criteria.PackageCriteria;
+import org.rhq.core.domain.criteria.RepoCriteria;
+import org.rhq.core.domain.util.PageControl;
+import org.rhq.core.domain.util.PageList;
+import org.rhq.enterprise.server.content.ContentManagerRemote;
+import org.rhq.enterprise.server.content.RepoManagerRemote;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class RepoScriptSourceProviderTest {
+
+ private static final String SCRIPT_CONTENTS = "java.lang.System.out.println('This works!');";
+ private static final String CORRECT_REPO_NAME = "my-repo";
+ private static final int CORRECT_REPO_ID = 1;
+ private static final String CORRECT_PACKAGE_NAME = "my-script.js";
+ private static final int CORRECT_PACKAGE_VERSION_ID = 2;
+
+ public void testCanDownloadScript() throws Exception {
+ RhqFacade rhqFacade = mock(RhqFacade.class);
+
+ RepoManagerRemote repoManager = mock(RepoManagerRemote.class);
+ ContentManagerRemote contentManager = mock(ContentManagerRemote.class);
+
+ when(rhqFacade.getProxy(RepoManagerRemote.class)).thenReturn(repoManager);
+ when(rhqFacade.getProxy(ContentManagerRemote.class)).thenReturn(contentManager);
+
+ when(repoManager.findReposByCriteria(any(Subject.class), any(RepoCriteria.class))).then(
+ new Answer<List<Repo>>() {
+ @Override
+ public List<Repo> answer(InvocationOnMock invocation) throws Throwable {
+ RepoCriteria crit = (RepoCriteria) invocation.getArguments()[1];
+
+ //this is so wrong...
+ Field f = RepoCriteria.class.getDeclaredField("filterName");
+ f.setAccessible(true);
+ String name = (String) f.get(crit);
+
+ if (CORRECT_REPO_NAME.equals(name)) {
+ Repo repo = new Repo(CORRECT_REPO_NAME);
+ repo.setId(CORRECT_REPO_ID);
+
+ PageList<Repo> ret = new PageList<Repo>(PageControl.getUnlimitedInstance());
+ ret.add(repo);
+ return ret;
+ } else {
+ return new PageList<Repo>(PageControl.getUnlimitedInstance());
+ }
+ }
+ });
+
+ when(contentManager.findPackagesWithLatestVersion(any(Subject.class), any(PackageCriteria.class))).then(
+ new Answer<List<PackageAndLatestVersionComposite>>() {
+ @Override
+ public List<PackageAndLatestVersionComposite> answer(InvocationOnMock invocation) throws Throwable {
+ PackageCriteria crit = (PackageCriteria) invocation.getArguments()[1];
+
+ //this is so wrong...
+ Field f = PackageCriteria.class.getDeclaredField("filterName");
+ f.setAccessible(true);
+ String name = (String) f.get(crit);
+
+ if (CORRECT_PACKAGE_NAME.equals(name)) {
+ PackageAndLatestVersionComposite composite = new PackageAndLatestVersionComposite();
+
+ composite
+ .setGeneralPackage(new org.rhq.core.domain.content.Package(CORRECT_PACKAGE_NAME, null));
+
+ PackageVersion pv = new PackageVersion();
+ pv.setId(CORRECT_PACKAGE_VERSION_ID);
+
+ composite.setLatestPackageVersion(pv);
+
+ PageList<PackageAndLatestVersionComposite> ret = new PageList<PackageAndLatestVersionComposite>(
+ PageControl.getUnlimitedInstance());
+ ret.add(composite);
+ return ret;
+ } else {
+ return new PageList<PackageAndLatestVersionComposite>(PageControl.getUnlimitedInstance());
+ }
+ }
+ });
+
+ when(repoManager.getPackageVersionBytes(any(Subject.class), anyInt(), anyInt())).then(
+ new Answer<byte[]>() {
+ @Override
+ public byte[] answer(InvocationOnMock invocation) throws Throwable {
+ int repoId = (Integer) invocation.getArguments()[1];
+ int packageId = (Integer) invocation.getArguments()[2];
+
+ if (repoId == CORRECT_REPO_ID && packageId == CORRECT_PACKAGE_VERSION_ID) {
+ return SCRIPT_CONTENTS.getBytes(Charset.forName("UTF-8"));
+ } else {
+ //TODO throw exceptions as the original method does
+ return null;
+ }
+ }
+ });
+
+ RepoScriptSourceProvider provider = new RepoScriptSourceProvider();
+ provider.rhqFacadeChanged(new StandardBindings(null, rhqFacade));
+
+ URI uri = new URI("rhq://repositories/" + CORRECT_REPO_NAME + "/" + CORRECT_PACKAGE_NAME);
+
+ Reader rdr = provider.getScriptSource(uri);
+
+ StringBuilder bld = new StringBuilder();
+ int c;
+ while ((c = rdr.read()) != -1) {
+ bld.append((char) c);
+ }
+
+ Assert.assertEquals(bld.toString(), SCRIPT_CONTENTS, "Unexpected script contents.");
+ }
+}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
index 69b3c7a..a415f25 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
@@ -18,9 +18,9 @@
*/
package org.rhq.enterprise.client.commands;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
-import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -28,12 +28,13 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import javax.jws.WebParam;
+
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.enterprise.client.ClientMain;
import org.rhq.enterprise.client.utility.ReflectionUtility;
-import javax.jws.WebParam;
-
/**
* @author Greg Hinkle
*/
@@ -61,16 +62,16 @@ public class HelpCommand implements ClientCommand {
tw.setHideRowCount(true);
tw.print(data);
} else if ("api".equals(args[1])) {
- Map<String, Object> services = client.getRemoteClient().getScriptingAPI();
+ Map<RhqManager, Object> services = client.getRemoteClient().getScriptingAPI();
if (args.length == 2) {
TabularWriter tw = new TabularWriter(client.getPrintWriter(), "API", "Package");
tw.setWidth(client.getConsoleWidth());
String[][] data = new String[services.size()][2];
int i = 0;
- for (String apiName : services.keySet()) {
- data[i][0] = apiName;
- Object service = services.get(apiName);
+ for (RhqManager api : services.keySet()) {
+ data[i][0] = api.name();
+ Object service = services.get(api);
data[i][1] = service.getClass().getInterfaces()[0].getPackage().getName();
i++;
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
index e80c346..a511310 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
@@ -27,7 +27,6 @@ import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Arrays;
-import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
@@ -38,7 +37,6 @@ import org.rhq.bindings.ScriptEngineFactory;
import org.rhq.bindings.StandardBindings;
import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
-import org.rhq.bindings.util.PackageFinder;
import org.rhq.enterprise.client.ClientMain;
import org.rhq.enterprise.client.Controller;
import org.rhq.enterprise.client.proxy.ConfigurationEditor;
@@ -49,6 +47,8 @@ import org.rhq.enterprise.client.script.CommandLineParseException;
import org.rhq.enterprise.client.script.NamedScriptArg;
import org.rhq.enterprise.client.script.ScriptArg;
import org.rhq.enterprise.client.script.ScriptCmdLine;
+import org.rhq.scripting.ScriptSourceProvider;
+import org.rhq.scripting.ScriptSourceProviderFactory;
/**
* @author Greg Hinkle
@@ -185,7 +185,9 @@ public class ScriptCommand implements ClientCommand {
ScriptEngine engine = client.getScriptEngine();
- ScriptEngineFactory.injectStandardBindings(engine, bindings, false);
+ ScriptSourceProvider[] sourceProviders = ScriptSourceProviderFactory.get(null);
+
+ ScriptEngineFactory.injectStandardBindings(engine, bindings, false, sourceProviders);
ScriptEngineFactory.bindIndirectionMethods(engine, "configurationEditor");
ScriptEngineFactory.bindIndirectionMethods(engine, "rhq");
@@ -206,7 +208,7 @@ public class ScriptCommand implements ClientCommand {
// update the engine with the new client bindings. Keep the existing engine bindings as they
// may contain bindings outside this standard set (like any var created by the script or command line user)
- ScriptEngineFactory.injectStandardBindings(engine, bindings, false);
+ ScriptEngineFactory.injectStandardBindings(engine, bindings, false, ScriptSourceProviderFactory.get(null));
}
return;
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/SamplesScriptSourceProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/SamplesScriptSourceProvider.java
new file mode 100644
index 0000000..ca328b1
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/SamplesScriptSourceProvider.java
@@ -0,0 +1,67 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.script;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.bindings.script.BaseRhqSchemeScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public class SamplesScriptSourceProvider extends BaseRhqSchemeScriptSourceProvider {
+
+ private static final Log LOG = LogFactory.getLog(SamplesScriptSourceProvider.class);
+
+ private static final String PREFIX = "//samples/";
+
+ @Override
+ protected Reader doGetScriptSource(URI scriptUri) {
+ String path = scriptUri.getSchemeSpecificPart();
+
+ if (!path.startsWith(PREFIX)) {
+ return null;
+ }
+
+ path = path.substring(2); //remove the leading '//';
+
+ //here we suppose that the CLI was started using the rhq-cli.(sh|bat) script
+ //which sets the working directory to the root of the CLI deployment
+ File file = new File(path);
+
+ try {
+ return new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
+ } catch (FileNotFoundException e) {
+ LOG.debug("Failed to locate the script at: " + scriptUri, e);
+ return null;
+ }
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
new file mode 100644
index 0000000..9722a1c
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -0,0 +1 @@
+org.rhq.enterprise.client.script.SamplesScriptSourceProvider
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
index 4ea3267..0fd4b92 100644
--- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
@@ -62,7 +62,7 @@ public class RemoteClient implements RhqFacade {
private final int port;
private boolean loggedIn;
private boolean connected;
- private Map<String, Object> managers;
+ private Map<RhqManager, Object> managers;
private Subject subject;
private Client remotingClient;
private String subsystem = null;
@@ -277,16 +277,16 @@ public class RemoteClient implements RhqFacade {
*
* @return Map K=manager name V=remote proxy
*/
- public Map<String, Object> getScriptingAPI() {
+ public Map<RhqManager, Object> getScriptingAPI() {
if (this.managers == null) {
- this.managers = new HashMap<String, Object>();
+ this.managers = new HashMap<RhqManager, Object>();
for (RhqManager manager : RhqManager.values()) {
if (manager.enabled()) {
try {
Object proxy = getProcessor(this, manager, true);
- this.managers.put(manager.name(), proxy);
+ this.managers.put(manager, proxy);
} catch (Throwable e) {
LOG.error("Failed to load manager " + manager + " due to missing class.", e);
}
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RhqDownloadsScriptSourceProvider.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RhqDownloadsScriptSourceProvider.java
new file mode 100644
index 0000000..663e1b3
--- /dev/null
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RhqDownloadsScriptSourceProvider.java
@@ -0,0 +1,89 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.clientapi;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.bindings.StandardBindings;
+import org.rhq.bindings.client.RhqFacade;
+import org.rhq.bindings.script.BaseRhqSchemeScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public class RhqDownloadsScriptSourceProvider extends BaseRhqSchemeScriptSourceProvider implements
+ StandardBindings.RhqFacadeChangeListener {
+
+ private static final Log LOG = LogFactory.getLog(RhqDownloadsScriptSourceProvider.class);
+
+ private static final String PREFIX = "//downloads/";
+
+ private RemoteClient remoteClient;
+
+ @Override
+ public void rhqFacadeChanged(StandardBindings bindings) {
+ RhqFacade facade = bindings.getAssociatedRhqFacade();
+
+ if (facade instanceof RemoteClient) {
+ remoteClient = (RemoteClient) facade;
+ } else {
+ remoteClient = null;
+ }
+ }
+
+ @Override
+ protected Reader doGetScriptSource(URI scriptUri) {
+ if (remoteClient == null) {
+ return null;
+ }
+
+ String path = scriptUri.getSchemeSpecificPart();
+
+ if (!path.startsWith(PREFIX)) {
+ return null;
+ }
+
+ path.substring(1); //remove the leading '/'
+
+ String urlString = remoteClient.getTransport() + "://" + remoteClient.getHost() + ":" + remoteClient.getPort()
+ + path;
+
+ try {
+ URL downloadUrl = new URL(urlString);
+
+ return new InputStreamReader(downloadUrl.openStream());
+ } catch (MalformedURLException e) {
+ LOG.debug("Failed to download the script from the RHQ server using URL: " + urlString, e);
+ } catch (IOException e) {
+ LOG.debug("Failed to download the script from the RHQ server using URL: " + urlString, e);
+ }
+
+ return null;
+ }
+}
diff --git a/modules/enterprise/remoting/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/remoting/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
new file mode 100644
index 0000000..4008d4a
--- /dev/null
+++ b/modules/enterprise/remoting/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -0,0 +1 @@
+org.rhq.enterprise.clientapi.RhqDownloadsScriptSourceProvider
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
index 9365304..ffeffd7 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
@@ -47,9 +47,19 @@ public interface ScriptEngineInitializer {
* @return a newly instantiated script engine configured as above
* @throws ScriptException
*/
- ScriptEngine instantiate(Set<String> packages, ScriptSourceProvider scriptSourceProvider, PermissionCollection permissions) throws ScriptException;
+ ScriptEngine instantiate(Set<String> packages, PermissionCollection permissions) throws ScriptException;
/**
+ * Installs given script source provider into the script engine.
+ *
+ * @param scriptEngine
+ * @param scriptSourceProvider
+ *
+ * @throws IllegalArgumentException if the script engine is not supported by this initializer
+ */
+ void installScriptSourceProvider(ScriptEngine scriptEngine, ScriptSourceProvider scriptSourceProvider);
+
+ /**
* This function returns a definition string in the script engine's language
* that provides an indirection to calling the method on the bound object.
*
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
index 2742970..067ed90 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
@@ -25,7 +25,15 @@ import org.jetbrains.annotations.Nullable;
/**
* This is the service interface for scripting language implementations for RHQ
* (loaded using the META-INF/services mechanism).
- *
+ * <p>
+ * Note that the API module provides no factory class that would load the ScriptEngineProvider
+ * implementations present on the classpath. That is because for the script engine to be
+ * usable by RHQ, it needs to be initialized with a couple of dependencies that are not
+ * appropriate for the API module.
+ * <p>
+ * The factory is located in the <code>rhq-script-bindings</code> module as
+ * <code>org.rhq.bindings.ScriptEngineFactory</code>.
+ *
* @author Lukas Krejci
*/
public interface ScriptEngineProvider {
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
index 276e385..c91b706 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
@@ -26,7 +26,10 @@ import java.net.URI;
* Scripts in RHQ can be stored in various locations or maybe not even in the filesystem.
* Implementations of this interface can be used to provide the contents of the scripts
* based on URIs.
- *
+ * <p>
+ * Implementations of this interface can be located using the {@link ScriptSourceProviderFactory}
+ * if they are registered in META-INF/services.
+ *
* @author Lukas Krejci
*/
public interface ScriptSourceProvider {
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProviderFactory.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProviderFactory.java
new file mode 100644
index 0000000..3373792
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProviderFactory.java
@@ -0,0 +1,61 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting;
+
+import java.util.ArrayList;
+import java.util.ServiceLoader;
+
+/**
+ * A factory class for the ScriptSourceProvider implementations.
+ *
+ * @author Lukas Krejci
+ */
+public final class ScriptSourceProviderFactory {
+
+ private ScriptSourceProviderFactory() {
+
+ }
+
+ /**
+ * Loads the set of the available {@link ScriptSourceProvider} implementations
+ * present on the classpath and registered in META-INF/services.
+ *
+ * @param classLoader the classloader to be used to locate the impls or null if the
+ * context class loader of the current thread should be used.
+ *
+ * @return the set of the script source providers available
+ */
+ public static ScriptSourceProvider[] get(ClassLoader classLoader) {
+ ArrayList<ScriptSourceProvider> ps = new ArrayList<ScriptSourceProvider>();
+
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ ServiceLoader<ScriptSourceProvider> loader = ServiceLoader.load(ScriptSourceProvider.class, classLoader);
+
+ for (ScriptSourceProvider provider : loader) {
+ ps.add(provider);
+ }
+
+ return ps.toArray(new ScriptSourceProvider[ps.size()]);
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
index 771ec1f..f4e2292 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
@@ -52,11 +52,10 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
private ScriptEngineManager engineManager = new ScriptEngineManager();
@Override
- public ScriptEngine instantiate(final Set<String> packages, final ScriptSourceProvider scriptSourceProvider,
- PermissionCollection permissions) throws ScriptException {
+ public ScriptEngine instantiate(final Set<String> packages, PermissionCollection permissions) throws ScriptException {
if (permissions == null) {
- return instantiateUnsecured(packages, scriptSourceProvider);
+ return instantiateUnsecured(packages);
} else {
try {
CodeSource cs = new CodeSource(null, (Certificate[]) null);
@@ -67,7 +66,7 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
return AccessController.doPrivileged(new PrivilegedExceptionAction<ScriptEngine>() {
@Override
public ScriptEngine run() throws Exception {
- return instantiateUnsecured(packages, scriptSourceProvider);
+ return instantiateUnsecured(packages);
}
}, acc);
} catch (PrivilegedActionException e) {
@@ -81,6 +80,21 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
}
@Override
+ public void installScriptSourceProvider(ScriptEngine scriptEngine, ScriptSourceProvider scriptSourceProvider) {
+ if (!(scriptEngine instanceof RhinoScriptEngine)) {
+ throw new IllegalArgumentException("Provided script engine cannot be handled by " + this.getClass()
+ + ". Expected " + RhinoScriptEngine.class + " but got "
+ + (scriptEngine == null ? "null" : scriptEngine.getClass().getName()));
+ }
+
+ RhinoScriptEngine eng = (RhinoScriptEngine) scriptEngine;
+
+ if (scriptSourceProvider != null) {
+ eng.setModuleSourceProvider(new ScriptSourceToModuleSourceProviderAdapter(scriptSourceProvider));
+ }
+ }
+
+ @Override
public Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> methods) {
if (methods.size() == 0) {
return Collections.emptySet();
@@ -132,7 +146,7 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
return errorMessage;
}
- private ScriptEngine instantiateUnsecured(Set<String> packages, ScriptSourceProvider scriptSourceProvider)
+ private ScriptEngine instantiateUnsecured(Set<String> packages)
throws ScriptException {
RhinoScriptEngine eng = (RhinoScriptEngine) engineManager.getEngineByName("rhino-nonjdk");
@@ -140,10 +154,6 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
throw new IllegalStateException("Failed to instantiate the 'rhino-nonjdk' script engine. This means that either the required library is missing from the classpath or that there are some security issues preventing it from being instantiated.");
}
- if (scriptSourceProvider != null) {
- eng.setModuleSourceProvider(new ScriptSourceToModuleSourceProviderAdapter(scriptSourceProvider));
- }
-
for (String pkg : packages) {
eng.eval("importPackage(" + pkg + ")");
}
diff --git a/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java
index 3a50839..e71155b 100644
--- a/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java
+++ b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java
@@ -89,7 +89,7 @@ public class InitializerTest {
perms.add(new FilePermission("<<ALL FILES>>", "read"));
perms.add(new PropertyPermission("*", "read"));
- ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null, perms);
+ ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), perms);
try {
eng.eval("java.lang.System.exit(1)");
@@ -103,7 +103,7 @@ public class InitializerTest {
String script = "var m = require('rhq://test-module1.js'); m.func1();";
//first let's try to find the scripts with the default source provider...
- ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null, null);
+ ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null);
try {
eng.eval(script);
fail("The module should not have been loaded using the default source provider.");
@@ -111,17 +111,18 @@ public class InitializerTest {
//expected
}
- eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), new ScriptSourceProvider() {
+ eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null);
+ new JsEngineInitializer().installScriptSourceProvider(eng, new ScriptSourceProvider() {
@Override
public Reader getScriptSource(URI location) {
if (!"rhq".equals(location.getScheme())) {
return null;
}
String scriptName = location.getSchemeSpecificPart().substring(2); //remove the '//'
- InputStream src = getClass().getClassLoader().getResourceAsStream(scriptName);
+ InputStream src = getClass().getClassLoader().getResourceAsStream(scriptName);
return new InputStreamReader(src);
}
- }, null);
+ });
try {
eng.eval(script);
@@ -133,7 +134,7 @@ public class InitializerTest {
public void indirectionMethodsValid() throws Exception {
JsEngineInitializer initializer = new JsEngineInitializer();
- ScriptEngine eng = initializer.instantiate(Collections.<String>emptySet(), null, null);
+ ScriptEngine eng = initializer.instantiate(Collections.<String>emptySet(), null);
TestClass myObject = new TestClass();
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
index 9ee50b8..07ed8eb 100644
--- a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
@@ -43,8 +43,7 @@ public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
private ScriptEngineManager engineManager = new ScriptEngineManager();
@Override
- public ScriptEngine instantiate(Set<String> packages, ScriptSourceProvider scriptSourceProvider,
- PermissionCollection permissions) throws ScriptException {
+ public ScriptEngine instantiate(Set<String> packages, PermissionCollection permissions) throws ScriptException {
ScriptEngine eng = engineManager.getEngineByName("python");
@@ -52,23 +51,26 @@ public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
eng.eval("from " + pkg + " import *\n");
}
- //TODO add support for script source providers... possibly using http://www.python.org/dev/peps/pep-0302/
-
//fingers crossed we can secure jython like this
return new SandboxedScriptEngine(eng, permissions);
}
@Override
+ public void installScriptSourceProvider(ScriptEngine scriptEngine, ScriptSourceProvider provider) {
+ //TODO add support for script source providers... possibly using http://www.python.org/dev/peps/pep-0302/
+ }
+
+ @Override
public Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> overloadedMethods) {
if (overloadedMethods == null || overloadedMethods.isEmpty()) {
return Collections.emptySet();
}
Set<Integer> argCnts = new HashSet<Integer>();
- for(Method m : overloadedMethods) {
+ for (Method m : overloadedMethods) {
argCnts.add(m.getParameterTypes().length);
}
-
+
String methodName = overloadedMethods.iterator().next().getName();
StringBuilder functionBody = new StringBuilder();
@@ -76,18 +78,18 @@ public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
functionBody.append("\t").append("if len(kwargs) > 0:\n");
functionBody.append("\t\t").append("raise ValueError(\"Named arguments not supported for Java methods\")\n");
functionBody.append("\t").append("argCnt = len(args)\n");
-
- for(Integer argCnt : argCnts) {
+
+ for (Integer argCnt : argCnts) {
functionBody.append("\t").append("if argCnt == ").append(argCnt).append(":\n");
functionBody.append("\t\treturn ").append(boundObjectName).append(".").append(methodName).append("(");
int last = argCnt - 1;
- for(int i = 0; i < argCnt; ++i) {
+ for (int i = 0; i < argCnt; ++i) {
functionBody.append("args[").append(i).append("]");
if (i < last) {
functionBody.append(", ");
}
}
- functionBody.append(")\n");
+ functionBody.append(")\n");
}
return Collections.singleton(functionBody.toString());
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 384a6e0..7f8efad 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
@@ -44,7 +44,7 @@ public class LocalClient implements RhqFacade {
private static final Log LOG = LogFactory.getLog(LocalClient.class);
private Subject subject;
- private Map<String, Object> managers;
+ private Map<RhqManager, Object> managers;
public LocalClient(Subject subject) {
this.subject = subject;
@@ -70,10 +70,10 @@ public class LocalClient implements RhqFacade {
}
@Override
- public Map<String, Object> getScriptingAPI() {
+ public Map<RhqManager, Object> getScriptingAPI() {
if (managers == null) {
- managers = new HashMap<String, Object>();
+ managers = new HashMap<RhqManager, Object>();
for (final RhqManager manager : RhqManager.values()) {
if (manager.enabled()) {
@@ -85,7 +85,7 @@ public class LocalClient implements RhqFacade {
}
});
- managers.put(manager.name(), proxy);
+ managers.put(manager, proxy);
} catch (Throwable e) {
LOG.error("Failed to load manager " + manager + " due to missing class.", e);
}
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/RhqDownloadsScriptSourceProvider.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/RhqDownloadsScriptSourceProvider.java
new file mode 100644
index 0000000..7b9232a
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/RhqDownloadsScriptSourceProvider.java
@@ -0,0 +1,80 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.bindings.script.BaseRhqSchemeScriptSourceProvider;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.common.ServerDetails;
+import org.rhq.enterprise.server.auth.SubjectManagerLocal;
+import org.rhq.enterprise.server.system.SystemManagerLocal;
+import org.rhq.enterprise.server.util.LookupUtil;
+
+/**
+ * @author Lukas Krejci
+ */
+public class RhqDownloadsScriptSourceProvider extends BaseRhqSchemeScriptSourceProvider {
+
+ private static final Log LOG = LogFactory.getLog(RhqDownloadsScriptSourceProvider.class);
+
+ private static final String PREFIX = "//downloads/";
+
+ private SystemManagerLocal systemManager = LookupUtil.getSystemManager();
+ private SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
+
+ @Override
+ protected Reader doGetScriptSource(URI scriptUri) {
+ String path = scriptUri.getSchemeSpecificPart();
+
+ if (!path.startsWith(PREFIX)) {
+ return null;
+ }
+
+ path = path.substring(PREFIX.length());
+
+ Subject overlord = subjectManager.getOverlord();
+
+ ServerDetails serverDetails = systemManager.getServerDetails(overlord);
+
+ String serverHomeDir = serverDetails.getDetails().get(ServerDetails.Detail.SERVER_HOME_DIR);
+
+ File downloads = new File(serverHomeDir, "deploy/rhq.ear/rhq-downloads");
+
+ File file = new File(downloads, path);
+
+ try {
+ return new InputStreamReader(new FileInputStream(file));
+ } catch (FileNotFoundException e) {
+ LOG.debug("Failed to locate the download file: " + scriptUri, e);
+ return null;
+ }
+ }
+
+}
diff --git a/modules/enterprise/server/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/server/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
new file mode 100644
index 0000000..7523c32
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -0,0 +1 @@
+org.rhq.enterprise.client.RhqDownloadsScriptSourceProvider
diff --git a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
index 2236923..61f9ea0 100644
--- a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
+++ b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
@@ -112,7 +112,7 @@ public class LocalClientTest extends JMockTest {
LocalClient lc = new LocalClient(null);
//this call creates the proxy and is theoretically prone to the context classloader
- Object am = lc.getScriptingAPI().get("AlertManager");
+ Object am = lc.getScriptingAPI().get(RhqManager.AlertManager);
//check that only the simplified method exists on the returned object
try {
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerLocal.java
index fc72896..abc8ba5 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerLocal.java
@@ -421,7 +421,7 @@ public interface ContentManagerLocal {
InstalledPackage getBackingPackageForResource(Subject subject, int resourceId);
/**
- * @see {@link ContentManagerRemote#getPackageBytes(Subject, int, int)
+ * @see {@link ContentManagerRemote#getPackageBytes(Subject, int, int)}
*/
byte[] getPackageBytes(Subject user, int resourceId, int installedPackageId);
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerRemote.java
index a75d78f..e2e37c5 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerRemote.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerRemote.java
@@ -292,5 +292,4 @@ public interface ContentManagerRemote {
@WebMethod
byte[] getPackageBytes(@WebParam(name = "subject") Subject user, @WebParam(name = "resourceId") int resourceId,
@WebParam(name = "installedPackageId") int installedPackageId);
-
}
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerBean.java
index 72d7bee..9bcf553 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerBean.java
@@ -18,6 +18,8 @@
*/
package org.rhq.enterprise.server.content;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Comparator;
@@ -990,6 +992,35 @@ public class RepoManagerBean implements RepoManagerLocal, RepoManagerRemote {
repo.addRepoRelationship(repoRelationship);
}
+ @Override
+ public byte[] getPackageVersionBytes(Subject subject, int repoId, int packageVersionId) {
+ if (!authzManager.canViewRepo(subject, repoId)) {
+ throw new PermissionException("User [" + subject + "] cannot access a repo with id " + repoId);
+ }
+
+ //check that the provided package version actually belongs to the repo
+ Repo repo = entityManager.find(Repo.class, repoId);
+
+ PackageVersion pv = entityManager.find(PackageVersion.class, packageVersionId);
+ if (pv == null || !pv.getRepos().contains(repo)) {
+ throw new IllegalArgumentException("The package version with id " + packageVersionId
+ + " does not belong to the repo with id " + repoId + " or does not exist.");
+ }
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ contentSourceManager.outputPackageVersionBits(pv, out);
+
+ byte[] ret = out.toByteArray();
+
+ try {
+ out.close();
+ } catch (IOException e) {
+ //this is not gonna happen with a byte array stream
+ }
+
+ return ret;
+ }
+
private void validateFields(Repo repo) throws RepoException {
if (repo.getName() == null || repo.getName().trim().equals("")) {
throw new RepoException("Repo name is required");
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerLocal.java
index 51957bd..94c1159 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerLocal.java
@@ -18,7 +18,6 @@
*/
package org.rhq.enterprise.server.content;
-import java.util.Comparator;
import java.util.List;
import javax.ejb.Local;
@@ -415,4 +414,6 @@ public interface RepoManagerLocal {
* @return
*/
List<SubscribedRepo> findSubscriptions(Subject subject, int resourceId);
+
+ byte[] getPackageVersionBytes(Subject user, int repoId, int packageVersionId);
}
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerRemote.java
index d144443..45c0c5a 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerRemote.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/RepoManagerRemote.java
@@ -18,7 +18,6 @@
*/
package org.rhq.enterprise.server.content;
-import java.util.Comparator;
import java.util.List;
import javax.ejb.Remote;
@@ -260,4 +259,17 @@ public interface RepoManagerRemote {
@WebParam(name = "subject") Subject subject, //
@WebParam(name = "repoIds") int[] repoIds) //
throws Exception;
+
+ /**
+ * This method allows for downloading the bytes of an arbitrary package version. This call can be dangerous with
+ * large packages because it will attempt to load the whole package in memory.
+ *
+ * @param user
+ * @param repoId
+ * @param packageVersionId
+ * @return the bytes of the package version
+ */
+ @WebMethod
+ byte[] getPackageVersionBytes(@WebParam(name = "subject") Subject user, @WebParam(name = "repoId") int repoId,
+ @WebParam(name = "packageVersionId") int packageVersionId);
}
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
index 27cdabd..69d797a 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
@@ -903,6 +903,10 @@ public class WebservicesManagerBean implements WebservicesRemote {
return repoManager.synchronizeRepos(subject, repoIds);
}
+ public byte[] getPackageVersionBytes(Subject user, int repoId, int packageVersionId) {
+ return repoManager.getPackageVersionBytes(user, repoId, packageVersionId);
+ }
+
//REPOMANAGER: END ----------------------------------
//RESOURCEFACTORYMANAGER: BEGIN ----------------------------------
diff --git a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
index 09dc8a5..8ca0b39 100644
--- a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
+++ b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
@@ -61,6 +61,8 @@ import org.rhq.enterprise.server.plugin.pc.alert.AlertSender;
import org.rhq.enterprise.server.plugin.pc.alert.AlertSenderValidationResults;
import org.rhq.enterprise.server.util.LookupUtil;
import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptSourceProvider;
+import org.rhq.scripting.ScriptSourceProviderFactory;
/**
* Uses CLI to perform the alert notification.
@@ -425,9 +427,10 @@ public class CliSender extends AlertSender<CliComponent> {
if (engine == null) {
engine = ScriptEngineFactory.getSecuredScriptEngine(ENGINE_NAME,
new PackageFinder(Collections.<File> emptyList()), bindings, new StandardScriptPermissions());
- } else {
- ScriptEngineFactory.injectStandardBindings(engine, bindings, true);
}
+ //TODO is this OK, or should we use a different classloader than the context classloader?
+ ScriptSourceProvider[] providers = ScriptSourceProviderFactory.get(null);
+ ScriptEngineFactory.injectStandardBindings(engine, bindings, true, providers);
++ENGINES_IN_USE;
commit 73da0abdb93a5925e79ea1d35a62d15557bd96e1
Merge: fbb4fd5 b48676a
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 2 14:47:36 2012 +0200
Merge remote-tracking branch 'origin/master' into lkrejci/modular-scripting
commit fbb4fd565dec268144e29692bb8c20cc98e9c487
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 2 14:36:31 2012 +0200
Javascript completor now correctly outputs the signatures of the methods.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java
index ff98b5c..a8da737 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java
@@ -22,6 +22,7 @@ package org.rhq.enterprise.client.utility;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import javax.jws.WebParam;
@@ -34,6 +35,26 @@ import org.rhq.scripting.MetadataProvider;
public class CLIMetadataProvider implements MetadataProvider {
@Override
+ public Method getUnproxiedMethod(Method method) {
+ if (Proxy.isProxyClass(method.getDeclaringClass())) {
+ //ok, don't look at the proxy class but at the interface that is actually defining this method
+ for (Class<?> iface : method.getDeclaringClass().getInterfaces()) {
+ try {
+ Method ifaceMethod = iface.getMethod(method.getName(), method.getParameterTypes());
+ return ifaceMethod;
+ } catch (NoSuchMethodException e) {
+ //well, never mind, let's try the next interface
+ } catch (SecurityException e) {
+ //whoa
+ throw new IllegalStateException("Current code doesn't have reflection permissions.", e);
+ }
+ }
+ }
+
+ return method;
+ }
+
+ @Override
public String getParameterName(Method method, int parameterIndex) {
String name = null;
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java
index 598d952..448da46 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java
@@ -37,6 +37,21 @@ import java.lang.reflect.Type;
public interface MetadataProvider {
/**
+ * Many of the objects that RHQ exposes in the scripting environment are implemented
+ * using proxies. But proxies don't seem to maintain the generic types on them.
+ * <p>
+ * This method tries to find out if given method comes from a proxy class and if it is,
+ * it tries to find the method in one of the interfaces that the proxy implements that
+ * corresponds to it. That method is going to have all the metadata - i.e. annotations,
+ * generics, etc.
+ *
+ * @param method the method to inspect
+ * @return the method from one of the interfaces implemented by the given method's declaring class
+ * or the given method itself if it is not proxied.
+ */
+ Method getUnproxiedMethod(Method method);
+
+ /**
* Tries to determine the name of a parameter on a method.
*
* @param method the method
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
index 36eb060..f90969a 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
@@ -276,6 +276,9 @@ public class JavascriptCompletor implements CodeCompletion {
maxReturnLength = sig[0].length();
}
+ output.println();
+ output.println();
+
for (String[] sig : signatures) {
for (i = 0; i < (maxReturnLength - sig[0].length()); i++) {
output.print(" ");
@@ -500,6 +503,7 @@ public class JavascriptCompletor implements CodeCompletion {
StringBuilder buf = new StringBuilder();
Type[] params = m.getGenericParameterTypes();
int i = 0;
+ m = metadataProvider.getUnproxiedMethod(m);
buf.append(metadataProvider.getTypeName(m.getGenericReturnType(), false));
buf.append(" ");
commit fca41ffec5ff7d32735f4c25d033b240a6d1df4b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 2 14:32:56 2012 +0200
The CLI interactive console correctly handles the case where the completor outputs some additional text to the output.
The output is flushed and the commandline is re-printed underneath it.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 5d1af69..cfe1686 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -241,7 +241,7 @@ public class ClientMain {
initCodeCompletion();
consoleReader.addCompletor(new MultiCompletor(new Completor[] {
- new CodeCompletionCompletorWrapper(codeCompletion, outputWriter), helpCompletor,
+ new CodeCompletionCompletorWrapper(codeCompletion, outputWriter, consoleReader), helpCompletor,
commandCompletor }));
// enable pagination
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ChangeRegisteringPrintWriter.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ChangeRegisteringPrintWriter.java
new file mode 100644
index 0000000..dfc03b4
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ChangeRegisteringPrintWriter.java
@@ -0,0 +1,233 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.utility;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Locale;
+
+/**
+ * @author Lukas Krejci
+ *
+ */
+public class ChangeRegisteringPrintWriter extends PrintWriter {
+
+ private boolean changed;
+
+ public ChangeRegisteringPrintWriter(Writer out) {
+ super(out);
+ }
+
+ public boolean isChanged() {
+ return changed;
+ }
+
+ public void setChanged(boolean changed) {
+ this.changed = changed;
+ }
+
+ @Override
+ public PrintWriter append(char c) {
+ setChanged(true);
+ return super.append(c);
+ }
+
+ @Override
+ public void write(int c) {
+ setChanged(true);
+ super.write(c);
+ }
+
+ @Override
+ public void write(char[] buf, int off, int len) {
+ setChanged(true);
+ super.write(buf, off, len);
+ }
+
+ @Override
+ public void write(char[] buf) {
+ setChanged(true);
+ super.write(buf);
+ }
+
+ @Override
+ public void write(String s, int off, int len) {
+ setChanged(true);
+ super.write(s, off, len);
+ }
+
+ @Override
+ public void write(String s) {
+ setChanged(true);
+ super.write(s);
+ }
+
+ @Override
+ public void print(boolean b) {
+ setChanged(true);
+ super.print(b);
+ }
+
+ @Override
+ public void print(char c) {
+ setChanged(true);
+ super.print(c);
+ }
+
+ @Override
+ public void print(int i) {
+ setChanged(true);
+ super.print(i);
+ }
+
+ @Override
+ public void print(long l) {
+ setChanged(true);
+ super.print(l);
+ }
+
+ @Override
+ public void print(float f) {
+ setChanged(true);
+ super.print(f);
+ }
+
+ @Override
+ public void print(double d) {
+ setChanged(true);
+ super.print(d);
+ }
+
+ @Override
+ public void print(char[] s) {
+ setChanged(true);
+ super.print(s);
+ }
+
+ @Override
+ public void print(String s) {
+ setChanged(true);
+ super.print(s);
+ }
+
+ @Override
+ public void print(Object obj) {
+ setChanged(true);
+ super.print(obj);
+ }
+
+ @Override
+ public void println() {
+ setChanged(true);
+ super.println();
+ }
+
+ @Override
+ public void println(boolean x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(char x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(int x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(long x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(float x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(double x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(char[] x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(String x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public void println(Object x) {
+ setChanged(true);
+ super.println(x);
+ }
+
+ @Override
+ public PrintWriter printf(String format, Object... args) {
+ setChanged(true);
+ return super.printf(format, args);
+ }
+
+ @Override
+ public PrintWriter printf(Locale l, String format, Object... args) {
+ setChanged(true);
+ return super.printf(l, format, args);
+ }
+
+ @Override
+ public PrintWriter format(String format, Object... args) {
+ setChanged(true);
+ return super.format(format, args);
+ }
+
+ @Override
+ public PrintWriter format(Locale l, String format, Object... args) {
+ setChanged(true);
+ return super.format(l, format, args);
+ }
+
+ @Override
+ public PrintWriter append(CharSequence csq) {
+ setChanged(true);
+ return super.append(csq);
+ }
+
+ @Override
+ public PrintWriter append(CharSequence csq, int start, int end) {
+ setChanged(true);
+ return super.append(csq, start, end);
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java
index 8f2570c..4be3613 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java
@@ -1,24 +1,48 @@
package org.rhq.enterprise.client.utility;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import jline.Completor;
+import jline.ConsoleReader;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.rhq.scripting.CodeCompletion;
public class CodeCompletionCompletorWrapper implements Completor {
+ private static final Log LOG = LogFactory.getLog(CodeCompletionCompletorWrapper.class);
+
private CodeCompletion completion;
- private PrintWriter output;
+ private ChangeRegisteringPrintWriter output;
+ private ConsoleReader consoleReader;
- public CodeCompletionCompletorWrapper(CodeCompletion completion, PrintWriter output) {
+ public CodeCompletionCompletorWrapper(CodeCompletion completion, PrintWriter output, ConsoleReader consoleReader) {
this.completion = completion;
- this.output = output;
+ this.output = new ChangeRegisteringPrintWriter(output);
+ this.consoleReader = consoleReader;
}
@Override
public int complete(String buffer, int cursor, List candidates) {
- return completion.complete(output, buffer, cursor, candidates);
+ String start = this.consoleReader.getCursorBuffer().getBuffer().toString();
+
+ output.setChanged(false);
+ int ret = completion.complete(output, buffer, cursor, candidates);
+
+ if (output.isChanged()) {
+ try {
+ output.flush();
+ consoleReader.printNewline();
+ consoleReader.drawLine();
+ } catch (IOException e) {
+ LOG.debug("Failed to draw a console reader line.", e);
+ }
+ }
+
+ return ret;
}
}
commit 1d1e61836632b35abe0a30b67b6b999cd4a84ef0
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jul 2 14:30:32 2012 +0200
InterfaceSimplifier now mirrors the generic type signatures and annotations
on all levels.
The newly generated class preserves the generic type signatures and
annotations on the class, method and method parameter levels.
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 d6835a0..dc29bc9 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
@@ -18,13 +18,37 @@
*/
package org.rhq.bindings.util;
+import java.util.ArrayList;
+import java.util.List;
+
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
+import javassist.Modifier;
import javassist.NotFoundException;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
import javassist.bytecode.ParameterAnnotationsAttribute;
+import javassist.bytecode.SignatureAttribute;
+import javassist.bytecode.annotation.Annotation;
+import javassist.bytecode.annotation.AnnotationMemberValue;
+import javassist.bytecode.annotation.ArrayMemberValue;
+import javassist.bytecode.annotation.BooleanMemberValue;
+import javassist.bytecode.annotation.ByteMemberValue;
+import javassist.bytecode.annotation.CharMemberValue;
+import javassist.bytecode.annotation.ClassMemberValue;
+import javassist.bytecode.annotation.DoubleMemberValue;
+import javassist.bytecode.annotation.EnumMemberValue;
+import javassist.bytecode.annotation.FloatMemberValue;
+import javassist.bytecode.annotation.IntegerMemberValue;
+import javassist.bytecode.annotation.LongMemberValue;
+import javassist.bytecode.annotation.MemberValue;
+import javassist.bytecode.annotation.MemberValueVisitor;
+import javassist.bytecode.annotation.ShortMemberValue;
+import javassist.bytecode.annotation.StringMemberValue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -33,18 +57,18 @@ import org.rhq.core.domain.auth.Subject;
/**
* The scripts can use simplified interfaces that omit the first "Subject" argument
- * to most methods. This helper class prepares such simplified interfaces.
+ * to most methods from RHQ's remote API. This helper class prepares such simplified interfaces.
*
* @author Greg Hinkle
* @author Lukas Krejci
*/
public class InterfaceSimplifier {
private static final Log LOG = LogFactory.getLog(InterfaceSimplifier.class);
-
+
private InterfaceSimplifier() {
-
+
}
-
+
public static Class<?> simplify(Class<?> intf) {
try {
ClassPool classPool = ClassPool.getDefault();
@@ -52,65 +76,125 @@ public class InterfaceSimplifier {
String simplifiedName = getSimplifiedName(intf);
LOG.debug("Simplifying " + intf + " (simplified interface name: " + simplifiedName + ")...");
+ CtClass cached = null;
try {
- @SuppressWarnings("unused")
- CtClass cached = classPool.get(simplifiedName);
+ cached = classPool.get(simplifiedName);
return Class.forName(simplifiedName, false, classPool.getClassLoader());
-
} catch (NotFoundException e) {
// ok... load it
} catch (ClassNotFoundException e) {
LOG.debug("Class [" + simplifiedName + "] not found - cause: " + e, e);
+ if (cached != null) {
+ // strange - we found the class definition in the class pool, which means we must have touched it
+ // before but Class.forName failed to find the class in the class pool's class loader.
+ return cached.toClass();
+ }
}
CtClass originalClass = classPool.get(intf.getName());
+ ClassFile originalClassFile = originalClass.getClassFile();
CtClass newClass = classPool.makeInterface(simplifiedName);
newClass.defrost();
+ ClassFile newClassFile = newClass.getClassFile();
+
+ //we'll be adding new constants to the class file (for generics and annotations)
+ ConstPool constPool = newClassFile.getConstPool();
+
+ //copy the annotations on the class
+ AnnotationsAttribute annotations = (AnnotationsAttribute) originalClassFile
+ .getAttribute(AnnotationsAttribute.visibleTag);
+ AnnotationsAttribute newAnnotations = copyAnnotations(annotations, constPool);
+ if (newAnnotations != null) {
+ newClassFile.addAttribute(newAnnotations);
+ }
+
+ //copy the generic signature of the class
+ SignatureAttribute signature = (SignatureAttribute) originalClassFile.getAttribute(SignatureAttribute.tag);
+ if (signature != null) {
+ newClassFile.addAttribute(new SignatureAttribute(constPool, signature.getSignature()));
+ }
+
+ //now copy over the methods
CtMethod[] methods = originalClass.getMethods();
for (CtMethod originalMethod : methods) {
+ //we are only simplifying interfaces here, but the CtClass.getMethods() also returns concrete methods
+ //inherited from Object. Let's just skip those - we don't need to worry about them...
+ if (!Modifier.isAbstract(originalMethod.getModifiers())) {
+ continue;
+ }
+
CtClass[] params = originalMethod.getParameterTypes();
- if (params.length > 0 && params[0].getName().equals(Subject.class.getName())) {
+ //capture all the runtime visible method annotations on the original method
+ annotations = (AnnotationsAttribute) originalMethod.getMethodInfo().getAttribute(
+ AnnotationsAttribute.visibleTag);
+
+ //capture all the runtime visible parameter annotations on the original method
+ ParameterAnnotationsAttribute parameterAnnotations = (ParameterAnnotationsAttribute) originalMethod
+ .getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag);
+
+ //capture the generic signature of the original method.
+ signature = (SignatureAttribute) originalMethod.getMethodInfo().getAttribute(
+ SignatureAttribute.tag);
+
+ boolean simplify = params.length > 0 && params[0].getName().equals(Subject.class.getName());
+
+ if (simplify) {
+ //generate new params, leaving out the first parameter (the subject)
CtClass[] simpleParams = new CtClass[params.length - 1];
System.arraycopy(params, 1, simpleParams, 0, params.length - 1);
- newClass.defrost();
+ params = simpleParams;
+ }
- CtMethod newMethod = CtNewMethod.abstractMethod(originalMethod.getReturnType(), originalMethod
-.getName(), simpleParams, null, newClass);
+ //generate the new method with possibly modified parameters
+ CtMethod newMethod = CtNewMethod.abstractMethod(originalMethod.getReturnType(),
+ originalMethod.getName(), params, originalMethod.getExceptionTypes(), newClass);
- ParameterAnnotationsAttribute originalAnnotationsAttribute = (ParameterAnnotationsAttribute) originalMethod
- .getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag);
+ //copy over the method annotations
+ annotations = copyAnnotations(annotations, constPool);
- // If there are any parameter annotations, copy the one's we're keeping
- if (originalAnnotationsAttribute != null) {
+ if (simplify) {
+ if (signature != null) {
+ //fun, we need to modify the signature, too, because we have left out the parameter
+ MethodSignature sig = MethodSignature.parse(signature.getSignature());
- javassist.bytecode.annotation.Annotation[][] originalAnnotations = originalAnnotationsAttribute
- .getAnnotations();
- javassist.bytecode.annotation.Annotation[][] newAnnotations = new javassist.bytecode.annotation.Annotation[originalAnnotations.length - 1][];
+ sig.paramTypes.remove(0);
- for (int i = 1; i < originalAnnotations.length; i++) {
- newAnnotations[i - 1] = new javassist.bytecode.annotation.Annotation[originalAnnotations[i].length];
- System.arraycopy(originalAnnotations[i], 0, newAnnotations[i - 1], 0,
- originalAnnotations[i].length);
- }
+ signature = new SignatureAttribute(constPool, sig.toString());
+ }
- ParameterAnnotationsAttribute newAnnotationsAttribute = new ParameterAnnotationsAttribute(
- newMethod.getMethodInfo().getConstPool(), ParameterAnnotationsAttribute.visibleTag);
+ //next, we need to copy the parameter annotations
+ parameterAnnotations = copyParameterAnnotations(parameterAnnotations, constPool, 1);
+ } else {
+ //just copy the sig and parameter annotations verbatim
+ if (signature != null) {
+ signature = new SignatureAttribute(constPool, signature.getSignature());
+ }
- newAnnotationsAttribute.setAnnotations(newAnnotations);
+ parameterAnnotations = copyParameterAnnotations(parameterAnnotations, constPool, 0);
+ }
- newMethod.getMethodInfo().addAttribute(newAnnotationsAttribute);
+ if (parameterAnnotations != null) {
+ newMethod.getMethodInfo().addAttribute(parameterAnnotations);
+ }
- }
+ if (signature != null) {
+ newMethod.getMethodInfo().addAttribute(signature);
+ }
- newClass.addMethod(newMethod);
+ if (annotations != null) {
+ newMethod.getMethodInfo().addAttribute(annotations);
}
+
+ //it is important to add the method directly to the classfile, not the class
+ //because otherwise the generics info wouldn't survive
+ newClassFile.addMethod(newMethod.getMethodInfo());
}
return newClass.toClass();
@@ -127,9 +211,266 @@ public class InterfaceSimplifier {
String fullName = interfaceClass.getName();
String simpleName = interfaceClass.getSimpleName();
Package pkg = interfaceClass.getPackage();
- String packageName = (pkg != null) ? pkg.getName() :
- fullName.substring(0, fullName.length() - (simpleName.length() + 1));
+ String packageName = (pkg != null) ? pkg.getName() : fullName.substring(0,
+ fullName.length() - (simpleName.length() + 1));
return packageName + ".wrapped." + simpleName + "Simple";
}
+ /**
+ * Copies the provided annotation into the provided const pool.
+ *
+ * @param annotation
+ * @param constPool
+ * @return
+ * @throws NotFoundException
+ */
+ private static Annotation cloneAnnotation(Annotation annotation, final ConstPool constPool)
+ throws NotFoundException {
+
+ Annotation ret = new Annotation(annotation.getTypeName(), constPool);
+
+ for (Object m : annotation.getMemberNames()) {
+ final String memberName = (String) m;
+
+ MemberValue origValue = annotation.getMemberValue(memberName);
+ final MemberValue[] newValue = new MemberValue[1];
+
+ origValue.accept(new ArrayIndexAssigningVisitor(newValue, 0, constPool));
+
+ ret.addMemberValue(memberName, newValue[0]);
+ }
+
+ return ret;
+ }
+
+ private static AnnotationsAttribute copyAnnotations(AnnotationsAttribute annotations, ConstPool constPool)
+ throws NotFoundException {
+ if (annotations != null) {
+ Annotation[] origAnnotations = annotations.getAnnotations();
+ Annotation[] newClassAnnotations = new Annotation[origAnnotations.length];
+ for (int i = 0; i < newClassAnnotations.length; ++i) {
+ newClassAnnotations[i] = cloneAnnotation(origAnnotations[i], constPool);
+ }
+
+ AnnotationsAttribute newAnnotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+ newAnnotations.setAnnotations(newClassAnnotations);
+
+ return newAnnotations;
+ }
+
+ return null;
+ }
+
+ private static ParameterAnnotationsAttribute copyParameterAnnotations(
+ ParameterAnnotationsAttribute parameterAnnotations, ConstPool constPool, int fromIndex)
+ throws NotFoundException {
+
+ if (parameterAnnotations != null) {
+ Annotation[][] originalAnnotations = parameterAnnotations.getAnnotations();
+
+ //return early if there are no annotations to copy
+ if (originalAnnotations.length - fromIndex <= 0) {
+ return null;
+ }
+
+ Annotation[][] newParameterAnnotations = new Annotation[originalAnnotations.length - fromIndex][];
+
+ for (int i = fromIndex; i < originalAnnotations.length; i++) {
+ newParameterAnnotations[i - fromIndex] = new Annotation[originalAnnotations[i].length];
+ for (int j = 0; j < originalAnnotations[i].length; ++j) {
+ Annotation origAnnotation = originalAnnotations[i][j];
+
+ newParameterAnnotations[i - fromIndex][j] = cloneAnnotation(origAnnotation, constPool);
+ }
+ }
+
+ ParameterAnnotationsAttribute newAnnotationsAttribute = new ParameterAnnotationsAttribute(constPool,
+ ParameterAnnotationsAttribute.visibleTag);
+
+ newAnnotationsAttribute.setAnnotations(newParameterAnnotations);
+
+ return newAnnotationsAttribute;
+ }
+
+ return null;
+ }
+
+ private static class ArrayIndexAssigningVisitor implements MemberValueVisitor {
+ private MemberValue[] array;
+ private int index;
+ private ConstPool constPool;
+
+ public ArrayIndexAssigningVisitor(MemberValue[] array, int index, ConstPool constPool) {
+ this.array = array;
+ this.index = index;
+ this.constPool = constPool;
+ }
+
+ @Override
+ public void visitStringMemberValue(StringMemberValue node) {
+ array[index] = new StringMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitShortMemberValue(ShortMemberValue node) {
+ array[index] = new ShortMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitLongMemberValue(LongMemberValue node) {
+ array[index] = new LongMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitIntegerMemberValue(IntegerMemberValue node) {
+ array[index] = new IntegerMemberValue(constPool, node.getValue());
+ }
+
+ @Override
+ public void visitFloatMemberValue(FloatMemberValue node) {
+ array[index] = new FloatMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitEnumMemberValue(EnumMemberValue node) {
+ EnumMemberValue val = new EnumMemberValue(constPool);
+ val.setType(node.getType());
+ val.setValue(node.getValue());
+ array[index] = val;
+ }
+
+ @Override
+ public void visitDoubleMemberValue(DoubleMemberValue node) {
+ array[index] = new DoubleMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitClassMemberValue(ClassMemberValue node) {
+ array[index] = new ClassMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitCharMemberValue(CharMemberValue node) {
+ array[index] = new CharMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitByteMemberValue(ByteMemberValue node) {
+ array[index] = new ByteMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitBooleanMemberValue(BooleanMemberValue node) {
+ array[index] = new BooleanMemberValue(node.getValue(), constPool);
+ }
+
+ @Override
+ public void visitArrayMemberValue(ArrayMemberValue node) {
+ ArrayMemberValue val = new ArrayMemberValue(node.getType(), constPool);
+ MemberValue[] newVals = new MemberValue[node.getValue().length];
+ for (int i = 0; i < node.getValue().length; ++i) {
+ node.getValue()[i].accept(new ArrayIndexAssigningVisitor(newVals, i, constPool));
+ }
+
+ val.setValue(newVals);
+ array[index] = val;
+ }
+
+ @Override
+ public void visitAnnotationMemberValue(AnnotationMemberValue node) {
+ array[index] = new AnnotationMemberValue(node.getValue(), constPool);
+ }
+ }
+
+ //a quick and dirty method signature parser
+ //see http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4
+ private static class MethodSignature {
+ public String returnType;
+ public List<String> paramTypes = new ArrayList<String>();
+ public String typeParameters;
+ public String exceptionTypes;
+
+ public static MethodSignature parse(String signature) {
+ int startParams = signature.indexOf('(') + 1;
+ int endParams = signature.indexOf(')');
+ int startExceptions = signature.indexOf('^');
+
+ MethodSignature sig = new MethodSignature();
+ sig.typeParameters = signature.substring(0, startParams - 1);
+ if (startExceptions == -1) {
+ sig.returnType = signature.substring(endParams + 1);
+ sig.exceptionTypes = "";
+ } else {
+ sig.returnType = signature.substring(endParams + 1, startExceptions);
+ sig.exceptionTypes = signature.substring(startExceptions);
+ }
+
+ int idx = startParams;
+ while (idx < endParams) {
+ int end = findEndOfTypeSignature(idx, signature);
+ sig.paramTypes.add(signature.substring(idx, end));
+ idx = end;
+ }
+
+ return sig;
+ }
+
+ private static int findEndOfTypeSignature(int idx, String signature) {
+ int c = signature.charAt(idx);
+
+ switch (c) {
+ case 'L':
+ return findEndOfClassSignature(idx, signature);
+ case '[':
+ return findEndOfTypeSignature(idx + 1, signature);
+ case 'T':
+ return signature.indexOf(';', idx + 1) + 1;
+ default:
+ return idx + 1;
+ }
+ }
+
+ private static int findEndOfClassSignature(int indexOfL, String signature) {
+ int idx = indexOfL + 1;
+
+ int genericDeclDepth = 0;
+
+ while (idx < signature.length()) {
+ boolean sigComplete = false;
+
+ char c = signature.charAt(idx++);
+ switch (c) {
+ case '<':
+ genericDeclDepth++;
+ break;
+ case '>':
+ genericDeclDepth--;
+ break;
+ case ';':
+ sigComplete = genericDeclDepth == 0;
+ break;
+ }
+
+ if (sigComplete) {
+ break;
+ }
+ }
+
+ return idx;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder bld = new StringBuilder(typeParameters);
+ bld.append("(");
+ for (String p : paramTypes) {
+ bld.append(p);
+ }
+ bld.append(")");
+ bld.append(returnType);
+ bld.append(exceptionTypes);
+
+ return bld.toString();
+ }
+ }
}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
new file mode 100644
index 0000000..4312153
--- /dev/null
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
@@ -0,0 +1,317 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.List;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.auth.Subject;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class InterfaceSimplifierTest {
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD })
+ public static @interface MyAnnotation {
+ int value() default 1;
+
+ String parameter();
+ }
+
+ public static interface NoSimplifications {
+ void voidMethodWithNoExceptions();
+
+ void voidMethodWithExceptions() throws IOException, InterruptedException;
+
+ int intMethod();
+
+ Object objectMethod();
+
+ Object objectMethodWithParams(int p1, Object p2);
+
+ Object objectMethodWithParamsAndExceptions(int p1, Object p2) throws IOException, InterruptedException;
+ }
+
+ public static interface Simplifications {
+ void voidMethodWithNoExceptions(Subject s);
+
+ void voidMethodWithExceptions(Subject s) throws IOException, InterruptedException;
+
+ int intMethod(Subject s);
+
+ Object objectMethod(Subject s);
+
+ Object objectMethodWithParams(Subject s, int p1, Object p2);
+
+ Object objectMethodWithParamsAndExceptions(Subject s, int p1, Object p2) throws IOException,
+ InterruptedException;
+ }
+
+ public static interface Generics<C extends Type> {
+ void genericParameters(List<String> p, int p2);
+
+ <T extends Type> T typeParameters(T p, int p2);
+
+ <T extends Type> T typeParametersSimplified(Subject s, T p, int p2);
+
+ <T extends C> T classTypeParameters(T p, int p2);
+ }
+
+
+ @MyAnnotation(parameter = "CLASS")
+ public static interface Annotations {
+
+ @MyAnnotation(value = 2, parameter = "a")
+ int method(@MyAnnotation(parameter = "b") int p) throws IOException;
+
+ @MyAnnotation(value = 2, parameter = "c")
+ int methodSimplified(@MyAnnotation(parameter = "disappear") Subject s, @MyAnnotation(parameter = "d") int p)
+ throws IOException;
+ }
+
+ public void testNoSimplifications() throws Exception {
+ Class<?> iface = InterfaceSimplifier.simplify(NoSimplifications.class);
+
+ Method voidMethodWithNoExceptions = iface.getMethod("voidMethodWithNoExceptions");
+ Assert.assertEquals(voidMethodWithNoExceptions.getReturnType(), void.class);
+
+ Method voidMethodWithExceptions = iface.getMethod("voidMethodWithExceptions");
+ List<Class<?>> exceptions = Arrays.asList(voidMethodWithExceptions.getExceptionTypes());
+ Assert.assertTrue(exceptions.contains(IOException.class),
+ "The 'voidMethodWithExceptions doesn't seem to declare throws IOException");
+ Assert.assertTrue(exceptions.contains(InterruptedException.class),
+ "The 'voidMethodWithExceptions doesn't seem to declare throws InterruptedException");
+
+ Method intMethod = iface.getMethod("intMethod");
+ Assert.assertEquals(intMethod.getReturnType(), int.class);
+
+ Method objectMethod = iface.getMethod("objectMethod");
+ Assert.assertEquals(objectMethod.getReturnType(), Object.class);
+
+ Method objectMethodWithParams = iface.getMethod("objectMethodWithParams", int.class, Object.class);
+ Assert.assertEquals(objectMethodWithParams.getReturnType(), Object.class);
+
+ Method objectMethodWithParamsAndExceptions = iface.getMethod("objectMethodWithParamsAndExceptions", int.class,
+ Object.class);
+ Assert.assertEquals(objectMethodWithParamsAndExceptions.getReturnType(), Object.class);
+ exceptions = Arrays.asList(objectMethodWithParamsAndExceptions.getExceptionTypes());
+ Assert.assertTrue(exceptions.contains(IOException.class),
+ "The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws IOException");
+ Assert.assertTrue(exceptions.contains(InterruptedException.class),
+ "The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws InterruptedException");
+ }
+
+ public void testSimplifications() throws Exception {
+ Class<?> iface = InterfaceSimplifier.simplify(Simplifications.class);
+
+ //These tests are exactly the same as for the NoSimplifications class, because
+ //the simplifier should leave out all the subject parameters in the Simplifications
+ //class' methods.
+
+ Method voidMethodWithNoExceptions = iface.getMethod("voidMethodWithNoExceptions");
+ Assert.assertEquals(voidMethodWithNoExceptions.getReturnType(), void.class);
+
+ Method voidMethodWithExceptions = iface.getMethod("voidMethodWithExceptions");
+ List<Class<?>> exceptions = Arrays.asList(voidMethodWithExceptions.getExceptionTypes());
+ Assert.assertTrue(exceptions.contains(IOException.class),
+ "The 'voidMethodWithExceptions doesn't seem to declare throws IOException");
+ Assert.assertTrue(exceptions.contains(InterruptedException.class),
+ "The 'voidMethodWithExceptions doesn't seem to declare throws InterruptedException");
+
+ Method intMethod = iface.getMethod("intMethod");
+ Assert.assertEquals(intMethod.getReturnType(), int.class);
+
+ Method objectMethod = iface.getMethod("objectMethod");
+ Assert.assertEquals(objectMethod.getReturnType(), Object.class);
+
+ Method objectMethodWithParams = iface.getMethod("objectMethodWithParams", int.class, Object.class);
+ Assert.assertEquals(objectMethodWithParams.getReturnType(), Object.class);
+
+ Method objectMethodWithParamsAndExceptions = iface.getMethod("objectMethodWithParamsAndExceptions", int.class,
+ Object.class);
+ Assert.assertEquals(objectMethodWithParamsAndExceptions.getReturnType(), Object.class);
+ exceptions = Arrays.asList(objectMethodWithParamsAndExceptions.getExceptionTypes());
+ Assert.assertTrue(exceptions.contains(IOException.class),
+ "The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws IOException");
+ Assert.assertTrue(exceptions.contains(InterruptedException.class),
+ "The 'objectMethodWithParamsAndExceptions doesn't seem to declare throws InterruptedException");
+ }
+
+ public <T> void testGenerics() throws Exception {
+ @SuppressWarnings("unchecked")
+ Class<T> iface = (Class<T>) InterfaceSimplifier.simplify(Generics.class);
+
+ TypeVariable<Class<T>>[] classTypeParameters = iface.getTypeParameters();
+ Assert.assertEquals(classTypeParameters.length, 1, "There should be 1 type parameter on the class Generics.");
+ TypeVariable<?> typeVariable = classTypeParameters[0];
+ Assert.assertEquals(typeVariable.getName(), "C", "Unexpected type parameter name on 'Generics' class.");
+ Type[] bounds = typeVariable.getBounds();
+ Assert.assertEquals(bounds.length, 1, "The type parameter on the class 'Generics' should have 1 upper bound.");
+ Assert.assertEquals(bounds[0], Type.class,
+ "The type parameter on the class 'Generics' should have the upper bound of the Type class.");
+
+ Method genericParameters = iface.getMethod("genericParameters", List.class, int.class);
+ Assert.assertEquals(genericParameters.getReturnType(), void.class);
+
+ Type firstParamType = genericParameters.getGenericParameterTypes()[0];
+ Assert.assertTrue(firstParamType instanceof ParameterizedType,
+ "The first parameter of the 'genericParameters' should be parameterized.");
+ Assert.assertEquals(((ParameterizedType) firstParamType).getRawType(), List.class,
+ "The first parameter of the 'genericParameters' method should be a List.");
+ Assert.assertEquals(((ParameterizedType) firstParamType).getActualTypeArguments()[0], String.class,
+ "The first parameter of the 'genericParamters' method should be a List<String>");
+
+ Method typeParameters = iface.getMethod("typeParameters", Type.class, int.class);
+ Assert.assertEquals(typeParameters.getReturnType(), Type.class);
+
+ TypeVariable<Method>[] typeVariables = typeParameters.getTypeParameters();
+ Assert.assertEquals(typeVariables.length, 1,
+ "There should be 1 type parameter on the the 'typeParameters' method.");
+ typeVariable = typeVariables[0];
+ Assert.assertEquals(typeVariable.getName(), "T", "Unexpected type parameter name on 'typeParameters' method.");
+ bounds = typeVariable.getBounds();
+ Assert.assertEquals(bounds.length, 1, "The type parameter on the method 'typeParameters' should have 1 upper bound.");
+ Assert.assertEquals(bounds[0], Type.class,
+ "The type parameter on the method 'typeParameters' should have the upper bound of the Type class.");
+
+ Type returnType = typeParameters.getGenericReturnType();
+ Assert.assertTrue(returnType instanceof TypeVariable,
+ "The generic return type of the 'typeParameters' class should be a type variable.");
+ typeVariable = (TypeVariable<?>) returnType;
+ Assert.assertEquals(typeVariable.getName(), "T",
+ "Unexpected type parameter at the return type of the 'typeParameters' method.");
+
+ Method typeParametersSimplified = iface.getMethod("typeParametersSimplified", Type.class, int.class);
+ Assert.assertEquals(typeParameters.getReturnType(), Type.class);
+
+ typeVariables = typeParametersSimplified.getTypeParameters();
+ Assert.assertEquals(typeVariables.length, 1,
+ "There should be 1 type parameter on the the 'typeParametersSimplified' method.");
+ typeVariable = typeVariables[0];
+ Assert.assertEquals(typeVariable.getName(), "T",
+ "Unexpected type parameter name on 'typeParametersSimplified' method.");
+ bounds = typeVariable.getBounds();
+ Assert.assertEquals(bounds.length, 1,
+ "The type parameter on the method 'typeParametersSimplified' should have 1 upper bound.");
+ Assert
+ .assertEquals(bounds[0], Type.class,
+ "The type parameter on the method 'typeParametersSimplified' should have the upper bound of the Type class.");
+
+ returnType = typeParametersSimplified.getGenericReturnType();
+ Assert.assertTrue(returnType instanceof TypeVariable,
+ "The generic return type of the 'typeParametersSimplified' class should be a type variable.");
+ typeVariable = (TypeVariable<?>) returnType;
+ Assert.assertEquals(typeVariable.getName(), "T",
+ "Unexpected type parameter at the return type of the 'typeParametersSimplified' method.");
+ }
+
+ public void testAnnotations() throws Exception {
+ Class<?> iface = InterfaceSimplifier.simplify(Annotations.class);
+
+ Annotation[] annotations = iface.getAnnotations();
+ Assert.assertEquals(annotations.length, 1, "UNexpected number of annotations on the 'Annotations' class.");
+ Annotation annotation = annotations[0];
+ Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
+ "Unexpected annotation type on the class 'Annotations");
+ Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
+ "Unexpected value of the 'value' attribute on the annotation on the 'Annotations' class.");
+ Assert.assertEquals(((MyAnnotation) annotation).parameter(), "CLASS",
+ "Unexpected value of the 'parameter' attribute on the annotation on the 'Annotations' class.");
+
+ Method method = iface.getMethod("method", int.class);
+ annotations = method.getAnnotations();
+ Assert.assertEquals(annotations.length, 1, "Unexpected number of annotations on the 'method' method.");
+
+ annotation = annotations[0];
+ Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
+ "Unexpected annotation type on the method 'method");
+
+ Assert.assertEquals(((MyAnnotation) annotation).value(), 2,
+ "Unexpected value of the 'value' attribute on the annotation on the 'method' method.");
+ Assert.assertEquals(((MyAnnotation) annotation).parameter(), "a",
+ "Unexpected value of the 'parameter' attribute on the annotation on the 'method' method.");
+
+ Annotation[][] parameterAnnotations = method.getParameterAnnotations();
+
+ Assert
+ .assertEquals(parameterAnnotations.length, 1,
+ "Method 'Annotations.method(int)' has 1 parameter with annotations but we got a different number of parameters.");
+
+ Assert.assertEquals(parameterAnnotations[0].length, 1,
+ "The parameter of 'Annotations.method(int)' method has an annotation but we couldn't detect any.");
+
+ annotation = parameterAnnotations[0][0];
+ Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
+ "Unexpected annotation type on the parameter 'p' of 'Annotations.method(int)'");
+ Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
+ "Unexpected value of the 'value' of the annotation on the parameter p of 'Annotations.method(int)'.");
+ Assert.assertEquals(((MyAnnotation) annotation).parameter(), "b",
+ "Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.method(int)'.");
+
+ method = iface.getMethod("methodSimplified", int.class);
+ annotations = method.getAnnotations();
+ Assert
+ .assertEquals(annotations.length, 1, "Unexpected number of annotations on the 'methodSimplified' method.");
+
+ annotation = annotations[0];
+ Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
+ "Unexpected annotation type on the method 'methodSimplified");
+
+ Assert.assertEquals(((MyAnnotation) annotation).value(), 2,
+ "Unexpected value of the 'value' attribute on the annotation on the 'methodSimplified' method.");
+ Assert.assertEquals(((MyAnnotation) annotation).parameter(), "c",
+ "Unexpected value of the 'parameter' attribute on the annotation on the 'methodSimplified' method.");
+
+ parameterAnnotations = method.getParameterAnnotations();
+
+ Assert
+ .assertEquals(parameterAnnotations.length, 1,
+ "Method 'Annotations.methodSimplified(int)' has 1 parameter with annotations but we got a different number of parameters.");
+
+ Assert.assertEquals(parameterAnnotations[0].length, 1,
+ "The parameter of 'Annotations.methodSimplified(int)' method has an annotation but we couldn't detect any.");
+
+ annotation = parameterAnnotations[0][0];
+ Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
+ "Unexpected annotation type on the parameter 'p' of 'Annotations.methodSimplified(int)'");
+ Assert.assertEquals(((MyAnnotation) annotation).value(), 1,
+ "Unexpected value of the 'value' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
+ Assert
+ .assertEquals(((MyAnnotation) annotation).parameter(), "d",
+ "Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
+ }
+}
commit 76a9b14a24bf361f6916203c133500a77d98da73
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 15:29:43 2012 +0200
Rename the method to be more descriptive.
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 e350302..384a6e0 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
@@ -81,7 +81,7 @@ public class LocalClient implements RhqFacade {
Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
- return getProxy(getLocalSLSB(manager), manager.remote());
+ return getScriptingProxy(getLocalSLSB(manager), manager);
}
});
@@ -112,8 +112,8 @@ public class LocalClient implements RhqFacade {
return remoteApiIface.cast(proxy);
}
- private <T> T getProxy(Object slsb, Class<T> iface) {
- RhqManager manager = RhqManager.forInterface(iface);
+ private Object getScriptingProxy(Object slsb, RhqManager manager) {
+ Class<?> iface = manager.remote();
Class<?> simplified = null;
@@ -128,7 +128,7 @@ public class LocalClient implements RhqFacade {
Object proxy = Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] { simplified },
new LocalClientProxy(slsb, this, manager));
- return iface.cast(proxy);
+ return proxy;
}
private Object getLocalSLSB(RhqManager manager) {
commit 56d3bb7e5f2b3ec81138841058edebe9a6bc1ac2
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 15:29:15 2012 +0200
CLI code completion should now be language independent.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 9811d2a..5d1af69 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -40,9 +40,6 @@ import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
-import gnu.getopt.Getopt;
-import gnu.getopt.LongOpt;
-
import jline.ArgumentCompletor;
import jline.Completor;
import jline.ConsoleReader;
@@ -56,7 +53,11 @@ import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.commands.ClientCommand;
import org.rhq.enterprise.client.commands.ScriptCommand;
import org.rhq.enterprise.client.script.CommandLineParseException;
+import org.rhq.enterprise.client.utility.CLIMetadataProvider;
+import org.rhq.enterprise.client.utility.CodeCompletionCompletorWrapper;
+import org.rhq.enterprise.client.utility.DummyCodeCompletion;
import org.rhq.enterprise.clientapi.RemoteClient;
+import org.rhq.scripting.CodeCompletion;
/**
* @author Greg Hinkle
@@ -100,7 +101,7 @@ public class ClientMain {
// The subject that will be used to carry out all requested actions
private Subject subject;
- private InteractiveJavascriptCompletor serviceCompletor;
+ private CodeCompletion codeCompletion;
private boolean interactiveMode = true;
@@ -199,12 +200,9 @@ public class ClientMain {
sc.initClient(this);
}
- private void initServiceCompletor() {
- this.serviceCompletor.setContext(getScriptEngine().getContext());
-
- if (remoteClient != null) {
- this.serviceCompletor.setServices(remoteClient.getScriptingAPI());
- }
+ private void initCodeCompletion() {
+ this.codeCompletion.setScriptContext(getScriptEngine().getContext());
+ this.codeCompletion.setMetadataProvider(new CLIMetadataProvider());
}
public ClientMain() {
@@ -233,12 +231,19 @@ public class ClientMain {
Completor helpCompletor = new ArgumentCompletor(new Completor[] { new SimpleCompletor("help"),
new SimpleCompletor(commands.keySet().toArray(new String[commands.size()])) });
- this.serviceCompletor = new InteractiveJavascriptCompletor(consoleReader);
- consoleReader.addCompletor(new MultiCompletor(new Completor[] { serviceCompletor, helpCompletor,
+ this.codeCompletion = ScriptEngineFactory.getCodeCompletion(getLanguage());
+ if (codeCompletion == null) {
+ //the language module for this language doesn't support code completion
+ //let's provide a dummy one.
+ codeCompletion = new DummyCodeCompletion();
+ }
+
+ initCodeCompletion();
+
+ consoleReader.addCompletor(new MultiCompletor(new Completor[] {
+ new CodeCompletionCompletorWrapper(codeCompletion, outputWriter), helpCompletor,
commandCompletor }));
- initServiceCompletor();
-
// enable pagination
consoleReader.setUsePagination(true);
}
@@ -375,7 +380,7 @@ public class ClientMain {
} else {
boolean result = commands.get("exec").execute(this, args);
if (loggedIn()) {
- this.serviceCompletor.setContext(getScriptEngine().getContext());
+ this.codeCompletion.setScriptContext(getScriptEngine().getContext());
}
return result;
@@ -595,7 +600,7 @@ public class ClientMain {
initScriptCommand();
if (isInteractiveMode()) {
- initServiceCompletor();
+ initCodeCompletion();
}
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
deleted file mode 100644
index 0f1b232..0000000
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
+++ /dev/null
@@ -1,603 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2008 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.enterprise.client;
-
-import java.beans.BeanInfo;
-import java.beans.Introspector;
-import java.beans.MethodDescriptor;
-import java.beans.PropertyDescriptor;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import javax.jws.WebParam;
-import javax.script.Bindings;
-import javax.script.ScriptContext;
-
-import jline.Completor;
-import jline.ConsoleReader;
-
-import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.client.utility.ReflectionUtility;
-import org.rhq.enterprise.clientapi.RemoteClientProxy;
-
-/**
- * A Contextual JavaScript interactive completor. Not perfect, but
- * handles a fair number of cases.
- *
- * @author Greg Hinkle
- */
-public class InteractiveJavascriptCompletor implements Completor {
-
- private Map<String, Object> services;
-
- private ScriptContext context;
-
- private String lastComplete;
-
- // Consecutive times this exact complete has been requested
- private int recomplete;
- private ConsoleReader consoleReader;
-
- private static final Set<String> IGNORED_METHODS;
- static {
- IGNORED_METHODS = new HashSet<String>();
- IGNORED_METHODS.add("newProxyInstance");
- IGNORED_METHODS.add("hashCode");
- IGNORED_METHODS.add("equals");
- IGNORED_METHODS.add("getInvocationHandler");
- IGNORED_METHODS.add("setHandler");
- IGNORED_METHODS.add("isProxyClass");
- IGNORED_METHODS.add("newProxyInstance");
- IGNORED_METHODS.add("getProxyClass");
- IGNORED_METHODS.add("main");
- IGNORED_METHODS.add("handler");
- IGNORED_METHODS.add("init");
- IGNORED_METHODS.add("initChildren");
- IGNORED_METHODS.add("initMeasurements");
- IGNORED_METHODS.add("initOperations");
- }
-
- public InteractiveJavascriptCompletor(ConsoleReader reader) {
- this.consoleReader = reader;
- }
-
- public void setServices(Map<String, Object> services) {
- this.services = services;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public int complete(String s, int i, @SuppressWarnings("rawtypes") List list) {
- try {
- if (lastComplete != null && lastComplete.equals(s)) {
- recomplete++;
- } else {
- recomplete = 1;
- }
-
- lastComplete = s;
-
- String base = s;
-
- int rootLength = 0;
-
- if (s.indexOf('=') > 0) {
- base = s.substring(s.indexOf("=") + 1).trim();
- rootLength = s.length() - base.length();
- }
-
- String[] call = base.split("\\.");
- if (base.endsWith(".")) {
- String[] argPadded = new String[call.length + 1];
- System.arraycopy(call, 0, argPadded, 0, call.length);
- argPadded[call.length] = "";
- call = argPadded;
- }
-
- if (call.length == 1) {
- Map<String, Object> matches = getContextMatches(call[0]);
- if (matches.size() == 1 && matches.containsKey(call[0]) && !s.endsWith(".")) {
- list.add(".");
- return rootLength + call[0].length() + 1;
-
- } else {
- list.addAll(matches.keySet());
- }
- } else {
- Object rootObject = context.getAttribute(call[0]);
- if (rootObject != null) {
- String theRest = base.substring(call[0].length() + 1, base.length());
- int matchIndex = contextComplete(rootObject, theRest, i, list);
- Collections.sort(list);
- return rootLength + call[0].length() + 1 + matchIndex;
- }
- }
-
- Collections.sort(list);
-
- return (list.size() == 0) ? (-1) : rootLength;
- } catch (Exception e) {
- e.printStackTrace();
- return -1;
- }
- }
-
- /**
- * Base Object can be an object where we're looking for methods on it, or an
- * interface. This recursively works off the completions left to right.
- *
- * Objects can be completed with fields or method calls.
- * method parameters are completed with type matching
- * method result chainings are completed based on declared return types
- *
- * e.g. have a Resource in context as myResource. Original string is
- * "myResource.name". This method would be called with a baseObject ==
- * to myResource and the string "name".
- *
- * Note: this method will not and should not execute methods, but will
- * read field properties to continue chained completions.
- *
- * @param baseObject the context object or class to complete from
- * @param s the relative command string to check
- * @param i
- * @param list
- * @return location of relative completion
- */
- private int contextComplete(Object baseObject, String s, int i, List<String> list) {
- String temp = s.split("\\(", 2)[0];
- if (temp.contains(".")) {
- String[] call = temp.split("\\.", 2);
-
- String next = call[0];
- if (next.contains("(")) {
- next = next.substring(0, next.indexOf("("));
- }
-
- Map<String, List<Object>> matches = getContextMatches(baseObject, next);
- if (!matches.isEmpty()) {
- Object rootObject = matches.get(next).get(0);
- if (rootObject instanceof PropertyDescriptor && !(baseObject instanceof Class)) {
- try {
- rootObject = invoke(baseObject, ((PropertyDescriptor) rootObject).getReadMethod());
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else if (rootObject instanceof Method) {
- rootObject = ((Method) rootObject).getReturnType();
- }
-
- return call[0].length() + 1 + contextComplete(rootObject, call[1], i, list);
- } else {
- return -1;
- }
- } else {
- String[] call = s.split("\\(", 2);
-
- Map<String, List<Object>> matches = getContextMatches(baseObject, call[0]);
-
- if (call.length == 2 && matches.containsKey(call[0])) {
-
- int x = 0;
- for (String key : matches.keySet()) {
-
- List<Object> matchList = matches.get(key);
-
- if (recomplete == 2) {
- List<Method> methods = new ArrayList<Method>();
- for (Object match : matchList) {
- if (match instanceof Method) {
- methods.add((Method) match);
- }
- }
- displaySignatures(baseObject, methods.toArray(new Method[methods.size()]));
- return -1;
- }
-
- for (Object match : matchList) {
-
- if (key.equals(call[0]) && match instanceof Method) {
-
- int result = completeParameters(baseObject, call[1], i, list, (Method) match); // x should be the same for all calls
- if (result > 0) {
- x = result;
- }
- }
- }
- }
- return call[0].length() + 1 + x;
- }
-
- if (matches.size() == 1 && matches.containsKey(call[0])) {
- if (matches.get(call[0]).get(0) instanceof Method) {
- list.add("(" + (((Method) matches.get(call[0]).get(0)).getParameterTypes().length == 0 ? ")" : ""));
- }
- return call[0].length() + 1;
- }
-
- if (recomplete == 2) {
- List<Method> methods = new ArrayList<Method>();
- for (List<Object> matchList : matches.values()) {
- for (Object val : matchList) {
- if (val instanceof Method) {
- methods.add((Method) val);
- }
- }
- }
- displaySignatures(baseObject, methods.toArray(new Method[methods.size()]));
- } else {
- if (matches.size() == 1 && matches.values().iterator().next().get(0) instanceof Method) {
- list.add(matches.keySet().iterator().next()
- + "("
- + ((((Method) matches.values().iterator().next().get(0)).getParameterTypes().length == 0 ? ")"
- : "")));
- } else {
- list.addAll(matches.keySet());
- }
- }
- return 0;
-
- }
- }
-
- private void displaySignatures(Object object, Method... methods) {
- try {
- String start = this.consoleReader.getCursorBuffer().getBuffer().toString();
- while ((this.consoleReader.getCursorBuffer().cursor > 0) && this.consoleReader.backspace()) {
- ;
- }
- this.consoleReader.printNewline();
- this.consoleReader.printNewline();
- String[][] signatures = new String[methods.length][];
- int i = 0;
- for (Method m : methods) {
- signatures[i++] = getSignature(object, m).split(" ", 2);
- }
-
- int maxReturnLength = 0;
- for (String[] sig : signatures) {
- if (sig[0].length() > maxReturnLength)
- maxReturnLength = sig[0].length();
- }
-
- for (String[] sig : signatures) {
- for (i = 0; i < (maxReturnLength - sig[0].length()); i++) {
- this.consoleReader.printString(" ");
- }
-
- this.consoleReader.printString(sig[0]);
- this.consoleReader.printString(" ");
- this.consoleReader.printString(sig[1]);
- this.consoleReader.printNewline();
- }
-
- this.consoleReader.drawLine();
- this.consoleReader.putString(start);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Split apart the parameters to a method call and complete the last parameter. If the last
- * paramater has a valid value close that field with a "," for the next param or a ")" if is
- * the last parameter. Does all machting according to the type of the parameters of the
- * supplied method.
- *
- * @param baseObject
- * @param params
- * @param i
- * @param list
- * @param method
- * @return
- */
- public int completeParameters(Object baseObject, String params, int i, List<String> list, Method method) {
-
- String[] paramList = params.split(",");
-
- Class<?>[] c = method.getParameterTypes();
-
- String lastParam = paramList[paramList.length - 1];
- int paramIndex = paramList.length - 1;
- if (params.trim().endsWith(",")) {
- lastParam = "";
- paramIndex++;
- }
-
- int baseLength = 0;
-
- for (int x = 0; x < paramIndex; x++) {
- Object paramFound = context.getAttribute(paramList[x]);
-
- if (paramFound != null && !c[x].isAssignableFrom(paramFound.getClass())) {
- return -1;
- }
- baseLength += paramList[x].length() + 1;
- }
-
- if (paramIndex >= c.length) {
- if (params.endsWith(")")) {
- return -1;
- } else {
- list.add(params + ")");
- return (params + ")").length();
- }
- } else {
-
- if (baseObject instanceof Map && method.getName().equals("get") && method.getParameterTypes().length == 1) {
- //unused Class<?> keyType = method.getParameterTypes()[0];
- for (Object key : ((Map<?, ?>) baseObject).keySet()) {
- String lookupChoice = "\'" + String.valueOf(key) + "\'";
- if (lookupChoice.startsWith(lastParam)) {
- list.add(lookupChoice);
- }
- }
- if (list.size() == 1) {
- list.set(0, list.get(0) + ")");
- }
-
- } else {
- Class<?> parameterType = c[paramIndex];
-
- Map<String, Object> matches = getContextMatches(lastParam, parameterType);
-
- if (matches.size() == 1 && matches.containsKey(lastParam)) {
-
- list.add(paramIndex == c.length - 1 ? ")" : ",");
- return baseLength + lastParam.length();
- } else {
- list.addAll(matches.keySet());
- }
- }
-
- return baseLength;
- }
- }
-
- private Map<String, Object> getContextMatches(String start) {
- Map<String, Object> found = new HashMap<String, Object>();
- if (context != null) {
- for (Integer scope : context.getScopes()) {
- Bindings bindings = context.getBindings(scope);
- for (String var : bindings.keySet()) {
- if (var.startsWith(start)) {
- found.put(var, bindings.get(var));
- }
- }
- }
- }
- if (services != null) {
- for (String var : services.keySet()) {
- if (var.startsWith(start)) {
- found.put(var, services.get(var));
- }
- }
- }
- return found;
- }
-
- /**
- * Look through all available contexts to find bindings that both start with
- * the supplied start and match the typeFilter.
- * @param start
- * @param typeFilter
- * @return
- */
- private Map<String, Object> getContextMatches(String start, Class<?> typeFilter) {
- Map<String, Object> found = new HashMap<String, Object>();
- if (context != null) {
- for (int scope : context.getScopes()) {
- Bindings bindings = context.getBindings(scope);
- for (String var : bindings.keySet()) {
- if (var.startsWith(start)) {
-
- if ((bindings.get(var) != null && typeFilter.isAssignableFrom(bindings.get(var).getClass()))
- || recomplete == 3) {
- found.put(var, bindings.get(var));
- }
- }
- }
- }
-
- if (typeFilter.isEnum()) {
- for (Object ec : typeFilter.getEnumConstants()) {
- Enum<?> e = (Enum<?>) ec;
- String code = typeFilter.getSimpleName() + "." + e.name();
- if (code.startsWith(start)) {
- found.put(typeFilter.getSimpleName() + "." + e.name(), e);
- }
- }
- }
- }
- return found;
- }
-
- private Map<String, List<Object>> getContextMatches(Object baseObject, String start) {
- Map<String, List<Object>> found = new HashMap<String, List<Object>>();
-
- Class<?> baseObjectClass = null;
- if (baseObject instanceof Class) {
- baseObjectClass = (Class<?>) baseObject;
- } else {
- baseObjectClass = baseObject.getClass();
- }
-
- try {
- if (baseObjectClass.equals(Void.TYPE))
- return found;
-
- BeanInfo info = null;
- if (baseObjectClass.isInterface() || baseObjectClass.equals(Object.class)) {
- info = Introspector.getBeanInfo(baseObjectClass);
- } else {
- info = Introspector.getBeanInfo(baseObjectClass, Object.class);
- }
-
- Set<Method> methodsCovered = new HashSet<Method>();
-
- PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
- for (PropertyDescriptor desc : descriptors) {
- if (desc.getName().startsWith(start) && (!IGNORED_METHODS.contains(desc.getName()))) {
-
- List<Object> list = found.get(desc.getName());
- if (list == null) {
- list = new ArrayList<Object>();
- found.put(desc.getName(), list);
- }
- list.add(desc);
-
- methodsCovered.add(desc.getReadMethod());
- methodsCovered.add(desc.getWriteMethod());
- }
- }
-
- MethodDescriptor[] methods = info.getMethodDescriptors();
- for (MethodDescriptor desc : methods) {
- if (desc.getName().startsWith(start) && !methodsCovered.contains(desc.getMethod())
- && !desc.getName().startsWith("_d") && !IGNORED_METHODS.contains(desc.getName())) {
-
- Method m = desc.getMethod();
- boolean isProxy = isProxyMethod(baseObject, m);
- Class<?>[] parameters = m.getParameterTypes();
- boolean startsWithSubject = ((parameters.length > 0) && parameters[0].equals(Subject.class));
- if ((isProxy && startsWithSubject) || !startsWithSubject) {
-
- List<Object> list = found.get(desc.getName());
- if (list == null) {
- list = new ArrayList<Object>();
- found.put(desc.getName(), list);
- }
- list.add(m);
- }
- }
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- return found;
- }
-
- private static String getSignature(Object object, Method m) {
-
- // If its our service proxy lookup the original interface that has the annotations
- if (isProxyMethod(object, m)) {
- Class<?>[] params = m.getParameterTypes();
- Class<?>[] newParams = new Class[params.length + 1];
- System.arraycopy(params, 0, newParams, 1, params.length);
- newParams[0] = Subject.class;
-
- try {
- m =
- ((RemoteClientProxy) Proxy.getInvocationHandler(object)).getRemoteInterface().getDeclaredMethod(
- m.getName(), newParams);
- } catch (NoSuchMethodException nsme) {
- //
- }
- }
-
- StringBuilder buf = new StringBuilder();
- Type[] params = m.getGenericParameterTypes();
- Annotation[][] annotations = m.getParameterAnnotations();
- int i = 0;
-
- buf.append(ReflectionUtility.getSimpleTypeString(m.getGenericReturnType()));
- buf.append(" ");
-
- buf.append(m.getName());
- buf.append("(");
- boolean first = true;
- for (Type type : params) {
- if (i == 0 && type.equals(Subject.class)) {
- i++;
- continue;
- }
- if (!first) {
- buf.append(", ");
- } else {
- first = false;
- }
-
- String name = null;
-
- if (annotations != null && annotations.length >= i) {
- Annotation[] as = annotations[i];
- for (Annotation a : as) {
- if (a instanceof WebParam) {
- name = ReflectionUtility.getSimpleTypeString(type) + " " + ((WebParam) a).name();
- }
- }
- }
-
- if (name == null) {
- name = ReflectionUtility.getSimpleTypeString(type);
- }
-
- buf.append(name);
-
- i++;
- }
- buf.append(")");
- return buf.toString();
- }
-
- private static boolean isProxyMethod(Object object, Method m) {
-
- boolean result = false;
- if (object instanceof Proxy) {
- if (Proxy.getInvocationHandler(object) instanceof RemoteClientProxy) {
- try {
- m =
- ((RemoteClientProxy) Proxy.getInvocationHandler(object)).getRemoteInterface()
- .getDeclaredMethod(m.getName(), m.getParameterTypes());
- } catch (NoSuchMethodException e) {
- result = true;
- }
- }
- }
- return result;
- }
-
- public ScriptContext getContext() {
- return context;
- }
-
- public void setContext(ScriptContext context) {
- this.context = context;
- }
-
- private static Object invoke(Object o, Method m) throws IllegalAccessException, InvocationTargetException {
- boolean access = m.isAccessible();
- m.setAccessible(true);
- try {
- return m.invoke(o);
- } finally {
- m.setAccessible(access);
- }
- }
-}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java
new file mode 100644
index 0000000..ff98b5c
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CLIMetadataProvider.java
@@ -0,0 +1,72 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.utility;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import javax.jws.WebParam;
+
+import org.rhq.scripting.MetadataProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public class CLIMetadataProvider implements MetadataProvider {
+
+ @Override
+ public String getParameterName(Method method, int parameterIndex) {
+ String name = null;
+
+ Annotation[][] paramAnnotations = method.getParameterAnnotations();
+
+ if (paramAnnotations.length > parameterIndex) {
+ Annotation[] annotations = paramAnnotations[parameterIndex];
+ for (Annotation a : annotations) {
+ if (a instanceof WebParam) {
+ name = ((WebParam) a).name();
+ break;
+ }
+ }
+ }
+
+ return name;
+ }
+
+ @Override
+ public String getDocumentation(Class<?> clazz) {
+ // TODO it'd be fantastic if we could do this, wouldn't it?
+ return null;
+ }
+
+ @Override
+ public String getDocumentation(Method method) {
+ // TODO it'd be fantastic if we could do this, wouldn't it?
+ return null;
+ }
+
+ @Override
+ public String getTypeName(Type type, boolean fullNames) {
+ return ReflectionUtility.getTypeString(type, fullNames);
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java
new file mode 100644
index 0000000..8f2570c
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/CodeCompletionCompletorWrapper.java
@@ -0,0 +1,24 @@
+package org.rhq.enterprise.client.utility;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import jline.Completor;
+
+import org.rhq.scripting.CodeCompletion;
+
+public class CodeCompletionCompletorWrapper implements Completor {
+
+ private CodeCompletion completion;
+ private PrintWriter output;
+
+ public CodeCompletionCompletorWrapper(CodeCompletion completion, PrintWriter output) {
+ this.completion = completion;
+ this.output = output;
+ }
+
+ @Override
+ public int complete(String buffer, int cursor, List candidates) {
+ return completion.complete(output, buffer, cursor, candidates);
+ }
+}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/DummyCodeCompletion.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/DummyCodeCompletion.java
new file mode 100644
index 0000000..2c28a4f
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/DummyCodeCompletion.java
@@ -0,0 +1,50 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.utility;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import javax.script.ScriptContext;
+
+import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.MetadataProvider;
+
+/**
+ * @author Lukas Krejci
+ *
+ */
+public class DummyCodeCompletion implements CodeCompletion {
+
+ @Override
+ public void setScriptContext(ScriptContext scriptContext) {
+ }
+
+ @Override
+ public void setMetadataProvider(MetadataProvider metadataProvider) {
+ }
+
+ @Override
+ public int complete(PrintWriter output, String context, int cursorPosition, List candidates) {
+ return cursorPosition;
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ReflectionUtility.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ReflectionUtility.java
index c5c2f08..3bcb1cc 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ReflectionUtility.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/utility/ReflectionUtility.java
@@ -18,10 +18,10 @@
*/
package org.rhq.enterprise.client.utility;
-import java.lang.reflect.Type;
+import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.lang.reflect.GenericArrayType;
import java.lang.reflect.WildcardType;
/**
@@ -32,6 +32,10 @@ public class ReflectionUtility {
public static String getSimpleTypeString(Type type) {
+ return getTypeString(type, false);
+ }
+
+ public static String getTypeString(Type type, boolean fullNames) {
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
Type[] typeArguments = pType.getActualTypeArguments();
@@ -43,26 +47,29 @@ public class ReflectionUtility {
typeArgString += ",";
}
- typeArgString += getSimpleTypeString(typeArgument);
+ typeArgString += getTypeString(typeArgument, fullNames);
}
- return getSimpleTypeString(pType.getRawType()) + "<" + typeArgString + ">";
-
+ return getTypeString(pType.getRawType(), fullNames) + "<" + typeArgString + ">";
} else if (type instanceof TypeVariable) {
- TypeVariable vType = (TypeVariable) type;
- return vType.getClass().getSimpleName();
+ TypeVariable<?> vType = (TypeVariable<?>) type;
+ return getName(vType.getClass(), fullNames);
} else if (type instanceof GenericArrayType) {
GenericArrayType aType = (GenericArrayType) type;
- return aType.getClass().getSimpleName() + "[" + getSimpleTypeString(aType.getGenericComponentType()) + "]";
+ return getName(aType.getClass(), fullNames) + "["
+ + getTypeString(aType.getGenericComponentType(), fullNames) + "]";
} else if (type instanceof WildcardType) {
- return ((WildcardType)type).toString();
+ return ((WildcardType) type).toString();
} else {
if (type == null) {
return "";
} else {
- return ((Class)type).getSimpleName();
+ return getName((Class<?>) type, fullNames);
}
}
}
+ private static String getName(Class<?> cls, boolean fullName) {
+ return fullName ? cls.getName() : cls.getSimpleName();
+ }
}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
index d1df7f5..36eb060 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
@@ -38,6 +38,9 @@ import java.util.Set;
import javax.script.Bindings;
import javax.script.ScriptContext;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.rhq.scripting.CodeCompletion;
import org.rhq.scripting.MetadataProvider;
@@ -50,7 +53,8 @@ import org.rhq.scripting.MetadataProvider;
*/
public class JavascriptCompletor implements CodeCompletion {
- private static final String SUBJECT_CLASS_NAME = "org.rhq.core.domain.auth.Subject";
+ private static final Log LOG = LogFactory.getLog(JavascriptCompletor.class);
+
private ScriptContext context;
private MetadataProvider metadataProvider;
@@ -473,26 +477,21 @@ public class JavascriptCompletor implements CodeCompletion {
&& !desc.getName().startsWith("_d") && !IGNORED_METHODS.contains(desc.getName())) {
Method m = desc.getMethod();
- //TODO THIS IS SO WRONG - the methods are there but we just "forget" to
- //to mention them to the user.
- boolean isProxy = isProxyMethod(baseObject, m);
- Class<?>[] parameters = m.getParameterTypes();
- boolean startsWithSubject = ((parameters.length > 0) && isSubjectClass(parameters[0]));
- if ((isProxy && startsWithSubject) || !startsWithSubject) {
-
- List<Object> list = found.get(desc.getName());
- if (list == null) {
- list = new ArrayList<Object>();
- found.put(desc.getName(), list);
- }
- list.add(m);
+
+ List<Object> list = found.get(desc.getName());
+ if (list == null) {
+ list = new ArrayList<Object>();
+ found.put(desc.getName(), list);
}
+ list.add(m);
}
}
} catch (Exception e) {
+ LOG.info("Failure during code completion", e);
e.printStackTrace(output);
}
+
return found;
}
@@ -529,26 +528,6 @@ public class JavascriptCompletor implements CodeCompletion {
return buf.toString();
}
- private static boolean isProxyMethod(Object object, Method m) {
- // TODO this has to be changed - we have the MetaDataProvider which should be able
- // to provide enough info so that the completors don't have to go through these hoops.
- return false;
-
- // boolean result = false;
- // if (object instanceof Proxy) {
- // if (Proxy.getInvocationHandler(object) instanceof RemoteClientProxy) {
- // try {
- // m =
- // ((RemoteClientProxy) Proxy.getInvocationHandler(object)).getRemoteInterface()
- // .getDeclaredMethod(m.getName(), m.getParameterTypes());
- // } catch (NoSuchMethodException e) {
- // result = true;
- // }
- // }
- // }
- // return result;
- }
-
@Override
public void setScriptContext(ScriptContext context) {
this.context = context;
@@ -563,16 +542,4 @@ public class JavascriptCompletor implements CodeCompletion {
m.setAccessible(access);
}
}
-
- private boolean isSubjectClass(Class<?> cls) {
- while (cls != null) {
- if (SUBJECT_CLASS_NAME.equals(cls.getName())) {
- return true;
- }
-
- cls = cls.getSuperclass();
- }
-
- return false;
- }
}
commit c7cd0fa0318df8039f0864dd2faf3b2d67d4aaef
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 15:23:53 2012 +0200
The simplified interfaces no longer inherit from the remote interfaces.
They just provide the same (or simplified) methods.
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 8407206..d6835a0 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
@@ -47,31 +47,29 @@ public class InterfaceSimplifier {
public static Class<?> simplify(Class<?> intf) {
try {
- ClassPool cp = ClassPool.getDefault();
+ ClassPool classPool = ClassPool.getDefault();
String simplifiedName = getSimplifiedName(intf);
LOG.debug("Simplifying " + intf + " (simplified interface name: " + simplifiedName + ")...");
try {
- CtClass cached = cp.get(simplifiedName);
- return Class.forName(simplifiedName, false, cp.getClassLoader());
+ @SuppressWarnings("unused")
+ CtClass cached = classPool.get(simplifiedName);
+ return Class.forName(simplifiedName, false, classPool.getClassLoader());
} catch (NotFoundException e) {
// ok... load it
} catch (ClassNotFoundException e) {
- LOG.debug("Class [" + simplifiedName + "] not found - cause: " + e);
+ LOG.debug("Class [" + simplifiedName + "] not found - cause: " + e, e);
}
- CtClass cc = cp.get(intf.getName());
+ CtClass originalClass = classPool.get(intf.getName());
- CtClass cz = cp.getAndRename(intf.getName(), simplifiedName);
- // CtClass cz = cp.makeInterface(simpleName, cc);
+ CtClass newClass = classPool.makeInterface(simplifiedName);
- cz.defrost();
+ newClass.defrost();
- cz.setSuperclass(cc);
-
- CtMethod[] methods = cc.getMethods();
+ CtMethod[] methods = originalClass.getMethods();
for (CtMethod originalMethod : methods) {
@@ -81,10 +79,10 @@ public class InterfaceSimplifier {
CtClass[] simpleParams = new CtClass[params.length - 1];
System.arraycopy(params, 1, simpleParams, 0, params.length - 1);
- cz.defrost();
+ newClass.defrost();
CtMethod newMethod = CtNewMethod.abstractMethod(originalMethod.getReturnType(), originalMethod
- .getName(), simpleParams, null, cz);
+.getName(), simpleParams, null, newClass);
ParameterAnnotationsAttribute originalAnnotationsAttribute = (ParameterAnnotationsAttribute) originalMethod
.getMethodInfo().getAttribute(ParameterAnnotationsAttribute.visibleTag);
@@ -111,11 +109,11 @@ public class InterfaceSimplifier {
}
- cz.addMethod(newMethod);
+ newClass.addMethod(newMethod);
}
}
- return cz.toClass();
+ return newClass.toClass();
} catch (NotFoundException e) {
LOG.debug("Failed to simplify " + intf + " - cause: " + e);
diff --git a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
index eac3705..2236923 100644
--- a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
+++ b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
@@ -33,14 +33,13 @@ import javax.naming.spi.InitialContextFactory;
import org.jmock.Expectations;
import org.jmock.api.Invocation;
import org.jmock.lib.action.CustomAction;
+import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.rhq.bindings.client.RhqManager;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.LocalClient;
-import org.rhq.enterprise.server.alert.AlertManagerLocal;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
import org.rhq.test.JMockTest;
/**
@@ -67,13 +66,6 @@ public class LocalClientTest extends JMockTest {
@Test
public void testResilienceAgainstContextClassloaders() throws Exception {
CONTEXT_MOCK_FOR_TEST = context.mock(Context.class);
- final AlertManagerRemote alertManagerMock = (AlertManagerRemote) Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { AlertManagerRemote.class, AlertManagerLocal.class }, new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return null;
- }
-
- });
context.checking(new Expectations() {{
allowing(CONTEXT_MOCK_FOR_TEST).lookup(with(any(String.class)));
@@ -122,8 +114,14 @@ public class LocalClientTest extends JMockTest {
//this call creates the proxy and is theoretically prone to the context classloader
Object am = lc.getScriptingAPI().get("AlertManager");
- //check that both the original and simplified methods exist on the returned object
- am.getClass().getMethod("deleteAlerts", new Class<?>[] { Subject.class, int[].class });
+ //check that only the simplified method exists on the returned object
+ try {
+ am.getClass().getMethod("deleteAlerts", new Class<?>[] { Subject.class, int[].class });
+ Assert.fail("The original remote interface method should not be available on the scripting API proxy.");
+ } catch (NoSuchMethodException e) {
+ //expected
+ }
+
am.getClass().getMethod("deleteAlerts", new Class<?>[] { int[].class });
} finally {
Thread.currentThread().setContextClassLoader(origCl);
commit 32a6fdd2b88f8711c1d1c14763e8230a62a8eb91
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 15:21:44 2012 +0200
Fixing the local client tests and some leftover compilation errors in
the code after the RhqFacade refactoring.
diff --git a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
index 74f7ab8..f13e087 100644
--- a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
+++ b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
@@ -2,7 +2,9 @@ package org.rhq.enterprise.server.perspective;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.jboss.seam.annotations.web.RequestParameter;
+
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.resource.Resource;
@@ -37,7 +39,7 @@ public class AbstractPerspectiveResourceUIBean extends AbstractPerspectiveUIBean
Subject subject = this.perspectiveClient.getSubject();
// ***NOTE***: The javassist.NotFoundException stack traces that are logged by this call can be ignored.
- ResourceManagerRemote resourceManager = remoteClient.getResourceManager();
+ ResourceManagerRemote resourceManager = remoteClient.getProxy(ResourceManagerRemote.class);
ResourceCriteria resourceCriteria = new ResourceCriteria();
resourceCriteria.addFilterId(this.rhqResourceId);
PageList<Resource> resources = resourceManager.findResourcesByCriteria(subject, resourceCriteria);
diff --git a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
index 77d8f24..d73431a 100644
--- a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
+++ b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
@@ -87,7 +87,7 @@ public class PerspectiveClientUIBean {
if (subject == null) {
RemoteClient remoteClient = getRemoteClient();
// ***NOTE***: The javassist.NotFoundException stack traces that are logged by this call can be ignored.
- SubjectManagerRemote subjectManager = remoteClient.getSubjectManager();
+ SubjectManagerRemote subjectManager = remoteClient.getProxy(SubjectManagerRemote.class);
if (this.rhqSessionId != null) {
log.info("Retrieving subject for user [" + getUsername() + "] and sessionId [" + this.rhqSessionId
+ "]...");
diff --git a/modules/enterprise/server/client-api/pom.xml b/modules/enterprise/server/client-api/pom.xml
index 81b559c..91682fa 100644
--- a/modules/enterprise/server/client-api/pom.xml
+++ b/modules/enterprise/server/client-api/pom.xml
@@ -46,7 +46,15 @@
<artifactId>test-utils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
- </dependency>
+ </dependency>
+
+ <!-- Needed for the successful creation of the fake SLSB impls in the LocalClient test. -->
+ <dependency>
+ <groupId>org.opensymphony.quartz</groupId>
+ <artifactId>quartz</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
</dependencies>
<profiles>
diff --git a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
index e9bd928..eac3705 100644
--- a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
+++ b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
@@ -31,9 +31,12 @@ import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import org.jmock.Expectations;
+import org.jmock.api.Invocation;
+import org.jmock.lib.action.CustomAction;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.LocalClient;
import org.rhq.enterprise.server.alert.AlertManagerLocal;
@@ -74,7 +77,36 @@ public class LocalClientTest extends JMockTest {
context.checking(new Expectations() {{
allowing(CONTEXT_MOCK_FOR_TEST).lookup(with(any(String.class)));
- will(returnValue(alertManagerMock));
+ will(new CustomAction("Fake JNDI lookup") {
+
+ @Override
+ public Object invoke(Invocation invocation) throws Throwable {
+ //the JNDI name is "rhq/<BEAN_NAME>/local"
+ String jndiName = (String) invocation.getParameter(0);
+
+ String beanName = jndiName.substring(jndiName.indexOf('/') + 1, jndiName.lastIndexOf('/'));
+
+ String managerName = beanName.substring(0, beanName.length() - "Bean".length());
+
+ //we basically need to define a mock implementation of both the local and remote
+ //interface here - as if it were a proper SLSB.
+ RhqManager manager = Enum.valueOf(RhqManager.class, managerName);
+ Class<?> remoteIface = manager.remote();
+
+ String localIfaceName = remoteIface.getName().substring(0,
+ remoteIface.getName().length() - "Remote".length())
+ + "Local";
+ Class<?> localIface = Class.forName(localIfaceName);
+
+ return Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { localIface,
+ remoteIface }, new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+ }
+ });
allowing(CONTEXT_MOCK_FOR_TEST).close();
}});
commit 910d79257e03273a2f43465c41e9690373630ccd
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 12:25:57 2012 +0200
Resolve the initializer using the ScriptEngine.NAME parameter rather than
the language name - this is in line with how the different language support
modules identify themselves.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
index df78b41..fbaf1a7 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
@@ -216,7 +216,9 @@ public class ScriptEngineFactory {
return;
}
- ScriptEngineInitializer initializer = getInitializer(scriptEngine.getFactory().getLanguageName());
+ ScriptEngineInitializer initializer = getInitializer((String) scriptEngine.getFactory().getParameter(
+ ScriptEngine.NAME));
+
try {
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass(), Object.class);
MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors();
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
index db22068..2742970 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
@@ -32,6 +32,8 @@ public interface ScriptEngineProvider {
/**
* @return the scripting language understood by this provider.
+ * This must return the same string as <code>ScriptEngine.getFactory().getParameter(ScriptEngine.NAME)</code>
+ * of the script engine that this provider provides (through the initializer).
*/
@NotNull
String getSupportedLanguage();
commit cdf864db43dc1c9908bdf157dd1eb283e05f5b14
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 12:22:41 2012 +0200
Removing all the get*Manager() methods on the RhqFacade and replacing it
with one generic getProxy(Class) method. This method returns a proxy of
the original remote API interface (i.e. the interface is NOT simplified).
The getManagers() method returns a map of manager proxies where
the interfaces ARE simplified.
This is in preparation for making the simplified interfaces NOT implement
the original remote APIs so that we have greater freedom in what can be done
with the API and also make code completion significantly more clean and
simple.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
index 05e6a09..812b4ae 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
@@ -120,7 +120,7 @@ public class StandardBindings extends HashMap<String, Object> {
put(SCRIPT_UTIL, new ScriptUtil(rhqFacade));
if (rhqFacade != null) {
- managers = rhqFacade.getManagers();
+ managers = rhqFacade.getScriptingAPI();
put(SUBJECT, rhqFacade.getSubject());
put(PROXY_FACTORY, new ResourceClientFactory(rhqFacade, output));
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
index ef46bb2..bd37131 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
@@ -50,23 +50,31 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Class<?>[] params = method.getParameterTypes();
-
+ Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
+ Class<?> originalClass;
+ if (interfaces != null && interfaces.length > 0) {
+ originalClass = interfaces[0];
+ } else {
+ originalClass = method.getDeclaringClass();
+ }
+
try {
- Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
-
- Class<?> originalClass;
- if (interfaces != null && interfaces.length > 0) {
- originalClass = interfaces[0];
- } else {
- originalClass = method.getDeclaringClass();
- }
-
// See if this method really exists or if its a simplified set of parameters
originalClass.getMethod(method.getName(), method.getParameterTypes());
-
- } catch (Exception e) {
+ } catch (NoSuchMethodException e) {
// If this was not in the original interface it must've been added in the Simplifier... add back the subject argument
+ Class<?>[] origParams = method.getParameterTypes();
+ Class<?>[] params = new Class<?>[origParams.length + 1];
+ params[0] = Subject.class;
+ System.arraycopy(method.getParameterTypes(), 0, params, 1, origParams.length);
+
+ try {
+ method = originalClass.getMethod(method.getName(), params);
+ } catch (NoSuchMethodException e2) {
+ throw new IllegalArgumentException("Method " + method + " doesn't seem to be present on the interface "
+ + originalClass + " neither in its original or simplified form.");
+ }
+
int numArgs = (null == args) ? 0 : args.length;
Object[] newArgs = new Object[numArgs + 1];
if (numArgs > 0) {
@@ -74,17 +82,9 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
}
newArgs[0] = getRhqFacade().getSubject();
args = newArgs;
-
- int numParams = (null == params) ? 0 : params.length;
- Class<?>[] newParams = new Class[numParams + 1];
- if (numParams > 0) {
- System.arraycopy(params, 0, newParams, 1, numParams);
- }
- newParams[0] = Subject.class;
- params = newParams;
}
- return doInvoke(proxy, method, params, args);
+ return doInvoke(proxy, method, args);
}
/**
@@ -93,10 +93,9 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
*
* @param proxy the proxy the method is executing on
* @param originalMethod the original method
- * @param argTypes the de-simplified argument types
* @param args the de-simplified argumens
* @return the result of the invocation
* @throws Throwable if invocation throws an error
*/
- protected abstract Object doInvoke(Object proxy, Method originalMethod, Class<?>[] argTypes, Object[] args) throws Throwable;
+ protected abstract Object doInvoke(Object proxy, Method originalMethod, Object[] args) throws Throwable;
}
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 ff39171..10ee612 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
@@ -67,7 +67,12 @@ import org.rhq.core.domain.util.PageOrdering;
import org.rhq.core.domain.util.Summary;
import org.rhq.core.server.MeasurementConverter;
import org.rhq.core.util.MessageDigestGenerator;
+import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
import org.rhq.enterprise.server.content.ContentManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
+import org.rhq.enterprise.server.operation.OperationManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceManagerRemote;
import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
/**
@@ -199,7 +204,8 @@ public class ResourceClientProxy {
public void init() {
- this.resource = remoteClient.getResourceManager().getResource(remoteClient.getSubject(), resourceId);
+ this.resource = remoteClient.getProxy(ResourceManagerRemote.class).getResource(remoteClient.getSubject(),
+ resourceId);
// Lazy init children, not here
initMeasurements();
@@ -217,10 +223,11 @@ public class ResourceClientProxy {
}
private void initConfigDefs() {
- this.resourceConfigurationDefinition = remoteClient.getConfigurationManager()
+ ConfigurationManagerRemote configurationManager = remoteClient.getProxy(ConfigurationManagerRemote.class);
+ this.resourceConfigurationDefinition = configurationManager
.getResourceConfigurationDefinitionWithTemplatesForResourceType(remoteClient.getSubject(),
resource.getResourceType().getId());
- this.pluginConfigurationDefinition = remoteClient.getConfigurationManager()
+ this.pluginConfigurationDefinition = configurationManager
.getPluginConfigurationDefinitionForResourceType(remoteClient.getSubject(),
resource.getResourceType().getId());
}
@@ -228,7 +235,7 @@ public class ResourceClientProxy {
private void initChildren() {
ResourceCriteria criteria = new ResourceCriteria();
criteria.addFilterParentResourceId(resourceId);
- PageList<Resource> childResources = remoteClient.getResourceManager().findResourcesByCriteria(
+ PageList<Resource> childResources = remoteClient.getProxy(ResourceManagerRemote.class).findResourcesByCriteria(
remoteClient.getSubject(), criteria);
for (Resource child : childResources) {
@@ -242,7 +249,7 @@ public class ResourceClientProxy {
// criteria.addFilterResourceTypeName(resource.getResourceType().getName());
// criteria.setStrict(true);
- this.measurementDefinitions = remoteClient.getMeasurementDefinitionManager()
+ this.measurementDefinitions = remoteClient.getProxy(MeasurementDefinitionManagerRemote.class)
.findMeasurementDefinitionsByCriteria(remoteClient.getSubject(), criteria);
this.measurementMap = new HashMap<String, Measurement>();
@@ -263,7 +270,8 @@ public class ResourceClientProxy {
criteria.fetchParametersConfigurationDefinition(true);
criteria.fetchResultsConfigurationDefinition(true);
- this.operationDefinitions = remoteClient.getOperationManager().findOperationDefinitionsByCriteria(
+ this.operationDefinitions = remoteClient.getProxy(OperationManagerRemote.class)
+ .findOperationDefinitionsByCriteria(
remoteClient.getSubject(), criteria);
for (OperationDefinition def : operationDefinitions) {
@@ -274,7 +282,7 @@ public class ResourceClientProxy {
}
private void initContent() {
- ContentManagerRemote contentManager = remoteClient.getContentManager();
+ ContentManagerRemote contentManager = remoteClient.getProxy(ContentManagerRemote.class);
List<PackageType> types = null;
try {
types = contentManager.findPackageTypes(remoteClient.getSubject(), resource.getResourceType().getName(),
@@ -302,7 +310,7 @@ public class ResourceClientProxy {
}
public List<PackageVersion> getInstalledPackages() {
- ContentManagerRemote contentManager = remoteClient.getContentManager();
+ ContentManagerRemote contentManager = remoteClient.getProxy(ContentManagerRemote.class);
PackageVersionCriteria criteria = new PackageVersionCriteria();
criteria.addFilterResourceId(resourceId);
@@ -359,7 +367,7 @@ public class ResourceClientProxy {
public Object getValue() {
try {
- Set<MeasurementData> d = remoteClient.getMeasurementDataManager().findLiveData(
+ Set<MeasurementData> d = remoteClient.getProxy(MeasurementDataManagerRemote.class).findLiveData(
remoteClient.getSubject(), resourceId, new int[] { definition.getId() });
MeasurementData data = d.iterator().next();
return data.getValue();
@@ -404,7 +412,9 @@ public class ResourceClientProxy {
Configuration parameters = ConfigurationClassBuilder.translateParametersToConfig(definition
.getParametersConfigurationDefinition(), args);
- ResourceOperationSchedule schedule = remoteClient.getOperationManager().scheduleResourceOperation(
+ OperationManagerRemote operationManager = remoteClient.getProxy(OperationManagerRemote.class);
+
+ ResourceOperationSchedule schedule = operationManager.scheduleResourceOperation(
remoteClient.getSubject(), resourceId, definition.getName(), 0, 0, 0, 30000, parameters,
"Executed from commandline");
@@ -421,7 +431,7 @@ public class ResourceClientProxy {
ResourceOperationHistory history = null;
while (history == null && retries-- > 0) {
Thread.sleep(1000);
- PageList<ResourceOperationHistory> histories = remoteClient.getOperationManager()
+ PageList<ResourceOperationHistory> histories = operationManager
.findResourceOperationHistoriesByCriteria(remoteClient.getSubject(), criteria);
if (histories.size() > 0 && histories.get(0).getStatus() != OperationRequestStatus.INPROGRESS) {
history = histories.get(0);
@@ -459,7 +469,8 @@ public class ResourceClientProxy {
public Configuration getPluginConfiguration() {
if (!LazyLoadScenario.isShouldLoad())
return null;
- return remoteClient.getConfigurationManager().getPluginConfiguration(remoteClient.getSubject(),
+ return remoteClient.getProxy(ConfigurationManagerRemote.class).getPluginConfiguration(
+ remoteClient.getSubject(),
resourceClientProxy.resourceId);
}
@@ -468,8 +479,8 @@ public class ResourceClientProxy {
}
public PluginConfigurationUpdate updatePluginConfiguration(Configuration configuration) {
- PluginConfigurationUpdate update =
- remoteClient.getConfigurationManager().updatePluginConfiguration(
+ PluginConfigurationUpdate update = remoteClient.getProxy(ConfigurationManagerRemote.class)
+ .updatePluginConfiguration(
remoteClient.getSubject(),
resourceClientProxy.getId(),
configuration);
@@ -481,7 +492,8 @@ public class ResourceClientProxy {
if (!LazyLoadScenario.isShouldLoad())
return null;
- return remoteClient.getConfigurationManager().getResourceConfiguration(remoteClient.getSubject(),
+ return remoteClient.getProxy(ConfigurationManagerRemote.class).getResourceConfiguration(
+ remoteClient.getSubject(),
resourceClientProxy.resourceId);
}
@@ -490,8 +502,8 @@ public class ResourceClientProxy {
}
public ResourceConfigurationUpdate updateResourceConfiguration(Configuration configuration) {
- ResourceConfigurationUpdate update =
- remoteClient.getConfigurationManager().updateResourceConfiguration(
+ ResourceConfigurationUpdate update = remoteClient.getProxy(ConfigurationManagerRemote.class)
+ .updateResourceConfiguration(
remoteClient.getSubject(),
resourceClientProxy.getId(),
configuration);
@@ -502,7 +514,8 @@ public class ResourceClientProxy {
public InstalledPackage getBackingContent() {
- return remoteClient.getContentManager().getBackingPackageForResource(remoteClient.getSubject(), resourceClientProxy.resourceId);
+ return remoteClient.getProxy(ContentManagerRemote.class).getBackingPackageForResource(
+ remoteClient.getSubject(), resourceClientProxy.resourceId);
}
/**
@@ -538,8 +551,9 @@ public class ResourceClientProxy {
InstalledPackage oldPackage = getBackingContent();
- PackageVersion pv =
- remoteClient.getContentManager().createPackageVersionWithDisplayVersion(
+ ContentManagerRemote contentManager = remoteClient.getProxy(ContentManagerRemote.class);
+
+ PackageVersion pv = contentManager.createPackageVersionWithDisplayVersion(
remoteClient.getSubject(),
oldPackage.getPackageVersion().getGeneralPackage().getName(),
oldPackage.getPackageVersion().getGeneralPackage().getPackageType().getId(),
@@ -548,7 +562,7 @@ public class ResourceClientProxy {
oldPackage.getPackageVersion().getArchitecture().getId(),
fileContents);
- remoteClient.getContentManager().deployPackagesWithNote(
+ contentManager.deployPackagesWithNote(
remoteClient.getSubject(),
new int[] { resourceClientProxy.getId()},
new int[] {pv.getId()},
@@ -564,8 +578,7 @@ public class ResourceClientProxy {
File file = new File(fileName);
- byte[] data =
- remoteClient.getContentManager().getPackageBytes(
+ byte[] data = remoteClient.getProxy(ContentManagerRemote.class).getPackageBytes(
remoteClient.getSubject(), resourceClientProxy.resourceId, installedPackage.getId());
FileOutputStream fos = new FileOutputStream(file);
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
index ca7fa83..b975436 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
@@ -22,35 +22,6 @@ package org.rhq.bindings.client;
import java.util.Map;
import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
-import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
/**
* This is an interface through which the script can communicate with RHQ server.
@@ -70,69 +41,22 @@ public interface RhqFacade {
boolean isLoggedIn();
- AlertManagerRemote getAlertManager();
-
- AlertDefinitionManagerRemote getAlertDefinitionManager();
-
- AvailabilityManagerRemote getAvailabilityManager();
-
- BundleManagerRemote getBundleManager();
-
- CallTimeDataManagerRemote getCallTimeDataManager();
-
- RepoManagerRemote getRepoManager();
-
- ConfigurationManagerRemote getConfigurationManager();
-
- ContentManagerRemote getContentManager();
-
- DataAccessManagerRemote getDataAccessManager();
-
- DiscoveryBossRemote getDiscoveryBoss();
-
- DriftManagerRemote getDriftManager();
-
- EventManagerRemote getEventManager();
-
- MeasurementBaselineManagerRemote getMeasurementBaselineManager();
-
- MeasurementDataManagerRemote getMeasurementDataManager();
-
- MeasurementDefinitionManagerRemote getMeasurementDefinitionManager();
-
- MeasurementScheduleManagerRemote getMeasurementScheduleManager();
-
- OperationManagerRemote getOperationManager();
-
- ResourceManagerRemote getResourceManager();
-
- ResourceFactoryManagerRemote getResourceFactoryManager();
-
- ResourceGroupManagerRemote getResourceGroupManager();
-
- ResourceTypeManagerRemote getResourceTypeManager();
-
- RoleManagerRemote getRoleManager();
-
- SavedSearchManagerRemote getSavedSearchManager();
-
- SubjectManagerRemote getSubjectManager();
-
- SupportManagerRemote getSupportManager();
-
- SystemManagerRemote getSystemManager();
-
- RemoteInstallManagerRemote getRemoteInstallManager();
-
- TagManagerRemote getTagManager();
-
- SynchronizationManagerRemote getSynchronizationManager();
-
/**
* This map is constructed using all the elements in the {@link RhqManager} enum which are then proxied
* using this instance.
*
* @return a map of all available proxied managers keyed by their names.
*/
- Map<String, Object> getManagers();
+ Map<String, Object> getScriptingAPI();
+
+ /**
+ * Unlike the {@link #getScriptingAPI()} method that returns objects with modified signatures
+ * meant to be used by the scripting environment, this method provides the access to the "raw"
+ * remote API interface implementation backed by this RHQ facade implementation.
+ *
+ * @param remoteApiIface one of the RHQ's remote API interfaces of which the proxied instance
+ * should be returned
+ * @return the proxy of the remote API interface backed by this facade
+ */
+ <T> T getProxy(Class<T> remoteApiIface);
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptAssert.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptAssert.java
index 15ed37c..a268621 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptAssert.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptAssert.java
@@ -245,20 +245,30 @@ public class ScriptAssert {
}
}
- public void assertEquals(Object[] actual, Object[] expected, String msg) {
- if (actual == expected) {
- return;
- }
+ /* These are no longer needed in Rhino 1.7R3, because native javascript arrays implement
+ * the collection interfaces.
+ */
+ /*
+ public void assertEquals(Object[] actual, Object[] expected, String msg) {
+ if (actual == expected) {
+ return;
+ }
- if ((actual == null && expected != null) || (actual != null && expected == null)) {
- if (msg != null) {
- fail(msg);
- } else {
- fail("Arrays not equal: " + Arrays.toString(expected) + " and " + Arrays.toString(actual));
+ if ((actual == null && expected != null) || (actual != null && expected == null)) {
+ if (msg != null) {
+ fail(msg);
+ } else {
+ fail("Arrays not equal: " + Arrays.toString(expected) + " and " + Arrays.toString(actual));
+ }
}
+ assertEquals(Arrays.asList(actual), Arrays.asList(expected), msg);
+ }
+
+ public void assertEquals(Object[] actual, Object[] expected) {
+ assertEquals(actual, expected, null);
}
- assertEquals(Arrays.asList(actual), Arrays.asList(expected), msg);
- }
+
+ */
public void assertEqualsNoOrder(Object[] actual, Object[] expected, String msg) {
if (actual == expected) {
@@ -288,10 +298,6 @@ public class ScriptAssert {
}
}
- public void assertEquals(Object[] actual, Object[] expected) {
- assertEquals(actual, expected, null);
- }
-
public void assertEqualsNoOrder(Object[] actual, Object[] expected) {
assertEqualsNoOrder(actual, expected, null);
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
index 6ed095e..bd121e7 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/ScriptUtil.java
@@ -43,6 +43,7 @@ import org.rhq.core.domain.operation.bean.ResourceOperationSchedule;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
+import org.rhq.enterprise.server.operation.OperationManagerRemote;
import org.rhq.enterprise.server.resource.ResourceManagerRemote;
/**
@@ -74,7 +75,7 @@ public class ScriptUtil {
throw new IllegalStateException("The findResources() method requires a connection to the RHQ server.");
}
- ResourceManagerRemote resourceManager = remoteClient.getResourceManager();
+ ResourceManagerRemote resourceManager = remoteClient.getProxy(ResourceManagerRemote.class);
ResourceCriteria criteria = new ResourceCriteria();
criteria.addFilterName(string);
@@ -166,7 +167,7 @@ public class ScriptUtil {
while(history == null && i < maxIntervals) {
Thread.sleep(intervalDuration);
- PageList<ResourceOperationHistory> histories = remoteClient.getOperationManager()
+ PageList<ResourceOperationHistory> histories = remoteClient.getProxy(OperationManagerRemote.class)
.findResourceOperationHistoriesByCriteria(remoteClient.getSubject(), criteria);
if (histories.size() > 0 && histories.get(0).getStatus() != OperationRequestStatus.INPROGRESS) {
history = histories.get(0);
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
index a3dcbdc..d42f3e1 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/FakeRhqFacade.java
@@ -24,35 +24,6 @@ import java.util.Map;
import org.rhq.bindings.client.RhqFacade;
import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
-import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
public class FakeRhqFacade implements RhqFacade {
@@ -71,127 +42,13 @@ public class FakeRhqFacade implements RhqFacade {
public boolean isLoggedIn() {
return false;
}
-
- public AlertManagerRemote getAlertManager() {
- return null;
- }
-
- public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return null;
- }
-
- public AvailabilityManagerRemote getAvailabilityManager() {
- return null;
- }
-
- public BundleManagerRemote getBundleManager() {
- return null;
- }
-
- public CallTimeDataManagerRemote getCallTimeDataManager() {
- return null;
- }
-
- public RepoManagerRemote getRepoManager() {
- return null;
- }
-
- public ConfigurationManagerRemote getConfigurationManager() {
- return null;
- }
-
- public ContentManagerRemote getContentManager() {
- return null;
- }
-
- public DataAccessManagerRemote getDataAccessManager() {
- return null;
- }
-
- public DiscoveryBossRemote getDiscoveryBoss() {
- return null;
- }
-
- public EventManagerRemote getEventManager() {
- return null;
- }
-
- public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
- return null;
- }
-
- public MeasurementDataManagerRemote getMeasurementDataManager() {
- return null;
- }
-
- public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
- return null;
- }
-
- public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
- return null;
- }
-
- public OperationManagerRemote getOperationManager() {
- return null;
- }
-
- public ResourceManagerRemote getResourceManager() {
- return null;
- }
-
- public ResourceFactoryManagerRemote getResourceFactoryManager() {
- return null;
- }
-
- public ResourceGroupManagerRemote getResourceGroupManager() {
- return null;
- }
-
- public ResourceTypeManagerRemote getResourceTypeManager() {
- return null;
- }
-
- public RoleManagerRemote getRoleManager() {
- return null;
- }
-
- public SavedSearchManagerRemote getSavedSearchManager() {
- return null;
- }
-
- public SubjectManagerRemote getSubjectManager() {
- return null;
- }
-
- public SupportManagerRemote getSupportManager() {
- return null;
- }
-
- public SystemManagerRemote getSystemManager() {
- return null;
- }
-
- public RemoteInstallManagerRemote getRemoteInstallManager() {
- return null;
- }
-
- public TagManagerRemote getTagManager() {
- return null;
- }
-
- @Override
- public DriftManagerRemote getDriftManager() {
- return null;
+
+ public Map<String, Object> getScriptingAPI() {
+ return Collections.emptyMap();
}
@Override
- public SynchronizationManagerRemote getSynchronizationManager() {
+ public <T> T getProxy(Class<T> remoteApiIface) {
return null;
}
-
- public Map<String, Object> getManagers() {
- return Collections.emptyMap();
- }
-
}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/ScriptEngineTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/ScriptEngineTest.java
index 15184c1..c3d5450 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/ScriptEngineTest.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/ScriptEngineTest.java
@@ -86,11 +86,13 @@ public class ScriptEngineTest {
}
private ScriptEngine getScriptEngine() throws ScriptException, IOException {
- return ScriptEngineFactory.getScriptEngine("JavaScript", new PackageFinder(Collections.<File>emptyList()), EMPTY_BINDINGS);
+ return ScriptEngineFactory.getScriptEngine("javascript", new PackageFinder(Collections.<File> emptyList()),
+ EMPTY_BINDINGS);
}
private ScriptEngine getSecuredScriptEngine() throws ScriptException, IOException {
- return ScriptEngineFactory.getSecuredScriptEngine("JavaScript", new PackageFinder(Collections.<File>emptyList()), EMPTY_BINDINGS, new StandardScriptPermissions());
+ return ScriptEngineFactory.getSecuredScriptEngine("javascript",
+ new PackageFinder(Collections.<File> emptyList()), EMPTY_BINDINGS, new StandardScriptPermissions());
}
private void assertSecurityExceptionPresent(Throwable t) {
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/ScriptAssertTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/ScriptAssertTest.java
index 8fed153..1d7f657 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/ScriptAssertTest.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/ScriptAssertTest.java
@@ -46,8 +46,7 @@ public class ScriptAssertTest {
@BeforeTest
public void verifyScriptEngineIsAvailable() throws Exception {
StandardBindings bindings = new StandardBindings(new PrintWriter(System.out), new FakeRhqFacade());
- engine =
- ScriptEngineFactory.getScriptEngine("JavaScript", new PackageFinder(Collections.<File> emptyList()),
+ engine = ScriptEngineFactory.getScriptEngine("javascript", new PackageFinder(Collections.<File> emptyList()),
bindings);
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 4a1ed62..9811d2a 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -203,7 +203,7 @@ public class ClientMain {
this.serviceCompletor.setContext(getScriptEngine().getContext());
if (remoteClient != null) {
- this.serviceCompletor.setServices(remoteClient.getManagers());
+ this.serviceCompletor.setServices(remoteClient.getScriptingAPI());
}
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
index 4676730..69b3c7a 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/HelpCommand.java
@@ -61,7 +61,7 @@ public class HelpCommand implements ClientCommand {
tw.setHideRowCount(true);
tw.print(data);
} else if ("api".equals(args[1])) {
- Map<String, Object> services = client.getRemoteClient().getManagers();
+ Map<String, Object> services = client.getRemoteClient().getScriptingAPI();
if (args.length == 2) {
TabularWriter tw = new TabularWriter(client.getPrintWriter(), "API", "Package");
tw.setWidth(client.getConsoleWidth());
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
index fc59cf3..8cf3ceb 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
@@ -1,36 +1,23 @@
package org.rhq.enterprise.client.commands;
-import static java.util.Collections.*;
import static java.util.Arrays.asList;
-import static org.testng.Assert.*;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
-import java.util.Map;
-import java.util.Collections;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
+
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
import org.testng.annotations.Test;
-import org.rhq.enterprise.client.ClientMain;
-import org.rhq.enterprise.client.commands.ScriptCommand;
-import org.rhq.enterprise.clientapi.RemoteClient;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
+
import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.bindings.util.ScriptUtil;
import org.rhq.core.domain.auth.Subject;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptContext;
-import javax.script.Bindings;
+import org.rhq.enterprise.client.ClientMain;
+import org.rhq.enterprise.clientapi.RemoteClient;
public class ScriptCommandTest {
@@ -122,56 +109,6 @@ public class ScriptCommandTest {
public RemoteClientStub(String host, int port) {
super(host, port);
}
-
- @Override
- public AlertManagerRemote getAlertManager() {
- return null;
- }
-
- @Override
- public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return null;
- }
-
- @Override
- public ConfigurationManagerRemote getConfigurationManager() {
- return null;
- }
-
- @Override
- public RepoManagerRemote getRepoManager() {
- return null;
- }
-
- @Override
- public ContentManagerRemote getContentManager() {
- return null;
- }
-
- @Override
- public OperationManagerRemote getOperationManager() {
- return null;
- }
-
- @Override
- public RoleManagerRemote getRoleManager() {
- return null;
- }
-
- @Override
- public ResourceManagerRemote getResourceManager() {
- return null;
- }
-
- @Override
- public ResourceGroupManagerRemote getResourceGroupManager() {
- return null;
- }
-
- @Override
- public SubjectManagerRemote getSubjectManager() {
- return null;
- }
}
}
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
index 57829b9..4ea3267 100644
--- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.clientapi;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
@@ -28,44 +29,18 @@ import org.apache.maven.artifact.versioning.ComparableVersion;
import org.jboss.remoting.Client;
import org.jboss.remoting.InvokerLocator;
+import org.jboss.remoting.invocation.NameBasedInvocation;
import org.jboss.remoting.security.SSLSocketBuilder;
import org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker;
import org.rhq.bindings.client.RhqFacade;
import org.rhq.bindings.client.RhqManager;
+import org.rhq.bindings.util.InterfaceSimplifier;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.common.ProductInfo;
import org.rhq.enterprise.communications.util.SecurityUtil;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
/**
* A remote access client that provides transparent servlet-based proxies to an RHQ Server.
@@ -127,6 +102,29 @@ public class RemoteClient implements RhqFacade {
this.subsystem = subsystem;
}
+ public <T> T remoteInvoke(RhqManager manager, Method method, Class<T> expectedReturnType, Object... parameters)
+ throws Throwable {
+
+ String methodSig = manager.beanName() + ":" + method.getName();
+
+ Class<?>[] paramTypes = method.getParameterTypes();
+ String[] paramSig = new String[paramTypes.length];
+ for (int x = 0; x < paramTypes.length; x++) {
+ paramSig[x] = paramTypes[x].getName();
+ }
+
+ NameBasedInvocation request = new NameBasedInvocation(methodSig, parameters, paramSig);
+
+ Object response = getRemotingClient().invoke(request);
+
+ if (response instanceof Throwable) {
+ throw (Throwable) response;
+ }
+
+ return response == null ? null : expectedReturnType.cast(response);
+
+ }
+
/**
* Connects to the remote server and logs in with the given credentials.
* After successfully executing this, {@link #isLoggedIn()} will be <code>true</code>
@@ -144,7 +142,16 @@ public class RemoteClient implements RhqFacade {
logout();
doConnect();
- this.subject = getSubjectManager().login(user, password);
+ Method loginMethod = SubjectManagerRemote.class.getDeclaredMethod("login", String.class, String.class);
+
+ try {
+ this.subject = remoteInvoke(RhqManager.SubjectManager, loginMethod, Subject.class, user, password);
+ } catch (Exception e) {
+ throw e;
+ } catch (Throwable e) {
+ throw new Exception("Failed to login due to a throwable of type " + e.getClass().getName(), e);
+ }
+
this.loggedIn = true;
return this.subject;
@@ -154,12 +161,16 @@ public class RemoteClient implements RhqFacade {
* Logs out from the server and disconnects this client.
*/
public void logout() {
- try {
- if (this.loggedIn && this.subject != null) {
- getSubjectManager().logout(this.subject);
+ if (this.loggedIn && this.subject != null) {
+ try {
+ Method logoutMethod = SubjectManagerRemote.class.getDeclaredMethod("logout", Subject.class);
+ remoteInvoke(RhqManager.SubjectManager, logoutMethod, Void.class, this.subject);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException(
+ "Couldn't find the logout method on the SubjectManagerRemote interface.", e);
+ } catch (Throwable e) {
+ // just keep going so we can disconnect this client
}
- } catch (Exception e) {
- // just keep going so we can disconnect this client
}
doDisconnect();
@@ -260,145 +271,25 @@ public class RemoteClient implements RhqFacade {
this.transport = transport;
}
- public AlertManagerRemote getAlertManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.AlertManager);
- }
-
- public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.AlertDefinitionManager);
- }
-
- public AvailabilityManagerRemote getAvailabilityManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.AvailabilityManager);
- }
-
- public BundleManagerRemote getBundleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.BundleManager);
- }
-
- public CallTimeDataManagerRemote getCallTimeDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.CallTimeDataManager);
- }
-
- public DriftManagerRemote getDriftManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.DriftManager);
- }
-
- public DriftTemplateManagerRemote getDriftTemplateManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.DriftTemplateManager);
- }
-
- public RepoManagerRemote getRepoManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.RepoManager);
- }
-
- public ConfigurationManagerRemote getConfigurationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ConfigurationManager);
- }
-
- public ContentManagerRemote getContentManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ContentManager);
- }
-
- public DataAccessManagerRemote getDataAccessManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.DataAccessManager);
- }
-
- public DiscoveryBossRemote getDiscoveryBoss() {
- return RemoteClientProxy.getProcessor(this, RhqManager.DiscoveryBoss);
- }
-
- public EventManagerRemote getEventManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.EventManager);
- }
-
- public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementBaselineManager);
- }
-
- public MeasurementDataManagerRemote getMeasurementDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementDataManager);
- }
-
- public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementDefinitionManager);
- }
-
- public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementScheduleManager);
- }
-
- public OperationManagerRemote getOperationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.OperationManager);
- }
-
- public ResourceManagerRemote getResourceManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ResourceManager);
- }
-
- public ResourceFactoryManagerRemote getResourceFactoryManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ResourceFactoryManager);
- }
-
- public ResourceGroupManagerRemote getResourceGroupManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ResourceGroupManager);
- }
-
- public ResourceTypeManagerRemote getResourceTypeManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.ResourceTypeManager);
- }
-
- public RoleManagerRemote getRoleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.RoleManager);
- }
-
- public SavedSearchManagerRemote getSavedSearchManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.SavedSearchManager);
- }
-
- public SubjectManagerRemote getSubjectManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.SubjectManager);
- }
-
- public SupportManagerRemote getSupportManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.SupportManager);
- }
-
- public SystemManagerRemote getSystemManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.SystemManager);
- }
-
- public RemoteInstallManagerRemote getRemoteInstallManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.RemoteInstallManager);
- }
-
- public TagManagerRemote getTagManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.TagManager);
- }
-
- public SynchronizationManagerRemote getSynchronizationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManager.SynchronizationManager);
- }
-
/**
* Returns the map of all remote managers running in the server that this
* client can talk to.
*
* @return Map K=manager name V=remote proxy
*/
- public Map<String, Object> getManagers() {
+ public Map<String, Object> getScriptingAPI() {
if (this.managers == null) {
this.managers = new HashMap<String, Object>();
for (RhqManager manager : RhqManager.values()) {
- try {
- Method m = this.getClass().getMethod("get" + manager.name());
- if (manager.enabled()) {
- this.managers.put(manager.name(), m.invoke(this));
+ if (manager.enabled()) {
+ try {
+ Object proxy = getProcessor(this, manager, true);
+ this.managers.put(manager.name(), proxy);
+ } catch (Throwable e) {
+ LOG.error("Failed to load manager " + manager + " due to missing class.", e);
}
- } catch (Throwable e) {
- LOG.error("Failed to load manager " + manager + " due to missing class.", e);
}
}
}
@@ -407,6 +298,17 @@ public class RemoteClient implements RhqFacade {
}
@Override
+ public <T> T getProxy(Class<T> remoteApiIface) {
+ RhqManager manager = RhqManager.forInterface(remoteApiIface);
+
+ if (manager == null) {
+ throw new IllegalArgumentException("Unknown remote interface " + remoteApiIface);
+ }
+
+ return getProcessor(this, manager, false);
+ }
+
+ @Override
public String toString() {
return this.getClass().getSimpleName() + "[" + "transport=" + transport + ", host=" + host + ", port=" + port
+ ", subsystem=" + subsystem + ", connected=" + connected + ", loggedIn=" + loggedIn + ", subject="
@@ -443,6 +345,20 @@ public class RemoteClient implements RhqFacade {
return (this.serverInfo != null) ? this.serverInfo.getBuildNumber() : null;
}
+ @SuppressWarnings("unchecked")
+ private static <T> T getProcessor(RemoteClient remoteClient, RhqManager manager, boolean simplify) {
+ try {
+ RemoteClientProxy gpc = new RemoteClientProxy(remoteClient, manager);
+
+ Class<?> intf = simplify ? InterfaceSimplifier.simplify(manager.remote()) : manager.remote();
+
+ return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { intf },
+ gpc);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to get remote connection proxy", e);
+ }
+ }
+
private void doDisconnect() {
try {
if (this.remotingClient != null && this.remotingClient.isConnected()) {
@@ -470,13 +386,21 @@ public class RemoteClient implements RhqFacade {
this.remotingClient.connect();
// make sure the remote server can support this client
- this.serverInfo = getSystemManager().getProductInfo(subject);
try {
+ Method getProductInfoMethod = SystemManagerRemote.class.getDeclaredMethod("getProductInfo", Subject.class);
+
+ this.serverInfo = remoteInvoke(RhqManager.SystemManager, getProductInfoMethod, ProductInfo.class,
+ this.subject);
checkServerSupported(this.serverInfo);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Could not find the getProductInfo(Subject) method on the SystemManager.",
+ e);
} catch (Exception e) {
// our client cannot be supported by the server - disconnect and rethrow the exception
doDisconnect();
throw e;
+ } catch (Throwable e) {
+ throw new IllegalStateException("Unknown error occured during connect.", e);
}
}
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
index 23dea3c..a6b04f1 100644
--- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
@@ -19,16 +19,12 @@
package org.rhq.enterprise.clientapi;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.jboss.remoting.invocation.NameBasedInvocation;
-
import org.rhq.bindings.client.AbstractRhqFacadeProxy;
import org.rhq.bindings.client.RhqManager;
-import org.rhq.bindings.util.InterfaceSimplifier;
import org.rhq.core.domain.server.ExternalizableStrategy;
/**
@@ -46,24 +42,11 @@ public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
super(client, manager);
}
+ @Deprecated
public Class<?> getRemoteInterface() {
return this.getManager().remote();
}
- @SuppressWarnings("unchecked")
- public static <T> T getProcessor(RemoteClient remoteClient, RhqManager manager) {
- try {
- RemoteClientProxy gpc = new RemoteClientProxy(remoteClient, manager);
-
- Class<?> intf = InterfaceSimplifier.simplify(manager.remote());
-
- return (T) Proxy
- .newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] { intf }, gpc);
- } catch (Exception e) {
- throw new RuntimeException("Failed to get remote connection proxy", e);
- }
- }
-
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return super.invoke(proxy, method, args);
@@ -73,29 +56,9 @@ public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
}
}
- protected Object doInvoke(Object proxy, Method originalMethod, java.lang.Class<?>[] argTypes, Object[] args) throws Throwable {
+ protected Object doInvoke(Object proxy, Method originalMethod, Object[] args) throws Throwable {
ExternalizableStrategy.setStrategy(ExternalizableStrategy.Subsystem.REFLECTIVE_SERIALIZATION);
- String methodName = getManager().beanName() + ":" + originalMethod.getName();
-
- String[] paramSig = createParamSignature(argTypes);
-
- NameBasedInvocation request = new NameBasedInvocation(methodName, args, paramSig);
-
- Object response = getRhqFacade().getRemotingClient().invoke(request);
-
- if (response instanceof Throwable) {
- throw (Throwable) response;
- }
-
- return response;
- }
-
- private String[] createParamSignature(Class<?>[] types) {
- String[] paramSig = new String[types.length];
- for (int x = 0; x < types.length; x++) {
- paramSig[x] = types[x].getName();
- }
- return paramSig;
+ return getRhqFacade().remoteInvoke(getManager(), originalMethod, Object.class, args);
}
}
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 dbb4424..e350302 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
@@ -19,11 +19,9 @@
package org.rhq.enterprise.client;
-import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
import java.util.HashMap;
import java.util.Map;
@@ -34,36 +32,6 @@ import org.rhq.bindings.client.RhqFacade;
import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.util.InterfaceSimplifier;
import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
-import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
import org.rhq.enterprise.server.util.LookupUtil;
/**
@@ -102,331 +70,46 @@ public class LocalClient implements RhqFacade {
}
@Override
- public AlertManagerRemote getAlertManager() {
- return AccessController.doPrivileged(new PrivilegedAction<AlertManagerRemote>() {
- @Override
- public AlertManagerRemote run() {
- return AccessController.doPrivileged(new PrivilegedAction<AlertManagerRemote>() {
- @Override
- public AlertManagerRemote run() {
- return getProxy(LookupUtil.getAlertManager(), AlertManagerRemote.class);
- }
- });
- }
- });
- }
-
- @Override
- public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return AccessController.doPrivileged(new PrivilegedAction<AlertDefinitionManagerRemote>() {
- @Override
- public AlertDefinitionManagerRemote run() {
- return getProxy(LookupUtil.getAlertDefinitionManager(), AlertDefinitionManagerRemote.class);
- }
- });
- }
+ public Map<String, Object> getScriptingAPI() {
+ if (managers == null) {
- @Override
- public AvailabilityManagerRemote getAvailabilityManager() {
- return AccessController.doPrivileged(new PrivilegedAction<AvailabilityManagerRemote>() {
- @Override
- public AvailabilityManagerRemote run() {
- return getProxy(LookupUtil.getAvailabilityManager(), AvailabilityManagerRemote.class);
- }
- });
- }
+ managers = new HashMap<String, Object>();
- @Override
- public BundleManagerRemote getBundleManager() {
- return AccessController.doPrivileged(new PrivilegedAction<BundleManagerRemote>() {
- @Override
- public BundleManagerRemote run() {
- return AccessController.doPrivileged(new PrivilegedAction<BundleManagerRemote>() {
- @Override
- public BundleManagerRemote run() {
- return getProxy(LookupUtil.getBundleManager(), BundleManagerRemote.class);
+ for (final RhqManager manager : RhqManager.values()) {
+ if (manager.enabled()) {
+ try {
+ Object proxy = AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ return getProxy(getLocalSLSB(manager), manager.remote());
+ }
+ });
+
+ managers.put(manager.name(), proxy);
+ } catch (Throwable e) {
+ LOG.error("Failed to load manager " + manager + " due to missing class.", e);
}
- });
- }
- });
- }
-
- @Override
- public CallTimeDataManagerRemote getCallTimeDataManager() {
- return AccessController.doPrivileged(new PrivilegedAction<CallTimeDataManagerRemote>() {
- @Override
- public CallTimeDataManagerRemote run() {
- return getProxy(LookupUtil.getCallTimeDataManager(), CallTimeDataManagerRemote.class);
- }
- });
- }
-
- @Override
- public RepoManagerRemote getRepoManager() {
- return AccessController.doPrivileged(new PrivilegedAction<RepoManagerRemote>() {
- @Override
- public RepoManagerRemote run() {
- return getProxy(LookupUtil.getRepoManagerLocal(), RepoManagerRemote.class);
- }
- });
- }
-
- @Override
- public ConfigurationManagerRemote getConfigurationManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ConfigurationManagerRemote>() {
- @Override
- public ConfigurationManagerRemote run() {
- return getProxy(LookupUtil.getConfigurationManager(), ConfigurationManagerRemote.class);
- }
- });
- }
-
- @Override
- public ContentManagerRemote getContentManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ContentManagerRemote>() {
- @Override
- public ContentManagerRemote run() {
- return getProxy(LookupUtil.getContentManager(), ContentManagerRemote.class);
- }
- });
- }
-
- @Override
- public DataAccessManagerRemote getDataAccessManager() {
- return AccessController.doPrivileged(new PrivilegedAction<DataAccessManagerRemote>() {
- @Override
- public DataAccessManagerRemote run() {
- return getProxy(LookupUtil.getDataAccessManager(), DataAccessManagerRemote.class);
- }
- });
- }
-
- @Override
- public DiscoveryBossRemote getDiscoveryBoss() {
- return AccessController.doPrivileged(new PrivilegedAction<DiscoveryBossRemote>() {
- @Override
- public DiscoveryBossRemote run() {
- return getProxy(LookupUtil.getDiscoveryBoss(), DiscoveryBossRemote.class);
- }
- });
- }
-
- @Override
- public DriftManagerRemote getDriftManager() {
- return AccessController.doPrivileged(new PrivilegedAction<DriftManagerRemote>() {
- @Override
- public DriftManagerRemote run() {
- return getProxy(LookupUtil.getDriftManager(), DriftManagerRemote.class);
- }
- });
- }
-
- public DriftTemplateManagerRemote getDriftTemplateManager() {
- return AccessController.doPrivileged(new PrivilegedAction<DriftTemplateManagerRemote>() {
- @Override
- public DriftTemplateManagerRemote run() {
- return getProxy(LookupUtil.getDriftTemplateManager(), DriftTemplateManagerRemote.class);
- }
- });
- }
-
- @Override
- public EventManagerRemote getEventManager() {
- return AccessController.doPrivileged(new PrivilegedAction<EventManagerRemote>() {
- @Override
- public EventManagerRemote run() {
- return getProxy(LookupUtil.getEventManager(), EventManagerRemote.class);
- }
- });
- }
-
- @Override
- public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
- return AccessController.doPrivileged(new PrivilegedAction<MeasurementBaselineManagerRemote>() {
- @Override
- public MeasurementBaselineManagerRemote run() {
- return getProxy(LookupUtil.getMeasurementBaselineManager(), MeasurementBaselineManagerRemote.class);
- }
- });
- }
-
- @Override
- public MeasurementDataManagerRemote getMeasurementDataManager() {
- return AccessController.doPrivileged(new PrivilegedAction<MeasurementDataManagerRemote>() {
- @Override
- public MeasurementDataManagerRemote run() {
- return getProxy(LookupUtil.getMeasurementDataManager(), MeasurementDataManagerRemote.class);
- }
- });
- }
-
- @Override
- public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
- return AccessController.doPrivileged(new PrivilegedAction<MeasurementDefinitionManagerRemote>() {
- @Override
- public MeasurementDefinitionManagerRemote run() {
- return getProxy(LookupUtil.getMeasurementDefinitionManager(), MeasurementDefinitionManagerRemote.class);
- }
- });
- }
-
- @Override
- public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
- return AccessController.doPrivileged(new PrivilegedAction<MeasurementScheduleManagerRemote>() {
- @Override
- public MeasurementScheduleManagerRemote run() {
- return getProxy(LookupUtil.getMeasurementScheduleManager(), MeasurementScheduleManagerRemote.class);
- }
- });
- }
-
- @Override
- public OperationManagerRemote getOperationManager() {
- return AccessController.doPrivileged(new PrivilegedAction<OperationManagerRemote>() {
- @Override
- public OperationManagerRemote run() {
- return getProxy(LookupUtil.getOperationManager(), OperationManagerRemote.class);
- }
- });
- }
-
- @Override
- public ResourceManagerRemote getResourceManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ResourceManagerRemote>() {
- @Override
- public ResourceManagerRemote run() {
- return getProxy(LookupUtil.getResourceManager(), ResourceManagerRemote.class);
- }
- });
- }
-
- @Override
- public ResourceFactoryManagerRemote getResourceFactoryManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ResourceFactoryManagerRemote>() {
- @Override
- public ResourceFactoryManagerRemote run() {
- return getProxy(LookupUtil.getResourceFactoryManager(), ResourceFactoryManagerRemote.class);
- }
- });
- }
-
- @Override
- public ResourceGroupManagerRemote getResourceGroupManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ResourceGroupManagerRemote>() {
- @Override
- public ResourceGroupManagerRemote run() {
- return getProxy(LookupUtil.getResourceGroupManager(), ResourceGroupManagerRemote.class);
- }
- });
- }
-
- @Override
- public ResourceTypeManagerRemote getResourceTypeManager() {
- return AccessController.doPrivileged(new PrivilegedAction<ResourceTypeManagerRemote>() {
- @Override
- public ResourceTypeManagerRemote run() {
- return getProxy(LookupUtil.getResourceTypeManager(), ResourceTypeManagerRemote.class);
- }
- });
- }
-
- @Override
- public RoleManagerRemote getRoleManager() {
- return AccessController.doPrivileged(new PrivilegedAction<RoleManagerRemote>() {
- @Override
- public RoleManagerRemote run() {
- return getProxy(LookupUtil.getRoleManager(), RoleManagerRemote.class);
- }
- });
- }
-
- @Override
- public SavedSearchManagerRemote getSavedSearchManager() {
- return AccessController.doPrivileged(new PrivilegedAction<SavedSearchManagerRemote>() {
- @Override
- public SavedSearchManagerRemote run() {
- return getProxy(LookupUtil.getSavedSearchManager(), SavedSearchManagerRemote.class);
- }
- });
- }
-
- @Override
- public SubjectManagerRemote getSubjectManager() {
- return AccessController.doPrivileged(new PrivilegedAction<SubjectManagerRemote>() {
- @Override
- public SubjectManagerRemote run() {
- return getProxy(LookupUtil.getSubjectManager(), SubjectManagerRemote.class);
- }
- });
- }
-
- @Override
- public SupportManagerRemote getSupportManager() {
- return AccessController.doPrivileged(new PrivilegedAction<SupportManagerRemote>() {
- @Override
- public SupportManagerRemote run() {
- return getProxy(LookupUtil.getSupportManager(), SupportManagerRemote.class);
- }
- });
- }
-
- @Override
- public SystemManagerRemote getSystemManager() {
- return AccessController.doPrivileged(new PrivilegedAction<SystemManagerRemote>() {
- @Override
- public SystemManagerRemote run() {
- return getProxy(LookupUtil.getSystemManager(), SystemManagerRemote.class);
- }
- });
- }
-
- @Override
- public RemoteInstallManagerRemote getRemoteInstallManager() {
- return AccessController.doPrivileged(new PrivilegedAction<RemoteInstallManagerRemote>() {
- @Override
- public RemoteInstallManagerRemote run() {
- return getProxy(LookupUtil.getRemoteInstallManager(), RemoteInstallManagerRemote.class);
+ }
}
- });
- }
+ }
- @Override
- public TagManagerRemote getTagManager() {
- return AccessController.doPrivileged(new PrivilegedAction<TagManagerRemote>() {
- @Override
- public TagManagerRemote run() {
- return getProxy(LookupUtil.getTagManager(), TagManagerRemote.class);
- }
- });
+ return managers;
}
@Override
- public SynchronizationManagerRemote getSynchronizationManager() {
- return AccessController.doPrivileged(new PrivilegedAction<SynchronizationManagerRemote>() {
- @Override
- public SynchronizationManagerRemote run() {
- return getProxy(LookupUtil.getSynchronizationManager(), SynchronizationManagerRemote.class);
- }
- });
- }
+ public <T> T getProxy(Class<T> remoteApiIface) {
+ RhqManager manager = RhqManager.forInterface(remoteApiIface);
- @Override
- public Map<String, Object> getManagers() {
- if (managers == null) {
+ if (manager == null) {
+ throw new IllegalArgumentException("Unknown remote interface " + remoteApiIface);
+ }
- managers = new HashMap<String, Object>();
+ Object localSLSB = getLocalSLSB(manager);
- for (RhqManager manager : RhqManager.values()) {
- try {
- Method m = getClass().getMethod("get" + manager.name());
- managers.put(manager.name(), m.invoke(this));
- } catch (Throwable e) {
- LOG.error("Failed to load manager " + manager + " due to missing class.", e);
- }
- }
- }
+ Object proxy = Proxy.newProxyInstance(remoteApiIface.getClassLoader(), new Class<?>[] { remoteApiIface },
+ new LocalClientProxy(localSLSB, this, manager));
- return managers;
+ return remoteApiIface.cast(proxy);
}
private <T> T getProxy(Object slsb, Class<T> iface) {
@@ -442,10 +125,77 @@ public class LocalClient implements RhqFacade {
Thread.currentThread().setContextClassLoader(cl);
}
- Object proxy =
- Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] { simplified }, new LocalClientProxy(slsb,
- this, manager));
+ Object proxy = Proxy.newProxyInstance(iface.getClassLoader(), new Class<?>[] { simplified },
+ new LocalClientProxy(slsb, this, manager));
return iface.cast(proxy);
}
+
+ private Object getLocalSLSB(RhqManager manager) {
+ switch (manager) {
+ case AlertDefinitionManager:
+ return LookupUtil.getAlertDefinitionManager();
+ case AlertManager:
+ return LookupUtil.getAlertManager();
+ case AvailabilityManager:
+ return LookupUtil.getAvailabilityManager();
+ case BundleManager:
+ return LookupUtil.getBundleManager();
+ case CallTimeDataManager:
+ return LookupUtil.getCallTimeDataManager();
+ case ConfigurationManager:
+ return LookupUtil.getConfigurationManager();
+ case ContentManager:
+ return LookupUtil.getContentManager();
+ case DataAccessManager:
+ return LookupUtil.getDataAccessManager();
+ case DiscoveryBoss:
+ return LookupUtil.getDiscoveryBoss();
+ case DriftManager:
+ return LookupUtil.getDriftManager();
+ case DriftTemplateManager:
+ return LookupUtil.getDriftTemplateManager();
+ case EventManager:
+ return LookupUtil.getEventManager();
+ case MeasurementBaselineManager:
+ return LookupUtil.getMeasurementBaselineManager();
+ case MeasurementDataManager:
+ return LookupUtil.getMeasurementDataManager();
+ case MeasurementDefinitionManager:
+ return LookupUtil.getMeasurementDefinitionManager();
+ case MeasurementScheduleManager:
+ return LookupUtil.getMeasurementScheduleManager();
+ case OperationManager:
+ return LookupUtil.getOperationManager();
+ case RemoteInstallManager:
+ return LookupUtil.getRemoteInstallManager();
+ case RepoManager:
+ return LookupUtil.getRepoManagerLocal();
+ case ResourceFactoryManager:
+ return LookupUtil.getResourceFactoryManager();
+ case ResourceGroupManager:
+ return LookupUtil.getResourceGroupManager();
+ case ResourceManager:
+ return LookupUtil.getResourceManager();
+ case ResourceTypeManager:
+ return LookupUtil.getResourceTypeManager();
+ case RoleManager:
+ return LookupUtil.getRoleManager();
+ case SavedSearchManager:
+ return LookupUtil.getSavedSearchManager();
+ case SubjectManager:
+ return LookupUtil.getSubjectManager();
+ case SupportManager:
+ return LookupUtil.getSupportManager();
+ case SynchronizationManager:
+ return LookupUtil.getSynchronizationManager();
+ case SystemManager:
+ return LookupUtil.getSystemManager();
+ case TagManager:
+ return LookupUtil.getTagManager();
+ }
+
+ throw new IllegalStateException("LocalClient does not handle the manager: " + manager
+ + ". This is a bug, please report it.");
+ }
}
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
index 67bd5e7..54d20e0 100644
--- a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
+++ b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
@@ -56,9 +56,10 @@ public class LocalClientProxy extends AbstractRhqFacadeProxy<LocalClient> {
}
}
- protected Object doInvoke(Object proxy, Method originalMethod, java.lang.Class<?>[] argTypes, final Object[] args) throws Throwable {
+ protected Object doInvoke(Object proxy, Method originalMethod, final Object[] args) throws Throwable {
try {
- final Method realMethod = localSLSB.getClass().getMethod(originalMethod.getName(), argTypes);
+ final Method realMethod = localSLSB.getClass().getMethod(originalMethod.getName(),
+ originalMethod.getParameterTypes());
//run this through the privileged block to elevate the privs of the script
//the scripts don't have the AllowEjbAccessPermission but this code has
@@ -72,7 +73,9 @@ public class LocalClientProxy extends AbstractRhqFacadeProxy<LocalClient> {
}
});
} catch (NoSuchMethodException e) {
- throw new IllegalArgumentException("Method [" + originalMethod + "] does not have a desimplified counterpart with arguments " + Arrays.asList(argTypes) + ".", e);
+ throw new IllegalArgumentException("Method [" + originalMethod
+ + "] does not have a desimplified counterpart with arguments "
+ + Arrays.asList(originalMethod.getParameterTypes()) + ".", e);
}
};
diff --git a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
index 3b949e3..e9bd928 100644
--- a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
+++ b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/test/LocalClientTest.java
@@ -31,7 +31,6 @@ import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import org.jmock.Expectations;
-import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -89,7 +88,7 @@ public class LocalClientTest extends JMockTest {
LocalClient lc = new LocalClient(null);
//this call creates the proxy and is theoretically prone to the context classloader
- AlertManagerRemote am = lc.getAlertManager();
+ Object am = lc.getScriptingAPI().get("AlertManager");
//check that both the original and simplified methods exist on the returned object
am.getClass().getMethod("deleteAlerts", new Class<?>[] { Subject.class, int[].class });
commit 09e7b73cf38378ccc825b8c9041ddc8b6a6b5429
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 09:37:39 2012 +0200
Renaming "RhqManagers" to "RhqManager".
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
index 7f3a56f..ef46bb2 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
@@ -34,9 +34,9 @@ import org.rhq.core.domain.auth.Subject;
public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements InvocationHandler {
private T facade;
- private RhqManagers manager;
+ private RhqManager manager;
- protected AbstractRhqFacadeProxy(T facade, RhqManagers manager) {
+ protected AbstractRhqFacadeProxy(T facade, RhqManager manager) {
this.facade = facade;
this.manager = manager;
}
@@ -45,7 +45,7 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
return facade;
}
- protected RhqManagers getManager() {
+ protected RhqManager getManager() {
return manager;
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
index f2bde27..ca7fa83 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqFacade.java
@@ -129,7 +129,7 @@ public interface RhqFacade {
SynchronizationManagerRemote getSynchronizationManager();
/**
- * This map is constructed using all the elements in the {@link RhqManagers} enum which are then proxied
+ * This map is constructed using all the elements in the {@link RhqManager} enum which are then proxied
* using this instance.
*
* @return a map of all available proxied managers keyed by their names.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManager.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManager.java
new file mode 100644
index 0000000..cfa52e2
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManager.java
@@ -0,0 +1,133 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 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.bindings.client;
+
+import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
+import org.rhq.enterprise.server.alert.AlertManagerRemote;
+import org.rhq.enterprise.server.auth.SubjectManagerRemote;
+import org.rhq.enterprise.server.authz.RoleManagerRemote;
+import org.rhq.enterprise.server.bundle.BundleManagerRemote;
+import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
+import org.rhq.enterprise.server.content.ContentManagerRemote;
+import org.rhq.enterprise.server.content.RepoManagerRemote;
+import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
+import org.rhq.enterprise.server.drift.DriftManagerRemote;
+import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
+import org.rhq.enterprise.server.event.EventManagerRemote;
+import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
+import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
+import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
+import org.rhq.enterprise.server.operation.OperationManagerRemote;
+import org.rhq.enterprise.server.report.DataAccessManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
+import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
+import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
+import org.rhq.enterprise.server.support.SupportManagerRemote;
+import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
+import org.rhq.enterprise.server.system.SystemManagerRemote;
+import org.rhq.enterprise.server.tagging.TagManagerRemote;
+
+/**
+ * An enumeration of all remote SLSBs of the RHQ server.
+ *
+ * @author Lukas Krejci
+ * @author Greg Hinkle
+ */
+public enum RhqManager {
+ AlertManager(AlertManagerRemote.class, "${AlertManager}"), //
+ AlertDefinitionManager(AlertDefinitionManagerRemote.class, "${AlertDefinitionManager}"), //
+ AvailabilityManager(AvailabilityManagerRemote.class, "${AvailabilityManager}"), //
+ BundleManager(BundleManagerRemote.class, "${BundleManager}"), //
+ CallTimeDataManager(CallTimeDataManagerRemote.class, "${CallTimeDataManager}"), //
+ RepoManager(RepoManagerRemote.class, "${RepoManager}"), //
+ ConfigurationManager(ConfigurationManagerRemote.class, "${ConfigurationManager}"), //
+ ContentManager(ContentManagerRemote.class, "${ContentManager}"), //
+ DataAccessManager(DataAccessManagerRemote.class, "${DataAccessManager}"), //
+ DriftManager(DriftManagerRemote.class, "${DriftManager}"), //
+ DriftTemplateManager(DriftTemplateManagerRemote.class, "${DriftTemplateManager}"), //
+ DiscoveryBoss(DiscoveryBossRemote.class, "${DiscoveryBoss}"), //
+ EventManager(EventManagerRemote.class, "${EventManager}"), //
+ MeasurementBaselineManager(MeasurementBaselineManagerRemote.class, "${MeasurementBaselineManager}"), //
+ MeasurementDataManager(MeasurementDataManagerRemote.class, "${MeasurementDataManager}"), //
+ MeasurementDefinitionManager(MeasurementDefinitionManagerRemote.class, "${MeasurementDefinitionManager}"), //
+ MeasurementScheduleManager(MeasurementScheduleManagerRemote.class, "${MeasurementScheduleManager}"), //
+ OperationManager(OperationManagerRemote.class, "${OperationManager}"), //
+ ResourceManager(ResourceManagerRemote.class, "${ResourceManager}"), //
+ ResourceFactoryManager(ResourceFactoryManagerRemote.class, "${ResourceFactoryManager}"), //
+ ResourceGroupManager(ResourceGroupManagerRemote.class, "${ResourceGroupManager}"), //
+ ResourceTypeManager(ResourceTypeManagerRemote.class, "${ResourceTypeManager}"), //
+ RoleManager(RoleManagerRemote.class, "${RoleManager}"), //
+ SavedSearchManager(SavedSearchManagerRemote.class, "${SavedSearchManager}"), //
+ SubjectManager(SubjectManagerRemote.class, "${SubjectManager}"), //
+ SupportManager(SupportManagerRemote.class, "${SupportManager}"), //
+ SystemManager(SystemManagerRemote.class, "${SystemManager}"), //
+ RemoteInstallManager(RemoteInstallManagerRemote.class, "${RemoteInstallManager}"), //
+ TagManager(TagManagerRemote.class, "${TagManager}"), //
+ SynchronizationManager(SynchronizationManagerRemote.class, "${SynchronizationManager}");
+
+ private Class<?> remote;
+ private String remoteName;
+ private String beanName;
+ private boolean enabled;
+
+ private RhqManager(Class<?> remote, String enable) {
+ this.remote = remote;
+ this.beanName = this.name() + "Bean";
+ this.remoteName = this.name() + "Remote";
+ //defaults and evaluates to TRUE unless the string contains "false". Done to defend against
+ //possible errors in string replacement during rhq build.
+ this.enabled = true;
+ if ((enable != null) && (enable.trim().length() > 0)) {
+ this.enabled = (enable.trim().equalsIgnoreCase("false")) ? Boolean.FALSE : Boolean.TRUE;
+ }
+ }
+
+ public static RhqManager forInterface(Class<?> iface) {
+ for (RhqManager m : values()) {
+ if (m.remote().equals(iface)) {
+ return m;
+ }
+ }
+
+ return null;
+ }
+
+ public Class<?> remote() {
+ return this.remote;
+ }
+
+ public String beanName() {
+ return this.beanName;
+ }
+
+ public String remoteName() {
+ return this.remoteName;
+ }
+
+ public boolean enabled() {
+ return this.enabled;
+ }
+}
\ No newline at end of file
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManagers.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManagers.java
deleted file mode 100644
index dfe600c..0000000
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/RhqManagers.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 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.bindings.client;
-
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
-import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
-
-/**
- * An enumeration of all remote SLSBs of the RHQ server.
- *
- * @author Lukas Krejci
- * @author Greg Hinkle
- */
-public enum RhqManagers {
- AlertManager(AlertManagerRemote.class, "${AlertManager}"), //
- AlertDefinitionManager(AlertDefinitionManagerRemote.class, "${AlertDefinitionManager}"), //
- AvailabilityManager(AvailabilityManagerRemote.class, "${AvailabilityManager}"), //
- BundleManager(BundleManagerRemote.class, "${BundleManager}"), //
- CallTimeDataManager(CallTimeDataManagerRemote.class, "${CallTimeDataManager}"), //
- RepoManager(RepoManagerRemote.class, "${RepoManager}"), //
- ConfigurationManager(ConfigurationManagerRemote.class, "${ConfigurationManager}"), //
- ContentManager(ContentManagerRemote.class, "${ContentManager}"), //
- DataAccessManager(DataAccessManagerRemote.class, "${DataAccessManager}"), //
- DriftManager(DriftManagerRemote.class, "${DriftManager}"), //
- DriftTemplateManager(DriftTemplateManagerRemote.class, "${DriftTemplateManager}"), //
- DiscoveryBoss(DiscoveryBossRemote.class, "${DiscoveryBoss}"), //
- EventManager(EventManagerRemote.class, "${EventManager}"), //
- MeasurementBaselineManager(MeasurementBaselineManagerRemote.class, "${MeasurementBaselineManager}"), //
- MeasurementDataManager(MeasurementDataManagerRemote.class, "${MeasurementDataManager}"), //
- MeasurementDefinitionManager(MeasurementDefinitionManagerRemote.class, "${MeasurementDefinitionManager}"), //
- MeasurementScheduleManager(MeasurementScheduleManagerRemote.class, "${MeasurementScheduleManager}"), //
- OperationManager(OperationManagerRemote.class, "${OperationManager}"), //
- ResourceManager(ResourceManagerRemote.class, "${ResourceManager}"), //
- ResourceFactoryManager(ResourceFactoryManagerRemote.class, "${ResourceFactoryManager}"), //
- ResourceGroupManager(ResourceGroupManagerRemote.class, "${ResourceGroupManager}"), //
- ResourceTypeManager(ResourceTypeManagerRemote.class, "${ResourceTypeManager}"), //
- RoleManager(RoleManagerRemote.class, "${RoleManager}"), //
- SavedSearchManager(SavedSearchManagerRemote.class, "${SavedSearchManager}"), //
- SubjectManager(SubjectManagerRemote.class, "${SubjectManager}"), //
- SupportManager(SupportManagerRemote.class, "${SupportManager}"), //
- SystemManager(SystemManagerRemote.class, "${SystemManager}"), //
- RemoteInstallManager(RemoteInstallManagerRemote.class, "${RemoteInstallManager}"), //
- TagManager(TagManagerRemote.class, "${TagManager}"), //
- SynchronizationManager(SynchronizationManagerRemote.class, "${SynchronizationManager}");
-
- private Class<?> remote;
- private String remoteName;
- private String beanName;
- private boolean enabled;
-
- private RhqManagers(Class<?> remote, String enable) {
- this.remote = remote;
- this.beanName = this.name() + "Bean";
- this.remoteName = this.name() + "Remote";
- //defaults and evaluates to TRUE unless the string contains "false". Done to defend against
- //possible errors in string replacement during rhq build.
- this.enabled = true;
- if ((enable != null) && (enable.trim().length() > 0)) {
- this.enabled = (enable.trim().equalsIgnoreCase("false")) ? Boolean.FALSE : Boolean.TRUE;
- }
- }
-
- public static RhqManagers forInterface(Class<?> iface) {
- for (RhqManagers m : values()) {
- if (m.remote().equals(iface)) {
- return m;
- }
- }
-
- return null;
- }
-
- public Class<?> remote() {
- return this.remote;
- }
-
- public String beanName() {
- return this.beanName;
- }
-
- public String remoteName() {
- return this.remoteName;
- }
-
- public boolean enabled() {
- return this.enabled;
- }
-}
\ No newline at end of file
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
index f3084d1..e80c346 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
@@ -36,7 +36,7 @@ import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.ScriptEngineFactory;
import org.rhq.bindings.StandardBindings;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.bindings.util.PackageFinder;
import org.rhq.enterprise.client.ClientMain;
@@ -295,6 +295,6 @@ public class ScriptCommand implements ClientCommand {
public String getDetailedHelp() {
return "Execute a statement or a script. The following service managers are available: "
- + Arrays.toString(RhqManagers.values());
+ + Arrays.toString(RhqManager.values());
}
}
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
index fe109a8..fc59cf3 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
@@ -23,7 +23,7 @@ import org.rhq.enterprise.server.authz.RoleManagerRemote;
import org.rhq.enterprise.server.resource.ResourceManagerRemote;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.output.TabularWriter;
import org.rhq.bindings.util.ScriptUtil;
import org.rhq.core.domain.auth.Subject;
@@ -76,7 +76,7 @@ public class ScriptCommandTest {
ScriptEngine scriptEngine = client.getScriptEngine();
List<String> mgrsNotBound = new ArrayList<String>();
- for (RhqManagers mgr : RhqManagers.values()) {
+ for (RhqManager mgr : RhqManager.values()) {
if (!scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).containsKey(mgr.name())) {
mgrsNotBound.add(mgr.remoteName());
}
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
index d0fb529..57829b9 100644
--- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
@@ -32,7 +32,7 @@ import org.jboss.remoting.security.SSLSocketBuilder;
import org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker;
import org.rhq.bindings.client.RhqFacade;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.common.ProductInfo;
import org.rhq.enterprise.communications.util.SecurityUtil;
@@ -261,123 +261,123 @@ public class RemoteClient implements RhqFacade {
}
public AlertManagerRemote getAlertManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AlertManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.AlertManager);
}
public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AlertDefinitionManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.AlertDefinitionManager);
}
public AvailabilityManagerRemote getAvailabilityManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AvailabilityManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.AvailabilityManager);
}
public BundleManagerRemote getBundleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.BundleManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.BundleManager);
}
public CallTimeDataManagerRemote getCallTimeDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.CallTimeDataManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.CallTimeDataManager);
}
public DriftManagerRemote getDriftManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DriftManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.DriftManager);
}
public DriftTemplateManagerRemote getDriftTemplateManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DriftTemplateManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.DriftTemplateManager);
}
public RepoManagerRemote getRepoManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RepoManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.RepoManager);
}
public ConfigurationManagerRemote getConfigurationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ConfigurationManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ConfigurationManager);
}
public ContentManagerRemote getContentManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ContentManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ContentManager);
}
public DataAccessManagerRemote getDataAccessManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DataAccessManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.DataAccessManager);
}
public DiscoveryBossRemote getDiscoveryBoss() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DiscoveryBoss);
+ return RemoteClientProxy.getProcessor(this, RhqManager.DiscoveryBoss);
}
public EventManagerRemote getEventManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.EventManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.EventManager);
}
public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementBaselineManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementBaselineManager);
}
public MeasurementDataManagerRemote getMeasurementDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementDataManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementDataManager);
}
public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementDefinitionManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementDefinitionManager);
}
public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementScheduleManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.MeasurementScheduleManager);
}
public OperationManagerRemote getOperationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.OperationManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.OperationManager);
}
public ResourceManagerRemote getResourceManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ResourceManager);
}
public ResourceFactoryManagerRemote getResourceFactoryManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceFactoryManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ResourceFactoryManager);
}
public ResourceGroupManagerRemote getResourceGroupManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceGroupManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ResourceGroupManager);
}
public ResourceTypeManagerRemote getResourceTypeManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceTypeManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.ResourceTypeManager);
}
public RoleManagerRemote getRoleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RoleManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.RoleManager);
}
public SavedSearchManagerRemote getSavedSearchManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SavedSearchManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.SavedSearchManager);
}
public SubjectManagerRemote getSubjectManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SubjectManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.SubjectManager);
}
public SupportManagerRemote getSupportManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SupportManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.SupportManager);
}
public SystemManagerRemote getSystemManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SystemManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.SystemManager);
}
public RemoteInstallManagerRemote getRemoteInstallManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RemoteInstallManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.RemoteInstallManager);
}
public TagManagerRemote getTagManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.TagManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.TagManager);
}
public SynchronizationManagerRemote getSynchronizationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SynchronizationManager);
+ return RemoteClientProxy.getProcessor(this, RhqManager.SynchronizationManager);
}
/**
@@ -391,7 +391,7 @@ public class RemoteClient implements RhqFacade {
this.managers = new HashMap<String, Object>();
- for (RhqManagers manager : RhqManagers.values()) {
+ for (RhqManager manager : RhqManager.values()) {
try {
Method m = this.getClass().getMethod("get" + manager.name());
if (manager.enabled()) {
diff --git a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
index e9751c2..23dea3c 100644
--- a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
+++ b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
import org.jboss.remoting.invocation.NameBasedInvocation;
import org.rhq.bindings.client.AbstractRhqFacadeProxy;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.util.InterfaceSimplifier;
import org.rhq.core.domain.server.ExternalizableStrategy;
@@ -42,7 +42,7 @@ public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
private static final Log LOG = LogFactory.getLog(RemoteClientProxy.class);
- public RemoteClientProxy(RemoteClient client, RhqManagers manager) {
+ public RemoteClientProxy(RemoteClient client, RhqManager manager) {
super(client, manager);
}
@@ -51,7 +51,7 @@ public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
}
@SuppressWarnings("unchecked")
- public static <T> T getProcessor(RemoteClient remoteClient, RhqManagers manager) {
+ public static <T> T getProcessor(RemoteClient remoteClient, RhqManager manager) {
try {
RemoteClientProxy gpc = new RemoteClientProxy(remoteClient, manager);
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 2e48048..dbb4424 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
@@ -31,7 +31,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.client.RhqFacade;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
import org.rhq.bindings.util.InterfaceSimplifier;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
@@ -416,7 +416,7 @@ public class LocalClient implements RhqFacade {
managers = new HashMap<String, Object>();
- for (RhqManagers manager : RhqManagers.values()) {
+ for (RhqManager manager : RhqManager.values()) {
try {
Method m = getClass().getMethod("get" + manager.name());
managers.put(manager.name(), m.invoke(this));
@@ -430,7 +430,7 @@ public class LocalClient implements RhqFacade {
}
private <T> T getProxy(Object slsb, Class<T> iface) {
- RhqManagers manager = RhqManagers.forInterface(iface);
+ RhqManager manager = RhqManager.forInterface(iface);
Class<?> simplified = null;
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
index e9f1e4d..67bd5e7 100644
--- a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
+++ b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
@@ -28,7 +28,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.client.AbstractRhqFacadeProxy;
-import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.client.RhqManager;
/**
* This is a proxy interface that simply calls given (de-simplified) method on a provided object.
@@ -42,7 +42,7 @@ public class LocalClientProxy extends AbstractRhqFacadeProxy<LocalClient> {
private Object localSLSB;
- public LocalClientProxy(Object localSLSB, LocalClient client, RhqManagers manager) {
+ public LocalClientProxy(Object localSLSB, LocalClient client, RhqManager manager) {
super(client, manager);
this.localSLSB = localSLSB;
}
commit 573dd3715035d095bd65f823dc9539e72a27fbe9
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Jun 20 09:22:03 2012 +0200
Making it all compile again and provide basic abstractions to cut the code completion out of the CLI.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
index da544c4..05e6a09 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
@@ -133,13 +133,11 @@ public class StandardBindings extends HashMap<String, Object> {
putAll(managers);
}
- @Override
public void preInject(ScriptEngine scriptEngine) {
((ScriptUtil) get(SCRIPT_UTIL)).init(scriptEngine);
((ScriptAssert) get(ASSERT)).init(scriptEngine);
}
- @Override
public void postInject(ScriptEngine scriptEngine) {
ScriptEngineFactory.bindIndirectionMethods(scriptEngine, SCRIPT_UTIL);
ScriptEngineFactory.bindIndirectionMethods(scriptEngine, ASSERT);
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 7ebe141..4a1ed62 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -22,6 +22,9 @@
*/
package org.rhq.enterprise.client;
+import gnu.getopt.Getopt;
+import gnu.getopt.LongOpt;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
@@ -34,6 +37,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
@@ -44,6 +50,8 @@ import jline.MultiCompletor;
import jline.SimpleCompletor;
import mazz.i18n.Msg;
+import org.rhq.bindings.ScriptEngineFactory;
+import org.rhq.bindings.util.PackageFinder;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.commands.ClientCommand;
import org.rhq.enterprise.client.commands.ScriptCommand;
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
index f51e148..0f1b232 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
@@ -91,6 +91,7 @@ public class InteractiveJavascriptCompletor implements Completor {
this.services = services;
}
+ @Override
@SuppressWarnings("unchecked")
public int complete(String s, int i, @SuppressWarnings("rawtypes") List list) {
try {
@@ -168,7 +169,7 @@ public class InteractiveJavascriptCompletor implements Completor {
* @param list
* @return location of relative completion
*/
- public int contextComplete(Object baseObject, String s, int i, List<String> list) {
+ private int contextComplete(Object baseObject, String s, int i, List<String> list) {
String temp = s.split("\\(", 2)[0];
if (temp.contains(".")) {
String[] call = temp.split("\\.", 2);
diff --git a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
index ecf304e..202bef7 100644
--- a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
+++ b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
@@ -67,6 +67,10 @@
<copy file="${settings.localRepository}/net/sf/opencsv/opencsv/${opencsv.version}/opencsv-${opencsv.version}.jar" tofile="${lib.home}/opencsv-${opencsv.version}.jar" verbose="true" />
<copy file="${settings.localRepository}/org/rhq/rhq-script-bindings/${project.version}/rhq-script-bindings-${project.version}.jar" tofile="${lib.home}/rhq-script-bindings-${project.version}.jar" verbose="true" />
<copy file="${settings.localRepository}/hibernate-annotations/hibernate-annotations/${hibernate-annotations.version}/hibernate-annotations-${hibernate-annotations.version}.jar" todir="${lib.home}" verbose="true" />
+
+ <copy file="${settings.localRepository}/org/rhq/rhq-scripting-api/${project.version}/rhq-scripting-api-${project.version}.jar" tofile="${lib.home}/rhq-scripting-api-${project.version}.jar" verbose="true" />
+ <copy file="${settings.localRepository}/org/rhq/rhq-scripting-javascript/${project.version}/rhq-scripting-javascript-${project.version}.jar" tofile="${lib.home}/rhq-scripting-javascript-${project.version}.jar" verbose="true" />
+ <copy file="${settings.localRepository}/org/mozilla/rhino/${rhino.version}/rhino-${rhino.version}.jar" tofile="${lib.home}/rhino-${rhino.version}.jar" verbose="true" />
</target>
<target name="prepare-samples-dir">
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
index f8beb28..2c89718 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
@@ -19,8 +19,11 @@
package org.rhq.scripting;
+import java.io.PrintWriter;
import java.util.List;
+import javax.script.ScriptContext;
+
/**
* An interface for performing code completion in a given language.
* This inspired by the jline's <code>Completor</code> interface but
@@ -32,8 +35,29 @@ import java.util.List;
public interface CodeCompletion {
/**
+ * The code completor can use the provided script context to find out about possible
+ * completions.
+ * <p>
+ * This method is called once before the first {@link #complete(PrintWriter, String, int, List)} method
+ * is called.
+ *
+ * @param scriptContext
+ */
+ void setScriptContext(ScriptContext scriptContext);
+
+ /**
+ * Provides the code completion implementation with the metadata provider it can use to
+ * format more meaningful completion hints.
+ *
+ * @param metadataProvider
+ */
+ void setMetadataProvider(MetadataProvider metadataProvider);
+
+ /**
* Generates the completion candidates and fills the supplied list with them.
*
+ * @param output the output the completor can use to write some additional information to convey some
+ * additional info to the user
* @param context the context of the code completion, usually that is the current
* statement being edited by the user
* @param cursorPosition the position of the cursor inside the context
@@ -42,5 +66,5 @@ public interface CodeCompletion {
* @return the character index in the context for which the completion candidates were
* actually generated (might be different from the cursorPosition).
*/
- int complete(String context, int cursorPosition, @SuppressWarnings("rawtypes") List candidates);
+ int complete(PrintWriter output, String context, int cursorPosition, @SuppressWarnings("rawtypes") List candidates);
}
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java
new file mode 100644
index 0000000..598d952
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/MetadataProvider.java
@@ -0,0 +1,69 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+/**
+ * The scripting implementations don't have to supply an implementation of this interface but
+ * rather one is provided to the {@link CodeCompletion}.
+ * <p>
+ * The code completion can ask this implementation for various metadata on methods or classes.
+ * <p>
+ * The {@link CodeCompletion} implementations can use this information to format more meaningful
+ * completion hints.
+ *
+ * @author Lukas Krejci
+ */
+public interface MetadataProvider {
+
+ /**
+ * Tries to determine the name of a parameter on a method.
+ *
+ * @param method the method
+ * @param parameterIndex the index of the method parameter
+ * @return The name of the parameter or null if it could not be determined
+ */
+ String getParameterName(Method method, int parameterIndex);
+
+ /**
+ * @param clazz
+ * @return the (java)doc on a class or null if none could be determined
+ */
+ String getDocumentation(Class<?> clazz);
+
+ /**
+ * @param method
+ * @return the (java)doc on a method or null if none could be determined
+ */
+ String getDocumentation(Method method);
+
+ /**
+ * This is a utility method for determining the type name including the type parameters.
+ *
+ * @param type the type to determine the name for
+ * @param fullNames if true, all the types and type parameters will be the full class names, otherwise
+ * only the simple name of the class / type parameter will be used.
+ * @return the name of the type including type parameters
+ */
+ String getTypeName(Type type, boolean fullNames);
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
new file mode 100644
index 0000000..d1df7f5
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JavascriptCompletor.java
@@ -0,0 +1,578 @@
+/*
+ * 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.scripting.javascript;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.MethodDescriptor;
+import java.beans.PropertyDescriptor;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+
+import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.MetadataProvider;
+
+/**
+ * A Contextual JavaScript interactive completor. Not perfect, but
+ * handles a fair number of cases.
+ *
+ * @author Greg Hinkle
+ * @author Lukas Krejci
+ */
+public class JavascriptCompletor implements CodeCompletion {
+
+ private static final String SUBJECT_CLASS_NAME = "org.rhq.core.domain.auth.Subject";
+ private ScriptContext context;
+ private MetadataProvider metadataProvider;
+
+ private String lastComplete;
+
+ // Consecutive times this exact complete has been requested
+ private int recomplete;
+
+ private static final Set<String> IGNORED_METHODS;
+ static {
+ IGNORED_METHODS = new HashSet<String>();
+ IGNORED_METHODS.add("newProxyInstance");
+ IGNORED_METHODS.add("hashCode");
+ IGNORED_METHODS.add("equals");
+ IGNORED_METHODS.add("getInvocationHandler");
+ IGNORED_METHODS.add("setHandler");
+ IGNORED_METHODS.add("isProxyClass");
+ IGNORED_METHODS.add("newProxyInstance");
+ IGNORED_METHODS.add("getProxyClass");
+ IGNORED_METHODS.add("main");
+ IGNORED_METHODS.add("handler");
+ IGNORED_METHODS.add("init");
+ IGNORED_METHODS.add("initChildren");
+ IGNORED_METHODS.add("initMeasurements");
+ IGNORED_METHODS.add("initOperations");
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public int complete(PrintWriter output, String s, int i, @SuppressWarnings("rawtypes") List list) {
+ try {
+ if (lastComplete != null && lastComplete.equals(s)) {
+ recomplete++;
+ } else {
+ recomplete = 1;
+ }
+
+ lastComplete = s;
+
+ String base = s;
+
+ int rootLength = 0;
+
+ if (s.indexOf('=') > 0) {
+ base = s.substring(s.indexOf("=") + 1).trim();
+ rootLength = s.length() - base.length();
+ }
+
+ String[] call = base.split("\\.");
+ if (base.endsWith(".")) {
+ String[] argPadded = new String[call.length + 1];
+ System.arraycopy(call, 0, argPadded, 0, call.length);
+ argPadded[call.length] = "";
+ call = argPadded;
+ }
+
+ if (call.length == 1) {
+ Map<String, Object> matches = getContextMatches(call[0]);
+ if (matches.size() == 1 && matches.containsKey(call[0]) && !s.endsWith(".")) {
+ list.add(".");
+ return rootLength + call[0].length() + 1;
+
+ } else {
+ list.addAll(matches.keySet());
+ }
+ } else {
+ Object rootObject = context.getAttribute(call[0]);
+ if (rootObject != null) {
+ String theRest = base.substring(call[0].length() + 1, base.length());
+ int matchIndex = contextComplete(output, rootObject, theRest, i, list);
+ Collections.sort(list);
+ return rootLength + call[0].length() + 1 + matchIndex;
+ }
+ }
+
+ Collections.sort(list);
+
+ return (list.size() == 0) ? (-1) : rootLength;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
+ @Override
+ public void setMetadataProvider(MetadataProvider metadataProvider) {
+ this.metadataProvider = metadataProvider;
+ }
+
+ /**
+ * Base Object can be an object where we're looking for methods on it, or an
+ * interface. This recursively works off the completions left to right.
+ *
+ * Objects can be completed with fields or method calls.
+ * method parameters are completed with type matching
+ * method result chainings are completed based on declared return types
+ *
+ * e.g. have a Resource in context as myResource. Original string is
+ * "myResource.name". This method would be called with a baseObject ==
+ * to myResource and the string "name".
+ *
+ * Note: this method will not and should not execute methods, but will
+ * read field properties to continue chained completions.
+ *
+ * @param output the output that can the completor use to convey info to the user
+ * @param baseObject the context object or class to complete from
+ * @param s the relative command string to check
+ * @param i
+ * @param list
+ * @return location of relative completion
+ */
+ private int contextComplete(PrintWriter output, Object baseObject, String s, int i, List<String> list) {
+ String temp = s.split("\\(", 2)[0];
+ if (temp.contains(".")) {
+ String[] call = temp.split("\\.", 2);
+
+ String next = call[0];
+ if (next.contains("(")) {
+ next = next.substring(0, next.indexOf("("));
+ }
+
+ Map<String, List<Object>> matches = getContextMatches(output, baseObject, next);
+ if (!matches.isEmpty()) {
+ Object rootObject = matches.get(next).get(0);
+ if (rootObject instanceof PropertyDescriptor && !(baseObject instanceof Class)) {
+ try {
+ rootObject = invoke(baseObject, ((PropertyDescriptor) rootObject).getReadMethod());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } else if (rootObject instanceof Method) {
+ rootObject = ((Method) rootObject).getReturnType();
+ }
+
+ return call[0].length() + 1 + contextComplete(output, rootObject, call[1], i, list);
+ } else {
+ return -1;
+ }
+ } else {
+ String[] call = s.split("\\(", 2);
+
+ Map<String, List<Object>> matches = getContextMatches(output, baseObject, call[0]);
+
+ if (call.length == 2 && matches.containsKey(call[0])) {
+
+ int x = 0;
+ for (String key : matches.keySet()) {
+
+ List<Object> matchList = matches.get(key);
+
+ if (recomplete == 2) {
+ List<Method> methods = new ArrayList<Method>();
+ for (Object match : matchList) {
+ if (match instanceof Method) {
+ methods.add((Method) match);
+ }
+ }
+ displaySignatures(output, baseObject, methods.toArray(new Method[methods.size()]));
+ return -1;
+ }
+
+ for (Object match : matchList) {
+
+ if (key.equals(call[0]) && match instanceof Method) {
+
+ int result = completeParameters(baseObject, call[1], i, list, (Method) match); // x should be the same for all calls
+ if (result > 0) {
+ x = result;
+ }
+ }
+ }
+ }
+ return call[0].length() + 1 + x;
+ }
+
+ if (matches.size() == 1 && matches.containsKey(call[0])) {
+ if (matches.get(call[0]).get(0) instanceof Method) {
+ list.add("(" + (((Method) matches.get(call[0]).get(0)).getParameterTypes().length == 0 ? ")" : ""));
+ }
+ return call[0].length() + 1;
+ }
+
+ if (recomplete == 2) {
+ List<Method> methods = new ArrayList<Method>();
+ for (List<Object> matchList : matches.values()) {
+ for (Object val : matchList) {
+ if (val instanceof Method) {
+ methods.add((Method) val);
+ }
+ }
+ }
+ displaySignatures(output, baseObject, methods.toArray(new Method[methods.size()]));
+ } else {
+ if (matches.size() == 1 && matches.values().iterator().next().get(0) instanceof Method) {
+ list.add(matches.keySet().iterator().next()
+ + "("
+ + ((((Method) matches.values().iterator().next().get(0)).getParameterTypes().length == 0 ? ")"
+ : "")));
+ } else {
+ list.addAll(matches.keySet());
+ }
+ }
+ return 0;
+
+ }
+ }
+
+ private void displaySignatures(PrintWriter output, Object object, Method... methods) {
+ try {
+ String[][] signatures = new String[methods.length][];
+ int i = 0;
+ for (Method m : methods) {
+ signatures[i++] = getSignature(object, m).split(" ", 2);
+ }
+
+ int maxReturnLength = 0;
+ for (String[] sig : signatures) {
+ if (sig[0].length() > maxReturnLength)
+ maxReturnLength = sig[0].length();
+ }
+
+ for (String[] sig : signatures) {
+ for (i = 0; i < (maxReturnLength - sig[0].length()); i++) {
+ output.print(" ");
+ }
+
+ output.print(sig[0]);
+ output.print(" ");
+ output.print(sig[1]);
+ output.println();
+ }
+ } catch (Exception e) {
+ e.printStackTrace(output);
+ }
+ }
+
+ /**
+ * Split apart the parameters to a method call and complete the last parameter. If the last
+ * paramater has a valid value close that field with a "," for the next param or a ")" if is
+ * the last parameter. Does all machting according to the type of the parameters of the
+ * supplied method.
+ *
+ * @param baseObject
+ * @param params
+ * @param i
+ * @param list
+ * @param method
+ * @return
+ */
+ public int completeParameters(Object baseObject, String params, int i, List<String> list, Method method) {
+
+ String[] paramList = params.split(",");
+
+ Class<?>[] c = method.getParameterTypes();
+
+ String lastParam = paramList[paramList.length - 1];
+ int paramIndex = paramList.length - 1;
+ if (params.trim().endsWith(",")) {
+ lastParam = "";
+ paramIndex++;
+ }
+
+ int baseLength = 0;
+
+ for (int x = 0; x < paramIndex; x++) {
+ Object paramFound = context.getAttribute(paramList[x]);
+
+ if (paramFound != null && !c[x].isAssignableFrom(paramFound.getClass())) {
+ return -1;
+ }
+ baseLength += paramList[x].length() + 1;
+ }
+
+ if (paramIndex >= c.length) {
+ if (params.endsWith(")")) {
+ return -1;
+ } else {
+ list.add(params + ")");
+ return (params + ")").length();
+ }
+ } else {
+
+ if (baseObject instanceof Map && method.getName().equals("get") && method.getParameterTypes().length == 1) {
+ //unused Class<?> keyType = method.getParameterTypes()[0];
+ for (Object key : ((Map<?, ?>) baseObject).keySet()) {
+ String lookupChoice = "\'" + String.valueOf(key) + "\'";
+ if (lookupChoice.startsWith(lastParam)) {
+ list.add(lookupChoice);
+ }
+ }
+ if (list.size() == 1) {
+ list.set(0, list.get(0) + ")");
+ }
+
+ } else {
+ Class<?> parameterType = c[paramIndex];
+
+ Map<String, Object> matches = getContextMatches(lastParam, parameterType);
+
+ if (matches.size() == 1 && matches.containsKey(lastParam)) {
+
+ list.add(paramIndex == c.length - 1 ? ")" : ",");
+ return baseLength + lastParam.length();
+ } else {
+ list.addAll(matches.keySet());
+ }
+ }
+
+ return baseLength;
+ }
+ }
+
+ private Map<String, Object> getContextMatches(String start) {
+ Map<String, Object> found = new HashMap<String, Object>();
+ if (context != null) {
+ for (Integer scope : context.getScopes()) {
+ Bindings bindings = context.getBindings(scope);
+ for (String var : bindings.keySet()) {
+ if (var.startsWith(start)) {
+ found.put(var, bindings.get(var));
+ }
+ }
+ }
+ }
+
+ //this was originally part of the code completor that lived in the CLI
+ //I don't think we need it, because the services are present under the
+ //same names in the context. This code can never add any new matches.
+ /*
+ if (services != null) {
+ for (String var : services.keySet()) {
+ if (var.startsWith(start)) {
+ found.put(var, services.get(var));
+ }
+ }
+ }
+ */
+
+ return found;
+ }
+
+ /**
+ * Look through all available contexts to find bindings that both start with
+ * the supplied start and match the typeFilter.
+ * @param start
+ * @param typeFilter
+ * @return
+ */
+ private Map<String, Object> getContextMatches(String start, Class<?> typeFilter) {
+ Map<String, Object> found = new HashMap<String, Object>();
+ if (context != null) {
+ for (int scope : context.getScopes()) {
+ Bindings bindings = context.getBindings(scope);
+ for (String var : bindings.keySet()) {
+ if (var.startsWith(start)) {
+
+ if ((bindings.get(var) != null && typeFilter.isAssignableFrom(bindings.get(var).getClass()))
+ || recomplete == 3) {
+ found.put(var, bindings.get(var));
+ }
+ }
+ }
+ }
+
+ if (typeFilter.isEnum()) {
+ for (Object ec : typeFilter.getEnumConstants()) {
+ Enum<?> e = (Enum<?>) ec;
+ String code = typeFilter.getSimpleName() + "." + e.name();
+ if (code.startsWith(start)) {
+ found.put(typeFilter.getSimpleName() + "." + e.name(), e);
+ }
+ }
+ }
+ }
+ return found;
+ }
+
+ private Map<String, List<Object>> getContextMatches(PrintWriter output, Object baseObject, String start) {
+ Map<String, List<Object>> found = new HashMap<String, List<Object>>();
+
+ Class<?> baseObjectClass = null;
+ if (baseObject instanceof Class) {
+ baseObjectClass = (Class<?>) baseObject;
+ } else {
+ baseObjectClass = baseObject.getClass();
+ }
+
+ try {
+ if (baseObjectClass.equals(Void.TYPE))
+ return found;
+
+ BeanInfo info = null;
+ if (baseObjectClass.isInterface() || baseObjectClass.equals(Object.class)) {
+ info = Introspector.getBeanInfo(baseObjectClass);
+ } else {
+ info = Introspector.getBeanInfo(baseObjectClass, Object.class);
+ }
+
+ Set<Method> methodsCovered = new HashSet<Method>();
+
+ PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
+ for (PropertyDescriptor desc : descriptors) {
+ if (desc.getName().startsWith(start) && (!IGNORED_METHODS.contains(desc.getName()))) {
+
+ List<Object> list = found.get(desc.getName());
+ if (list == null) {
+ list = new ArrayList<Object>();
+ found.put(desc.getName(), list);
+ }
+ list.add(desc);
+
+ methodsCovered.add(desc.getReadMethod());
+ methodsCovered.add(desc.getWriteMethod());
+ }
+ }
+
+ MethodDescriptor[] methods = info.getMethodDescriptors();
+ for (MethodDescriptor desc : methods) {
+ if (desc.getName().startsWith(start) && !methodsCovered.contains(desc.getMethod())
+ && !desc.getName().startsWith("_d") && !IGNORED_METHODS.contains(desc.getName())) {
+
+ Method m = desc.getMethod();
+ //TODO THIS IS SO WRONG - the methods are there but we just "forget" to
+ //to mention them to the user.
+ boolean isProxy = isProxyMethod(baseObject, m);
+ Class<?>[] parameters = m.getParameterTypes();
+ boolean startsWithSubject = ((parameters.length > 0) && isSubjectClass(parameters[0]));
+ if ((isProxy && startsWithSubject) || !startsWithSubject) {
+
+ List<Object> list = found.get(desc.getName());
+ if (list == null) {
+ list = new ArrayList<Object>();
+ found.put(desc.getName(), list);
+ }
+ list.add(m);
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace(output);
+ }
+ return found;
+ }
+
+ private String getSignature(Object object, Method m) {
+
+ StringBuilder buf = new StringBuilder();
+ Type[] params = m.getGenericParameterTypes();
+ int i = 0;
+
+ buf.append(metadataProvider.getTypeName(m.getGenericReturnType(), false));
+ buf.append(" ");
+
+ buf.append(m.getName());
+ buf.append("(");
+ boolean first = true;
+ for (Type type : params) {
+ if (!first) {
+ buf.append(", ");
+ } else {
+ first = false;
+ }
+
+ String name = metadataProvider.getTypeName(type, false);
+ String paramName = metadataProvider.getParameterName(m, i);
+ if (paramName != null) {
+ name += " " + paramName;
+ }
+
+ buf.append(name);
+
+ i++;
+ }
+ buf.append(")");
+ return buf.toString();
+ }
+
+ private static boolean isProxyMethod(Object object, Method m) {
+ // TODO this has to be changed - we have the MetaDataProvider which should be able
+ // to provide enough info so that the completors don't have to go through these hoops.
+ return false;
+
+ // boolean result = false;
+ // if (object instanceof Proxy) {
+ // if (Proxy.getInvocationHandler(object) instanceof RemoteClientProxy) {
+ // try {
+ // m =
+ // ((RemoteClientProxy) Proxy.getInvocationHandler(object)).getRemoteInterface()
+ // .getDeclaredMethod(m.getName(), m.getParameterTypes());
+ // } catch (NoSuchMethodException e) {
+ // result = true;
+ // }
+ // }
+ // }
+ // return result;
+ }
+
+ @Override
+ public void setScriptContext(ScriptContext context) {
+ this.context = context;
+ }
+
+ private static Object invoke(Object o, Method m) throws IllegalAccessException, InvocationTargetException {
+ boolean access = m.isAccessible();
+ m.setAccessible(true);
+ try {
+ return m.invoke(o);
+ } finally {
+ m.setAccessible(access);
+ }
+ }
+
+ private boolean isSubjectClass(Class<?> cls) {
+ while (cls != null) {
+ if (SUBJECT_CLASS_NAME.equals(cls.getName())) {
+ return true;
+ }
+
+ cls = cls.getSuperclass();
+ }
+
+ return false;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
index 984e5fc..02d54eb 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
@@ -42,8 +42,7 @@ public class JsEngineProvider implements ScriptEngineProvider {
@Override
public CodeCompletion getCodeCompletion() {
- // TODO copy this over from the CLI
- return null;
+ return new JavascriptCompletor();
}
}
commit d35d0f2fb6ae66ed339dbe2099a9d475a3d0cea6
Merge: 6bd2fa8 bc0ab10
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Jun 18 13:05:37 2012 +0200
Merge branch 'master' into lkrejci/modular-scripting
diff --cc modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 5021434,99595ab..7ebe141
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@@ -22,11 -22,7 +22,8 @@@
*/
package org.rhq.enterprise.client;
- import gnu.getopt.Getopt;
- import gnu.getopt.LongOpt;
-
import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
@@@ -37,9 -32,14 +34,9 @@@ import java.util.HashMap
import java.util.List;
import java.util.Map;
- import javax.script.ScriptEngine;
- import javax.script.ScriptException;
-import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.client.commands.ClientCommand;
-import org.rhq.enterprise.client.commands.ScriptCommand;
-import org.rhq.enterprise.client.script.CommandLineParseException;
-import org.rhq.enterprise.clientapi.RemoteClient;
-
+ import gnu.getopt.Getopt;
+ import gnu.getopt.LongOpt;
+
import jline.ArgumentCompletor;
import jline.Completor;
import jline.ConsoleReader;
@@@ -47,14 -47,6 +44,12 @@@ import jline.MultiCompletor
import jline.SimpleCompletor;
import mazz.i18n.Msg;
- import org.rhq.bindings.ScriptEngineFactory;
- import org.rhq.bindings.util.PackageFinder;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.client.commands.ClientCommand;
+import org.rhq.enterprise.client.commands.ScriptCommand;
+import org.rhq.enterprise.client.script.CommandLineParseException;
+import org.rhq.enterprise.clientapi.RemoteClient;
+
/**
* @author Greg Hinkle
* @author Simeon Pinder
commit 6bd2fa80f4871209de16e6560b374a24cd308666
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed May 30 20:13:46 2012 +0200
Experimental and unfinished support for python in the CLI.
There's no support for custom script sources and code completion.
diff --git a/modules/enterprise/scripting/python/pom.xml b/modules/enterprise/scripting/python/pom.xml
new file mode 100644
index 0000000..4b48e52
--- /dev/null
+++ b/modules/enterprise/scripting/python/pom.xml
@@ -0,0 +1,187 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>rhq-scripting-parent</artifactId>
+ <groupId>org.rhq</groupId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rhq-scripting-python</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ <name>RHQ Python support</name>
+ <description>Provides RHQ scripting in Python using Jython</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-scripting-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.python</groupId>
+ <artifactId>jython-standalone</artifactId>
+ <version>2.5.2</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../..</rhq.rootDir>
+ <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
+ <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/lib</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+
+ <execution>
+ <id>deploy</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+ <mkdir dir="${rhq.deploymentDir}" />
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Updating
+ ${deployment.file}...</echo>
+ <jar destfile="${deployment.file}" basedir="${project.build.outputDirectory}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <tasks>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Deleting
+ ${deployment.file}...</echo>
+ <delete file="${deployment.file}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>cobertura-plugins</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.cobertura</groupId>
+ <artifactId>cobertura</artifactId>
+ <version>1.9.4.1</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>cobertura-instrument</id>
+ <phase>pre-integration-test</phase>
+ <configuration>
+ <tasks>
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/cobertura/backup" />
+ <!-- backup all classes so that we
+ can instrument the original classes -->
+ <copy toDir="target/cobertura/backup" verbose="true" overwrite="true">
+ <fileset dir="target/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- create a properties file and
+ save there location of cobertura data file -->
+ <touch file="target/classes/cobertura.properties" />
+ <echo file="target/classes/cobertura.properties">net.sourceforge.cobertura.datafile=${project.build.directory}/cobertura/cobertura.ser</echo>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- instrument all classes in target/classes
+ directory -->
+ <cobertura-instrument datafile="${project.build.directory}/cobertura/cobertura.ser" todir="${project.build.directory}/classes">
+ <fileset dir="${project.build.directory}/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </cobertura-instrument>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>cobertura-report</id>
+ <phase>post-integration-test</phase>
+ <configuration>
+ <tasks>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/site/cobertura" />
+ <!-- restore classes from backup
+ folder to classes folder -->
+ <copy toDir="target/classes" verbose="true" overwrite="true">
+ <fileset dir="target/cobertura/backup">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- delete backup folder -->
+ <delete dir="target/cobertura/backup" />
+ <!-- create a code coverage report -->
+ <cobertura-report format="html" datafile="${project.build.directory}/cobertura/cobertura.ser" destdir="${project.build.directory}/site/cobertura">
+ <fileset dir="${basedir}/src/main/java">
+ <include name="**/*.java" />
+ </fileset>
+ </cobertura-report>
+ <!-- delete cobertura.properties
+ file -->
+ <delete file="target/classes/cobertura.properties" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
new file mode 100644
index 0000000..9ee50b8
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
@@ -0,0 +1,101 @@
+/*
+ * 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.scripting.python;
+
+import java.lang.reflect.Method;
+import java.security.PermissionCollection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptSourceProvider;
+import org.rhq.scripting.util.SandboxedScriptEngine;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
+
+ private ScriptEngineManager engineManager = new ScriptEngineManager();
+
+ @Override
+ public ScriptEngine instantiate(Set<String> packages, ScriptSourceProvider scriptSourceProvider,
+ PermissionCollection permissions) throws ScriptException {
+
+ ScriptEngine eng = engineManager.getEngineByName("python");
+
+ for (String pkg : packages) {
+ eng.eval("from " + pkg + " import *\n");
+ }
+
+ //TODO add support for script source providers... possibly using http://www.python.org/dev/peps/pep-0302/
+
+ //fingers crossed we can secure jython like this
+ return new SandboxedScriptEngine(eng, permissions);
+ }
+
+ @Override
+ public Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> overloadedMethods) {
+ if (overloadedMethods == null || overloadedMethods.isEmpty()) {
+ return Collections.emptySet();
+ }
+
+ Set<Integer> argCnts = new HashSet<Integer>();
+ for(Method m : overloadedMethods) {
+ argCnts.add(m.getParameterTypes().length);
+ }
+
+ String methodName = overloadedMethods.iterator().next().getName();
+ StringBuilder functionBody = new StringBuilder();
+
+ functionBody.append("def ").append(methodName).append("(*args, **kwargs):\n");
+ functionBody.append("\t").append("if len(kwargs) > 0:\n");
+ functionBody.append("\t\t").append("raise ValueError(\"Named arguments not supported for Java methods\")\n");
+ functionBody.append("\t").append("argCnt = len(args)\n");
+
+ for(Integer argCnt : argCnts) {
+ functionBody.append("\t").append("if argCnt == ").append(argCnt).append(":\n");
+ functionBody.append("\t\treturn ").append(boundObjectName).append(".").append(methodName).append("(");
+ int last = argCnt - 1;
+ for(int i = 0; i < argCnt; ++i) {
+ functionBody.append("args[").append(i).append("]");
+ if (i < last) {
+ functionBody.append(", ");
+ }
+ }
+ functionBody.append(")\n");
+ }
+
+ return Collections.singleton(functionBody.toString());
+ }
+
+ @Override
+ public String extractUserFriendlyErrorMessage(ScriptException e) {
+ return e.getMessage();
+ }
+
+}
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineProvider.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineProvider.java
new file mode 100644
index 0000000..c0bfcac
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.scripting.python;
+
+import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptEngineProvider;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class PythonScriptEngineProvider implements ScriptEngineProvider {
+
+ @Override
+ public String getSupportedLanguage() {
+ return "python";
+ }
+
+ @Override
+ public ScriptEngineInitializer getInitializer() {
+ return new PythonScriptEngineInitializer();
+ }
+
+ @Override
+ public CodeCompletion getCodeCompletion() {
+ // XXX are we gonna support code completion for multiple langs in the CLI?
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/scripting/python/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider
new file mode 100644
index 0000000..3882776
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider
@@ -0,0 +1 @@
+org.rhq.scripting.python.PythonScriptEngineProvider
commit 6515d4f28dfddde094d20b72bad3b2f038d70b13
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed May 30 20:08:03 2012 +0200
Initial impl of modules in the javascript CLI.
All should be hooked up apart from the actual bundling of now standalone
javascript support with the CLI. This will going to need some pom.xml love.
We now bundle our own ScriptEngine implementation based on the Phobos
javascript engine with modifications inspired by the version of the script
engine in JDK 1.6.30.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/SandboxedScriptEngine.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/SandboxedScriptEngine.java
deleted file mode 100644
index f994f09..0000000
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/SandboxedScriptEngine.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 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.bindings;
-
-import java.io.Reader;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.security.cert.Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-
-import javax.script.Bindings;
-import javax.script.ScriptContext;
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineFactory;
-import javax.script.ScriptException;
-
-/**
- * <b>DO NOT USE THIS CLASS DIRECTLY!!!!</b> Use {@link org.rhq.bindings.ScriptEngineFactory#getSecuredScriptEngine(String, org.rhq.bindings.util.PackageFinder, StandardBindings, PermissionCollection)}
- * method instead for a reliably secured script engine.
- * <p>
- * This is a decorator class for any other {@link ScriptEngine} implementation
- * that runs any of the eval methods with the defined set of {@link Permission}s.
- * <p>
- * For the permissions to have any effect, a SecurityManager has to be installed
- * in the current VM.
- *
- * @author Lukas Krejci
- */
-public class SandboxedScriptEngine implements ScriptEngine {
-
- private ScriptEngine engine;
- private AccessControlContext accessControlContext;
-
- public SandboxedScriptEngine(ScriptEngine engine) {
- this.engine = engine;
- }
-
- public SandboxedScriptEngine(ScriptEngine engine, PermissionCollection permissions) {
- this(engine);
- setPermissions(permissions);
- }
-
- public SandboxedScriptEngine(ScriptEngine engine, Collection<? extends Permission> permissions) {
- this(engine);
- setPermissions(permissions);
- }
-
- public void setPermissions(Permission... permissions) {
- setPermissions(Arrays.asList(permissions));
- }
-
- public void setPermissions(Collection<? extends Permission> permissions) {
- Permissions ps = new Permissions();
- for(Permission p : permissions) {
- ps.add(p);
- }
-
- setPermissions(ps);
- }
-
- public void setPermissions(PermissionCollection permissions) {
- CodeSource cs = new CodeSource(null, (Certificate[]) null);
-
- ProtectionDomain domain = new ProtectionDomain(cs, permissions);
- accessControlContext = new AccessControlContext(new ProtectionDomain[] { domain });
- }
-
- public Object eval(final String script, final ScriptContext context) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(script, context);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(reader, context);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public Object eval(final String script) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(script);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public Object eval(final Reader reader) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(reader);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public Object eval(final String script, final Bindings n) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(script, n);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public Object eval(final Reader reader, final Bindings n) throws ScriptException {
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
- public Object run() throws Exception {
- return engine.eval(reader, n);
- }
- }, accessControlContext);
- } catch (PrivilegedActionException e) {
- throw transfer(e);
- }
- }
-
- public void put(String key, Object value) {
- engine.put(key, value);
- }
-
- public Object get(String key) {
- return engine.get(key);
- }
-
- public Bindings getBindings(int scope) {
- return engine.getBindings(scope);
- }
-
- public void setBindings(Bindings bindings, int scope) {
- engine.setBindings(bindings, scope);
- }
-
- public Bindings createBindings() {
- return engine.createBindings();
- }
-
- public ScriptContext getContext() {
- return engine.getContext();
- }
-
- public void setContext(ScriptContext context) {
- engine.setContext(context);
- }
-
- public ScriptEngineFactory getFactory() {
- return engine.getFactory();
- }
-
- private static ScriptException transfer(PrivilegedActionException e) {
- if (e.getCause() instanceof ScriptException) {
- return (ScriptException) e.getCause();
- } else {
- return new ScriptException(e);
- }
- }
-}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
index 73ffc9d..df78b41 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
@@ -25,15 +25,7 @@ import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
-import java.net.URL;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.CodeSource;
import java.security.PermissionCollection;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.security.ProtectionDomain;
-import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -50,6 +42,7 @@ import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.util.NoTopLevelIndirection;
import org.rhq.bindings.util.PackageFinder;
+import org.rhq.scripting.CodeCompletion;
import org.rhq.scripting.ScriptEngineInitializer;
import org.rhq.scripting.ScriptEngineProvider;
@@ -131,19 +124,8 @@ public class ScriptEngineFactory {
*/
public static ScriptEngine getScriptEngine(String language, PackageFinder packageFinder, StandardBindings bindings)
throws ScriptException, IOException {
- ScriptEngineInitializer initializer = getInitializer(language);
-
- if (initializer == null) {
- return null;
- }
-
- ScriptEngine engine = initializer.instantiate(packageFinder.findPackages("org.rhq.core.domain"));
-
- if (bindings != null) {
- injectStandardBindings(engine, bindings, true);
- }
-
- return engine;
+
+ return getSecuredScriptEngine(language, packageFinder, bindings, null);
}
/**
@@ -155,45 +137,22 @@ public class ScriptEngineFactory {
*/
public static ScriptEngine getSecuredScriptEngine(final String language, final PackageFinder packageFinder,
final StandardBindings bindings, final PermissionCollection permissions) throws ScriptException, IOException {
- CodeSource src = new CodeSource(new URL("http://rhq-project.org/scripting"), (Certificate[]) null);
- ProtectionDomain scriptDomain = new ProtectionDomain(src, permissions);
- AccessControlContext ctx = new AccessControlContext(new ProtectionDomain[] { scriptDomain });
- try {
- return AccessController.doPrivileged(new PrivilegedExceptionAction<ScriptEngine>() {
- @Override
- public ScriptEngine run() throws Exception {
- //This might seem a bit excessive but is necessary due to the
- //change in security handling in the rhino script engine
- //that occured in Java6u27 (due to a CVE desribed here:
- //https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2011-3544)
-
- //In Java 6u26 and earlier, it was enough to wrap a script engine
- //in the sandbox and everything would work.
-
- //Java 6u27 introduced new behavior where the rhino script engine
- //remembers the access control context with which it has been
- //constructed and combines that with the callers protection domain
- //when a script is executed. Because this class has all perms and
- //all the code in RHQ that called ScriptEngine.eval* also
- //had all perms, the scripts would never be sandboxed even if the call
- //was pushed through the SandboxedScriptEngine.
-
- //This means that the below wrapping is necessary for the security
- //to work in java6 pre u27 while the surrounding privileged block
- //is necessary for the security to be applied in java6 u27 and later.
- return new SandboxedScriptEngine(getScriptEngine(language, packageFinder, bindings), permissions);
- }
- }, ctx);
- } catch (PrivilegedActionException e) {
- Throwable cause = e.getCause();
- if (cause instanceof IOException) {
- throw (IOException) cause;
- } else if (cause instanceof ScriptException) {
- throw (ScriptException) cause;
- } else {
- throw new ScriptException(e);
- }
+
+ ScriptEngineInitializer initializer = getInitializer(language);
+
+ if (initializer == null) {
+ return null;
+ }
+
+ //TODO change this so that we support supplying a custom module source provider so that callers
+ //have control over the location of the loadable scripts.
+ ScriptEngine engine = initializer.instantiate(packageFinder.findPackages("org.rhq.core.domain"), null, permissions);
+
+ if (bindings != null) {
+ injectStandardBindings(engine, bindings, true);
}
+
+ return engine;
}
/**
@@ -297,6 +256,12 @@ public class ScriptEngineFactory {
return provider == null ? null : provider.getInitializer();
}
+ public static CodeCompletion getCodeCompletion(String language) {
+ ScriptEngineProvider provider = KNOWN_PROVIDERS.get(language);
+
+ return provider == null ? null : provider.getCodeCompletion();
+ }
+
private static boolean shouldIndirect(Method method) {
return method.getAnnotation(NoTopLevelIndirection.class) == null;
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 308d6c1..5021434 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -26,15 +26,20 @@ import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.ByteArrayInputStream;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
import jline.ArgumentCompletor;
import jline.Completor;
import jline.ConsoleReader;
@@ -42,6 +47,8 @@ import jline.MultiCompletor;
import jline.SimpleCompletor;
import mazz.i18n.Msg;
+import org.rhq.bindings.ScriptEngineFactory;
+import org.rhq.bindings.util.PackageFinder;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.commands.ClientCommand;
import org.rhq.enterprise.client.commands.ScriptCommand;
@@ -81,20 +88,23 @@ public class ClientMain {
private int port = 7080;
private String user;
private String pass;
+ private String language;
private ArrayList<String> notes = new ArrayList<String>();
-
+
// reference to the webservice reference factory
private RemoteClient remoteClient;
// The subject that will be used to carry out all requested actions
private Subject subject;
-
+
private InteractiveJavascriptCompletor serviceCompletor;
private boolean interactiveMode = true;
private Recorder recorder = new NoOpRecorder();
+ private ScriptEngine engine;
+
private class StartupConfiguration {
public boolean askForPassword;
public boolean displayUsage;
@@ -187,8 +197,7 @@ public class ClientMain {
}
private void initServiceCompletor() {
- ScriptCommand sc = (ScriptCommand) commands.get("exec");
- this.serviceCompletor.setContext(sc.getContext());
+ this.serviceCompletor.setContext(getScriptEngine().getContext());
if (remoteClient != null) {
this.serviceCompletor.setServices(remoteClient.getManagers());
@@ -363,7 +372,7 @@ public class ClientMain {
} else {
boolean result = commands.get("exec").execute(this, args);
if (loggedIn()) {
- this.serviceCompletor.setContext(((ScriptCommand) commands.get("exec")).getContext());
+ this.serviceCompletor.setContext(getScriptEngine().getContext());
}
return result;
@@ -460,6 +469,7 @@ public class ClientMain {
new LongOpt("command", LongOpt.REQUIRED_ARGUMENT, null, 'c'),
new LongOpt("file", LongOpt.NO_ARGUMENT, null, 'f'),
new LongOpt("version", LongOpt.NO_ARGUMENT, null, 'v'),
+ new LongOpt("language", LongOpt.REQUIRED_ARGUMENT, null, 'l'),
new LongOpt("args-style", LongOpt.REQUIRED_ARGUMENT, null, -2) };
Getopt getopt = new Getopt("Cli", args, sopts, lopts, false);
@@ -540,6 +550,9 @@ public class ClientMain {
}
break;
}
+ case 'l':
+ this.language = getopt.getOptarg();
+ break;
}
}
@@ -619,12 +632,31 @@ public class ClientMain {
this.outputWriter = writer;
}
+ public String getLanguage() {
+ return this.language == null ? "javascript" : this.language;
+ }
+
public int getConsoleWidth() {
//the console reader might be null when this method is asked for the output
//width in non-interactive mode where we don't attach to stdin.
return this.consoleReader == null ? DEFAULT_CONSOLE_WIDTH : this.consoleReader.getTermwidth();
}
+ public ScriptEngine getScriptEngine() {
+ if (engine == null) {
+ try {
+ engine = ScriptEngineFactory.getScriptEngine(getLanguage(),
+ new PackageFinder(Arrays.asList(getLibDir())), null);
+ } catch (ScriptException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return engine;
+ }
+
public Map<String, ClientCommand> getCommands() {
return commands;
}
@@ -655,4 +687,9 @@ public class ClientMain {
public void setRecorder(Recorder recorder) {
this.recorder = recorder;
}
+
+ private static File getLibDir() {
+ String cwd = System.getProperty("user.dir");
+ return new File(cwd, "lib");
+ }
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
index 4e7e79a..f3084d1 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
@@ -56,7 +56,6 @@ import org.rhq.enterprise.client.script.ScriptCmdLine;
*/
public class ScriptCommand implements ClientCommand {
- private ScriptEngine jsEngine;
private StandardBindings bindings;
private final Log log = LogFactory.getLog(ScriptCommand.class);
@@ -66,21 +65,6 @@ public class ScriptCommand implements ClientCommand {
private boolean isMultilineScript = false;
private boolean inMultilineScript = false;
- public ScriptEngine getScriptEngine() {
- if (jsEngine == null) {
- try {
- jsEngine = ScriptEngineFactory.getScriptEngine("JavaScript",
- new PackageFinder(Arrays.asList(getLibDir())), null);
- } catch (ScriptException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- return jsEngine;
- }
-
public String getPromptCommandString() {
return "exec";
}
@@ -100,8 +84,8 @@ public class ScriptCommand implements ClientCommand {
CmdLineParser cmdLineParser = new CmdLineParser();
ScriptCmdLine scriptCmdLine = cmdLineParser.parse(args);
- bindScriptArgs(scriptCmdLine);
- executeUtilScripts();
+ bindScriptArgs(client, scriptCmdLine);
+ executeUtilScripts(client);
FileReader reader = new FileReader(scriptCmdLine.getScriptFileName());
try {
@@ -153,7 +137,7 @@ public class ScriptCommand implements ClientCommand {
try {
- Object result = getScriptEngine().eval(script.toString());
+ Object result = client.getScriptEngine().eval(script.toString());
inMultilineScript = false;
script = new StringBuilder();
if (result != null) {
@@ -199,7 +183,7 @@ public class ScriptCommand implements ClientCommand {
bindings.put("configurationEditor", new ConfigurationEditor(client));
bindings.put("rhq", new Controller(client));
- ScriptEngine engine = getScriptEngine();
+ ScriptEngine engine = client.getScriptEngine();
ScriptEngineFactory.injectStandardBindings(engine, bindings, false);
@@ -212,7 +196,7 @@ public class ScriptCommand implements ClientCommand {
initBindings(client);
} else {
- ScriptEngine engine = getScriptEngine();
+ ScriptEngine engine = client.getScriptEngine();
// remove any current manager bindings from the engine, they may not be valid for the
// new client. The new standard bindings will include any new managers.
@@ -228,12 +212,12 @@ public class ScriptCommand implements ClientCommand {
return;
}
- private void executeUtilScripts() {
+ private void executeUtilScripts(ClientMain client) {
InputStream stream = getClass().getResourceAsStream("test_utils.js");
InputStreamReader reader = new InputStreamReader(stream);
try {
- getScriptEngine().eval(reader);
+ client.getScriptEngine().eval(reader);
} catch (ScriptException e) {
log.warn("An error occurred while executing test_utils.js", e);
}
@@ -254,17 +238,17 @@ public class ScriptCommand implements ClientCommand {
}
- private void bindScriptArgs(ScriptCmdLine cmdLine) {
- bindArgsArray(cmdLine);
+ private void bindScriptArgs(ClientMain client, ScriptCmdLine cmdLine) {
+ bindArgsArray(client, cmdLine);
if (cmdLine.getArgType() == ScriptCmdLine.ArgType.NAMED) {
- bindNamedArgs(cmdLine);
+ bindNamedArgs(client, cmdLine);
}
- getScriptEngine().put("script", new File(cmdLine.getScriptFileName()).getName());
+ client.getScriptEngine().put("script", new File(cmdLine.getScriptFileName()).getName());
}
- private void bindArgsArray(ScriptCmdLine cmdLine) {
+ private void bindArgsArray(ClientMain client, ScriptCmdLine cmdLine) {
String[] args = new String[cmdLine.getArgs().size()];
int i = 0;
@@ -272,19 +256,19 @@ public class ScriptCommand implements ClientCommand {
args[i++] = arg.getValue();
}
- getScriptEngine().put("args", args);
+ client.getScriptEngine().put("args", args);
}
- private void bindNamedArgs(ScriptCmdLine cmdLine) {
+ private void bindNamedArgs(ClientMain client, ScriptCmdLine cmdLine) {
for (ScriptArg arg : cmdLine.getArgs()) {
NamedScriptArg namedArg = (NamedScriptArg) arg;
- getScriptEngine().put(namedArg.getName(), namedArg.getValue());
+ client.getScriptEngine().put(namedArg.getName(), namedArg.getValue());
}
}
- private boolean executeScriptFile(Reader reader, ClientMain client) {
+ private boolean executeScriptFile( Reader reader, ClientMain client) {
try {
- Object result = getScriptEngine().eval(reader);
+ Object result = client.getScriptEngine().eval(reader);
if (result != null) {
if (client.isInteractiveMode()) {
new TabularWriter(client.getPrintWriter()).print(result);
@@ -313,13 +297,4 @@ public class ScriptCommand implements ClientCommand {
return "Execute a statement or a script. The following service managers are available: "
+ Arrays.toString(RhqManagers.values());
}
-
- public ScriptContext getContext() {
- return getScriptEngine().getContext();
- }
-
- private File getLibDir() {
- String cwd = System.getProperty("user.dir");
- return new File(cwd, "lib");
- }
}
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
index 4120f2d..fe109a8 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
@@ -35,10 +35,11 @@ import javax.script.Bindings;
public class ScriptCommandTest {
ScriptCommand cmd;
+ ClientMain client;
@Test(enabled=false)
public void executeShouldSetDefaultBindings() throws Exception {
- ClientMain client = createClient();
+ client = createClient();
cmd = new ScriptCommand();
String script = "exec var x = 1;";
String[] args = asList(script).toArray(new String[] {});
@@ -61,7 +62,7 @@ public class ScriptCommandTest {
}
void assertSubjectBoundToScript() {
- ScriptEngine scriptEngine = cmd.getScriptEngine();
+ ScriptEngine scriptEngine = client.getScriptEngine();
Object subject = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).get("subject");
assertNotNull(subject, "Expected variable 'subject' to be bound to script in global scope");
@@ -72,7 +73,7 @@ public class ScriptCommandTest {
}
void assertManagersBoundToScript() {
- ScriptEngine scriptEngine = cmd.getScriptEngine();
+ ScriptEngine scriptEngine = client.getScriptEngine();
List<String> mgrsNotBound = new ArrayList<String>();
for (RhqManagers mgr : RhqManagers.values()) {
@@ -88,7 +89,7 @@ public class ScriptCommandTest {
}
void assertPrettyWriterBoundToScript() {
- ScriptEngine scriptEngine = cmd.getScriptEngine();
+ ScriptEngine scriptEngine = client.getScriptEngine();
Object writer = scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE).get("pretty");
assertNotNull(writer, "Expected variable 'pretty' to be bound to script in global scope");
@@ -99,7 +100,7 @@ public class ScriptCommandTest {
}
void assertScriptUtilsBoundToScript() {
- ScriptEngine scriptEngine = cmd.getScriptEngine();
+ ScriptEngine scriptEngine = client.getScriptEngine();
Object scriptUtil = scriptEngine.get("scriptUtil");
assertNotNull(scriptUtil, "Expected variable 'scriptUtil' to be bound to script in engine scope");
@@ -110,7 +111,7 @@ public class ScriptCommandTest {
}
void assertIsDefinedFunctionBoundToScript() {
- ScriptEngine scriptEngine = cmd.getScriptEngine();
+ ScriptEngine scriptEngine = client.getScriptEngine();
Object function = scriptEngine.get("isDefined");
assertNotNull(function, "Expected function 'isDefined' to be bound to script in engine scope");
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
index 1569896..f8beb28 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
@@ -19,11 +19,28 @@
package org.rhq.scripting;
+import java.util.List;
+
/**
- * TODO This will hook into the interactive code completion provided by the CLI.
- *
+ * An interface for performing code completion in a given language.
+ * This inspired by the jline's <code>Completor</code> interface but
+ * is defined separately so that we don't introduce a dependency on
+ * jline where it doesn't make sense.
+ *
* @author Lukas Krejci
*/
public interface CodeCompletion {
+ /**
+ * Generates the completion candidates and fills the supplied list with them.
+ *
+ * @param context the context of the code completion, usually that is the current
+ * statement being edited by the user
+ * @param cursorPosition the position of the cursor inside the context
+ * @param completions this should be a list of strings but is intentionally left raw
+ * so that this interface is compatible with jline's <code>Completor</code>.
+ * @return the character index in the context for which the completion candidates were
+ * actually generated (might be different from the cursorPosition).
+ */
+ int complete(String context, int cursorPosition, @SuppressWarnings("rawtypes") List candidates);
}
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
index 3286f39..9365304 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
@@ -20,6 +20,7 @@
package org.rhq.scripting;
import java.lang.reflect.Method;
+import java.security.PermissionCollection;
import java.util.Set;
import javax.script.ScriptEngine;
@@ -33,7 +34,20 @@ import javax.script.ScriptException;
*/
public interface ScriptEngineInitializer {
- ScriptEngine instantiate(Set<String> packages) throws ScriptException;
+ /**
+ * Instantiates a new script engine, makes the provided java packages "imported" into the context
+ * (i.e. classes from those packages can be instantiated without providing the full class name)
+ * and makes the script engine use the scriptSourceProvider to locate scripts.
+ *
+ * @param packages the list of java packages to be imported in the context
+ * @param scriptSourceProvider the provider of the sources of scripts - can be null to use no
+ * script source provider and depend on the default module loading mechanisms of the script language
+ * @param permissions the security permissions the script engine should execute the script with, can be null
+ * in which case the script engine is unsecured
+ * @return a newly instantiated script engine configured as above
+ * @throws ScriptException
+ */
+ ScriptEngine instantiate(Set<String> packages, ScriptSourceProvider scriptSourceProvider, PermissionCollection permissions) throws ScriptException;
/**
* This function returns a definition string in the script engine's language
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
new file mode 100644
index 0000000..276e385
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
@@ -0,0 +1,35 @@
+/*
+ * 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.scripting;
+
+import java.io.Reader;
+import java.net.URI;
+
+/**
+ * Scripts in RHQ can be stored in various locations or maybe not even in the filesystem.
+ * Implementations of this interface can be used to provide the contents of the scripts
+ * based on URIs.
+ *
+ * @author Lukas Krejci
+ */
+public interface ScriptSourceProvider {
+
+ Reader getScriptSource(URI location);
+}
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/util/SandboxedScriptEngine.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/util/SandboxedScriptEngine.java
new file mode 100644
index 0000000..857c6f1
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/util/SandboxedScriptEngine.java
@@ -0,0 +1,224 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 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.scripting.util;
+
+import java.io.Reader;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
+import java.util.Arrays;
+import java.util.Collection;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+
+/**
+ * This is a decorator class for any other {@link ScriptEngine} implementation
+ * that runs any of the eval methods with the defined set of {@link Permission}s.
+ * <p>
+ * For the permissions to have any effect, a SecurityManager has to be installed
+ * in the current VM.
+ * <p>
+ * This class is provided in hopes that it can help provide security to script engines
+ * that do not directly implement some kind of security measures.
+ *
+ * @author Lukas Krejci
+ */
+public class SandboxedScriptEngine implements ScriptEngine {
+
+ private ScriptEngine engine;
+ private AccessControlContext accessControlContext;
+
+ public SandboxedScriptEngine(ScriptEngine engine) {
+ this.engine = engine;
+ }
+
+ public SandboxedScriptEngine(ScriptEngine engine, PermissionCollection permissions) {
+ this(engine);
+ setPermissions(permissions);
+ }
+
+ public SandboxedScriptEngine(ScriptEngine engine, Collection<? extends Permission> permissions) {
+ this(engine);
+ setPermissions(permissions);
+ }
+
+ public void setPermissions(Permission... permissions) {
+ setPermissions(Arrays.asList(permissions));
+ }
+
+ public void setPermissions(Collection<? extends Permission> permissions) {
+ Permissions ps = new Permissions();
+ for(Permission p : permissions) {
+ ps.add(p);
+ }
+
+ setPermissions(ps);
+ }
+
+ public void setPermissions(PermissionCollection permissions) {
+ CodeSource cs = new CodeSource(null, (Certificate[]) null);
+
+ ProtectionDomain domain = new ProtectionDomain(cs, permissions);
+ accessControlContext = new AccessControlContext(new ProtectionDomain[] { domain });
+ }
+
+ @Override
+ public Object eval(final String script, final ScriptContext context) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(script, context);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(reader, context);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public Object eval(final String script) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(script);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public Object eval(final Reader reader) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(reader);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public Object eval(final String script, final Bindings n) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(script, n);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public Object eval(final Reader reader, final Bindings n) throws ScriptException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ @Override
+ public Object run() throws Exception {
+ return engine.eval(reader, n);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ throw transfer(e);
+ }
+ }
+
+ @Override
+ public void put(String key, Object value) {
+ engine.put(key, value);
+ }
+
+ @Override
+ public Object get(String key) {
+ return engine.get(key);
+ }
+
+ @Override
+ public Bindings getBindings(int scope) {
+ return engine.getBindings(scope);
+ }
+
+ @Override
+ public void setBindings(Bindings bindings, int scope) {
+ engine.setBindings(bindings, scope);
+ }
+
+ @Override
+ public Bindings createBindings() {
+ return engine.createBindings();
+ }
+
+ @Override
+ public ScriptContext getContext() {
+ return engine.getContext();
+ }
+
+ @Override
+ public void setContext(ScriptContext context) {
+ engine.setContext(context);
+ }
+
+ @Override
+ public ScriptEngineFactory getFactory() {
+ return engine.getFactory();
+ }
+
+ private static ScriptException transfer(PrivilegedActionException e) {
+ if (e.getCause() instanceof ScriptException) {
+ return (ScriptException) e.getCause();
+ } else {
+ return new ScriptException(e);
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index 8a5d96a..e813d3b 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -23,7 +23,8 @@
<version>1.7R3</version>
</dependency>
- <!-- JSR 223 support for Rhino -->
+<!-- we no longer depend on Phobos because we now bundle the Phobos script engine with
+ our own modifications.
<dependency>
<groupId>com.sun.phobos</groupId>
<artifactId>phobos-js</artifactId>
@@ -35,7 +36,7 @@
</exclusion>
</exclusions>
</dependency>
-
+ -->
</dependencies>
<build>
@@ -44,6 +45,9 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ <argLine>-Djava.security.manager -Djava.security.policy==${project.build.testOutputDirectory}/allow-all.policy</argLine>
+ <!-- This is important, because some of the tests try to exit the JVM. -->
+ <failIfNoTests>true</failIfNoTests>
</configuration>
</plugin>
</plugins>
diff --git a/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java
deleted file mode 100644
index 5a176fd..0000000
--- a/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 com.sun.phobos.script.javascript;
-
-import javax.script.ScriptContext;
-
-import org.mozilla.javascript.Context;
-import org.mozilla.javascript.Scriptable;
-
-/**
- * We want our script engine to mimic the real JDK one as close as
- * possible.
- *
- * The Phobos impl of the script engine leaves out the predefined
- * print() and println() functions, but we really want them.
- *
- * To add them back we need to override a package-private method
- * in the phobos script engine.
- *
- * @author Lukas Krejci
- */
-public class PrintHavingRhinoScriptEngine extends RhinoScriptEngine {
-
- //copied over from the JDK's impl of RhinoScriptEngine
- private static final String printSource =
- "function print(str, newline) { \n" +
- " if (typeof(str) == 'undefined') { \n" +
- " str = 'undefined'; \n" +
- " } else if (str == null) { \n" +
- " str = 'null'; \n" +
- " } \n" +
- " var out = context.getWriter(); \n" +
- " out.print(String(str)); \n" +
- " if (newline) out.print('\\n'); \n" +
- " out.flush(); \n" +
- "}\n" +
- "function println(str) { \n" +
- " print(str, true); \n" +
- "}";
-
- @Override
- Scriptable getRuntimeScope(ScriptContext ctxt) {
- Scriptable newScope = super.getRuntimeScope(ctxt);
-
- Context cx = enterContext();
- try {
- cx.evaluateString(newScope, printSource, "print", 1, null);
-
- return newScope;
- } finally {
- Context.exit();
- }
- }
-}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
index 803225f..771ec1f 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
@@ -20,6 +20,14 @@
package org.rhq.scripting.javascript;
import java.lang.reflect.Method;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.security.cert.Certificate;
import java.util.Collections;
import java.util.Set;
@@ -28,6 +36,9 @@ import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptSourceProvider;
+import org.rhq.scripting.javascript.engine.RhinoScriptEngine;
+import org.rhq.scripting.javascript.util.ScriptSourceToModuleSourceProviderAdapter;
/**
*
@@ -39,16 +50,34 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
private static final String WRAPPED_EXCEPTION_PREFIX = "Wrapped ";
private ScriptEngineManager engineManager = new ScriptEngineManager();
-
+
@Override
- public ScriptEngine instantiate(Set<String> packages) throws ScriptException {
- ScriptEngine eng = engineManager.getEngineByName("rhino-nonjdk");
+ public ScriptEngine instantiate(final Set<String> packages, final ScriptSourceProvider scriptSourceProvider,
+ PermissionCollection permissions) throws ScriptException {
- for(String pkg : packages) {
- eng.eval("importPackage(" + pkg + ")");
+ if (permissions == null) {
+ return instantiateUnsecured(packages, scriptSourceProvider);
+ } else {
+ try {
+ CodeSource cs = new CodeSource(null, (Certificate[]) null);
+ ProtectionDomain securityDomain = new ProtectionDomain(cs, permissions);
+
+ AccessControlContext acc = new AccessControlContext(new ProtectionDomain[] { securityDomain });
+
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<ScriptEngine>() {
+ @Override
+ public ScriptEngine run() throws Exception {
+ return instantiateUnsecured(packages, scriptSourceProvider);
+ }
+ }, acc);
+ } catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof ScriptException) {
+ throw (ScriptException) e.getCause();
+ } else {
+ throw new ScriptException("Script execution failed.");
+ }
+ }
}
-
- return eng;
}
@Override
@@ -56,50 +85,69 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
if (methods.size() == 0) {
return Collections.emptySet();
}
-
+
String methodName = methods.iterator().next().getName();
-
+
StringBuilder functionBuilder = new StringBuilder("function ");
functionBuilder.append(methodName).append("() { switch(arguments.length) { ");
-
- for(Method method : methods) {
+
+ for (Method method : methods) {
int argCnt = method.getParameterTypes().length;
functionBuilder.append("case ").append(argCnt).append(": ");
functionBuilder.append("return ").append(boundObjectName).append(".").append(methodName).append("(");
- for(int i = 0; i < argCnt; ++i) {
+ for (int i = 0; i < argCnt; ++i) {
if (i > 0) {
functionBuilder.append(", ");
}
-
+
functionBuilder.append("arguments[").append(i).append("]");
}
-
+
functionBuilder.append("); break; ");
}
-
+
functionBuilder.append(" default: throw \"Unsupported number of parameters.\"; } }");
-
+
return Collections.singleton(functionBuilder.toString());
}
-
+
@Override
public String extractUserFriendlyErrorMessage(ScriptException e) {
String errorMessage = e.getMessage();
-
+
int wrappedIdx = errorMessage.lastIndexOf(WRAPPED_EXCEPTION_PREFIX);
-
+
if (wrappedIdx < 0) {
return errorMessage;
}
-
- errorMessage = errorMessage.substring(wrappedIdx + WRAPPED_EXCEPTION_PREFIX.length());
-
+
+ errorMessage = errorMessage.substring(wrappedIdx + WRAPPED_EXCEPTION_PREFIX.length());
+
int sourceInfoStartIdx = errorMessage.indexOf(" (<Unknown source>#");
-
+
if (sourceInfoStartIdx >= 0) {
errorMessage = errorMessage.substring(0, sourceInfoStartIdx);
}
-
+
return errorMessage;
}
+
+ private ScriptEngine instantiateUnsecured(Set<String> packages, ScriptSourceProvider scriptSourceProvider)
+ throws ScriptException {
+ RhinoScriptEngine eng = (RhinoScriptEngine) engineManager.getEngineByName("rhino-nonjdk");
+
+ if (eng == null) {
+ throw new IllegalStateException("Failed to instantiate the 'rhino-nonjdk' script engine. This means that either the required library is missing from the classpath or that there are some security issues preventing it from being instantiated.");
+ }
+
+ if (scriptSourceProvider != null) {
+ eng.setModuleSourceProvider(new ScriptSourceToModuleSourceProviderAdapter(scriptSourceProvider));
+ }
+
+ for (String pkg : packages) {
+ eng.eval("importPackage(" + pkg + ")");
+ }
+
+ return eng;
+ }
}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
index 8d9e4b7..984e5fc 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
@@ -32,7 +32,7 @@ public class JsEngineProvider implements ScriptEngineProvider {
@Override
public String getSupportedLanguage() {
- return "JavaScript";
+ return "javascript";
}
@Override
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java
deleted file mode 100644
index 6bbf542..0000000
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.scripting.javascript;
-
-import java.io.PrintWriter;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class Main {
-
- public static void main(String[] args) throws Exception {
- ScriptEngineManager manager = new ScriptEngineManager();
- ScriptEngine engine = manager.getEngineByName("rhino-nonjdk");
-
- engine.getContext().setWriter(new PrintWriter(System.out, true));
- engine.eval("importPackage(java.lang); println('ahoj')");
- }
-}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java
deleted file mode 100644
index ed8a33f..0000000
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.scripting.javascript;
-
-import com.sun.phobos.script.javascript.PrintHavingRhinoScriptEngine;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class ScriptEngine extends PrintHavingRhinoScriptEngine {
-
- private ScriptEngineFactory factory;
-
- @Override
- public ScriptEngineFactory getFactory() {
- if (factory == null) {
- factory = new ScriptEngineFactory();
- }
-
- return factory;
- }
-
- void setFactory(ScriptEngineFactory factory) {
- this.factory = factory;
- }
-}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java
deleted file mode 100644
index 824f97a..0000000
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.scripting.javascript;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import javax.script.ScriptEngine;
-
-import com.sun.phobos.script.javascript.RhinoScriptEngineFactory;
-
-import org.mozilla.javascript.Context;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class ScriptEngineFactory extends RhinoScriptEngineFactory {
-
- private static final List<String> NAMES;
- private static final String ENGINE_VERSION;
- private static final String LANGUAGE_VERSION;
- static {
- Context.enter();
- ENGINE_VERSION = Context.getCurrentContext().getImplementationVersion();
- int ver = Context.getCurrentContext().getLanguageVersion();
- Context.exit();
-
- String version = null;
- if (ver == 0) {
- version = "1.7";
- } else {
- //the versions are formatted like 170 for 1.7, 180 for 1.8, etc
- int major = ver / 100;
- int minor = (ver - 100) / 10;
- version = major + "." + minor;
- }
-
- LANGUAGE_VERSION = version;
-
- NAMES = Collections.unmodifiableList(Arrays.asList("rhino-nonjdk"));
-
- }
-
- @Override
- public List<String> getNames() {
- return NAMES;
- }
-
- @Override
- public Object getParameter(String key) {
- if (ScriptEngine.ENGINE_VERSION.equals(key)) {
- return ENGINE_VERSION;
- } else if (ScriptEngine.LANGUAGE_VERSION.equals(key)) {
- return LANGUAGE_VERSION;
- } else {
- return super.getParameter(key);
- }
- }
-
- @Override
- public ScriptEngine getScriptEngine() {
- org.rhq.scripting.javascript.ScriptEngine engine = new org.rhq.scripting.javascript.ScriptEngine();
- engine.setFactory(this);
- return engine;
- }
-}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/ExternalScriptable.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/ExternalScriptable.java
new file mode 100644
index 0000000..e14dc4b
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/ExternalScriptable.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.NativeJavaClass;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Wrapper;
+
+/**
+ * ExternalScriptable is an implementation of Scriptable
+ * backed by a JSR 223 Bindings instance.
+ *
+ * @author Mike Grogan
+ * @author A. Sundararajan
+ * @since 1.6
+ */
+final class ExternalScriptable implements Scriptable {
+ /* Underlying ScriptContext that we use to store
+ * named variables of this scope.
+ */
+ private ScriptContext context;
+
+ /* JavaScript allows variables to be named as numbers (indexed
+ * properties). This way arrays, objects (scopes) are treated uniformly.
+ * Note that JSR 223 API supports only String named variables and
+ * so we can't store these in Bindings. Also, JavaScript allows name
+ * of the property name to be even empty String! Again, JSR 223 API
+ * does not support empty name. So, we use the following fallback map
+ * to store such variables of this scope. This map is not exposed to
+ * JSR 223 API. We can just script objects "as is" and need not convert.
+ */
+ private Map indexedProps;
+
+ // my prototype
+ private Scriptable prototype;
+ // my parent scope, if any
+ private Scriptable parent;
+
+ ExternalScriptable(ScriptContext context) {
+ this(context, new HashMap());
+ }
+
+ ExternalScriptable(ScriptContext context, Map indexedProps) {
+ if (context == null) {
+ throw new NullPointerException("context is null");
+ }
+ this.context = context;
+ this.indexedProps = indexedProps;
+ }
+
+ ScriptContext getContext() {
+ return context;
+ }
+
+ private boolean isInIndexedProps(Object key) {
+ return indexedProps != null && indexedProps.containsKey(key);
+ }
+
+ private boolean isEmpty(String name) {
+ return name.equals("");
+ }
+
+ /**
+ * Return the name of the class.
+ */
+ @Override
+ public String getClassName() {
+ return "Global";
+ }
+
+ /**
+ * Returns the value of the named property or NOT_FOUND.
+ *
+ * If the property was created using defineProperty, the
+ * appropriate getter method is called.
+ *
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ */
+ @Override
+ public synchronized Object get(String name, Scriptable start) {
+ if (isEmpty(name)) {
+ if (indexedProps.containsKey(name)) {
+ return indexedProps.get(name);
+ } else {
+ return NOT_FOUND;
+ }
+ } else {
+ synchronized (context) {
+ int scope = context.getAttributesScope(name);
+ if (scope != -1) {
+ Object value = context.getAttribute(name, scope);
+ return Context.javaToJS(value, this);
+ } else {
+ return NOT_FOUND;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the value of the indexed property or NOT_FOUND.
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return the value of the property (may be null), or NOT_FOUND
+ */
+ @Override
+ public synchronized Object get(int index, Scriptable start) {
+ Integer key = new Integer(index);
+ if (indexedProps.containsKey(index)) {
+ return indexedProps.get(key);
+ } else {
+ return NOT_FOUND;
+ }
+ }
+
+ /**
+ * Returns true if the named property is defined.
+ *
+ * @param name the name of the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the property was found in the object
+ */
+ @Override
+ public synchronized boolean has(String name, Scriptable start) {
+ if (isEmpty(name)) {
+ return indexedProps.containsKey(name);
+ } else {
+ synchronized (context) {
+ return context.getAttributesScope(name) != -1;
+ }
+ }
+ }
+
+ /**
+ * Returns true if the property index is defined.
+ *
+ * @param index the numeric index for the property
+ * @param start the object in which the lookup began
+ * @return true if and only if the property was found in the object
+ */
+ @Override
+ public synchronized boolean has(int index, Scriptable start) {
+ Integer key = new Integer(index);
+ return indexedProps.containsKey(key);
+ }
+
+ /**
+ * Sets the value of the named property, creating it if need be.
+ *
+ * @param name the name of the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ */
+ @Override
+ public void put(String name, Scriptable start, Object value) {
+ if (start == this) {
+ synchronized (this) {
+ if (isEmpty(name)) {
+ indexedProps.put(name, value);
+ } else {
+ synchronized (context) {
+ int scope = context.getAttributesScope(name);
+ if (scope == -1) {
+ scope = ScriptContext.ENGINE_SCOPE;
+ }
+ context.setAttribute(name, jsToJava(value), scope);
+ }
+ }
+ }
+ } else {
+ start.put(name, start, value);
+ }
+ }
+
+ /**
+ * Sets the value of the indexed property, creating it if need be.
+ *
+ * @param index the numeric index for the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ */
+ @Override
+ public void put(int index, Scriptable start, Object value) {
+ if (start == this) {
+ synchronized (this) {
+ indexedProps.put(new Integer(index), value);
+ }
+ } else {
+ start.put(index, start, value);
+ }
+ }
+
+ /**
+ * Removes a named property from the object.
+ *
+ * If the property is not found, no action is taken.
+ *
+ * @param name the name of the property
+ */
+ @Override
+ public synchronized void delete(String name) {
+ if (isEmpty(name)) {
+ indexedProps.remove(name);
+ } else {
+ synchronized (context) {
+ int scope = context.getAttributesScope(name);
+ if (scope != -1) {
+ context.removeAttribute(name, scope);
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the indexed property from the object.
+ *
+ * If the property is not found, no action is taken.
+ *
+ * @param index the numeric index for the property
+ */
+ @Override
+ public void delete(int index) {
+ indexedProps.remove(new Integer(index));
+ }
+
+ /**
+ * Get the prototype of the object.
+ * @return the prototype
+ */
+ @Override
+ public Scriptable getPrototype() {
+ return prototype;
+ }
+
+ /**
+ * Set the prototype of the object.
+ * @param prototype the prototype to set
+ */
+ @Override
+ public void setPrototype(Scriptable prototype) {
+ this.prototype = prototype;
+ }
+
+ /**
+ * Get the parent scope of the object.
+ * @return the parent scope
+ */
+ @Override
+ public Scriptable getParentScope() {
+ return parent;
+ }
+
+ /**
+ * Set the parent scope of the object.
+ * @param parent the parent scope to set
+ */
+ @Override
+ public void setParentScope(Scriptable parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Get an array of property ids.
+ *
+ * Not all property ids need be returned. Those properties
+ * whose ids are not returned are considered non-enumerable.
+ *
+ * @return an array of Objects. Each entry in the array is either
+ * a java.lang.String or a java.lang.Number
+ */
+ @Override
+ public synchronized Object[] getIds() {
+ String[] keys = getAllKeys();
+ int size = keys.length + indexedProps.size();
+ Object[] res = new Object[size];
+ System.arraycopy(keys, 0, res, 0, keys.length);
+ int i = keys.length;
+ // now add all indexed properties
+ for (Object index : indexedProps.keySet()) {
+ res[i++] = index;
+ }
+ return res;
+ }
+
+ /**
+ * Get the default value of the object with a given hint.
+ * The hints are String.class for type String, Number.class for type
+ * Number, Scriptable.class for type Object, and Boolean.class for
+ * type Boolean. <p>
+ *
+ * A <code>hint</code> of null means "no hint".
+ *
+ * See ECMA 8.6.2.6.
+ *
+ * @param hint the type hint
+ * @return the default value
+ */
+ @Override
+ public Object getDefaultValue(Class typeHint) {
+ for (int i=0; i < 2; i++) {
+ boolean tryToString;
+ if (typeHint == ScriptRuntime.StringClass) {
+ tryToString = (i == 0);
+ } else {
+ tryToString = (i == 1);
+ }
+
+ String methodName;
+ Object[] args;
+ if (tryToString) {
+ methodName = "toString";
+ args = ScriptRuntime.emptyArgs;
+ } else {
+ methodName = "valueOf";
+ args = new Object[1];
+ String hint;
+ if (typeHint == null) {
+ hint = "undefined";
+ } else if (typeHint == ScriptRuntime.StringClass) {
+ hint = "string";
+ } else if (typeHint == ScriptRuntime.ScriptableClass) {
+ hint = "object";
+ } else if (typeHint == ScriptRuntime.FunctionClass) {
+ hint = "function";
+ } else if (typeHint == ScriptRuntime.BooleanClass
+ || typeHint == Boolean.TYPE)
+ {
+ hint = "boolean";
+ } else if (typeHint == ScriptRuntime.NumberClass ||
+ typeHint == ScriptRuntime.ByteClass ||
+ typeHint == Byte.TYPE ||
+ typeHint == ScriptRuntime.ShortClass ||
+ typeHint == Short.TYPE ||
+ typeHint == ScriptRuntime.IntegerClass ||
+ typeHint == Integer.TYPE ||
+ typeHint == ScriptRuntime.FloatClass ||
+ typeHint == Float.TYPE ||
+ typeHint == ScriptRuntime.DoubleClass ||
+ typeHint == Double.TYPE)
+ {
+ hint = "number";
+ } else {
+ throw Context.reportRuntimeError(
+ "Invalid JavaScript value of type " +
+ typeHint.toString());
+ }
+ args[0] = hint;
+ }
+ Object v = ScriptableObject.getProperty(this, methodName);
+ if (!(v instanceof Function))
+ continue;
+ Function fun = (Function) v;
+ Context cx = RhinoScriptEngine.enterContext();
+ try {
+ v = fun.call(cx, fun.getParentScope(), this, args);
+ } finally {
+ cx.exit();
+ }
+ if (v != null) {
+ if (!(v instanceof Scriptable)) {
+ return v;
+ }
+ if (typeHint == ScriptRuntime.ScriptableClass
+ || typeHint == ScriptRuntime.FunctionClass)
+ {
+ return v;
+ }
+ if (tryToString && v instanceof Wrapper) {
+ // Let a wrapped java.lang.String pass for a primitive
+ // string.
+ Object u = ((Wrapper)v).unwrap();
+ if (u instanceof String)
+ return u;
+ }
+ }
+ }
+ // fall through to error
+ String arg = (typeHint == null) ? "undefined" : typeHint.getName();
+ throw Context.reportRuntimeError(
+ "Cannot find default value for object " + arg);
+ }
+
+ /**
+ * Implements the instanceof operator.
+ *
+ * @param instance The value that appeared on the LHS of the instanceof
+ * operator
+ * @return true if "this" appears in value's prototype chain
+ *
+ */
+ @Override
+ public boolean hasInstance(Scriptable instance) {
+ // Default for JS objects (other than Function) is to do prototype
+ // chasing.
+ Scriptable proto = instance.getPrototype();
+ while (proto != null) {
+ if (proto.equals(this)) return true;
+ proto = proto.getPrototype();
+ }
+ return false;
+ }
+
+ private String[] getAllKeys() {
+ ArrayList<String> list = new ArrayList<String>();
+ synchronized (context) {
+ for (int scope : context.getScopes()) {
+ Bindings bindings = context.getBindings(scope);
+ if (bindings != null) {
+ list.ensureCapacity(bindings.size());
+ for (String key : bindings.keySet()) {
+ list.add(key);
+ }
+ }
+ }
+ }
+ String[] res = new String[list.size()];
+ list.toArray(res);
+ return res;
+ }
+
+ /**
+ * We convert script values to the nearest Java value.
+ * We unwrap wrapped Java objects so that access from
+ * Bindings.get() would return "workable" value for Java.
+ * But, at the same time, we need to make few special cases
+ * and hence the following function is used.
+ */
+ private Object jsToJava(Object jsObj) {
+ if (jsObj instanceof Wrapper) {
+ Wrapper njb = (Wrapper) jsObj;
+ /* importClass feature of ImporterTopLevel puts
+ * NativeJavaClass in global scope. If we unwrap
+ * it, importClass won't work.
+ */
+ if (njb instanceof NativeJavaClass) {
+ return njb;
+ }
+
+ /* script may use Java primitive wrapper type objects
+ * (such as java.lang.Integer, java.lang.Boolean etc)
+ * explicitly. If we unwrap, then these script objects
+ * will become script primitive types. For example,
+ *
+ * var x = new java.lang.Double(3.0); print(typeof x);
+ *
+ * will print 'number'. We don't want that to happen.
+ */
+ Object obj = njb.unwrap();
+ if (obj instanceof Number || obj instanceof String ||
+ obj instanceof Boolean || obj instanceof Character) {
+ // special type wrapped -- we just leave it as is.
+ return njb;
+ } else {
+ // return unwrapped object for any other object.
+ return obj;
+ }
+ } else { // not-a-Java-wrapper
+ return jsObj;
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/JSAdapter.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/JSAdapter.java
new file mode 100644
index 0000000..b56bab7
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/JSAdapter.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.NativeJavaArray;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
+ * calls specially named JavaScript methods on an adaptee object when property
+ * access is attempted on it.
+ *
+ * Example:
+ *
+ * var y = {
+ * __get__ : function (name) { ... }
+ * __has__ : function (name) { ... }
+ * __put__ : function (name, value) {...}
+ * __delete__ : function (name) { ... }
+ * __getIds__ : function () { ... }
+ * };
+ *
+ * var x = new JSAdapter(y);
+ *
+ * x.i; // calls y.__get__
+ * i in x; // calls y.__has__
+ * x.p = 10; // calls y.__put__
+ * delete x.p; // calls y.__delete__
+ * for (i in x) { print(i); } // calls y.__getIds__
+ *
+ * If a special JavaScript method is not found in the adaptee, then JSAdapter
+ * forwards the property access to the adaptee itself.
+ *
+ * JavaScript caller of adapter object is isolated from the fact that
+ * the property access/mutation/deletion are really calls to
+ * JavaScript methods on adaptee. Use cases include 'smart'
+ * properties, property access tracing/debugging, encaptulation with
+ * easy client access - in short JavaScript becomes more "Self" like.
+ *
+ * Note that Rhino already supports special properties like __proto__
+ * (to set, get prototype), __parent__ (to set, get parent scope). We
+ * follow the same double underscore nameing convention here. Similarly
+ * the name JSAdapter is derived from JavaAdapter -- which is a facility
+ * to extend, implement Java classes/interfaces by JavaScript.
+ *
+ * @version 1.0
+ * @author A. Sundararajan
+ * @since 1.6
+ */
+public final class JSAdapter implements Scriptable, Function {
+ private JSAdapter(Scriptable obj) {
+ setAdaptee(obj);
+ }
+
+ // initializer to setup JSAdapter prototype in the given scope
+ public static void init(Context cx, Scriptable scope, boolean sealed)
+ throws RhinoException {
+ JSAdapter obj = new JSAdapter(cx.newObject(scope));
+ obj.setParentScope(scope);
+ obj.setPrototype(getFunctionPrototype(scope));
+ obj.isPrototype = true;
+ ScriptableObject.defineProperty(scope, "JSAdapter", obj,
+ ScriptableObject.DONTENUM);
+ }
+
+ @Override
+ public String getClassName() {
+ return "JSAdapter";
+ }
+
+ @Override
+ public Object get(String name, Scriptable start) {
+ Function func = getAdapteeFunction(GET_PROP);
+ if (func != null) {
+ return call(func, new Object[] { name });
+ } else {
+ start = getAdaptee();
+ return start.get(name, start);
+ }
+ }
+
+ @Override
+ public Object get(int index, Scriptable start) {
+ Function func = getAdapteeFunction(GET_PROP);
+ if (func != null) {
+ return call(func, new Object[] { new Integer(index) });
+ } else {
+ start = getAdaptee();
+ return start.get(index, start);
+ }
+ }
+
+ @Override
+ public boolean has(String name, Scriptable start) {
+ Function func = getAdapteeFunction(HAS_PROP);
+ if (func != null) {
+ Object res = call(func, new Object[] { name });
+ return Context.toBoolean(res);
+ } else {
+ start = getAdaptee();
+ return start.has(name, start);
+ }
+ }
+
+ @Override
+ public boolean has(int index, Scriptable start) {
+ Function func = getAdapteeFunction(HAS_PROP);
+ if (func != null) {
+ Object res = call(func, new Object[] { new Integer(index) });
+ return Context.toBoolean(res);
+ } else {
+ start = getAdaptee();
+ return start.has(index, start);
+ }
+ }
+
+ @Override
+ public void put(String name, Scriptable start, Object value) {
+ if (start == this) {
+ Function func = getAdapteeFunction(PUT_PROP);
+ if (func != null) {
+ call(func, new Object[] { name, value });
+ } else {
+ start = getAdaptee();
+ start.put(name, start, value);
+ }
+ } else {
+ start.put(name, start, value);
+ }
+ }
+
+ @Override
+ public void put(int index, Scriptable start, Object value) {
+ if (start == this) {
+ Function func = getAdapteeFunction(PUT_PROP);
+ if( func != null) {
+ call(func, new Object[] { new Integer(index), value });
+ } else {
+ start = getAdaptee();
+ start.put(index, start, value);
+ }
+ } else {
+ start.put(index, start, value);
+ }
+ }
+
+ @Override
+ public void delete(String name) {
+ Function func = getAdapteeFunction(DEL_PROP);
+ if (func != null) {
+ call(func, new Object[] { name });
+ } else {
+ getAdaptee().delete(name);
+ }
+ }
+
+ @Override
+ public void delete(int index) {
+ Function func = getAdapteeFunction(DEL_PROP);
+ if (func != null) {
+ call(func, new Object[] { new Integer(index) });
+ } else {
+ getAdaptee().delete(index);
+ }
+ }
+
+ @Override
+ public Scriptable getPrototype() {
+ return prototype;
+ }
+
+ @Override
+ public void setPrototype(Scriptable prototype) {
+ this.prototype = prototype;
+ }
+
+ @Override
+ public Scriptable getParentScope() {
+ return parent;
+ }
+
+ @Override
+ public void setParentScope(Scriptable parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public Object[] getIds() {
+ Function func = getAdapteeFunction(GET_PROPIDS);
+ if (func != null) {
+ Object val = call(func, new Object[0]);
+ // in most cases, adaptee would return native JS array
+ if (val instanceof NativeArray) {
+ NativeArray array = (NativeArray) val;
+ Object[] res = new Object[(int)array.getLength()];
+ for (int index = 0; index < res.length; index++) {
+ res[index] = mapToId(array.get(index, array));
+ }
+ return res;
+ } else if (val instanceof NativeJavaArray) {
+ // may be attempt wrapped Java array
+ Object tmp = ((NativeJavaArray)val).unwrap();
+ Object[] res;
+ if (tmp.getClass() == Object[].class) {
+ Object[] array = (Object[]) tmp;
+ res = new Object[array.length];
+ for (int index = 0; index < array.length; index++) {
+ res[index] = mapToId(array[index]);
+ }
+ } else {
+ // just return an empty array
+ res = Context.emptyArgs;
+ }
+ return res;
+ } else {
+ // some other return type, just return empty array
+ return Context.emptyArgs;
+ }
+ } else {
+ return getAdaptee().getIds();
+ }
+ }
+
+ @Override
+ public boolean hasInstance(Scriptable scriptable) {
+ if (scriptable instanceof JSAdapter) {
+ return true;
+ } else {
+ Scriptable proto = scriptable.getPrototype();
+ while (proto != null) {
+ if (proto.equals(this)) return true;
+ proto = proto.getPrototype();
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public Object getDefaultValue(Class hint) {
+ return getAdaptee().getDefaultValue(hint);
+ }
+
+ @Override
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ throws RhinoException {
+ if (isPrototype) {
+ return construct(cx, scope, args);
+ } else {
+ Scriptable tmp = getAdaptee();
+ if (tmp instanceof Function) {
+ return ((Function)tmp).call(cx, scope, tmp, args);
+ } else {
+ throw Context.reportRuntimeError("TypeError: not a function");
+ }
+ }
+ }
+
+ @Override
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ throws RhinoException {
+ if (isPrototype) {
+ Scriptable topLevel = ScriptableObject.getTopLevelScope(scope);
+ JSAdapter newObj;
+ if (args.length > 0) {
+ newObj = new JSAdapter(Context.toObject(args[0], topLevel));
+ } else {
+ throw Context.reportRuntimeError("JSAdapter requires adaptee");
+ }
+ return newObj;
+ } else {
+ Scriptable tmp = getAdaptee();
+ if (tmp instanceof Function) {
+ return ((Function)tmp).construct(cx, scope, args);
+ } else {
+ throw Context.reportRuntimeError("TypeError: not a constructor");
+ }
+ }
+ }
+
+ public Scriptable getAdaptee() {
+ return adaptee;
+ }
+
+ public void setAdaptee(Scriptable adaptee) {
+ if (adaptee == null) {
+ throw new NullPointerException("adaptee can not be null");
+ }
+ this.adaptee = adaptee;
+ }
+
+ //-- internals only below this point
+
+ // map a property id. Property id can only be an Integer or String
+ private Object mapToId(Object tmp) {
+ if (tmp instanceof Double) {
+ return new Integer(((Double)tmp).intValue());
+ } else {
+ return Context.toString(tmp);
+ }
+ }
+
+ private static Scriptable getFunctionPrototype(Scriptable scope) {
+ return ScriptableObject.getFunctionPrototype(scope);
+ }
+
+ private Function getAdapteeFunction(String name) {
+ Object o = ScriptableObject.getProperty(getAdaptee(), name);
+ return (o instanceof Function)? (Function)o : null;
+ }
+
+ private Object call(Function func, Object[] args) {
+ Context cx = Context.getCurrentContext();
+ Scriptable thisObj = getAdaptee();
+ Scriptable scope = func.getParentScope();
+ try {
+ return func.call(cx, scope, thisObj, args);
+ } catch (RhinoException re) {
+ throw Context.reportRuntimeError(re.getMessage());
+ }
+ }
+
+ private Scriptable prototype;
+ private Scriptable parent;
+ private Scriptable adaptee;
+ private boolean isPrototype;
+
+ // names of adaptee JavaScript functions
+ private static final String GET_PROP = "__get__";
+ private static final String HAS_PROP = "__has__";
+ private static final String PUT_PROP = "__put__";
+ private static final String DEL_PROP = "__delete__";
+ private static final String GET_PROPIDS = "__getIds__";
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoCompiledScript.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoCompiledScript.java
new file mode 100644
index 0000000..23d6b3c
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoCompiledScript.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine;
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+
+import org.rhq.scripting.javascript.engine.util.ExtendedScriptException;
+
+/**
+ * Represents compiled JavaScript code.
+ *
+ * @author Mike Grogan
+ * @version 1.0
+ * @since 1.6
+ */
+final class RhinoCompiledScript extends CompiledScript {
+
+ private RhinoScriptEngine engine;
+ private Script script;
+ private final static boolean DEBUG = RhinoScriptEngine.DEBUG;
+
+ RhinoCompiledScript(RhinoScriptEngine engine, Script script) {
+ this.engine = engine;
+ this.script = script;
+ }
+
+ @Override
+ public Object eval(ScriptContext context) throws ScriptException {
+
+ Object result = null;
+ Context cx = RhinoScriptEngine.enterContext();
+ try {
+
+ Scriptable scope = engine.getRuntimeScope(context);
+ Object ret = script.exec(cx, scope);
+ result = engine.unwrapReturnValue(ret);
+ } catch (JavaScriptException jse) {
+ if (DEBUG) jse.printStackTrace();
+ int line = (line = jse.lineNumber()) == 0 ? -1 : line;
+ Object value = jse.getValue();
+ String str = (value != null && value.getClass().getName().equals("org.mozilla.javascript.NativeError") ?
+ value.toString() :
+ jse.toString());
+ throw new ExtendedScriptException(jse, str, jse.sourceName(), line);
+ } catch (RhinoException re) {
+ if (DEBUG) re.printStackTrace();
+ int line = (line = re.lineNumber()) == 0 ? -1 : line;
+ throw new ExtendedScriptException(re, re.toString(), re.sourceName(), line);
+ } finally {
+ Context.exit();
+ }
+
+ return result;
+ }
+
+ @Override
+ public ScriptEngine getEngine() {
+ return engine;
+ }
+
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
new file mode 100644
index 0000000..a2dca22
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
@@ -0,0 +1,687 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 2006-2011 Oracle and/or its affiliates. All rights reserved.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common Development
+ * and Distribution License("CDDL") (collectively, the "License"). You
+ * may not use this file except in compliance with the License. You can
+ * obtain a copy of the License at
+ * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
+ * or packager/legal/LICENSE.txt. See the License for the specific
+ * language governing permissions and limitations under the License.
+ *
+ * When distributing the software, include this License Header Notice in each
+ * file and include the License file at packager/legal/LICENSE.txt.
+ *
+ * GPL Classpath Exception:
+ * Oracle designates this particular file as subject to the "Classpath"
+ * exception as provided by Oracle in the GPL Version 2 section of the License
+ * file that accompanied this code.
+ *
+ * Modifications:
+ * If applicable, add the following below the License Header, with the fields
+ * enclosed by brackets [] replaced by your own identifying information:
+ * "Portions Copyright [year] [name of copyright owner]"
+ *
+ * Contributor(s):
+ * If you wish your version of this file to be governed by only the CDDL or
+ * only the GPL Version 2, indicate your decision by adding "[Contributor]
+ * elects to include this software in this distribution under the [CDDL or GPL
+ * Version 2] license." If you don't indicate a single choice of license, a
+ * recipient has the option to distribute your version of this file under
+ * either the CDDL, the GPL Version 2 or to extend the choice of license to
+ * its licensees as provided above. However, if you add GPL Version 2 code
+ * and therefore, elected the GPL Version 2 license, then the option applies
+ * only if the new code is made subject to such option by the copyright
+ * holder.
+ */
+
+/*
+ * Portions Copyright 2012 RHQ Management Platform
+ */
+
+/*
+ * RHQ Management Platform elects to include this software in this distribution
+ * under the GPL Version 2 license.
+ */
+
+
+
+package org.rhq.scripting.javascript.engine;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+import javax.script.SimpleScriptContext;
+
+import org.mozilla.javascript.Callable;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.Function;
+import org.mozilla.javascript.ImporterTopLevel;
+import org.mozilla.javascript.JavaScriptException;
+import org.mozilla.javascript.LazilyLoadedCtor;
+import org.mozilla.javascript.RhinoException;
+import org.mozilla.javascript.Script;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Synchronizer;
+import org.mozilla.javascript.Undefined;
+import org.mozilla.javascript.Wrapper;
+import org.mozilla.javascript.commonjs.module.RequireBuilder;
+import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProvider;
+import org.mozilla.javascript.commonjs.module.provider.SoftCachingModuleScriptProvider;
+import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
+
+import org.rhq.scripting.javascript.engine.util.ExtendedScriptException;
+import org.rhq.scripting.javascript.engine.util.InterfaceImplementor;
+
+
+/**
+ * Implementation of <code>ScriptEngine</code> using the Mozilla Rhino
+ * interpreter.
+ *
+ * @author Mike Grogan
+ * @author A. Sundararajan
+ * @version 1.0
+ * @since 1.6
+ *
+ * Modified for phobos to remove some of the restrictions.
+ * Modified to allow subclassing and preprocessing of script source code.
+ * Modified to avoid using the RhinoTopLevel class, since that introduces
+ * a circularity that prevents objects from being garbage collected.
+ *
+ * @author Roberto Chinnici
+ *
+ * Modified so that the top level scope is an ImportTopLevel instance so
+ * that importClass and importPackage functions are available.
+ * Modified so that the "print" and "println" functions work the same as with
+ * the stock javascript script engine provided by the JVM.
+ * Modified to include the "require()" function by default.
+ * Modified to tighten the security of the script execution by running it in an
+ * AccessControlContext active at the time of the script engine creation.
+ *
+ * @author Lukas Krejci
+ */
+public class RhinoScriptEngine extends AbstractScriptEngine
+ implements Invocable, Compilable {
+
+ public static final boolean DEBUG = false;
+ private static final String TOPLEVEL_SCRIPT_NAME = "META-INF/toplevel.js";
+
+ private static class TopLevelScope extends ImporterTopLevel {
+
+ private static final long serialVersionUID = 1L;
+
+ private AccessControlContext acc;
+
+ public TopLevelScope(AccessControlContext acc, Context cx, boolean sealed) {
+ super(cx, sealed);
+ this.acc = acc;
+ }
+
+ public AccessControlContext getAccessControlContext() {
+ return acc;
+ }
+ }
+
+ /* Scope where standard JavaScript objects and our
+ * extensions to it are stored. Note that these are not
+ * user defined engine level global variables. These are
+ * variables have to be there on all compliant ECMAScript
+ * scopes. We put these standard objects in this top level.
+ */
+ private TopLevelScope topLevel;
+
+ /* map used to store indexed properties in engine scope
+ * refer to comment on 'indexedProps' in ExternalScriptable.java.
+ */
+ private Map<?, ?> indexedProps;
+
+ private ScriptEngineFactory factory;
+ private InterfaceImplementor implementor;
+
+ //LK - added support for CommonJS modules
+ private RequireBuilder requireBuilder;
+
+ //LK - make all the scripts run in an access control context
+ static {
+ ContextFactory.initGlobal(new ContextFactory() {
+ @Override
+ protected Object doTopCall(final Callable callable,
+ final Context cx, final Scriptable scope,
+ final Scriptable thisObj, final Object[] args) {
+ AccessControlContext accCtxt = null;
+ Scriptable global = ScriptableObject.getTopLevelScope(scope);
+ Scriptable globalProto = global.getPrototype();
+ if (globalProto instanceof TopLevelScope) {
+ accCtxt = ((TopLevelScope)globalProto).getAccessControlContext();
+ }
+
+ if (accCtxt != null) {
+ return AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ @Override
+ public Object run() {
+ return superDoTopCall(callable, cx, scope, thisObj, args);
+ }
+ }, accCtxt);
+ } else {
+ return superDoTopCall(callable, cx, scope, thisObj, args);
+ }
+ }
+
+ private Object superDoTopCall(final Callable callable,
+ final Context cx, final Scriptable scope,
+ final Scriptable thisObj, final Object[] args) {
+
+ return super.doTopCall(callable, cx, scope, thisObj, args);
+ }
+ });
+ }
+
+ /*
+ // in Phobos we want to support all javascript features
+ static {
+ ContextFactory.initGlobal(new ContextFactory() {
+ protected Context makeContext() {
+ Context cx = super.makeContext();
+ cx.setClassShutter(RhinoClassShutter.getInstance());
+ cx.setWrapFactory(RhinoWrapFactory.getInstance());
+ return cx;
+ }
+
+ public boolean hasFeature(Context cx, int feature) {
+ // we do not support E4X (ECMAScript for XML)!
+ if (feature == Context.FEATURE_E4X) {
+ return false;
+ } else {
+ return super.hasFeature(cx, feature);
+ }
+ }
+ });
+ }
+
+ static {
+ if (USE_INTERPRETER) {
+ ContextFactory.initGlobal(new ContextFactory() {
+ protected Context makeContext() {
+ Context cx = super.makeContext();
+ cx.setOptimizationLevel(-1);
+ return cx;
+ }
+ });
+ }
+ }
+ */
+
+ public RhinoScriptEngine() {
+ this(new UrlModuleSourceProvider(null, Arrays.asList(new File("./").toURI())));
+ }
+
+ /**
+ * Creates a new instance of RhinoScriptEngine with given moduleSourceProvider as the "locator"
+ * for the CommonJS modules.
+ *
+ * @param moduleSourceProvider the implementation able to locate sources of modules for CommonJS.
+ */
+ public RhinoScriptEngine(ModuleSourceProvider moduleSourceProvider) {
+
+ Context cx = enterContext();
+
+ try {
+ /*
+ * RRC - modified this code to register JSAdapter and some functions
+ * directly, without using a separate RhinoTopLevel class
+ */
+ /*
+ * LK - made the topLevel at the the ImporterTopLevel so that
+ * the circular reference to this script engine is avoided but
+ * all the functions (importClass, importPackage) are available.
+ * Also provide the security features similar to the bundled script engine.
+ */
+ topLevel = new TopLevelScope(AccessController.getContext(), cx, System.getSecurityManager() != null);
+
+ requireBuilder = new RequireBuilder();
+ setModuleSourceProvider(moduleSourceProvider);
+
+ new LazilyLoadedCtor(topLevel, "JSAdapter",
+ "org.rhq.scripting.javascript.engine.JSAdapter",
+ false);
+ // add top level functions
+ String names[] = { "bindings", "scope", "sync" };
+ topLevel.defineFunctionProperties(names, RhinoScriptEngine.class, ScriptableObject.DONTENUM);
+
+ processAllTopLevelScripts(cx);
+ } finally {
+ Context.exit();
+ }
+
+ indexedProps = new HashMap<Object, Object>();
+
+ //construct object used to implement getInterface
+ implementor = new InterfaceImplementor(this) {
+ @Override
+ protected Object convertResult(Method method, Object res)
+ throws ScriptException {
+ Class<?> desiredType = method.getReturnType();
+ if (desiredType == Void.TYPE) {
+ return null;
+ } else {
+ return Context.jsToJava(res, desiredType);
+ }
+ }
+ };
+ }
+
+ public void setModuleSourceProvider(ModuleSourceProvider provider) {
+ requireBuilder.setModuleScriptProvider(new SoftCachingModuleScriptProvider(provider));
+ }
+
+ @Override
+ public Object eval(Reader reader, ScriptContext ctxt)
+ throws ScriptException {
+ Object ret;
+
+ Context cx = enterContext();
+ try {
+ Scriptable scope = getRuntimeScope(ctxt);
+ scope.put("context", scope, ctxt);
+
+ // NOTE (RRC) - why does it look straight into the engine instead of asking
+ // the given ScriptContext object?
+ // Modified to use the context
+ // String filename = (String) get(ScriptEngine.FILENAME);
+ String filename = null;
+ if (ctxt != null && ctxt.getBindings(ScriptContext.ENGINE_SCOPE) != null) {
+ filename = (String) ctxt.getBindings(ScriptContext.ENGINE_SCOPE).get(RhinoScriptEngine.FILENAME);
+ }
+ if (filename == null) {
+ filename = (String) get(RhinoScriptEngine.FILENAME);
+ }
+
+ filename = filename == null ? "<Unknown source>" : filename;
+ ret = cx.evaluateReader(scope, preProcessScriptSource(reader), filename , 1, null);
+ } catch (JavaScriptException jse) {
+ if (DEBUG) jse.printStackTrace();
+ int line = (line = jse.lineNumber()) == 0 ? -1 : line;
+ Object value = jse.getValue();
+ String str = (value != null && value.getClass().getName().equals("org.mozilla.javascript.NativeError") ?
+ value.toString() :
+ jse.toString());
+ throw new ExtendedScriptException(jse, str, jse.sourceName(), line);
+ } catch (RhinoException re) {
+ if (DEBUG) re.printStackTrace();
+ int line = (line = re.lineNumber()) == 0 ? -1 : line;
+ throw new ExtendedScriptException(re, re.toString(), re.sourceName(), line);
+ } catch (IOException ee) {
+ throw new ScriptException(ee);
+ } finally {
+ Context.exit();
+ }
+
+ return unwrapReturnValue(ret);
+ }
+
+ @Override
+ public Object eval(String script, ScriptContext ctxt) throws ScriptException {
+ if (script == null) {
+ throw new NullPointerException("null script");
+ }
+ return eval(new StringReader(script), ctxt);
+ }
+
+ @Override
+ public ScriptEngineFactory getFactory() {
+ if (factory != null) {
+ return factory;
+ } else {
+ return new RhinoScriptEngineFactory();
+ }
+ }
+
+ @Override
+ public Bindings createBindings() {
+ return new SimpleBindings();
+ }
+
+ //Invocable methods
+ @Override
+ public Object invokeFunction(String name, Object... args)
+ throws ScriptException, NoSuchMethodException {
+ return invokeMethod(null, name, args);
+ }
+
+ @Override
+ public Object invokeMethod(Object thiz, String name, Object... args)
+ throws ScriptException, NoSuchMethodException {
+
+ Context cx = enterContext();
+ try {
+ if (name == null) {
+ throw new NullPointerException("method name is null");
+ }
+
+ if (thiz != null && !(thiz instanceof Scriptable)) {
+ thiz = Context.toObject(thiz, topLevel);
+ }
+
+ Scriptable engineScope = getRuntimeScope(context);
+ Scriptable localScope = (thiz != null)? (Scriptable) thiz :
+ engineScope;
+ Object obj = ScriptableObject.getProperty(localScope, name);
+ if (! (obj instanceof Function)) {
+ throw new NoSuchMethodException("no such method: " + name);
+ }
+
+ Function func = (Function) obj;
+ Scriptable scope = func.getParentScope();
+ if (scope == null) {
+ scope = engineScope;
+ }
+ Object result = func.call(cx, scope, localScope,
+ wrapArguments(args));
+ return unwrapReturnValue(result);
+ } catch (JavaScriptException jse) {
+ if (DEBUG) jse.printStackTrace();
+ int line = (line = jse.lineNumber()) == 0 ? -1 : line;
+ Object value = jse.getValue();
+ String str = (value != null && value.getClass().getName().equals("org.mozilla.javascript.NativeError") ?
+ value.toString() :
+ jse.toString());
+ throw new ExtendedScriptException(jse, str, jse.sourceName(), line);
+ } catch (RhinoException re) {
+ if (DEBUG) re.printStackTrace();
+ int line = (line = re.lineNumber()) == 0 ? -1 : line;
+ throw new ExtendedScriptException(re, re.toString(), re.sourceName(), line);
+ } finally {
+ Context.exit();
+ }
+ }
+
+ @Override
+ public <T> T getInterface(Class<T> clasz) {
+ try {
+ return implementor.getInterface(null, clasz);
+ } catch (ScriptException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public <T> T getInterface(Object thiz, Class<T> clasz) {
+ if (thiz == null) {
+ throw new IllegalArgumentException("script object can not be null");
+ }
+
+ try {
+ return implementor.getInterface(thiz, clasz);
+ } catch (ScriptException e) {
+ return null;
+ }
+ }
+
+ // RRC - not used
+ // LK - make it used again and modified to conform to the JVM version.
+ private static final String printSource =
+ "function print(str, newline) { \n" +
+ " if (typeof(str) == 'undefined') { \n" +
+ " str = 'undefined'; \n" +
+ " } else if (str == null) { \n" +
+ " str = 'null'; \n" +
+ " } \n" +
+ " var out = context.getWriter(); \n" +
+ " out.print(String(str)); \n" +
+ " if (newline) out.print('\\n'); \n" +
+ " out.flush(); \n" +
+ "}\n" +
+ "function println(str) { \n" +
+ " print(str, true); \n" +
+ "}";
+
+ Scriptable getRuntimeScope(ScriptContext ctxt) {
+ if (ctxt == null) {
+ throw new NullPointerException("null script context");
+ }
+
+ // we create a scope for the given ScriptContext
+ Scriptable newScope = new ExternalScriptable(ctxt, indexedProps);
+
+ // Set the prototype of newScope to be 'topLevel' so that
+ // JavaScript standard objects are visible from the scope.
+ newScope.setPrototype(topLevel);
+
+ // define "context" variable in the new scope
+ newScope.put("context", newScope, ctxt);
+
+
+ // RRC - save some time and don't define print
+ // LK - these functions are assumed by a lot of code so let's
+ // make them available
+ // define "print" function in the new scope
+ Context cx = enterContext();
+ try {
+ cx.evaluateString(newScope, printSource, "print", 1, null);
+ requireBuilder.createRequire(cx, topLevel).install(newScope);
+ } finally {
+ Context.exit();
+ }
+
+ return newScope;
+ }
+
+
+ //Compilable methods
+ @Override
+ public CompiledScript compile(String script) throws ScriptException {
+ return compile(new StringReader(script));
+ }
+
+ @Override
+ public CompiledScript compile(java.io.Reader script) throws ScriptException {
+ CompiledScript ret = null;
+ Context cx = enterContext();
+
+ try {
+ String filename = (String) get(RhinoScriptEngine.FILENAME);
+ if (filename == null) {
+ filename = "<Unknown Source>";
+ }
+
+ Script scr = cx.compileReader(preProcessScriptSource(script), filename, 1, null);
+ ret = new RhinoCompiledScript(this, scr);
+ } catch (Exception e) {
+ if (DEBUG) e.printStackTrace();
+ throw new ScriptException(e);
+ } finally {
+ Context.exit();
+ }
+ return ret;
+ }
+
+
+ //package-private helpers
+
+ static Context enterContext() {
+ // call this always so that initializer of this class runs
+ // and initializes custom wrap factory and class shutter.
+ return Context.enter();
+ }
+
+ void setEngineFactory(ScriptEngineFactory fac) {
+ factory = fac;
+ }
+
+ Object[] wrapArguments(Object[] args) {
+ if (args == null) {
+ return Context.emptyArgs;
+ }
+ Object[] res = new Object[args.length];
+ for (int i = 0; i < res.length; i++) {
+ res[i] = Context.javaToJS(args[i], topLevel);
+ }
+ return res;
+ }
+
+ Object unwrapReturnValue(Object result) {
+ if (result instanceof Wrapper) {
+ result = ( (Wrapper) result).unwrap();
+ }
+
+ return result instanceof Undefined ? null : result;
+ }
+
+ protected Reader preProcessScriptSource(Reader reader) throws ScriptException {
+ return reader;
+ }
+
+ protected void processAllTopLevelScripts(Context cx) {
+ processTopLevelScript(TOPLEVEL_SCRIPT_NAME, cx);
+ }
+
+ protected void processTopLevelScript(String scriptName, Context cx) {
+ InputStream toplevelScript = this.getClass().getClassLoader().getResourceAsStream(scriptName);
+ if (toplevelScript != null) {
+ Reader reader = new InputStreamReader(toplevelScript);
+ try {
+ cx.evaluateReader(topLevel, reader, scriptName, 1, null);
+ }
+ catch (Exception e) {
+ if (DEBUG) e.printStackTrace();
+ }
+ finally {
+ try {
+ toplevelScript.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ /**
+ * The bindings function takes a JavaScript scope object
+ * of type ExternalScriptable and returns the underlying Bindings
+ * instance.
+ *
+ * var page = scope(pageBindings);
+ * with (page) {
+ * // code that uses page scope
+ * }
+ * var b = bindings(page);
+ * // operate on bindings here.
+ */
+ public static Object bindings(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj) {
+ if (args.length == 1) {
+ Object arg = args[0];
+ if (arg instanceof Wrapper) {
+ arg = ((Wrapper)arg).unwrap();
+ }
+ if (arg instanceof ExternalScriptable) {
+ ScriptContext ctx = ((ExternalScriptable)arg).getContext();
+ Bindings bind = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
+ return Context.javaToJS(bind,
+ ScriptableObject.getTopLevelScope(thisObj));
+ }
+ }
+ return Context.getUndefinedValue();
+ }
+
+ /**
+ * The scope function creates a new JavaScript scope object
+ * with given Bindings object as backing store. This can be used
+ * to create a script scope based on arbitrary Bindings instance.
+ * For example, in webapp scenario, a 'page' level Bindings instance
+ * may be wrapped as a scope and code can be run in JavaScripe 'with'
+ * statement:
+ *
+ * var page = scope(pageBindings);
+ * with (page) {
+ * // code that uses page scope
+ * }
+ */
+ public static Object scope(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj) {
+ if (args.length == 1) {
+ Object arg = args[0];
+ if (arg instanceof Wrapper) {
+ arg = ((Wrapper)arg).unwrap();
+ }
+ if (arg instanceof Bindings) {
+ ScriptContext ctx = new SimpleScriptContext();
+ ctx.setBindings((Bindings)arg, ScriptContext.ENGINE_SCOPE);
+ Scriptable res = new ExternalScriptable(ctx);
+ res.setPrototype(ScriptableObject.getObjectPrototype(thisObj));
+ res.setParentScope(ScriptableObject.getTopLevelScope(thisObj));
+ return res;
+ }
+ }
+ return Context.getUndefinedValue();
+ }
+
+ /**
+ * The sync function creates a synchronized function (in the sense
+ * of a Java synchronized method) from an existing function. The
+ * new function synchronizes on the <code>this</code> object of
+ * its invocation.
+ * js> var o = { f : sync(function(x) {
+ * print("entry");
+ * Packages.java.lang.Thread.sleep(x*1000);
+ * print("exit");
+ * })};
+ * js> thread(function() {o.f(5);});
+ * entry
+ * js> thread(function() {o.f(5);});
+ * js>
+ * exit
+ * entry
+ * exit
+ */
+ public static Object sync(Context cx, Scriptable thisObj, Object[] args,
+ Function funObj) {
+ if (args.length == 1 && args[0] instanceof Function) {
+ return new Synchronizer((Function)args[0]);
+ } else {
+ throw Context.reportRuntimeError("wrong argument(s) for sync");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 0) {
+ System.out.println("No file specified");
+ return;
+ }
+
+ InputStreamReader r = new InputStreamReader(new FileInputStream(args[0]));
+ RhinoScriptEngine engine = new RhinoScriptEngine();
+
+ SimpleScriptContext context = new SimpleScriptContext();
+ engine.put(RhinoScriptEngine.FILENAME, args[0]);
+ engine.eval(r, context);
+ // added this statement to save some typing to most script authors
+ context.getWriter().flush();
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngineFactory.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngineFactory.java
new file mode 100644
index 0000000..2db613b
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngineFactory.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+
+import javax.script.ScriptEngine;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+
+import org.rhq.scripting.javascript.engine.util.ScriptEngineFactoryBase;
+
+/**
+ * Factory to create RhinoScriptEngine
+ *
+ * @version 1.0
+ * @author Mike Grogan
+ * @since 1.6
+ */
+public class RhinoScriptEngineFactory extends ScriptEngineFactoryBase {
+
+ public static final String USE_INTERPRETER_SYSTEM_PROPERTY = "org.rhq.scrpipting.javascript.useInterpreter";
+
+ private Properties properties;
+ private boolean initialized;
+ private ContextFactory.Listener listener;
+
+ public RhinoScriptEngineFactory() {
+ }
+
+ public RhinoScriptEngineFactory(ContextFactory.Listener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public List<String> getExtensions() {
+ return extensions;
+ }
+
+ @Override
+ public List<String> getMimeTypes() {
+ return mimeTypes;
+ }
+
+ @Override
+ public List<String> getNames() {
+ return names;
+ }
+
+ @Override
+ public Object getParameter(String key) {
+ if (key.equals(ScriptEngine.NAME)) {
+ return "javascript";
+ } else if (key.equals(ScriptEngine.ENGINE)) {
+ return "Mozilla Rhino";
+ } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
+ return "1.6R7";
+ } else if (key.equals(ScriptEngine.LANGUAGE)) {
+ return "ECMAScript";
+ } else if (key.equals(ScriptEngine.LANGUAGE_VERSION)) {
+ return "1.6";
+ } else if (key.equals("THREADING")) {
+ return "MULTITHREADED";
+ } else {
+ throw new IllegalArgumentException("Invalid key");
+ }
+ }
+
+ @Override
+ public ScriptEngine getScriptEngine() {
+ RhinoScriptEngine ret = new RhinoScriptEngine();
+ ret.setEngineFactory(this);
+ return ret;
+ }
+
+ public void initialize() {
+ if (!initialized) {
+ if ("true".equals(getProperty(USE_INTERPRETER_SYSTEM_PROPERTY))) {
+ if (!ContextFactory.hasExplicitGlobal()) {
+ ContextFactory.initGlobal(new ContextFactory() {
+ @Override
+ protected Context makeContext() {
+ Context cx = super.makeContext();
+ cx.setOptimizationLevel(-1);
+ return cx;
+ }
+ });
+ }
+ }
+ if (listener != null) {
+ ContextFactory.getGlobal().addListener(listener);
+ }
+ initialized = true;
+ }
+ }
+
+ public void destroy() {
+ if (initialized) {
+ if (listener != null) {
+ ContextFactory.getGlobal().removeListener(listener);
+ }
+ initialized = false;
+ }
+ }
+
+ public void setProperties(Properties properties) {
+ this.properties = properties;
+ }
+
+ private String getProperty(String key) {
+ String value = null;
+ if (properties != null) {
+ value = properties.getProperty(key);
+ }
+ if (value == null) {
+ value = System.getProperty(key);
+ }
+ return value;
+ }
+
+ private String getProperty(String name, String defaultValue) {
+ String s = getProperty(name);
+ return (s == null ? defaultValue : s);
+ }
+
+ @Override
+ public String getMethodCallSyntax(String obj, String method, String... args) {
+
+ String ret = obj + "." + method + "(";
+ int len = args.length;
+ if (len == 0) {
+ ret += ")";
+ return ret;
+ }
+
+ for (int i = 0; i < len; i++) {
+ ret += args[i];
+ if (i != len - 1) {
+ ret += ",";
+ } else {
+ ret += ")";
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public String getOutputStatement(String toDisplay) {
+ return "print(" + toDisplay + ")";
+ }
+
+ @Override
+ public String getProgram(String... statements) {
+ int len = statements.length;
+ String ret = "";
+ for (int i = 0; i < len; i++) {
+ ret += statements[i] + ";";
+ }
+
+ return ret;
+ }
+
+ public static void main(String[] args) {
+ RhinoScriptEngineFactory fact = new RhinoScriptEngineFactory();
+ System.out.println(fact.getParameter(ScriptEngine.ENGINE_VERSION));
+ }
+
+ private static List<String> names;
+ private static List<String> mimeTypes;
+ private static List<String> extensions;
+
+ static {
+ names = new ArrayList<String>(7);
+ names.add("rhino-nonjdk");
+ names.add("js");
+ names.add("rhino");
+ names.add("JavaScript");
+ names.add("javascript");
+ names.add("ECMAScript");
+ names.add("ecmascript");
+ names = Collections.unmodifiableList(names);
+
+ mimeTypes = new ArrayList<String>(4);
+ mimeTypes.add("application/javascript");
+ mimeTypes.add("application/ecmascript");
+ mimeTypes.add("text/javascript");
+ mimeTypes.add("text/ecmascript");
+ mimeTypes = Collections.unmodifiableList(mimeTypes);
+
+ extensions = new ArrayList<String>(1);
+ extensions.add("js");
+ extensions = Collections.unmodifiableList(extensions);
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ExtendedScriptException.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ExtendedScriptException.java
new file mode 100644
index 0000000..2979682
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ExtendedScriptException.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine.util;
+
+import javax.script.ScriptException;
+
+/**
+ * An extension of javax.script.ScriptException that allows
+ * the cause of an exception to be set.
+ */
+public class ExtendedScriptException extends ScriptException {
+
+ private static final long serialVersionUID = 1L;
+
+ private Throwable cause;
+
+ public ExtendedScriptException(
+ Throwable cause,
+ String message,
+ String fileName,
+ int lineNumber,
+ int columnNumber) {
+ super(message, fileName, lineNumber, columnNumber);
+ this.cause = cause;
+ }
+
+ public ExtendedScriptException(String s) {
+ super(s);
+ }
+
+ public ExtendedScriptException(Exception e) {
+ super(e);
+ }
+
+ public ExtendedScriptException(String message, String fileName, int lineNumber) {
+ super(message, fileName, lineNumber);
+ }
+
+ public ExtendedScriptException(Throwable cause, String message, String fileName, int lineNumber) {
+ super(message, fileName, lineNumber);
+ this.cause = cause;
+ }
+
+ public ExtendedScriptException(String message,
+ String fileName,
+ int lineNumber,
+ int columnNumber) {
+ super(message, fileName, lineNumber, columnNumber);
+ }
+
+ public Throwable getCause() {
+ return cause;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/InterfaceImplementor.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/InterfaceImplementor.java
new file mode 100644
index 0000000..b4880a7
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/InterfaceImplementor.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine.util;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import javax.script.Invocable;
+import javax.script.ScriptException;
+
+/*
+ * java.lang.reflect.Proxy based interface implementor. This is meant
+ * to be used to implement Invocable.getInterface.
+ *
+ * @version 1.0
+ * @author Mike Grogan
+ * @since 1.6
+ */
+public class InterfaceImplementor {
+
+ private Invocable engine;
+
+ /** Creates a new instance of Invocable */
+ public InterfaceImplementor(Invocable engine) {
+ this.engine = engine;
+ }
+
+ public class InterfaceImplementorInvocationHandler implements InvocationHandler {
+ private Invocable engine;
+ private Object thiz;
+ public InterfaceImplementorInvocationHandler(Invocable engine, Object thiz) {
+
+ this.engine = engine;
+ this.thiz = thiz;
+ }
+
+ @Override
+ public Object invoke(Object proxy , Method method, Object[] args)
+ throws java.lang.Throwable {
+ // give chance to convert input args
+ args = convertArguments(method, args);
+ Object result = engine.invokeMethod(thiz, method.getName(), args);
+ // give chance to convert the method result
+ return convertResult(method, result);
+ }
+ }
+
+ public <T> T getInterface(Object thiz, Class<T> iface)
+ throws ScriptException {
+ if (iface == null || !iface.isInterface()) {
+ throw new IllegalArgumentException("interface Class expected");
+ }
+ return iface.cast(Proxy.newProxyInstance(iface.getClassLoader(),
+ new Class[]{iface},
+ new InterfaceImplementorInvocationHandler(engine, thiz)));
+ }
+
+ // called to convert method result after invoke
+ protected Object convertResult(Method method, Object res)
+ throws ScriptException {
+ // default is identity conversion
+ return res;
+ }
+
+ // called to convert method arguments before invoke
+ protected Object[] convertArguments(Method method, Object[] args)
+ throws ScriptException {
+ // default is identity conversion
+ return args;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ScriptEngineFactoryBase.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ScriptEngineFactoryBase.java
new file mode 100644
index 0000000..89f12a9
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/util/ScriptEngineFactoryBase.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met: Redistributions of source code
+ * must retain the above copyright notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution. Neither the name of the Sun Microsystems nor the names of
+ * is contributors may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.rhq.scripting.javascript.engine.util;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+
+/*
+ * Abstract super class for factory implementations.
+ *
+ * @version 1.0
+ * @author Mike Grogan
+ * @since 1.6
+ */
+public abstract class ScriptEngineFactoryBase implements ScriptEngineFactory {
+
+ public String getName() {
+ return (String)getParameter(ScriptEngine.NAME);
+ }
+
+ @Override
+ public String getEngineName() {
+ return (String)getParameter(ScriptEngine.ENGINE);
+ }
+
+ @Override
+ public String getEngineVersion() {
+ return (String)getParameter(ScriptEngine.ENGINE_VERSION);
+ }
+
+ @Override
+ public String getLanguageName() {
+ return (String)getParameter(ScriptEngine.LANGUAGE);
+ }
+
+ @Override
+ public String getLanguageVersion() {
+ return (String)getParameter(ScriptEngine.LANGUAGE_VERSION);
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/util/ScriptSourceToModuleSourceProviderAdapter.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/util/ScriptSourceToModuleSourceProviderAdapter.java
new file mode 100644
index 0000000..fb3926b
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/util/ScriptSourceToModuleSourceProviderAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.scripting.javascript.util;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
+import org.mozilla.javascript.commonjs.module.provider.ModuleSourceProviderBase;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class ScriptSourceToModuleSourceProviderAdapter extends ModuleSourceProviderBase {
+ private static final long serialVersionUID = 1L;
+
+ private ScriptSourceProvider scriptSourceProvider;
+
+ public ScriptSourceToModuleSourceProviderAdapter(ScriptSourceProvider provider) {
+ scriptSourceProvider = provider;
+ }
+
+ @Override
+ protected ModuleSource loadFromPrivilegedLocations(String moduleId, Object validator) throws IOException,
+ URISyntaxException {
+
+ //if the URI is absolute, we make sure to define the ModuleSource as sandboxed.
+ //this is done by making the URI a "subpath" of the base.
+ URI uri = new URI(moduleId);
+ URI base = null;
+ if (uri.isAbsolute()) {
+ base = uri;
+ }
+ return loadFromUri(uri, base, validator);
+ }
+
+ @Override
+ protected ModuleSource loadFromUri(URI uri, URI base, Object validator) throws IOException, URISyntaxException {
+ URI fullUri = uri;
+ if (base != null) {
+ fullUri = uri.resolve(base);
+ }
+
+ Reader sourceReader = scriptSourceProvider.getScriptSource(fullUri);
+
+ if (sourceReader == null) {
+ return null;
+ } else {
+ return new ModuleSource(sourceReader, null, uri, base, validator);
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
index ab267fe..1480e04 100644
--- a/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
+++ b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -1 +1 @@
-org.rhq.scripting.javascript.ScriptEngineFactory
+org.rhq.scripting.javascript.engine.RhinoScriptEngineFactory
diff --git a/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/EngineTest.java b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/EngineTest.java
new file mode 100644
index 0000000..7ccde17
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/EngineTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.scripting.javascript;
+
+import static org.testng.Assert.*;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+
+import org.testng.annotations.Test;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+@Test
+public class EngineTest {
+
+ public void engineAvailable() throws Exception {
+ assertNotNull(getScriptEngine(), "Failed to obtain the script engine.");
+ }
+
+ public void printFunctionsAvailable() throws Exception {
+ String output = captureScriptOutput("print('a'); println('b');");
+ assertEquals(output, "ab\n", "Unexpected output printed by the print functions.");
+ }
+
+ public void importClassAndImportPackageAvailable() throws Exception {
+ String script = "importClass(java.lang.System); print(System.currentTimeMillis());";
+ String output = captureScriptOutput(script);
+ assertFalse(output.isEmpty(), "importClass function doesn't seem to work.");
+
+ script = "importPackage(java.util); println(new Date);";
+ output = captureScriptOutput(script);
+ assertFalse(output.isEmpty());
+ }
+
+ public void requireFunctionDefined() throws Exception {
+ String output = captureScriptOutput("print(typeof(require))");
+ assertEquals(output, "function", "The require function doesn't seem to be defined.");
+ }
+
+ public void modulesCanBeLoaded() throws Exception {
+ String script = "" +
+ "var m1 = require('target/test-classes/test-module1.js'); \n" +
+ "var m2 = require('target/test-classes/test-module2.js'); \n" +
+ "println(typeof(m1.func1));\n" +
+ "println(typeof(m1.func2));\n" +
+ "println(typeof(m2.func3));\n" +
+ "println(typeof(m2.func4));\n" +
+ "println(typeof(m2.func1));\n" +
+ "println(typeof(m2.func2));\n" +
+ "println(typeof(m1.func3));\n" +
+ "println(typeof(m1.func4));\n";
+ String output = "function\nfunction\nfunction\nfunction\nundefined\nundefined\nundefined\nundefined\n";
+
+ assertEquals(captureScriptOutput(script), output, "Unexpected functions found in modules");
+ }
+
+ private ScriptEngine getScriptEngine() {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ return manager.getEngineByName("rhino-nonjdk");
+ }
+
+ private String captureScriptOutput(String script) throws Exception {
+ ScriptEngine engine = getScriptEngine();
+
+ StringWriter stdOut = new StringWriter();
+ PrintWriter wrt = new PrintWriter(stdOut);
+
+ engine.getContext().setWriter(wrt);
+
+ engine.eval(script);
+
+ return stdOut.toString();
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java
new file mode 100644
index 0000000..3a50839
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/test/java/org/rhq/scripting/javascript/InitializerTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.scripting.javascript;
+
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.FilePermission;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.security.Permissions;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.PropertyPermission;
+import java.util.Set;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.testng.annotations.Test;
+
+import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+@Test
+public class InitializerTest {
+
+ //for Rhino to see this class, it must be public
+ public static class TestClass {
+ public boolean[] functionsCalled;
+
+ public TestClass() {
+ functionsCalled = new boolean[6];
+ }
+
+ public void func1() {
+ functionsCalled[0] = true;
+ }
+
+ public void func2() {
+ functionsCalled[1] = true;
+ }
+
+ public void func3() {
+ functionsCalled[2] = true;
+ }
+
+ public void func3(int a, int b) {
+ functionsCalled[3] = true;
+ }
+
+ public void func3(String a, String b) {
+ functionsCalled[4] = true;
+ }
+
+ public void func3(int a, int b, int c) {
+ functionsCalled[5] = true;
+ }
+ }
+
+ public void engineSecured() throws Exception {
+ Permissions perms = new Permissions();
+ //we need to be able to read files and props so that the default module source provider can work
+ perms.add(new FilePermission("<<ALL FILES>>", "read"));
+ perms.add(new PropertyPermission("*", "read"));
+
+ ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null, perms);
+
+ try {
+ eng.eval("java.lang.System.exit(1)");
+ } catch (Exception e) {
+ assertSecurityExceptionPresent(e);
+ }
+ }
+
+ public void scriptSourceProviderApplied() throws Exception {
+
+ String script = "var m = require('rhq://test-module1.js'); m.func1();";
+
+ //first let's try to find the scripts with the default source provider...
+ ScriptEngine eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), null, null);
+ try {
+ eng.eval(script);
+ fail("The module should not have been loaded using the default source provider.");
+ } catch (ScriptException e) {
+ //expected
+ }
+
+ eng = new JsEngineInitializer().instantiate(Collections.<String>emptySet(), new ScriptSourceProvider() {
+ @Override
+ public Reader getScriptSource(URI location) {
+ if (!"rhq".equals(location.getScheme())) {
+ return null;
+ }
+ String scriptName = location.getSchemeSpecificPart().substring(2); //remove the '//'
+ InputStream src = getClass().getClassLoader().getResourceAsStream(scriptName);
+ return new InputStreamReader(src);
+ }
+ }, null);
+
+ try {
+ eng.eval(script);
+ } catch (ScriptException e) {
+ fail("The module should have been loaded using the custom source provider. Error message: " + e.getMessage(), e);
+ }
+ }
+
+ public void indirectionMethodsValid() throws Exception {
+ JsEngineInitializer initializer = new JsEngineInitializer();
+
+ ScriptEngine eng = initializer.instantiate(Collections.<String>emptySet(), null, null);
+
+ TestClass myObject = new TestClass();
+
+ eng.put("myObject", myObject);
+
+ generateIndirectionMethods(initializer, eng, myObject, "myObject", "func1");
+ generateIndirectionMethods(initializer, eng, myObject, "myObject", "func2");
+ generateIndirectionMethods(initializer, eng, myObject, "myObject", "func3");
+
+ eng.eval("func1(); func2(); func3(); func3(1, 1); func3('a', 'b'); func3(1, 1, 1);");
+
+ assertTrue(myObject.functionsCalled[0], "Function func1() should have been called.");
+ assertTrue(myObject.functionsCalled[1], "Function func2() should have been called.");
+ assertTrue(myObject.functionsCalled[2], "Function func3() should have been called.");
+ assertTrue(myObject.functionsCalled[3], "Function func3(int, int) should have been called.");
+ assertTrue(myObject.functionsCalled[4], "Function func3(String, String) should have been called.");
+ assertTrue(myObject.functionsCalled[5], "Function func3(int, int, int) should have been called.");
+ }
+
+ private void assertSecurityExceptionPresent(Throwable t) {
+ boolean ok = false;
+ while (t != null) {
+ if (t instanceof SecurityException) {
+ ok = true;
+ break;
+ } else if ((t instanceof ScriptException) &&
+ (t.getMessage().contains("java.security.AccessControlException")
+ || t.getMessage().contains("java.lang.SecurityException"))) {
+ ok = true;
+ break;
+ }
+
+ t = t.getCause();
+ }
+
+ assertTrue(ok, "Didn't find a SecurityException, which should have occured.");
+ }
+
+ private void generateIndirectionMethods(ScriptEngineInitializer initializer, ScriptEngine eng, Object object, String objectName, String methodName) throws ScriptException {
+ Set<Method> methods = new HashSet<Method>();
+ for(Method m : object.getClass().getDeclaredMethods()) {
+ if (methodName.equals(m.getName())) {
+ methods.add(m);
+ }
+ }
+
+ eng.put(objectName, object);
+
+ for(String m : initializer.generateIndirectionMethods(objectName, methods)) {
+ eng.eval(m);
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/test/resources/allow-all.policy b/modules/enterprise/scripting/javascript/src/test/resources/allow-all.policy
new file mode 100644
index 0000000..cb9dbed
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/test/resources/allow-all.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
diff --git a/modules/enterprise/scripting/javascript/src/test/resources/test-module1.js b/modules/enterprise/scripting/javascript/src/test/resources/test-module1.js
new file mode 100644
index 0000000..7cc760b
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/test/resources/test-module1.js
@@ -0,0 +1,7 @@
+exports.func1 = function() {
+ return "func1";
+}
+
+exports.func2 = function() {
+ return "func2";
+}
\ No newline at end of file
diff --git a/modules/enterprise/scripting/javascript/src/test/resources/test-module2.js b/modules/enterprise/scripting/javascript/src/test/resources/test-module2.js
new file mode 100644
index 0000000..b2ad389
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/test/resources/test-module2.js
@@ -0,0 +1,7 @@
+exports.func3 = function() {
+ return "func3";
+}
+
+exports.func4 = function() {
+ return "func4";
+}
\ No newline at end of file
diff --git a/modules/enterprise/scripting/pom.xml b/modules/enterprise/scripting/pom.xml
index 92b72d4..c786cdc 100644
--- a/modules/enterprise/scripting/pom.xml
+++ b/modules/enterprise/scripting/pom.xml
@@ -13,7 +13,6 @@
<modules>
<module>api</module>
- <module>factory</module>
<module>javascript</module>
</modules>
commit ff78b1e7cf7b64f78fd155752222fba7ad4261f4
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri May 18 13:39:27 2012 +0200
An attempt to use Rhino 1.7R3 as a script engine. This will most probably
not work because of the number of changes in behavior between phobos-based
ScriptEngine and the JDK-bundled ScriptEngine.
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index bd0d585..8a5d96a 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -22,6 +22,20 @@
<artifactId>rhino</artifactId>
<version>1.7R3</version>
</dependency>
+
+ <!-- JSR 223 support for Rhino -->
+ <dependency>
+ <groupId>com.sun.phobos</groupId>
+ <artifactId>phobos-js</artifactId>
+ <version>0.6.2</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.sun.phobos</groupId>
+ <artifactId>phobos-rhino</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
</dependencies>
<build>
diff --git a/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java
new file mode 100644
index 0000000..5a176fd
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/com/sun/phobos/script/javascript/PrintHavingRhinoScriptEngine.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.sun.phobos.script.javascript;
+
+import javax.script.ScriptContext;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+
+/**
+ * We want our script engine to mimic the real JDK one as close as
+ * possible.
+ *
+ * The Phobos impl of the script engine leaves out the predefined
+ * print() and println() functions, but we really want them.
+ *
+ * To add them back we need to override a package-private method
+ * in the phobos script engine.
+ *
+ * @author Lukas Krejci
+ */
+public class PrintHavingRhinoScriptEngine extends RhinoScriptEngine {
+
+ //copied over from the JDK's impl of RhinoScriptEngine
+ private static final String printSource =
+ "function print(str, newline) { \n" +
+ " if (typeof(str) == 'undefined') { \n" +
+ " str = 'undefined'; \n" +
+ " } else if (str == null) { \n" +
+ " str = 'null'; \n" +
+ " } \n" +
+ " var out = context.getWriter(); \n" +
+ " out.print(String(str)); \n" +
+ " if (newline) out.print('\\n'); \n" +
+ " out.flush(); \n" +
+ "}\n" +
+ "function println(str) { \n" +
+ " print(str, true); \n" +
+ "}";
+
+ @Override
+ Scriptable getRuntimeScope(ScriptContext ctxt) {
+ Scriptable newScope = super.getRuntimeScope(ctxt);
+
+ Context cx = enterContext();
+ try {
+ cx.evaluateString(newScope, printSource, "print", 1, null);
+
+ return newScope;
+ } finally {
+ Context.exit();
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
index 3ad2296..803225f 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
@@ -42,7 +42,7 @@ public class JsEngineInitializer implements ScriptEngineInitializer {
@Override
public ScriptEngine instantiate(Set<String> packages) throws ScriptException {
- ScriptEngine eng = engineManager.getEngineByName("JavaScript");
+ ScriptEngine eng = engineManager.getEngineByName("rhino-nonjdk");
for(String pkg : packages) {
eng.eval("importPackage(" + pkg + ")");
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java
new file mode 100644
index 0000000..6bbf542
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/Main.java
@@ -0,0 +1,41 @@
+/*
+ * 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.scripting.javascript;
+
+import java.io.PrintWriter;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ ScriptEngineManager manager = new ScriptEngineManager();
+ ScriptEngine engine = manager.getEngineByName("rhino-nonjdk");
+
+ engine.getContext().setWriter(new PrintWriter(System.out, true));
+ engine.eval("importPackage(java.lang); println('ahoj')");
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java
new file mode 100644
index 0000000..ed8a33f
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngine.java
@@ -0,0 +1,45 @@
+/*
+ * 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.scripting.javascript;
+
+import com.sun.phobos.script.javascript.PrintHavingRhinoScriptEngine;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class ScriptEngine extends PrintHavingRhinoScriptEngine {
+
+ private ScriptEngineFactory factory;
+
+ @Override
+ public ScriptEngineFactory getFactory() {
+ if (factory == null) {
+ factory = new ScriptEngineFactory();
+ }
+
+ return factory;
+ }
+
+ void setFactory(ScriptEngineFactory factory) {
+ this.factory = factory;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java
new file mode 100644
index 0000000..824f97a
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/ScriptEngineFactory.java
@@ -0,0 +1,86 @@
+/*
+ * 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.scripting.javascript;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.script.ScriptEngine;
+
+import com.sun.phobos.script.javascript.RhinoScriptEngineFactory;
+
+import org.mozilla.javascript.Context;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class ScriptEngineFactory extends RhinoScriptEngineFactory {
+
+ private static final List<String> NAMES;
+ private static final String ENGINE_VERSION;
+ private static final String LANGUAGE_VERSION;
+ static {
+ Context.enter();
+ ENGINE_VERSION = Context.getCurrentContext().getImplementationVersion();
+ int ver = Context.getCurrentContext().getLanguageVersion();
+ Context.exit();
+
+ String version = null;
+ if (ver == 0) {
+ version = "1.7";
+ } else {
+ //the versions are formatted like 170 for 1.7, 180 for 1.8, etc
+ int major = ver / 100;
+ int minor = (ver - 100) / 10;
+ version = major + "." + minor;
+ }
+
+ LANGUAGE_VERSION = version;
+
+ NAMES = Collections.unmodifiableList(Arrays.asList("rhino-nonjdk"));
+
+ }
+
+ @Override
+ public List<String> getNames() {
+ return NAMES;
+ }
+
+ @Override
+ public Object getParameter(String key) {
+ if (ScriptEngine.ENGINE_VERSION.equals(key)) {
+ return ENGINE_VERSION;
+ } else if (ScriptEngine.LANGUAGE_VERSION.equals(key)) {
+ return LANGUAGE_VERSION;
+ } else {
+ return super.getParameter(key);
+ }
+ }
+
+ @Override
+ public ScriptEngine getScriptEngine() {
+ org.rhq.scripting.javascript.ScriptEngine engine = new org.rhq.scripting.javascript.ScriptEngine();
+ engine.setFactory(this);
+ return engine;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 0000000..ab267fe
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1 @@
+org.rhq.scripting.javascript.ScriptEngineFactory
commit 13e77125213af769de8f72ff5017458ba81a4cea
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu May 17 10:18:13 2012 +0200
Initial refactoring of the scripting infrastructure to allow language
support to be defined in a standalone module (and allow multiple script
langs in the future).
The ScriptEngineFactory now searches for the supported script langs using
META-INF/services.
The project probably doesn't compile with this commit but this will get
fixed by the future commits.
diff --git a/modules/enterprise/binding/pom.xml b/modules/enterprise/binding/pom.xml
index 7ca5ea1..6683bd8 100644
--- a/modules/enterprise/binding/pom.xml
+++ b/modules/enterprise/binding/pom.xml
@@ -19,6 +19,12 @@
<dependencies>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-scripting-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rhq-core-domain</artifactId>
<version>${project.version}</version>
@@ -30,6 +36,14 @@
</exclusions>
</dependency>
+ <!-- Provide a scripting impl for the tests -->
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-scripting-javascript</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rhq-core-domain</artifactId>
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
index 106c2ae..73ffc9d 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/ScriptEngineFactory.java
@@ -37,6 +37,7 @@ import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.ServiceLoader;
import java.util.Set;
import javax.script.Bindings;
@@ -47,10 +48,10 @@ import javax.script.ScriptException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.rhq.bindings.engine.JsEngineInitializer;
-import org.rhq.bindings.engine.ScriptEngineInitializer;
import org.rhq.bindings.util.NoTopLevelIndirection;
import org.rhq.bindings.util.PackageFinder;
+import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptEngineProvider;
/**
* This is RHQ specific imitation of ScriptEngineFactory.
@@ -68,13 +69,56 @@ import org.rhq.bindings.util.PackageFinder;
public class ScriptEngineFactory {
private static final Log LOG = LogFactory.getLog(ScriptEngineFactory.class);
- private static final ScriptEngineInitializer[] KNOWN_ENGINES = { new JsEngineInitializer() };
+ private static final Map<String, ScriptEngineProvider> KNOWN_PROVIDERS;
+ static {
+ KNOWN_PROVIDERS = new HashMap<String, ScriptEngineProvider>();
+
+ reloadScriptEngineProviders(null);
+ }
private ScriptEngineFactory() {
}
/**
+ * Reloads the list of the known script engine providers using the given classloader
+ * or the current thread's context classloader if it is null.
+ *
+ * @param classLoader the classloader used to find the script engine providers on the classpath
+ *
+ * @throws IllegalStateException if more than 1 script engine provider is found for a single language
+ */
+ public static void reloadScriptEngineProviders(ClassLoader classLoader) {
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getContextClassLoader();
+ }
+
+ ServiceLoader<ScriptEngineProvider> loader = ServiceLoader.load(ScriptEngineProvider.class, classLoader);
+
+ KNOWN_PROVIDERS.clear();
+
+ for (ScriptEngineProvider provider : loader) {
+ String lang = provider.getSupportedLanguage();
+
+ if (KNOWN_PROVIDERS.containsKey(lang)) {
+ String existing = KNOWN_PROVIDERS.get(lang).getClass().getName();
+ String thisOne = provider.getClass().getName();
+ throw new IllegalStateException("'" + lang + "' scripting language provided by at least 2 providers: '"
+ + existing + "' and '" + thisOne + "'. Only 1 provider per language is allowed.");
+ }
+
+ KNOWN_PROVIDERS.put(lang, provider);
+ }
+ }
+
+ /**
+ * @return the set of the scripting languages supported by this factory
+ */
+ public static Set<String> getSupportedLanguages() {
+ return new HashSet<String>(KNOWN_PROVIDERS.keySet());
+ }
+
+ /**
* Initializes the script engine for given language.
*
* @param language the language of the script to instantiate
@@ -248,13 +292,9 @@ public class ScriptEngineFactory {
}
public static ScriptEngineInitializer getInitializer(String language) {
- for (ScriptEngineInitializer i : KNOWN_ENGINES) {
- if (i.implementsLanguage(language)) {
- return i;
- }
- }
-
- return null;
+ ScriptEngineProvider provider = KNOWN_PROVIDERS.get(language);
+
+ return provider == null ? null : provider.getInitializer();
}
private static boolean shouldIndirect(Method method) {
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
index fd84c6d..da544c4 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardBindings.java
@@ -72,14 +72,17 @@ public class StandardBindings extends HashMap<String, Object> {
this.clazz = clazz;
}
+ @Override
public String getKey() {
return inner.getKey();
}
+ @Override
public T getValue() {
return clazz.cast(inner.getValue());
}
+ @Override
public T setValue(T value) {
return clazz.cast(inner.setValue(value));
}
@@ -130,11 +133,13 @@ public class StandardBindings extends HashMap<String, Object> {
putAll(managers);
}
+ @Override
public void preInject(ScriptEngine scriptEngine) {
((ScriptUtil) get(SCRIPT_UTIL)).init(scriptEngine);
((ScriptAssert) get(ASSERT)).init(scriptEngine);
}
+ @Override
public void postInject(ScriptEngine scriptEngine) {
ScriptEngineFactory.bindIndirectionMethods(scriptEngine, SCRIPT_UTIL);
ScriptEngineFactory.bindIndirectionMethods(scriptEngine, ASSERT);
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/JsEngineInitializer.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/JsEngineInitializer.java
deleted file mode 100644
index e810bfe..0000000
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/JsEngineInitializer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 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.bindings.engine;
-
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.Set;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptEngineManager;
-import javax.script.ScriptException;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class JsEngineInitializer implements ScriptEngineInitializer {
-
- private static final String WRAPPED_EXCEPTION_PREFIX = "Wrapped ";
-
- private ScriptEngineManager engineManager = new ScriptEngineManager();
-
- public boolean implementsLanguage(String language) {
- return language != null && ("JavaScript".equals(language) || "ECMAScript".equals(language));
- }
-
- public ScriptEngine instantiate(Set<String> packages) throws ScriptException {
- ScriptEngine eng = engineManager.getEngineByName("JavaScript");
-
- for(String pkg : packages) {
- eng.eval("importPackage(" + pkg + ")");
- }
-
- return eng;
- }
-
- public Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> methods) {
- if (methods.size() == 0) {
- return Collections.emptySet();
- }
-
- String methodName = methods.iterator().next().getName();
-
- StringBuilder functionBuilder = new StringBuilder("function ");
- functionBuilder.append(methodName).append("() { switch(arguments.length) { ");
-
- for(Method method : methods) {
- int argCnt = method.getParameterTypes().length;
- functionBuilder.append("case ").append(argCnt).append(": ");
- functionBuilder.append("return ").append(boundObjectName).append(".").append(methodName).append("(");
- for(int i = 0; i < argCnt; ++i) {
- if (i > 0) {
- functionBuilder.append(", ");
- }
-
- functionBuilder.append("arguments[").append(i).append("]");
- }
-
- functionBuilder.append("); break; ");
- }
-
- functionBuilder.append(" default: throw \"Unsupported number of parameters.\"; } }");
-
- return Collections.singleton(functionBuilder.toString());
- }
-
- public String extractUserFriendlyErrorMessage(ScriptException e) {
- String errorMessage = e.getMessage();
-
- int wrappedIdx = errorMessage.lastIndexOf(WRAPPED_EXCEPTION_PREFIX);
-
- if (wrappedIdx < 0) {
- return errorMessage;
- }
-
- errorMessage = errorMessage.substring(wrappedIdx + WRAPPED_EXCEPTION_PREFIX.length());
-
- int sourceInfoStartIdx = errorMessage.indexOf(" (<Unknown source>#");
-
- if (sourceInfoStartIdx >= 0) {
- errorMessage = errorMessage.substring(0, sourceInfoStartIdx);
- }
-
- return errorMessage;
- }
-}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/ScriptEngineInitializer.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/ScriptEngineInitializer.java
deleted file mode 100644
index 968e815..0000000
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/engine/ScriptEngineInitializer.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 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.bindings.engine;
-
-import java.lang.reflect.Method;
-import java.util.Set;
-
-import javax.script.ScriptEngine;
-import javax.script.ScriptException;
-
-/**
- * Is able to instantiate a script engine and import packages into the context
- * of the engine.
- *
- * @author Lukas Krejci
- */
-public interface ScriptEngineInitializer {
-
- boolean implementsLanguage(String language);
-
- ScriptEngine instantiate(Set<String> packages) throws ScriptException;
-
- /**
- * This function returns a definition string in the script engine's language
- * that provides an indirection to calling the method on the bound object.
- *
- * for example for parameters:
- * <ul>
- * <li> <code>boundObjectName = foo</code>
- * <li> <code> method = <int bar(int)></code>
- * </ul>
- * The method would generate this javascript:<br/>
- * <code>
- * function bar(arg) { return foo.bar(arg); }
- * </code>
- * <p>
- * This method gets passed all the overloaded versions of a method on the object (i.e.
- * all the methods with the same name) and is free to return any number of functions
- * that will map all the possible overloaded versions.
- * <p>
- * This is because different scripting languages have different support for function
- * overloading and different ways of handling varying number of arguments of a function.
- *
- * @param boundObjectName
- * @param overloadedMethods
- * @return a set of strings with top-level function definitions in the scripting language
- */
- Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> overloadedMethods);
-
- /**
- * At least the Rhino script engine for java script generates exceptions
- * whose error messages contain just "too much" information to be easily
- * decipherable by the end users.
- * <p>
- * This method extracts messages from the exception such that they are
- * presentable to the end user.
- * <p>
- * The returned string should only contain the error message. The filename, line
- * and column information should be stripped from it if at all possible.
- *
- * @param e
- * @return
- */
- String extractUserFriendlyErrorMessage(ScriptException e);
-}
diff --git a/modules/enterprise/pom.xml b/modules/enterprise/pom.xml
index 158709c..e572daf 100644
--- a/modules/enterprise/pom.xml
+++ b/modules/enterprise/pom.xml
@@ -46,6 +46,7 @@
<module>server/plugins</module>
<module>server/ear</module>
<module>binding</module>
+ <module>scripting</module>
<module>server/client-api</module>
<module>server/itests</module>
</modules>
@@ -67,6 +68,7 @@
<module>server/safe-invoker</module>
<module>server/sars</module>
<module>binding</module>
+ <module>scripting</module>
<module>remoting</module>
<module>gui</module>
<module>server/plugins</module>
diff --git a/modules/enterprise/scripting/api/pom.xml b/modules/enterprise/scripting/api/pom.xml
new file mode 100644
index 0000000..b3d036b
--- /dev/null
+++ b/modules/enterprise/scripting/api/pom.xml
@@ -0,0 +1,173 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>rhq-scripting-parent</artifactId>
+ <groupId>org.rhq</groupId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rhq-scripting-api</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ <name>RHQ Scripting API</name>
+ <description>Provides API for adding scripting support to RHQ using different javax.scripting - based interpreters</description>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../..</rhq.rootDir>
+ <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
+ <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/lib</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+
+ <execution>
+ <id>deploy</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+ <mkdir dir="${rhq.deploymentDir}" />
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Updating
+ ${deployment.file}...</echo>
+ <jar destfile="${deployment.file}" basedir="${project.build.outputDirectory}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <tasks>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Deleting
+ ${deployment.file}...</echo>
+ <delete file="${deployment.file}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>cobertura-plugins</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.cobertura</groupId>
+ <artifactId>cobertura</artifactId>
+ <version>1.9.4.1</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>cobertura-instrument</id>
+ <phase>pre-integration-test</phase>
+ <configuration>
+ <tasks>
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/cobertura/backup" />
+ <!-- backup all classes so that we
+ can instrument the original classes -->
+ <copy toDir="target/cobertura/backup" verbose="true" overwrite="true">
+ <fileset dir="target/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- create a properties file and
+ save there location of cobertura data file -->
+ <touch file="target/classes/cobertura.properties" />
+ <echo file="target/classes/cobertura.properties">net.sourceforge.cobertura.datafile=${project.build.directory}/cobertura/cobertura.ser</echo>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- instrument all classes in target/classes
+ directory -->
+ <cobertura-instrument datafile="${project.build.directory}/cobertura/cobertura.ser" todir="${project.build.directory}/classes">
+ <fileset dir="${project.build.directory}/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </cobertura-instrument>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>cobertura-report</id>
+ <phase>post-integration-test</phase>
+ <configuration>
+ <tasks>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/site/cobertura" />
+ <!-- restore classes from backup
+ folder to classes folder -->
+ <copy toDir="target/classes" verbose="true" overwrite="true">
+ <fileset dir="target/cobertura/backup">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- delete backup folder -->
+ <delete dir="target/cobertura/backup" />
+ <!-- create a code coverage report -->
+ <cobertura-report format="html" datafile="${project.build.directory}/cobertura/cobertura.ser" destdir="${project.build.directory}/site/cobertura">
+ <fileset dir="${basedir}/src/main/java">
+ <include name="**/*.java" />
+ </fileset>
+ </cobertura-report>
+ <!-- delete cobertura.properties
+ file -->
+ <delete file="target/classes/cobertura.properties" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
new file mode 100644
index 0000000..1569896
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/CodeCompletion.java
@@ -0,0 +1,29 @@
+/*
+ * 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.scripting;
+
+/**
+ * TODO This will hook into the interactive code completion provided by the CLI.
+ *
+ * @author Lukas Krejci
+ */
+public interface CodeCompletion {
+
+}
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
new file mode 100644
index 0000000..3286f39
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineInitializer.java
@@ -0,0 +1,80 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 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.scripting;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+/**
+ * Is able to instantiate a script engine and import packages into the context
+ * of the engine.
+ *
+ * @author Lukas Krejci
+ */
+public interface ScriptEngineInitializer {
+
+ ScriptEngine instantiate(Set<String> packages) throws ScriptException;
+
+ /**
+ * This function returns a definition string in the script engine's language
+ * that provides an indirection to calling the method on the bound object.
+ *
+ * for example for parameters:
+ * <ul>
+ * <li> <code>boundObjectName = foo</code>
+ * <li> <code> method = <int bar(int)></code>
+ * </ul>
+ * The method would generate this javascript:<br/>
+ * <code>
+ * function bar(arg) { return foo.bar(arg); }
+ * </code>
+ * <p>
+ * This method gets passed all the overloaded versions of a method on the object (i.e.
+ * all the methods with the same name) and is free to return any number of functions
+ * that will map all the possible overloaded versions.
+ * <p>
+ * This is because different scripting languages have different support for function
+ * overloading and different ways of handling varying number of arguments of a function.
+ *
+ * @param boundObjectName
+ * @param overloadedMethods
+ * @return a set of strings with top-level function definitions in the scripting language
+ */
+ Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> overloadedMethods);
+
+ /**
+ * At least the Rhino script engine for java script generates exceptions
+ * whose error messages contain just "too much" information to be easily
+ * decipherable by the end users.
+ * <p>
+ * This method extracts messages from the exception such that they are
+ * presentable to the end user.
+ * <p>
+ * The returned string should only contain the error message. The filename, line
+ * and column information should be stripped from it if at all possible.
+ *
+ * @param e
+ * @return
+ */
+ String extractUserFriendlyErrorMessage(ScriptException e);
+}
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
new file mode 100644
index 0000000..db22068
--- /dev/null
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptEngineProvider.java
@@ -0,0 +1,52 @@
+/*
+ * 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.scripting;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * This is the service interface for scripting language implementations for RHQ
+ * (loaded using the META-INF/services mechanism).
+ *
+ * @author Lukas Krejci
+ */
+public interface ScriptEngineProvider {
+
+ /**
+ * @return the scripting language understood by this provider.
+ */
+ @NotNull
+ String getSupportedLanguage();
+
+ /**
+ * @return an implementation of {@link ScriptEngineInitializer} that can instantiate
+ * and initialize a script engine for the supported language for use with RHQ.
+ */
+ @NotNull
+ ScriptEngineInitializer getInitializer();
+
+ /**
+ * @return a {@link CodeCompletion} implementation for the supported language or null
+ * if this provider doesn't provide one.
+ */
+ @Nullable
+ CodeCompletion getCodeCompletion();
+}
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
new file mode 100644
index 0000000..bd0d585
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -0,0 +1,187 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>rhq-scripting-parent</artifactId>
+ <groupId>org.rhq</groupId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rhq-scripting-javascript</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ <name>RHQ Javascript support</name>
+ <description>Provides RHQ scripting in Javascript using Rhino</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-scripting-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mozilla</groupId>
+ <artifactId>rhino</artifactId>
+ <version>1.7R3</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../..</rhq.rootDir>
+ <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
+ <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/lib</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+
+ <execution>
+ <id>deploy</id>
+ <phase>compile</phase>
+ <configuration>
+ <tasks>
+ <mkdir dir="${rhq.deploymentDir}" />
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Updating
+ ${deployment.file}...</echo>
+ <jar destfile="${deployment.file}" basedir="${project.build.outputDirectory}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <tasks>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar" />
+ <echo>*** Deleting
+ ${deployment.file}...</echo>
+ <delete file="${deployment.file}" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>cobertura-plugins</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.cobertura</groupId>
+ <artifactId>cobertura</artifactId>
+ <version>1.9.4.1</version>
+ </dependency>
+ </dependencies>
+ <executions>
+ <execution>
+ <id>cobertura-instrument</id>
+ <phase>pre-integration-test</phase>
+ <configuration>
+ <tasks>
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/cobertura/backup" />
+ <!-- backup all classes so that we
+ can instrument the original classes -->
+ <copy toDir="target/cobertura/backup" verbose="true" overwrite="true">
+ <fileset dir="target/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- create a properties file and
+ save there location of cobertura data file -->
+ <touch file="target/classes/cobertura.properties" />
+ <echo file="target/classes/cobertura.properties">net.sourceforge.cobertura.datafile=${project.build.directory}/cobertura/cobertura.ser</echo>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- instrument all classes in target/classes
+ directory -->
+ <cobertura-instrument datafile="${project.build.directory}/cobertura/cobertura.ser" todir="${project.build.directory}/classes">
+ <fileset dir="${project.build.directory}/classes">
+ <include name="**/*.class" />
+ </fileset>
+ </cobertura-instrument>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>cobertura-report</id>
+ <phase>post-integration-test</phase>
+ <configuration>
+ <tasks>
+ <taskdef classpathref="maven.plugin.classpath" resource="tasks.properties" />
+ <!-- prepare directory structure
+ for cobertura -->
+ <mkdir dir="target/cobertura" />
+ <mkdir dir="target/site/cobertura" />
+ <!-- restore classes from backup
+ folder to classes folder -->
+ <copy toDir="target/classes" verbose="true" overwrite="true">
+ <fileset dir="target/cobertura/backup">
+ <include name="**/*.class" />
+ </fileset>
+ </copy>
+ <!-- delete backup folder -->
+ <delete dir="target/cobertura/backup" />
+ <!-- create a code coverage report -->
+ <cobertura-report format="html" datafile="${project.build.directory}/cobertura/cobertura.ser" destdir="${project.build.directory}/site/cobertura">
+ <fileset dir="${basedir}/src/main/java">
+ <include name="**/*.java" />
+ </fileset>
+ </cobertura-report>
+ <!-- delete cobertura.properties
+ file -->
+ <delete file="target/classes/cobertura.properties" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
new file mode 100644
index 0000000..3ad2296
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineInitializer.java
@@ -0,0 +1,105 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2011 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.scripting.javascript;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.rhq.scripting.ScriptEngineInitializer;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class JsEngineInitializer implements ScriptEngineInitializer {
+
+ private static final String WRAPPED_EXCEPTION_PREFIX = "Wrapped ";
+
+ private ScriptEngineManager engineManager = new ScriptEngineManager();
+
+ @Override
+ public ScriptEngine instantiate(Set<String> packages) throws ScriptException {
+ ScriptEngine eng = engineManager.getEngineByName("JavaScript");
+
+ for(String pkg : packages) {
+ eng.eval("importPackage(" + pkg + ")");
+ }
+
+ return eng;
+ }
+
+ @Override
+ public Set<String> generateIndirectionMethods(String boundObjectName, Set<Method> methods) {
+ if (methods.size() == 0) {
+ return Collections.emptySet();
+ }
+
+ String methodName = methods.iterator().next().getName();
+
+ StringBuilder functionBuilder = new StringBuilder("function ");
+ functionBuilder.append(methodName).append("() { switch(arguments.length) { ");
+
+ for(Method method : methods) {
+ int argCnt = method.getParameterTypes().length;
+ functionBuilder.append("case ").append(argCnt).append(": ");
+ functionBuilder.append("return ").append(boundObjectName).append(".").append(methodName).append("(");
+ for(int i = 0; i < argCnt; ++i) {
+ if (i > 0) {
+ functionBuilder.append(", ");
+ }
+
+ functionBuilder.append("arguments[").append(i).append("]");
+ }
+
+ functionBuilder.append("); break; ");
+ }
+
+ functionBuilder.append(" default: throw \"Unsupported number of parameters.\"; } }");
+
+ return Collections.singleton(functionBuilder.toString());
+ }
+
+ @Override
+ public String extractUserFriendlyErrorMessage(ScriptException e) {
+ String errorMessage = e.getMessage();
+
+ int wrappedIdx = errorMessage.lastIndexOf(WRAPPED_EXCEPTION_PREFIX);
+
+ if (wrappedIdx < 0) {
+ return errorMessage;
+ }
+
+ errorMessage = errorMessage.substring(wrappedIdx + WRAPPED_EXCEPTION_PREFIX.length());
+
+ int sourceInfoStartIdx = errorMessage.indexOf(" (<Unknown source>#");
+
+ if (sourceInfoStartIdx >= 0) {
+ errorMessage = errorMessage.substring(0, sourceInfoStartIdx);
+ }
+
+ return errorMessage;
+ }
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
new file mode 100644
index 0000000..8d9e4b7
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/JsEngineProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.scripting.javascript;
+
+import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.ScriptEngineInitializer;
+import org.rhq.scripting.ScriptEngineProvider;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class JsEngineProvider implements ScriptEngineProvider {
+
+ @Override
+ public String getSupportedLanguage() {
+ return "JavaScript";
+ }
+
+ @Override
+ public ScriptEngineInitializer getInitializer() {
+ return new JsEngineInitializer();
+ }
+
+ @Override
+ public CodeCompletion getCodeCompletion() {
+ // TODO copy this over from the CLI
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider
new file mode 100644
index 0000000..1c55fe7
--- /dev/null
+++ b/modules/enterprise/scripting/javascript/src/main/resources/META-INF/services/org.rhq.scripting.ScriptEngineProvider
@@ -0,0 +1 @@
+org.rhq.scripting.javascript.JsEngineProvider
diff --git a/modules/enterprise/scripting/pom.xml b/modules/enterprise/scripting/pom.xml
new file mode 100644
index 0000000..92b72d4
--- /dev/null
+++ b/modules/enterprise/scripting/pom.xml
@@ -0,0 +1,34 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>rhq-enterprise-parent</artifactId>
+ <groupId>org.rhq</groupId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>rhq-scripting-parent</artifactId>
+ <packaging>pom</packaging>
+
+ <version>4.5.0-SNAPSHOT</version>
+ <name>RHQ Scripting Parent Module</name>
+
+ <modules>
+ <module>api</module>
+ <module>factory</module>
+ <module>javascript</module>
+ </modules>
+
+ <profiles>
+ <profile>
+ <id>experimental</id>
+ <activation>
+ <property>
+ <name>experimental-script-languages</name>
+ </property>
+ </activation>
+
+ <modules>
+ <module>python</module>
+ </modules>
+ </profile>
+ </profiles>
+</project>
diff --git a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
index da003f6..09dc8a5 100644
--- a/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
+++ b/modules/enterprise/server/plugins/alert-cli/src/main/java/org/rhq/enterprise/server/plugins/alertCli/CliSender.java
@@ -43,7 +43,6 @@ import org.apache.commons.logging.LogFactory;
import org.rhq.bindings.ScriptEngineFactory;
import org.rhq.bindings.StandardBindings;
import org.rhq.bindings.StandardScriptPermissions;
-import org.rhq.bindings.engine.ScriptEngineInitializer;
import org.rhq.bindings.util.PackageFinder;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.alert.notification.SenderResult;
@@ -61,6 +60,7 @@ import org.rhq.enterprise.server.content.RepoManagerLocal;
import org.rhq.enterprise.server.plugin.pc.alert.AlertSender;
import org.rhq.enterprise.server.plugin.pc.alert.AlertSenderValidationResults;
import org.rhq.enterprise.server.util.LookupUtil;
+import org.rhq.scripting.ScriptEngineInitializer;
/**
* Uses CLI to perform the alert notification.
11 years, 10 months
[rhq] Branch 'lkrejci/modular-scripting' - 2 commits - modules/enterprise
by lkrejci
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java | 12
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java | 4
modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java | 6
modules/enterprise/scripting/javascript/pom.xml | 1
modules/enterprise/scripting/python/pom.xml | 37 +-
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java | 41 ++
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java | 35 +
modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java | 136 +++++++
modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory | 1
modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java | 178 ++++++++++
modules/enterprise/scripting/python/src/test/resources/allow-all.policy | 3
11 files changed, 443 insertions(+), 11 deletions(-)
New commits:
commit 709521269eea8fa16f7c672e34ad90c0b748c0ee
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 16:39:49 2012 +0200
Fixing the last javascript specific thing in the CLI - no more assumptions
about the format of the error message.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index cfe1686..c7f6bbf 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -58,6 +58,7 @@ import org.rhq.enterprise.client.utility.CodeCompletionCompletorWrapper;
import org.rhq.enterprise.client.utility.DummyCodeCompletion;
import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.scripting.CodeCompletion;
+import org.rhq.scripting.ScriptEngineInitializer;
/**
* @author Greg Hinkle
@@ -108,6 +109,7 @@ public class ClientMain {
private Recorder recorder = new NoOpRecorder();
private ScriptEngine engine;
+ private ScriptEngineInitializer scriptEngineInitializer;
private class StartupConfiguration {
public boolean askForPassword;
@@ -675,6 +677,12 @@ public class ClientMain {
try {
engine = ScriptEngineFactory.getScriptEngine(getLanguage(),
new PackageFinder(Arrays.asList(getLibDir())), null);
+
+ if (engine == null) {
+ throw new IllegalStateException("The scripting language '" + getLanguage()
+ + "' could not be loaded.");
+ }
+ scriptEngineInitializer = ScriptEngineFactory.getInitializer(getLanguage());
} catch (ScriptException e) {
e.printStackTrace();
} catch (IOException e) {
@@ -685,6 +693,10 @@ public class ClientMain {
return engine;
}
+ public String getUsefulErrorMessage(ScriptException e) {
+ return scriptEngineInitializer.extractUserFriendlyErrorMessage(e);
+ }
+
public Map<String, ClientCommand> getCommands() {
return commands;
}
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
index a511310..42137af 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/ScriptCommand.java
@@ -151,9 +151,7 @@ public class ScriptCommand implements ClientCommand {
}
} catch (ScriptException e) {
- String message = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
- message = message.replace("sun.org.mozilla.javascript.internal.EcmaError: ", "");
- message = message.replace("(<Unknown source>#1) in <Unknown source> at line number 1", "");
+ String message = client.getUsefulErrorMessage(e);
client.getPrintWriter().println(message);
client.getPrintWriter().println(script);
commit 0520b77135d2be31dea003cb1b99bf75aac49a9b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Jul 12 16:35:16 2012 +0200
Finishing up the python support for CLI.
diff --git a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
index c91b706..e65f793 100644
--- a/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
+++ b/modules/enterprise/scripting/api/src/main/java/org/rhq/scripting/ScriptSourceProvider.java
@@ -34,5 +34,11 @@ import java.net.URI;
*/
public interface ScriptSourceProvider {
+ /**
+ * Returns the reader of the source of the script specified by given location.
+ *
+ * @param location the location of the script
+ * @return the reader of the script source or null if it could not be found
+ */
Reader getScriptSource(URI location);
}
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index 6009b1b..06bed89 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -23,7 +23,6 @@
<artifactId>rhino</artifactId>
<version>1.7R4</version>
</dependency>
-
</dependencies>
<build>
diff --git a/modules/enterprise/scripting/python/pom.xml b/modules/enterprise/scripting/python/pom.xml
index 4b48e52..b657dd2 100644
--- a/modules/enterprise/scripting/python/pom.xml
+++ b/modules/enterprise/scripting/python/pom.xml
@@ -27,10 +27,39 @@
<build>
<plugins>
<plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
- </configuration>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>jarjar-maven-plugin</artifactId>
+ <version>1.5</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>jarjar</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>org.python:jython-standalone</include>
+ </includes>
+ <rules>
+ <keep>
+ <pattern>*.**</pattern>
+ </keep>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ <argLine>-Djava.security.manager
+ -Djava.security.policy==${project.build.testOutputDirectory}/allow-all.policy</argLine>
+ <!-- This is important, because some of the tests try to exit
+ the JVM. -->
+ <failIfNoTests>true</failIfNoTests>
+ </configuration>
</plugin>
</plugins>
</build>
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java
new file mode 100644
index 0000000..2d1ef65
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineFactory.java
@@ -0,0 +1,41 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import javax.script.ScriptEngine;
+
+import org.python.jsr223.PyScriptEngineFactory;
+
+/**
+ * @author Lukas Krejci
+ *
+ */
+public class PythonScriptEngineFactory extends PyScriptEngineFactory {
+
+ @Override
+ public Object getParameter(String key) {
+ if (ScriptEngine.NAME.equals(key)) {
+ return "python";
+ } else {
+ return super.getParameter(key);
+ }
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
index 07ed8eb..e868204 100644
--- a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonScriptEngineInitializer.java
@@ -23,12 +23,19 @@ import java.lang.reflect.Method;
import java.security.PermissionCollection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Properties;
import java.util.Set;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.python.core.Py;
+import org.python.core.PySystemState;
+import org.python.util.PythonInterpreter;
+
import org.rhq.scripting.ScriptEngineInitializer;
import org.rhq.scripting.ScriptSourceProvider;
import org.rhq.scripting.util.SandboxedScriptEngine;
@@ -40,6 +47,16 @@ import org.rhq.scripting.util.SandboxedScriptEngine;
*/
public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
+ private static final Log LOG = LogFactory.getLog(PythonScriptEngineInitializer.class);
+
+ static {
+ Properties props = new Properties();
+ props.put("python.packages.paths", "java.class.path,sun.boot.class.path");
+ props.put("python.packages.directories", "java.ext.dirs");
+ props.put("python.cachedir.skip", false);
+ PythonInterpreter.initialize(System.getProperties(), props, null);
+ }
+
private ScriptEngineManager engineManager = new ScriptEngineManager();
@Override
@@ -47,17 +64,29 @@ public class PythonScriptEngineInitializer implements ScriptEngineInitializer {
ScriptEngine eng = engineManager.getEngineByName("python");
+ //XXX this might not work perfectly in jython
+ //but we can't make it work perfectly either, so let's just
+ //keep our fingers crossed..
+ //http://www.jython.org/jythonbook/en/1.0/ModulesPackages.html#from-import-statements
for (String pkg : packages) {
- eng.eval("from " + pkg + " import *\n");
+ try {
+ eng.eval("from " + pkg + " import *\n");
+ } catch (ScriptException e) {
+ //well, let's just keep things going, this is not fatal...
+ LOG.info("Python script engine could not pre-import members of package '" + pkg + "'.");
+ }
}
//fingers crossed we can secure jython like this
- return new SandboxedScriptEngine(eng, permissions);
+ return permissions == null ? eng : new SandboxedScriptEngine(eng, permissions);
}
@Override
public void installScriptSourceProvider(ScriptEngine scriptEngine, ScriptSourceProvider provider) {
- //TODO add support for script source providers... possibly using http://www.python.org/dev/peps/pep-0302/
+ PySystemState sys = Py.getSystemState();
+ if (sys != null) {
+ sys.path_hooks.append(new PythonSourceProvider(provider));
+ }
}
@Override
diff --git a/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java
new file mode 100644
index 0000000..70688d4
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/java/org/rhq/scripting/python/PythonSourceProvider.java
@@ -0,0 +1,136 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.python.core.Py;
+import org.python.core.PyObject;
+import org.python.core.imp;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * This class translates the requests for modules in python using the import
+ * statement into calls to RHQ's script source providers.
+ * <p>
+ * For a script to be downloadable using RHQ, one must add a path prefix to
+ * <code>sys.path</code> so that RHQ is aware of the available locations it should
+ * look into.
+ * <p>
+ * For example, if you have the RHQ repository script source provider available on
+ * the classpath of the CLI, you can add the following to the <code>sys.path</code>:
+ * <pre>
+ * <code>
+ * import sys
+ * sys.path.append("__rhq__:rhq:/repositories/my_repository")
+ * </code>
+ * </pre>
+ * and then you can import a module from that repository by the ordinary import statement:
+ * <pre>
+ * <code>
+ * import my_module
+ * </code>
+ * </pre>
+ * This will translate into a download of the script from the following location:
+ * <code>rhq://repositories/my_repository/my_module.py</code>.
+ *
+ * @author Lukas Krejci
+ */
+public class PythonSourceProvider extends PyObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final String RHQ_PATH_EXTENSION_PREFIX = "__rhq__:";
+
+ private ScriptSourceProvider scriptSourceProvider;
+ private String currentPathPrefix;
+
+ public PyObject __call__(PyObject args[], String keywords[]) {
+ if (args[0].toString().startsWith(RHQ_PATH_EXTENSION_PREFIX)) {
+ currentPathPrefix = args[0].toString().substring(RHQ_PATH_EXTENSION_PREFIX.length());
+ return this;
+ }
+ throw Py.ImportError("unable to handle");
+ }
+
+ private static class ReaderInputStream extends InputStream {
+ private Reader rdr;
+
+ public ReaderInputStream(Reader rdr) {
+ this.rdr = rdr;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return rdr.read();
+ }
+
+ }
+
+ public class Loader extends PyObject {
+
+ private static final long serialVersionUID = 1L;
+
+ private String prefix;
+
+ public Loader(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public PyObject load_module(String name) {
+ try {
+ URI uri = new URI(prefix + name + ".py");
+ Reader rdr = scriptSourceProvider.getScriptSource(uri);
+ return imp.createFromSource(name, new ReaderInputStream(rdr), uri.toString());
+ } catch (URISyntaxException e) {
+ return Py.None;
+ }
+ }
+ }
+
+ public PythonSourceProvider(ScriptSourceProvider scriptSourceProvider) {
+ this.scriptSourceProvider = scriptSourceProvider;
+ }
+
+ public PyObject find_module(String name) {
+ return find_module(name, Py.None);
+ }
+
+ public PyObject find_module(String name, PyObject path) {
+ try {
+ URI uri = new URI(currentPathPrefix + name + ".py");
+
+ return scriptSourceProvider.getScriptSource(uri) == null ? Py.None : new Loader(currentPathPrefix);
+ } catch (URISyntaxException e) {
+ return Py.None;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getType().toString();
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 0000000..f2c5a71
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1 @@
+org.rhq.scripting.python.PythonScriptEngineFactory
diff --git a/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java b/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java
new file mode 100644
index 0000000..b1d4a65
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/test/java/org/rhq/scripting/python/PythonScriptEngineInitializerTest.java
@@ -0,0 +1,178 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.scripting.python;
+
+import java.io.FilePermission;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.security.AccessControlException;
+import java.security.Permissions;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class PythonScriptEngineInitializerTest {
+
+ public static class Tester {
+ private int cnt = 0;
+
+ public void increment() {
+ ++cnt;
+ }
+
+ public int getInvocationCoung() {
+ return cnt;
+ }
+ }
+
+ private static final String EXPECTED_OUTPUT = "kachny";
+
+ public static class SourceProvider implements ScriptSourceProvider {
+ @Override
+ public Reader getScriptSource(URI scriptUri) {
+ if (scriptUri.toString().equals("test/test_module.py")) {
+ return new StringReader("print '" + EXPECTED_OUTPUT + "'");
+ }
+ return null;
+ }
+ }
+
+ public void testEngineInitialization() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ //just some code to test out this is python
+ engine.eval("from java.util import HashMap\nHashMap()");
+ }
+
+ public void testMethodIndirection() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ Bindings bindings = engine.createBindings();
+ Tester tester = new Tester();
+ bindings.put("tester", tester);
+
+ engine.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+
+ engine.eval("tester.increment()");
+
+ Assert.assertEquals(tester.getInvocationCoung(), 1, "Unexpected number of tester invocations.");
+
+ Map<String, Set<Method>> methods = getMethodsByName(Tester.class);
+ for (Set<Method> ms : methods.values()) {
+ Set<String> fns = initializer.generateIndirectionMethods("tester", ms);
+ for (String fn : fns) {
+ engine.eval(fn);
+ }
+ }
+
+ engine.eval("increment()");
+ Assert.assertEquals(tester.getInvocationCoung(), 2,
+ "Unexpected number of tester invocations after calling an indirected method.");
+ }
+
+ public void testSecuredEngine() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+
+ //jython seems to need these two..
+ Permissions perms = new Permissions();
+ perms.add(new RuntimePermission("createClassLoader"));
+ perms.add(new RuntimePermission("getProtectionDomain"));
+
+ //add permission to read files so that modules can be loaded, but writing should fail
+ perms.add(new FilePermission("<<ALL FILES>>", "read"));
+
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), perms);
+
+ try {
+ engine.eval("import os\nfp = open('pom.xml', 'w')");
+ Assert.fail("Opening a file for writing should have failed with a security exception.");
+ } catch (ScriptException e) {
+ checkIsCausedByAccessControlException(e);
+ }
+ }
+
+ public void testSourceProvider() throws Exception {
+ PythonScriptEngineInitializer initializer = new PythonScriptEngineInitializer();
+
+ ScriptEngine engine = initializer.instantiate(Collections.<String> emptySet(), null);
+
+ StringWriter wrt = new StringWriter();
+
+ engine.getContext().setWriter(wrt);
+
+ initializer.installScriptSourceProvider(engine, new SourceProvider());
+
+ engine
+ .eval("import sys\nsys.path.append('__rhq__:test-unsupported/')\nsys.path.append('__rhq__:test/')\nimport test_module");
+
+ Assert.assertEquals(wrt.toString(), EXPECTED_OUTPUT + "\n", "Unexpected output from a custom module.");
+ }
+
+ private void checkIsCausedByAccessControlException(Throwable e) {
+ Throwable ex = e;
+ while (ex != null) {
+ if (ex instanceof AccessControlException) {
+ return;
+ }
+
+ ex = ex.getCause();
+ }
+
+ Assert.fail("Expected an AccessControlException but the exception doesn't seem to be caused by it.", e);
+ }
+
+ private static Map<String, Set<Method>> getMethodsByName(Class<?> cls) {
+ Map<String, Set<Method>> ret = new HashMap<String, Set<Method>>();
+
+ for (Method m : cls.getDeclaredMethods()) {
+ Set<Method> methods = ret.get(m.getName());
+ if (methods == null) {
+ methods = new HashSet<Method>();
+ ret.put(m.getName(), methods);
+ }
+
+ methods.add(m);
+ }
+
+ return ret;
+ }
+}
diff --git a/modules/enterprise/scripting/python/src/test/resources/allow-all.policy b/modules/enterprise/scripting/python/src/test/resources/allow-all.policy
new file mode 100644
index 0000000..cb9dbed
--- /dev/null
+++ b/modules/enterprise/scripting/python/src/test/resources/allow-all.policy
@@ -0,0 +1,3 @@
+grant {
+ permission java.security.AllPermission;
+};
11 years, 10 months
[rhq] modules/enterprise
by Jiri Kremser
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java | 18 +++++++---
1 file changed, 13 insertions(+), 5 deletions(-)
New commits:
commit a49e0754c248ee94a1252dc7dbbbd2048133c426
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Thu Jul 12 14:50:02 2012 +0200
Monitoring -> Tables -> Get Live Value, the resulting modal window has now sorted records
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java
index fe00f1a..881a3a3 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java
@@ -19,12 +19,20 @@
package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
+
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
@@ -38,11 +46,6 @@ import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableListGrid;
import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableWindow;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* Views a resource's measurements in a tabular view.
*
@@ -118,6 +121,11 @@ public class MeasurementTableView extends Table<MeasurementTableDataSource> {
records.add(record);
}
}
+ Collections.sort(records, new Comparator<ListGridRecord>() {
+ public int compare(ListGridRecord o1, ListGridRecord o2) {
+ return o1.getAttribute("name").compareTo(o2.getAttribute("name"));
+ }
+ });
showLiveData(records);
}
11 years, 10 months
[rhq] modules/core modules/enterprise
by Jiri Kremser
modules/core/domain/src/main/java/org/rhq/core/domain/resource/Agent.java | 5
modules/core/domain/src/main/java/org/rhq/core/domain/resource/composite/ResourceIdWithAgentComposite.java | 48 ++++
modules/core/domain/src/main/java/org/rhq/core/domain/util/collection/ArrayUtils.java | 75 +++++++
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/MeasurementDataGWTService.java | 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitDataSource.java | 14 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitListView.java | 43 +++-
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/traits/TraitsView.java | 107 +++++++++-
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/traits/TraitsView.java | 86 ++++++++
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/MeasurementDataGWTServiceImpl.java | 9
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java | 67 +++++-
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerLocal.java | 15 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerRemote.java | 7
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java | 4
13 files changed, 466 insertions(+), 16 deletions(-)
New commits:
commit 6307139046317a3b9d0037e542197182c7ba1519
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Thu Jul 12 11:17:15 2012 +0200
[BZ Bug 808175 - Monitor>Traits subtab should provide a Get Live Value button] Added support for "getting live value" for resource's traits as well as for group's traits
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Agent.java b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Agent.java
index 220af57..e96e1d3 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Agent.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Agent.java
@@ -62,6 +62,10 @@ import org.rhq.core.domain.cloud.Server;
@NamedQuery(name = Agent.QUERY_REMOVE_SERVER_REFERENCE, query = "UPDATE Agent a SET a.server.id = NULL WHERE a.server.id = :serverId "),
@NamedQuery(name = Agent.QUERY_COUNT_ALL, query = "SELECT count(a.id) FROM Agent a"),
@NamedQuery(name = Agent.QUERY_FIND_RESOURCE_IDS_FOR_AGENT, query = "SELECT r.id FROM Resource r WHERE r.agent.id = :agentId"),
+ @NamedQuery(name = Agent.QUERY_FIND_RESOURCE_IDS_WITH_AGENTS_BY_RESOURCE_IDS, query = "" //
+ + "SELECT new org.rhq.core.domain.resource.composite.ResourceIdWithAgentComposite(r.id, r.agent) " //
+ + " FROM Resource r " //
+ + " WHERE r.id IN (:resourceIds)"),
@NamedQuery(name = Agent.QUERY_FIND_ALL_SUSPECT_AGENTS, query = "SELECT new org.rhq.core.domain.resource.composite.AgentLastAvailabilityPingComposite "
+ " ( "
+ " a.id,a.name,a.remoteEndpoint,a.lastAvailabilityPing,a.backFilled "
@@ -150,6 +154,7 @@ public class Agent implements Serializable {
public static final String QUERY_FIND_BY_SERVER = "Agent.findByServer";
public static final String QUERY_COUNT_ALL = "Agent.countAll";
public static final String QUERY_FIND_RESOURCE_IDS_FOR_AGENT = "Agent.findResourceIdsForAgent";
+ public static final String QUERY_FIND_RESOURCE_IDS_WITH_AGENTS_BY_RESOURCE_IDS = "Agent.findResourceIdsWithAgentsByResourceIds";
public static final String QUERY_FIND_ALL_SUSPECT_AGENTS = "Agent.findAllSuspectAgents";
public static final String QUERY_FIND_BY_AFFINITY_GROUP = "Agent.findByAffinityGroup";
public static final String QUERY_FIND_WITHOUT_AFFINITY_GROUP = "Agent.findWithoutAffinityGroup";
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/composite/ResourceIdWithAgentComposite.java b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/composite/ResourceIdWithAgentComposite.java
new file mode 100644
index 0000000..03fbef9
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/composite/ResourceIdWithAgentComposite.java
@@ -0,0 +1,48 @@
+/*
+ * 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.core.domain.resource.composite;
+
+import java.io.Serializable;
+
+import org.rhq.core.domain.resource.Agent;
+
+/**
+ * (resourceId, agent) tuple
+ *
+ * @author Jirka Kremser
+ */
+public class ResourceIdWithAgentComposite implements Serializable {
+
+ private static final long serialVersionUID = 42L;
+ private final int resourceId;
+ private final Agent agent;
+
+ public ResourceIdWithAgentComposite(int resourceId, Agent agent) {
+ this.resourceId = resourceId;
+ this.agent = agent;
+ }
+
+ public int getResourceId() {
+ return resourceId;
+ }
+
+ public Agent getAgent() {
+ return agent;
+ }
+}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/util/collection/ArrayUtils.java b/modules/core/domain/src/main/java/org/rhq/core/domain/util/collection/ArrayUtils.java
new file mode 100644
index 0000000..12fd15e
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/util/collection/ArrayUtils.java
@@ -0,0 +1,75 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.core.domain.util.collection;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author John Mazzitelli
+ * @author Joseph Marques
+ */
+public class ArrayUtils {
+
+ public static int[] unwrapCollection(Collection<Integer> input) {
+ if (input == null) {
+ return null;
+ }
+ Integer[] intermediate = input.toArray(new Integer[input.size()]);
+ return unwrapArray(intermediate);
+ }
+
+ public static int[] unwrapArray(Integer[] input) {
+ if (input == null) {
+ return null;
+ }
+ int[] output = new int[input.length];
+ for (int i = 0; i < input.length; i++) {
+ output[i] = input[i];
+ }
+ return output;
+ }
+
+ public static Integer[] wrapInArray(int[] input) {
+ if (input == null) {
+ return null;
+ }
+ Integer[] output = new Integer[input.length];
+ for (int i = 0; i < input.length; i++) {
+ output[i] = input[i];
+ }
+ return output;
+ }
+
+ public static List<Integer> wrapInList(int[] input) {
+ if (input == null) {
+ return null;
+ }
+ Integer[] intermediate = wrapInArray(input);
+
+ // do not use Arrays.asList because returned list needs to be modifiable
+ List<Integer> results = new ArrayList<Integer>();
+ for (Integer next : intermediate) {
+ results.add(next);
+ }
+ return results;
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/MeasurementDataGWTService.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/MeasurementDataGWTService.java
index 8db6354..8640383 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/MeasurementDataGWTService.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/MeasurementDataGWTService.java
@@ -50,6 +50,8 @@ public interface MeasurementDataGWTService extends RemoteService {
throws RuntimeException;
Set<MeasurementData> findLiveData(int resourceId, int[] definitionIds) throws RuntimeException;
+
+ Set<MeasurementData> findLiveDataForGroup(int groupId, int resourceId[], int[] definitionIds) throws RuntimeException;
List<List<MeasurementDataNumericHighLowComposite>> findDataForResource(int resourceId, int[] definitionIds,
long beginTime, long endTime, int numPoints) throws RuntimeException;
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitDataSource.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitDataSource.java
index 325a542..e640d1d 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitDataSource.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitDataSource.java
@@ -52,6 +52,10 @@ import org.rhq.enterprise.gui.coregui.client.util.RPCDataSource;
*/
public abstract class AbstractMeasurementDataTraitDataSource extends
RPCDataSource<MeasurementDataTrait, MeasurementDataTraitCriteria> {
+
+ public static final String FIELD_METRIC_SCHED_ID = "id";
+ public static final String FIELD_METRIC_NAME = "name";
+
private MeasurementDataGWTServiceAsync measurementService = GWTServiceLookup.getMeasurementDataService();
protected AbstractMeasurementDataTraitDataSource() {
@@ -70,9 +74,13 @@ public abstract class AbstractMeasurementDataTraitDataSource extends
primaryKeyField.setHidden(true);
fields.add(primaryKeyField);
- DataSourceIntegerField idField = new DataSourceIntegerField("id", MSG.dataSource_traits_field_definitionID());
+ DataSourceIntegerField idField = new DataSourceIntegerField(FIELD_METRIC_SCHED_ID, MSG.dataSource_traits_field_definitionID());
idField.setHidden(true);
fields.add(idField);
+
+ DataSourceIntegerField nameField = new DataSourceIntegerField(FIELD_METRIC_NAME, MSG.common_title_name());
+ nameField.setHidden(true);
+ fields.add(nameField);
return fields;
}
@@ -173,11 +181,13 @@ public abstract class AbstractMeasurementDataTraitDataSource extends
ListGridRecord record = new ListGridRecord();
record.setAttribute("primaryKey", from.getScheduleId() + ":" + from.getTimestamp());
- record.setAttribute("id", from.getSchedule().getDefinition().getId()); // used for detail view
+ record.setAttribute(FIELD_METRIC_SCHED_ID, from.getSchedule().getDefinition().getId()); // used for detail view
record.setAttribute(MeasurementDataTraitCriteria.SORT_FIELD_TIMESTAMP, new Date(from.getTimestamp()));
record.setAttribute(MeasurementDataTraitCriteria.SORT_FIELD_DISPLAY_NAME, from.getSchedule().getDefinition()
.getDisplayName());
record.setAttribute(MeasurementDataTraitCriteria.SORT_FIELD_VALUE, from.getValue());
+ record.setAttribute(FIELD_METRIC_NAME, from.getSchedule().getDefinition().getName());
+ record.setAttribute(MeasurementDataTraitCriteria.FILTER_FIELD_RESOURCE_ID, from.getSchedule().getResource().getId());
return record;
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitListView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitListView.java
index 2f842a8..6a9d35b 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitListView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/AbstractMeasurementDataTraitListView.java
@@ -19,16 +19,23 @@
package org.rhq.enterprise.gui.coregui.client.inventory.common;
import java.util.ArrayList;
+import java.util.List;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.SortSpecifier;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.SortDirection;
+import com.smartgwt.client.widgets.events.CloseClickEvent;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.criteria.MeasurementDataTraitCriteria;
+import org.rhq.enterprise.gui.coregui.client.components.table.TableAction;
import org.rhq.enterprise.gui.coregui.client.components.table.TableSection;
+import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableListGrid;
+import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableWindow;
/**
* A view that displays a non-paginated table of {@link org.rhq.core.domain.measurement.MeasurementDataTrait trait}s,
@@ -36,7 +43,7 @@ import org.rhq.enterprise.gui.coregui.client.components.table.TableSection;
*
* @author Ian Springer
*/
-public abstract class AbstractMeasurementDataTraitListView extends TableSection {
+public abstract class AbstractMeasurementDataTraitListView extends TableSection<AbstractMeasurementDataTraitDataSource> {
private static final String TITLE = MSG.view_metric_traits();
private static final String[] EXCLUDED_FIELD_NAMES = new String[0];
@@ -70,11 +77,45 @@ public abstract class AbstractMeasurementDataTraitListView extends TableSection
// Set widths and cell formatters on the fields.
ListGridField displayNameField = listGrid.getField(MeasurementDataTraitCriteria.SORT_FIELD_DISPLAY_NAME);
displayNameField.setWidth("20%");
+
+ addTableAction(extendLocatorId("liveValue"), MSG.view_measureTable_getLive(), getLiveValueAction());
}
+
+ protected abstract TableAction getLiveValueAction();
+
+ protected abstract LocatableListGrid decorateLiveDataGrid(List<ListGridRecord> records);
@Override
protected String getDetailsLinkColumnName() {
return MeasurementDataTraitCriteria.SORT_FIELD_DISPLAY_NAME;
}
+
+ public void showLiveData(List<ListGridRecord> records) {
+ final LocatableWindow liveDataWindow = new LocatableWindow(extendLocatorId("liveDataWindow"));
+ liveDataWindow.setTitle(MSG.view_measureTable_live_title());
+ liveDataWindow.setShowModalMask(true);
+ liveDataWindow.setShowMinimizeButton(false);
+ liveDataWindow.setShowMaximizeButton(true);
+ liveDataWindow.setShowCloseButton(true);
+ liveDataWindow.setShowResizer(true);
+ liveDataWindow.setCanDragResize(true);
+ liveDataWindow.setDismissOnEscape(true);
+ liveDataWindow.setIsModal(true);
+ liveDataWindow.setWidth(700);
+ liveDataWindow.setHeight(425);
+ liveDataWindow.setAutoCenter(true);
+ liveDataWindow.centerInPage();
+ liveDataWindow.addCloseClickHandler(new CloseClickHandler() {
+ @Override
+ public void onCloseClick(CloseClickEvent event) {
+ liveDataWindow.destroy();
+ refreshTableInfo();
+ }
+ });
+
+ LocatableListGrid liveDataGrid = decorateLiveDataGrid(records);
+ liveDataWindow.addItem(liveDataGrid);
+ liveDataWindow.show();
+ }
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/traits/TraitsView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/traits/TraitsView.java
index ed91c6a..f7eb900 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/traits/TraitsView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/traits/TraitsView.java
@@ -18,7 +18,18 @@
*/
package org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.traits;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.Criteria;
+import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
@@ -27,9 +38,16 @@ import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.criteria.MeasurementDataTraitCriteria;
+import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.core.domain.util.collection.ArrayUtils;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.LinkManager;
+import org.rhq.enterprise.gui.coregui.client.components.table.TableAction;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractMeasurementDataTraitDataSource;
import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractMeasurementDataTraitListView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.AncestryUtil;
+import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableListGrid;
import org.rhq.enterprise.gui.coregui.client.util.selenium.SeleniumUtility;
/**
@@ -71,7 +89,6 @@ public class TraitsView extends AbstractMeasurementDataTraitListView {
//resourceNameField.setCanGroupBy(true);
AncestryUtil.setupAncestryListGridField(listGrid);
- super.configureTable();
}
@Override
@@ -87,4 +104,92 @@ public class TraitsView extends AbstractMeasurementDataTraitListView {
return criteria;
}
+
+ @Override
+ protected TableAction getLiveValueAction() {
+ return new TableAction() {
+ @Override
+ public boolean isEnabled(ListGridRecord[] selection) {
+ return selection != null && selection.length > 0;
+ }
+
+ @Override
+ public void executeAction(ListGridRecord[] selection, Object actionValue) {
+ if (selection == null || selection.length == 0) {
+ return;
+ }
+ final Map<String, ListGridRecord> selectedRecords = new HashMap<String, ListGridRecord>();
+ int[] definitionIds = new int[selection.length];
+ int i = 0;
+ Set<Integer> resourceIds = new HashSet<Integer>();
+ for (ListGridRecord record : selection) {
+ Integer defId = record
+ .getAttributeAsInt(AbstractMeasurementDataTraitDataSource.FIELD_METRIC_SCHED_ID);
+ definitionIds[i++] = defId.intValue();
+ int resourceId = record.getAttributeAsInt(MeasurementDataTraitCriteria.FILTER_FIELD_RESOURCE_ID);
+ resourceIds.add(resourceId);
+
+ selectedRecords.put(
+ resourceId + ":"
+ + record.getAttribute(AbstractMeasurementDataTraitDataSource.FIELD_METRIC_NAME), record);
+ }
+
+ // actually go out and ask the agents for the data
+ GWTServiceLookup.getMeasurementDataService(60000).findLiveDataForGroup(groupId,
+ ArrayUtils.unwrapCollection(resourceIds), definitionIds, new AsyncCallback<Set<MeasurementData>>() {
+ @Override
+ public void onSuccess(Set<MeasurementData> result) {
+ if (result == null) {
+ result = new HashSet<MeasurementData>(0);
+ }
+ ArrayList<ListGridRecord> records = new ArrayList<ListGridRecord>(result.size());
+ for (MeasurementData data : result) {
+ ListGridRecord record = selectedRecords.get(data.getName());
+ record.setAttribute("value", data.getValue());
+ records.add(record);
+ }
+ Collections.sort(records, new Comparator<ListGridRecord>() {
+ public int compare(ListGridRecord o1, ListGridRecord o2) {
+ return o1.getAttribute("name").compareTo(o2.getAttribute("name"));
+ }
+ });
+ showLiveData(records);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_measureTable_getLive_failure(), caught);
+ }
+ });
+ }
+ };
+ }
+
+ @Override
+ protected LocatableListGrid decorateLiveDataGrid(List<ListGridRecord> records) {
+ LocatableListGrid liveDataGrid = new LocatableListGrid(extendLocatorId("liveDataListGrid"));
+ liveDataGrid.setShowAllRecords(true);
+ liveDataGrid.setData(records.toArray(new ListGridRecord[records.size()]));
+ liveDataGrid.setSelectionType(SelectionStyle.NONE);
+ ListGridField name = new ListGridField(MeasurementDataTraitCriteria.SORT_FIELD_DISPLAY_NAME,
+ MSG.dataSource_traits_field_trait());
+ ListGridField value = new ListGridField("value", MSG.common_title_value());
+ ListGridField resourceNameField = new ListGridField(MeasurementDataTraitCriteria.SORT_FIELD_RESOURCE_NAME,
+ MSG.common_title_resource());
+ resourceNameField.setCellFormatter(new CellFormatter() {
+ public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
+ String url = LinkManager.getResourceLink(listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID));
+ return SeleniumUtility.getLocatableHref(url, o.toString(), null);
+ }
+ });
+ resourceNameField.setShowHover(true);
+ resourceNameField.setHoverCustomizer(new HoverCustomizer() {
+ public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) {
+ return AncestryUtil.getResourceHoverHTML(listGridRecord, 0);
+ }
+ });
+ liveDataGrid.setFields(name, value, resourceNameField, AncestryUtil.setupAncestryListGridField());
+
+ return liveDataGrid;
+ }
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/traits/TraitsView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/traits/TraitsView.java
index fe7b9ec..ad422b7 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/traits/TraitsView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/traits/TraitsView.java
@@ -18,11 +18,30 @@
*/
package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.traits;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.Criteria;
+import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.criteria.MeasurementDataTraitCriteria;
+import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.components.table.TableAction;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractMeasurementDataTraitDataSource;
import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractMeasurementDataTraitListView;
+import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableListGrid;
/**
* The Resource Monitoring>Traits subtab.
@@ -55,4 +74,71 @@ public class TraitsView extends AbstractMeasurementDataTraitListView {
return criteria;
}
+
+ @Override
+ protected TableAction getLiveValueAction() {
+ return new TableAction() {
+ @Override
+ public boolean isEnabled(ListGridRecord[] selection) {
+ return selection != null && selection.length > 0;
+ }
+
+ @Override
+ public void executeAction(ListGridRecord[] selection, Object actionValue) {
+ if (selection == null || selection.length == 0) {
+ return;
+ }
+ final Map<String, String> scheduleNames = new HashMap<String, String>();
+ int[] definitionIds = new int[selection.length];
+ int i = 0;
+ for (ListGridRecord record : selection) {
+ Integer defId = record.getAttributeAsInt(AbstractMeasurementDataTraitDataSource.FIELD_METRIC_SCHED_ID);
+ definitionIds[i++] = defId.intValue();
+
+ scheduleNames.put(record.getAttribute(AbstractMeasurementDataTraitDataSource.FIELD_METRIC_NAME), record.getAttribute(MeasurementDataTraitCriteria.SORT_FIELD_DISPLAY_NAME));
+ }
+
+ // actually go out and ask the agents for the data
+ GWTServiceLookup.getMeasurementDataService(60000).findLiveData(resourceId,
+ definitionIds, new AsyncCallback<Set<MeasurementData>>() {
+ public void onSuccess(Set<MeasurementData> result) {
+ if (result == null) {
+ result = new HashSet<MeasurementData>(0);
+ }
+ ArrayList<ListGridRecord> records = new ArrayList<ListGridRecord>(result.size());
+ for (MeasurementData data : result) {
+ ListGridRecord record = new ListGridRecord();
+ record.setAttribute("name", scheduleNames.get(data.getName()));
+ record.setAttribute("value", data.getValue());
+ records.add(record);
+ }
+ Collections.sort(records, new Comparator<ListGridRecord>() {
+ public int compare(ListGridRecord o1, ListGridRecord o2) {
+ return o1.getAttribute("name").compareTo(o2.getAttribute("name"));
+ }
+ });
+ showLiveData(records);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_measureTable_getLive_failure(), caught);
+ }
+ });
+ }
+ };
+ }
+
+ @Override
+ protected LocatableListGrid decorateLiveDataGrid(List<ListGridRecord> records) {
+ LocatableListGrid liveDataGrid = new LocatableListGrid(extendLocatorId("liveDataListGrid"));
+ liveDataGrid.setShowAllRecords(true);
+ liveDataGrid.setData(records.toArray(new ListGridRecord[records.size()]));
+ liveDataGrid.setSelectionType(SelectionStyle.NONE);
+ ListGridField name = new ListGridField("name", MSG.dataSource_traits_field_trait());
+ ListGridField value = new ListGridField("value", MSG.common_title_value());
+ liveDataGrid.setFields(name, value);
+
+ return liveDataGrid;
+ }
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/MeasurementDataGWTServiceImpl.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/MeasurementDataGWTServiceImpl.java
index 071881e..eec590d 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/MeasurementDataGWTServiceImpl.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/MeasurementDataGWTServiceImpl.java
@@ -81,6 +81,15 @@ public class MeasurementDataGWTServiceImpl extends AbstractGWTServiceImpl implem
throw getExceptionToThrowToClient(t);
}
}
+
+ public Set<MeasurementData> findLiveDataForGroup(int groupId, int resourceId[], int[] definitionIds) throws RuntimeException {
+ try {
+ return SerialUtility.prepare(dataManager.findLiveDataForGroup(getSessionSubject(), groupId, resourceId, definitionIds),
+ "MeasurementDataService.findLiveDataForGroup");
+ } catch (Throwable t) {
+ throw getExceptionToThrowToClient(t);
+ }
+ }
public List<List<MeasurementDataNumericHighLowComposite>> findDataForResource(int resourceId, int[] definitionIds,
long beginTime, long endTime, int numPoints) throws RuntimeException {
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
index 32567f0..65ec2a7 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
@@ -73,6 +73,7 @@ import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.domain.resource.composite.ResourceIdWithAgentComposite;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.server.PersistenceUtility;
import org.rhq.core.domain.util.OrderingField;
@@ -775,6 +776,7 @@ public class MeasurementDataManagerBean implements MeasurementDataManagerLocal,
return results;
}
+ @Override
@SuppressWarnings("unchecked")
public Set<MeasurementData> findLiveData(Subject subject, int resourceId, int[] definitionIds) {
if (authorizationManager.canViewResource(subject, resourceId) == false) {
@@ -782,10 +784,11 @@ public class MeasurementDataManagerBean implements MeasurementDataManagerLocal,
+ "] does not have permission to view live measurement data for resource[id=" + resourceId + "]");
}
- Resource resource = entityManager.find(Resource.class, resourceId);
- Agent agent = resource.getAgent();
+ Query query = entityManager.createNamedQuery(Agent.QUERY_FIND_BY_RESOURCE_ID);
+ query.setParameter("resourceId", resourceId);
+ Agent agent = (Agent) query.getSingleResult();
- Query query = entityManager.createNamedQuery(MeasurementSchedule.FIND_BY_RESOURCE_IDS_AND_DEFINITION_IDS);
+ query = entityManager.createNamedQuery(MeasurementSchedule.FIND_BY_RESOURCE_IDS_AND_DEFINITION_IDS);
query.setParameter("definitionIds", ArrayUtils.wrapInList(definitionIds));
query.setParameter("resourceIds", Arrays.asList(resourceId));
List<MeasurementSchedule> schedules = query.getResultList();
@@ -796,7 +799,7 @@ public class MeasurementDataManagerBean implements MeasurementDataManagerLocal,
}
AgentClient ac = agentClientManager.getAgentClient(agent);
- Set<MeasurementData> values = ac.getMeasurementAgentService().getRealTimeMeasurementValue(resourceId,requests);
+ Set<MeasurementData> values = ac.getMeasurementAgentService().getRealTimeMeasurementValue(resourceId, requests);
//[BZ 760139] always return non-null value even when there are errors on the server side. Avoids cryptic
// Global UI Exceptions when attempting to serialize null responses.
if (values == null) {
@@ -807,6 +810,49 @@ public class MeasurementDataManagerBean implements MeasurementDataManagerLocal,
}
@Override
+ @SuppressWarnings("unchecked")
+ public Set<MeasurementData> findLiveDataForGroup(Subject subject, int groupId, int resourceIds[],
+ int[] definitionIds) {
+ if (authorizationManager.canViewGroup(subject, groupId) == false) {
+ throw new PermissionException("User [" + subject.getName()
+ + "] does not have permission to view measurement data for resourceGroup[id=" + groupId + "]");
+ }
+ Set<MeasurementData> values = new HashSet<MeasurementData>();
+
+ if (resourceIds != null) {
+ Query query = entityManager.createNamedQuery(Agent.QUERY_FIND_RESOURCE_IDS_WITH_AGENTS_BY_RESOURCE_IDS);
+ query.setParameter("resourceIds", ArrayUtils.wrapInList(resourceIds));
+ List<ResourceIdWithAgentComposite> resourceIdsWithAgents = query.getResultList();
+
+ for (ResourceIdWithAgentComposite resourceIdWithAgent : resourceIdsWithAgents) {
+ query = entityManager.createNamedQuery(MeasurementSchedule.FIND_BY_RESOURCE_IDS_AND_DEFINITION_IDS);
+ query.setParameter("definitionIds", ArrayUtils.wrapInList(definitionIds));
+ query.setParameter("resourceIds", Arrays.asList(resourceIdWithAgent.getResourceId()));
+ List<MeasurementSchedule> schedules = query.getResultList();
+
+ Map<Integer, Integer> scheduleIdToResourceIdMap = new HashMap<Integer, Integer>(schedules.size());
+ Set<MeasurementScheduleRequest> requests = new HashSet<MeasurementScheduleRequest>(schedules.size());
+ for (MeasurementSchedule schedule : schedules) {
+ requests.add(new MeasurementScheduleRequest(schedule));
+ scheduleIdToResourceIdMap.put(schedule.getId(), resourceIdWithAgent.getResourceId());
+ }
+
+ AgentClient ac = agentClientManager.getAgentClient(resourceIdWithAgent.getAgent());
+ Set<MeasurementData> newValues = ac.getMeasurementAgentService().getRealTimeMeasurementValue(
+ resourceIdWithAgent.getResourceId(), requests);
+ values.addAll(newValues);
+
+ // Add the resource id as a prefix of the name, because the name is not unique across different platforms
+ for (MeasurementData value : newValues) {
+ value.setName(String.valueOf(scheduleIdToResourceIdMap.get(value.getScheduleId())) + ":"
+ + value.getName());
+ }
+ }
+ }
+ return values;
+ }
+
+ @Override
public List<MeasurementDataNumeric> findRawData(Subject subject, int scheduleId, long startTime, long endTime) {
List<MeasurementDataNumeric> result = new ArrayList<MeasurementDataNumeric>();
@@ -817,23 +863,22 @@ public class MeasurementDataManagerBean implements MeasurementDataManagerLocal,
try {
connection = rhqDs.getConnection();
ps = connection.prepareStatement( // TODO supply real impl that spans multiple tables
- "SELECT time_stamp,value FROM " + table + " WHERE schedule_id= ? AND time_stamp BETWEEN ? AND ?");
- ps.setLong(1,scheduleId);
- ps.setLong(2,startTime);
- ps.setLong(3,endTime);
+ "SELECT time_stamp,value FROM " + table + " WHERE schedule_id= ? AND time_stamp BETWEEN ? AND ?");
+ ps.setLong(1, scheduleId);
+ ps.setLong(2, startTime);
+ ps.setLong(3, endTime);
rs = ps.executeQuery();
while (rs.next()) {
- MeasurementDataNumeric point = new MeasurementDataNumeric(rs.getLong(1),scheduleId,rs.getDouble(2));
+ MeasurementDataNumeric point = new MeasurementDataNumeric(rs.getLong(1), scheduleId, rs.getDouble(2));
result.add(point);
}
} catch (SQLException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ e.printStackTrace(); // TODO: Customise this generated block
} finally {
JDBCUtil.safeClose(connection, ps, rs);
}
-
return result;
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerLocal.java
index 4ea8302..a285a7f 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerLocal.java
@@ -164,11 +164,24 @@ public interface MeasurementDataManagerLocal {
/**
* Get live metrics for a given MeasurementSchedule
*
- * @param sched MeasurementSchedule to obtain the data for
+ * @param subject the user that is requesting the data
+ * @param resourceId the id of the resource
+ * @param definitionIds the array of ids of schedule definitions
*
* @return MeasurementData for this Schedule
*/
Set<MeasurementData> findLiveData(Subject subject, int resourceId, int[] definitionIds);
+
+ /**
+ * Get live metrics for a given MeasurementSchedule
+ *
+ * @param subject the user that is requesting the data
+ * @param resourceId the array of ids of the resources
+ * @param definitionIds the array of ids of schedule definitions
+ *
+ * @return MeasurementData for this Schedule
+ */
+ Set<MeasurementData> findLiveDataForGroup(Subject subject, int groupId, int[] resourceId, int[] definitionIds);
/**
* Returns a list of numeric data point lists for the given compatible group - one per specified measurement
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerRemote.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerRemote.java
index 032f8f9..d3163ec 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerRemote.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerRemote.java
@@ -71,6 +71,13 @@ public interface MeasurementDataManagerRemote {
@WebParam(name = "subject") Subject subject, //
@WebParam(name = "resourceId") int resourceId, //
@WebParam(name = "definitionIds") int[] definitionIds);
+
+ @WebMethod
+ Set<MeasurementData> findLiveDataForGroup(//
+ @WebParam(name = "subject") Subject subject, //
+ @WebParam(name = "groupId") int groupId,//
+ @WebParam(name = "resourceId") int[] resourceId, //
+ @WebParam(name = "definitionIds") int[] definitionIds);
@WebMethod
@XmlJavaTypeAdapter(MeasurementDataNumericHighLowCompositeAdapter.class)
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
index 27cdabd..1ba4d97 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/webservices/WebservicesManagerBean.java
@@ -666,6 +666,10 @@ public class WebservicesManagerBean implements WebservicesRemote {
public Set<MeasurementData> findLiveData(Subject subject, int resourceId, int[] definitionIds) {
return measurementDataManager.findLiveData(subject, resourceId, definitionIds);
}
+
+ public Set<MeasurementData> findLiveDataForGroup(Subject subject, int groupId, int[] resourceId, int[] definitionIds) {
+ return measurementDataManager.findLiveDataForGroup(subject, groupId, resourceId, definitionIds);
+ }
public List<MeasurementDataTrait> findTraits(Subject subject, int resourceId, int definitionId) {
return measurementDataManager.findTraits(subject, resourceId, definitionId);
11 years, 10 months
[rhq] 2 commits - modules/enterprise modules/plugins
by Heiko W. Rupp
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/AgentPluginScanner.java | 68 +++++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/PluginDeploymentScanner.java | 16 +
modules/plugins/noop/pom.xml | 119 ++++++++++
modules/plugins/noop/src/main/java/org/rhq/plugins/noop/NoopComponent.java | 67 +++++
modules/plugins/noop/src/main/resources/META-INF/rhq-plugin.xml | 25 ++
modules/plugins/pom.xml | 7
6 files changed, 289 insertions(+), 13 deletions(-)
New commits:
commit db5166849d9de681ec1ea65826799f445c272ef0
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jul 12 10:19:07 2012 +0200
No-op plugin that is meant as base for jar-less plugins that should not be discovered on the agent side, but which are served through the server side REST-api.
diff --git a/modules/plugins/noop/pom.xml b/modules/plugins/noop/pom.xml
new file mode 100644
index 0000000..cc31433
--- /dev/null
+++ b/modules/plugins/noop/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-plugins-parent</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-no-op-plugin</artifactId>
+ <packaging>jar</packaging>
+
+ <name>RHQ No-op Plugin</name>
+ <description>An abstract plugin for plugins defining resource types without java-agent support.</description>
+
+ <profiles>
+
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../..</rhq.rootDir>
+ <rhq.containerDir>${rhq.rootDir}/${rhq.defaultDevContainerPath}</rhq.containerDir>
+ <rhq.deploymentDir>${rhq.containerDir}/jbossas/server/default/deploy/${rhq.earName}/rhq-downloads/rhq-plugins
+ </rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+
+ <execution>
+ <id>deploy</id>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <mkdir dir="${rhq.deploymentDir}"/>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar"/>
+ <echo>*** Updating ${deployment.file}...</echo>
+ <jar destfile="${deployment.file}" basedir="${project.build.outputDirectory}"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>deploy-jar-meta-inf</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar"/>
+ <echo>*** Updating META-INF dir in ${deployment.file}...</echo>
+ <unjar src="${project.build.directory}/${project.build.finalName}.jar"
+ dest="${project.build.outputDirectory}">
+ <patternset>
+ <include name="META-INF/**"/>
+ </patternset>
+ </unjar>
+ <jar destfile="${deployment.file}" manifest="${project.build.outputDirectory}/META-INF/MANIFEST.MF"
+ update="true">
+ </jar>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <target>
+ <property name="deployment.file" location="${rhq.deploymentDir}/${project.build.finalName}.jar"/>
+ <echo>*** Deleting ${deployment.file}...</echo>
+ <delete file="${deployment.file}"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
\ No newline at end of file
diff --git a/modules/plugins/noop/src/main/java/org/rhq/plugins/noop/NoopComponent.java b/modules/plugins/noop/src/main/java/org/rhq/plugins/noop/NoopComponent.java
new file mode 100644
index 0000000..184397d
--- /dev/null
+++ b/modules/plugins/noop/src/main/java/org/rhq/plugins/noop/NoopComponent.java
@@ -0,0 +1,67 @@
+/*
+ * 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.noop;
+
+import java.util.Collections;
+import java.util.Set;
+
+import org.rhq.core.domain.measurement.AvailabilityType;
+import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+import org.rhq.core.pluginapi.inventory.ResourceContext;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
+
+/**
+ * Class that servers as discovery and component class
+ * The discovery will on purpose never discover a resource
+ * @author Heiko W. Rupp
+ */
+public class NoopComponent implements ResourceComponent, ResourceDiscoveryComponent {
+ @Override
+ public void start(ResourceContext context) throws Exception {
+ // nothing to do
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+
+ @Override
+ public AvailabilityType getAvailability() {
+ return null;
+ }
+
+ /**
+ * Discovery method, that on purpose always returns an empty set of discovered resources.
+ * This plugin is meant as base for plugins that
+ * @param context the discovery context that provides the information to the component that helps it perform its
+ * discovery
+ *
+ * @return An empty set
+ * @throws Exception in the case something is sooo wrong ..
+ */
+ @Override
+ public Set<DiscoveredResourceDetails> discoverResources(
+ ResourceDiscoveryContext context) throws Exception {
+ return Collections.emptySet();
+ }
+}
diff --git a/modules/plugins/noop/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/noop/src/main/resources/META-INF/rhq-plugin.xml
new file mode 100644
index 0000000..721f737
--- /dev/null
+++ b/modules/plugins/noop/src/main/resources/META-INF/rhq-plugin.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<plugin name="No-op"
+ displayName="Abstract NO-OP plugin"
+ description="Abstract plugin supporting concrete plugins that don't want java-agent support"
+ package="org.rhq.plugins.noop"
+ xmlns="urn:xmlns:rhq-plugin">
+</plugin>
\ No newline at end of file
diff --git a/modules/plugins/pom.xml b/modules/plugins/pom.xml
index d101f55..350c11e 100644
--- a/modules/plugins/pom.xml
+++ b/modules/plugins/pom.xml
@@ -53,14 +53,14 @@
<type>test-jar</type>
<scope>test</scope>
</dependency>
-
+
<dependency>
<groupId>${rhq.groupId}</groupId>
<artifactId>test-utils</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
-
+
<!--
TODO: This is a fix for the Javac bug requiring annotations to be available when compiling dependent classes.
It is fixed in JDK 6.
@@ -93,7 +93,8 @@
<!-- core plugins -->
<module>platform</module>
<module>jmx</module>
- <module>rhq-agent</module>
+ <module>rhq-agent</module>
+ <module>noop</module>
</modules>
<profiles>
commit 1656f7c935fe1b9aedd3807d12b3ac635f3ab530
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jul 12 10:17:13 2012 +0200
BZ 74168 - allow to deploy descriptor-only "agent" plugins.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/AgentPluginScanner.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/AgentPluginScanner.java
index c787943..cbed5c8 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/AgentPluginScanner.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/AgentPluginScanner.java
@@ -24,6 +24,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
+import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.sql.Connection;
@@ -34,6 +35,8 @@ import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
import javax.sql.DataSource;
import javax.transaction.TransactionManager;
@@ -94,8 +97,14 @@ public class AgentPluginScanner {
void registerAgentPlugins() throws Exception {
try {
for (DeploymentInfo di : this.scanned) {
- log.debug("Hot deploying agent plugin [" + di.url + "]...");
- this.agentPluginDeployer.pluginDetected(di);
+ if (di.url.getFile().endsWith("-rhq-plugin.xml")) {
+ // Create a plugin jar and deploy next time
+ createPluginJarFromDescriptorFile(di.url.getFile());
+ }
+ else {
+ log.debug("Hot deploying agent plugin [" + di.url + "]...");
+ this.agentPluginDeployer.pluginDetected(di);
+ }
}
// Register all the new plugins.
@@ -107,6 +116,53 @@ public class AgentPluginScanner {
}
}
+ /**
+ * We take a plugin descriptor and wrap it into a jar file. The original file is
+ * deleted if the wrapping succeeds.
+ * @param fileName Full path of the file to wrap.
+ */
+ private void createPluginJarFromDescriptorFile(String fileName) throws Exception {
+ if (!fileName.endsWith("-rhq-plugin.xml")) {
+ log.warn("The passed file does not end in -rhq-plugin.xml, will not process it");
+ return;
+ }
+
+ log.info("Found a plugin-descriptor at [" + fileName + "], creating a jar from it to be deployed at the next scan");
+ File descriptor = new File(fileName);
+ String name = descriptor.getName();
+ int pos = name.lastIndexOf(".xml");
+ name = name.substring(0,pos) + ".jar"; // TODO special name for those plugins?
+ String parent = descriptor.getParent();
+ JarOutputStream jos = null;
+ FileInputStream fis = null;
+ boolean success = false;
+ try {
+ jos = new JarOutputStream(new FileOutputStream(new File(parent,name)));
+ JarEntry jarEntry = new JarEntry("META-INF");
+ jos.putNextEntry(jarEntry);
+ jarEntry = new JarEntry("META-INF/rhq-plugin.xml");
+ jos.putNextEntry(jarEntry);
+ fis = new FileInputStream(descriptor);
+ int i;
+ while ((i= fis.read())>0) {
+ jos.write(i);
+ }
+ jos.flush();
+ success = true;
+ } catch (IOException e) {
+ log.error("Failed creating the plugin jar from the descriptor: " + e.getMessage());
+ throw e;
+ }
+ finally {
+ JDBCUtil.safeClose(jos);
+ JDBCUtil.safeClose(fis);
+ }
+ if (success) {
+ boolean deleted = descriptor.delete();
+ log.info("Deleted the now obsolete plugin descriptor: " + deleted);
+ }
+ }
+
void agentPluginScan() throws Exception {
// this method just scans the filesystem and database for agent plugin changes but makes
// no attempt to register them or do anything with the agent plugin deployer.
@@ -136,7 +192,7 @@ public class AgentPluginScanner {
/**
* Scans the plugin directory and updates our cache of known plugin files.
* This will purge any old plugins that are deemed obsolete.
- *
+ *
* @return a list of files that appear to be new or updated and should be deployed
*/
List<File> agentPluginScanFilesystem() {
@@ -145,7 +201,7 @@ public class AgentPluginScanner {
// get the current list of plugins deployed on the filesystem
File[] pluginJars = this.agentPluginDeployer.getPluginDir().listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
- return name.endsWith(".jar");
+ return name.endsWith(".jar") || name.endsWith("-rhq-plugin.xml");
}
});
@@ -384,10 +440,10 @@ public class AgentPluginScanner {
log.debug(message);
}
} else {
- //inform on the info level so that it's clear from the logs that the new file
+ //inform on the info level so that it's clear from the logs that the new file
//is going to be used.
log.info(message);
- }
+ }
}
} else {
log.info("Found agent plugin in the DB that we do not yet have: " + name);
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/PluginDeploymentScanner.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/PluginDeploymentScanner.java
index 05865c9..2093041 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/PluginDeploymentScanner.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/plugin/PluginDeploymentScanner.java
@@ -200,12 +200,12 @@ public class PluginDeploymentScanner implements PluginDeploymentScannerMBean {
if (listFiles == null || listFiles.length == 0) {
return; // nothing to do
}
-
for (File file : listFiles) {
File destinationDirectory;
- if (file.getName().endsWith(".jar")) {
+ boolean isJarLess = file.getName().endsWith("-rhq-plugin.xml");
+ if (file.getName().endsWith(".jar") || isJarLess ) {
try {
- if (null == AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(file.toURI().toURL())) {
+ if (!isJarLess && null == AgentPluginDescriptorUtil.loadPluginDescriptorFromUrl(file.toURI().toURL())) {
throw new NullPointerException("no xml descriptor found in jar");
}
destinationDirectory = getAgentPluginDir();
@@ -244,10 +244,18 @@ public class PluginDeploymentScanner implements PluginDeploymentScannerMBean {
log.error("Failed to set mtime to [" + new Date(file.lastModified()) + "] on file ["
+ realPluginFile + "].");
}
- log.info("Found plugin jar at [" + file.getAbsolutePath() + "] and placed it at ["
+ String tmp;
+ if (!isJarLess)
+ tmp = "jar";
+ else
+ tmp = "descriptor";
+ log.info("Found plugin " + tmp + " at [" + file.getAbsolutePath() + "] and placed it at ["
+ realPluginFile.getAbsolutePath() + "]");
}
}
+ else {
+ log.info("Found a plugin at [" + file.getAbsolutePath() + "], which is the same as the existing one. It will be ignored");
+ }
boolean deleted = file.delete();
if (!deleted) {
log.info("The plugin jar found at[" + file.getAbsolutePath()
11 years, 10 months
[rhq] 2 commits - modules/enterprise
by mike thompson
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java | 23 ++++++++--
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java | 3 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentDriftLocal.java | 4 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java | 2
10 files changed, 40 insertions(+), 4 deletions(-)
New commits:
commit 7c360f94c89a59f008065b737f6c10a01826fb61
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Wed Jul 11 08:54:21 2012 -0700
Trivial: fix incorrect imports on ConfigurationEditor
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
index 4e188dd..48df2d6 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
@@ -52,8 +52,26 @@ import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.ValuesManager;
import com.smartgwt.client.widgets.form.events.ItemChangedEvent;
import com.smartgwt.client.widgets.form.events.ItemChangedHandler;
-import com.smartgwt.client.widgets.form.fields.*;
-import com.smartgwt.client.widgets.form.fields.events.*;
+import com.smartgwt.client.widgets.form.fields.ButtonItem;
+import com.smartgwt.client.widgets.form.fields.CanvasItem;
+import com.smartgwt.client.widgets.form.fields.CheckboxItem;
+import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
+import com.smartgwt.client.widgets.form.fields.FloatItem;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.PasswordItem;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.SpacerItem;
+import com.smartgwt.client.widgets.form.fields.SpinnerItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+import com.smartgwt.client.widgets.form.fields.TextAreaItem;
+import com.smartgwt.client.widgets.form.fields.TextItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
+import com.smartgwt.client.widgets.form.fields.events.FocusEvent;
+import com.smartgwt.client.widgets.form.fields.events.FocusHandler;
import com.smartgwt.client.widgets.form.validator.CustomValidator;
import com.smartgwt.client.widgets.form.validator.FloatRangeValidator;
import com.smartgwt.client.widgets.form.validator.IntegerRangeValidator;
@@ -118,7 +136,6 @@ import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableSectionStack
import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableToolStrip;
import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableVLayout;
import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableWindow;
-import sun.net.www.content.image.png;
/**
* A SmartGWT widget for editing an RHQ {@link Configuration} that conforms to a {@link ConfigurationDefinition}.
commit c0721b423ae75cc18d6969d56a128be882b8edc0
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Tue Jul 10 08:56:48 2012 -0700
Add Swagger documentation annotations to CSV Export Reports.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
index d106ed6..373643b 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
@@ -17,7 +17,7 @@ public interface AlertDefinitionLocal {
@GZIP
@GET
@Produces({"text/csv"})
- @ApiOperation(value = "Export the AlertDefinitions in the system")
+ @ApiOperation(value = "Export the AlertDefinitions as CSV")
StreamingOutput alertDefinitions(@Context HttpServletRequest request);
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
index 00079e7..31c3df8 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
@@ -9,6 +9,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/configurationHistory")
@@ -19,5 +20,6 @@ public interface ConfigurationHistoryLocal {
@GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Configuration History data as CSV")
StreamingOutput configurationHistory(@Context HttpServletRequest request);
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
index f3da333..fed7590 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
@@ -28,6 +28,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/driftCompliance")
@@ -38,6 +39,7 @@ public interface DriftComplianceLocal {
@GZIP
@GET
@Produces({"text/csv", "application/xml"})
+ @ApiOperation(value = "Export the drift compliance data")
StreamingOutput generateReport(
@Context HttpServletRequest request,
@QueryParam("resourceTypeId") String resourceTypeId,
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
index 43f2ee1..6b046dc 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
@@ -28,11 +28,13 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/inventorySummary")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The inventory summary report")
+
public interface InventorySummaryLocal {
/**
@@ -49,6 +51,7 @@ public interface InventorySummaryLocal {
@GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Inventory Summary data as CSV")
StreamingOutput generateReport(
@Context HttpServletRequest request,
@QueryParam("resourceTypeId") String resourceTypeId,
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
index 7ee299a..1567fb5 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
@@ -30,6 +30,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/platformUtilization")
@@ -40,6 +41,7 @@ public interface PlatformUtilizationLocal {
@GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Platform utilization data as CSV")
StreamingOutput generateReport(@Context HttpServletRequest request);
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
index 606be2b..979cb29 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
@@ -11,6 +11,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/recentAlerts")
@@ -21,6 +22,7 @@ public interface RecentAlertLocal {
@GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Recent Alert data as CSV")
StreamingOutput recentAlerts(
@QueryParam("alertPriority") @DefaultValue("high,medium,low") String alertPriority,
@QueryParam("startTime") Long startTime,
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentDriftLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentDriftLocal.java
index fe2fd8b..8bea6c7 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentDriftLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentDriftLocal.java
@@ -10,14 +10,18 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/recentDrift")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The recent drift report")
public interface RecentDriftLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Recent drift data as CSV")
StreamingOutput recentDrift(
@QueryParam("categories") String categories,
@QueryParam("snapshot") Integer snapshot,
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
index 187ad1d..191623a 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
@@ -11,6 +11,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/recentOperations")
@@ -21,6 +22,7 @@ public interface RecentOperationsLocal {
@GZIP
@GET
@Produces({"text/csv"})
+ @ApiOperation(value = "Export the Recent Operations Data as CSV")
StreamingOutput recentOperations(
@QueryParam("status") @DefaultValue("inprogress,success,failure,canceled") String operationRequestStatus,
@QueryParam("startTime") Long startTime,
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
index 217920f..019a715 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
@@ -9,6 +9,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
@Path("/suspectMetrics")
@@ -19,6 +20,7 @@ public interface SuspectMetricLocal {
@GZIP
@GET
@Produces("text/csv")
+ @ApiOperation(value = "Export the Suspect Metrics data as CSV")
StreamingOutput suspectMetrics(@Context HttpServletRequest request);
}
11 years, 10 months
[rhq] 2 commits - modules/enterprise
by Heiko W. Rupp
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java | 13
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java | 2
modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/alert/test/AlertManagerBeanTest.java | 177 +++++-----
3 files changed, 116 insertions(+), 76 deletions(-)
New commits:
commit db20267b46527836cbe22b9a2138a4b70ee26885
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Jul 11 13:53:56 2012 +0200
Make AlertManagerBeanTest I18N aware.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java
index 7700e1a..ade9639 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/i18n/AlertI18NResourceKeys.java
@@ -68,7 +68,7 @@ public interface AlertI18NResourceKeys {
@I18NMessages({ @I18NMessage("Avail stays NOT UP") })
String ALERT_AVAILABILITY_DURATION_NOT_UP_SHORT = "alert.condition.availability.duration.notup.short";
- // Foo Prop > 10.0% of Baseline Mean Value
+ // Foo Prop > 10.0% of Baseline Mean Value
@I18NMessages({ @I18NMessage("{0} {1} {2} of Baseline Mean Value") /*, @I18NMessage(locale = "de", value = "") */})
String ALERT_BASELINE_MEAN = "alert.condition.baseline.mean";
@@ -137,7 +137,8 @@ public interface AlertI18NResourceKeys {
@I18NMessages({ @I18NMessage("{0} Val Chg"), @I18NMessage(locale = "de", value = "{0} Wert�nd.") })
String ALERT_METRIC_CHANGED_SHORT = "alert.condition.metric.changed.short";
- @I18NMessages({ @I18NMessage("Operation [{0}] has status=[{1}]") /*, @I18NMessage(locale = "de", value = "") */})
+ @I18NMessages({ @I18NMessage("Operation [{0}] has status=[{1}]") ,
+ @I18NMessage(locale = "de", value = "Operation [{0}] hat den Status [{1}]") })
String ALERT_OPERATION = "alert.condition.op";
@I18NMessages({ @I18NMessage("Op [{0}]={1}") /*, @I18NMessage(locale = "de", value = "") */})
@@ -188,11 +189,11 @@ public interface AlertI18NResourceKeys {
// Foo Value is Between 1.0B and 2.0B, Inclusive
@I18NMessages({ @I18NMessage("{0} Value is Between {1} and {2}, Inclusive"),
- @I18NMessage(locale = "de", value = "{0} Der Wert zwischen {1} und {2}, pauschal") })
+ @I18NMessage(locale = "de", value = "{0} Der Wert ist zwischen {1} und {2}, inklusiv") })
String ALERT_RANGE_INSIDE_INCL = "alert.condition.range.in.incl";
@I18NMessages({ @I18NMessage("{0} Between {1} - {2}, incl"),
- @I18NMessage(locale = "de", value = "{0} zwischen {1} und {2}, paus") })
+ @I18NMessage(locale = "de", value = "{0} zwischen {1} und {2}, inkl") })
String ALERT_RANGE_INSIDE_INCL_SHORT = "alert.condition.range.in.incl.short";
@I18NMessages({ @I18NMessage("{0} Value is Between {1} and {2}, Exclusive"),
@@ -228,4 +229,8 @@ public interface AlertI18NResourceKeys {
@I18NMessages({ @I18NMessage("\\ - Cond {0}: {1}\\n\\\n" + "\\ - Time: {2}\\n\\\n" + "\\ - Det: {3}\\n\\\n"),
@I18NMessage(locale = "de", value = " - Bed {0}: {1}\\n\\\n - Zeit: {2}\\n\\\n" + "\\ - Det: {3}\\n\\\n") })
String ALERT_EMAIL_CONDITION_LOG_FORMAT_SHORT = "alert.email.condition.log.format.short";
+
+ // Needed for the AlertManagerBeanTest
+ @I18NMessages({ @I18NMessage("Cond(?:ition)?"), @I18NMessage(locale = "de", value = "Bed(?:ingung)?")})
+ String ALERT_CONDITION_PATTERN = "alert.condition.pattern";
}
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/alert/test/AlertManagerBeanTest.java b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/alert/test/AlertManagerBeanTest.java
index b6fcf14..131db7d 100644
--- a/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/alert/test/AlertManagerBeanTest.java
+++ b/modules/enterprise/server/jar/src/test/java/org/rhq/enterprise/server/alert/test/AlertManagerBeanTest.java
@@ -19,40 +19,44 @@ import org.rhq.core.domain.operation.OperationRequestStatus;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.alert.AlertManagerBean;
+import org.rhq.enterprise.server.alert.i18n.AlertI18NFactory;
+import org.rhq.enterprise.server.alert.i18n.AlertI18NResourceKeys;
@Test
public class AlertManagerBeanTest {
+ private static final String TEN_PERCENT = String.format("%2.1f%%", 10d);
+ private static final String TWELVE_DOT_5_B = String.format("%2.1fB", 12.5d);
private String pretty;
public void testPrettyPrintAVAILABILITY() {
AlertCondition condition = createCondition(AlertConditionCategory.AVAILABILITY,
AlertConditionOperator.AVAIL_GOES_UP.name(), null, null, null, null);
pretty = getPrettyAlertConditionString(condition);
- assert "Availability goes UP".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UP);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Avail goes UP".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UP_SHORT);
condition = createCondition(AlertConditionCategory.AVAILABILITY, AlertConditionOperator.AVAIL_GOES_DOWN.name(),
null, null, null, null);
pretty = getPrettyAlertConditionString(condition);
- assert "Availability goes DOWN".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DOWN);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Avail goes DOWN".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DOWN_SHORT);
condition = createCondition(AlertConditionCategory.AVAILABILITY,
AlertConditionOperator.AVAIL_GOES_DISABLED.name(), null, null, null, null);
pretty = getPrettyAlertConditionString(condition);
- assert "Availability goes DISABLED".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DISABLED);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Avail goes DISABLED".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_DISABLED_SHORT);
condition = createCondition(AlertConditionCategory.AVAILABILITY,
AlertConditionOperator.AVAIL_GOES_UNKNOWN.name(), null, null, null, null);
pretty = getPrettyAlertConditionString(condition);
- assert "Availability goes UNKNOWN".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UNKNOWN);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Avail goes UNKNOWN".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_AVAILABILITY_GOES_UNKNOWN_SHORT);
}
@@ -77,9 +81,10 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.THRESHOLD, md.getDisplayName(), ">", 12.5d,
null, md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop > 12.5B".equals(pretty) : pretty;
+ String ref = String.format("Foo Prop > %2.1fB",12.5d);
+ assert ref.equals(pretty) : pretty;
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop > 12.5B".equals(pretty) : pretty;
+ assert ref.equals(pretty) : pretty;
}
public void testPrettyPrintTHRESHOLD_Calltime() {
@@ -88,17 +93,19 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.THRESHOLD, regex, ">", 12.5d, "MAX", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MAX > 12.5B with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_WITH_EXPR, "CT Prop", "MAX", ">",
+ TWELVE_DOT_5_B, regex);
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MAX > 12.5B matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_WITH_EXPR_SHORT, "CT Prop", "MAX", ">",
+ TWELVE_DOT_5_B, regex);
// no regex
condition = createCondition(AlertConditionCategory.THRESHOLD, null, ">", 12.5d, "MAX", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MAX > 12.5B".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD,"CT Prop","MAX",">", TWELVE_DOT_5_B);
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MAX > 12.5B".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_THRESHOLD_SHORT, "CT Prop", "MAX", ">",
+ TWELVE_DOT_5_B);
}
public void testPrettyPrintBASELINE() {
@@ -106,21 +113,21 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.BASELINE, md.getDisplayName(), ">", 0.10d,
"mean", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% of Baseline Mean Value".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MEAN,"Foo Prop",">", TEN_PERCENT);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% bl mean".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MEAN_SHORT,"Foo Prop",">", TEN_PERCENT);
condition = createCondition(AlertConditionCategory.BASELINE, md.getDisplayName(), ">", 0.10d, "min", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% of Baseline Minimum Value".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MIN,"Foo Prop",">", TEN_PERCENT);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% bl min".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MIN_SHORT,"Foo Prop",">", TEN_PERCENT);
condition = createCondition(AlertConditionCategory.BASELINE, md.getDisplayName(), ">", 0.10d, "max", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% of Baseline Maximum Value".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MAX,"Foo Prop",">", TEN_PERCENT);
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop > 10.0% bl max".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_BASELINE_MAX_SHORT,"Foo Prop",">", TEN_PERCENT);
}
public void testPrettyPrintCHANGE() {
@@ -128,9 +135,9 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.CHANGE, md.getDisplayName(), null, null,
null, md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop Value Changed".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CHANGED, "Foo Prop");
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop Val Chg".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_SHORT, "Foo Prop");
}
public void testPrettyPrintCHANGE_Calltime() {
@@ -139,85 +146,100 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.CHANGE, regex, "LO", 0.10d, "MIN", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MIN shrinks by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ String msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHRINKS);
+ String ref = "Calltime Metric CT Prop MIN %s by at least %2.1f%% with calltime destination matching \"some.*(reg)?ex$\"";
+ String refs = "CT Prop MIN %s by %2.1f%% matching \"some.*(reg)?ex$\"";
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MIN shrinks by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, regex, "CH", 0.10d, "MIN", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MIN changes by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_CHANGES);
+ assert String.format(ref,msg,10.0f).equals(pretty) ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MIN changes by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) ;
+
condition = createCondition(AlertConditionCategory.CHANGE, regex, "HI", 0.10d, "MIN", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_GROWS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MIN grows by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MIN grows by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
+
+ ref = "Calltime Metric CT Prop MAX %s by at least %2.1f%% with calltime destination matching \"some.*(reg)?ex$\"";
+ refs = "CT Prop MAX %s by %2.1f%% matching \"some.*(reg)?ex$\"";
condition = createCondition(AlertConditionCategory.CHANGE, regex, "LO", 0.10d, "MAX", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHRINKS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MAX shrinks by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MAX shrinks by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, regex, "CH", 0.10d, "MAX", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_CHANGES);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MAX changes by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MAX changes by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, regex, "HI", 0.10d, "MAX", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_GROWS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop MAX grows by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop MAX grows by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
+
+ ref = "Calltime Metric CT Prop AVG %s by at least %2.1f%% with calltime destination matching \"some.*(reg)?ex$\"";
+ refs = "CT Prop AVG %s by %2.1f%% matching \"some.*(reg)?ex$\"";
condition = createCondition(AlertConditionCategory.CHANGE, regex, "LO", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHRINKS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG shrinks by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG shrinks by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, regex, "CH", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_CHANGES);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG changes by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG changes by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, regex, "HI", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_GROWS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG grows by at least 10.0% with calltime destination matching \"some.*(reg)?ex$\""
- .equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG grows by 10.0% matching \"some.*(reg)?ex$\"".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
// no regex
+ ref = "Calltime Metric CT Prop AVG %s by at least %2.1f%%";
+ refs = "CT Prop AVG %s by %2.1f%%";
+
condition = createCondition(AlertConditionCategory.CHANGE, null, "LO", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_SHRINKS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG shrinks by at least 10.0%".equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG shrinks by 10.0%".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, null, "CH", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_CHANGES);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG changes by at least 10.0%".equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG changes by 10.0%".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
condition = createCondition(AlertConditionCategory.CHANGE, null, "HI", 0.10d, "AVG", md);
+ msg = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_METRIC_CALLTIME_CHANGE_GROWS);
pretty = getPrettyAlertConditionString(condition);
- assert "Calltime Metric CT Prop AVG grows by at least 10.0%".equals(pretty) : pretty;
+ assert String.format(ref,msg,10.0f).equals(pretty) : pretty ;
pretty = getShortPrettyAlertConditionString(condition);
- assert "CT Prop AVG grows by 10.0%".equals(pretty) : pretty;
+ assert String.format(refs,msg,10.0f).equals(pretty) : pretty + " \n<=> " +String.format(refs,msg,10.0f);
}
public void testPrettyPrintTRAIT() {
@@ -225,25 +247,25 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.TRAIT, md.getDisplayName(), null, null, null,
md);
pretty = getPrettyAlertConditionString(condition);
- assert "Blah Trait Value Changed".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CHANGED,"Blah Trait");
pretty = getShortPrettyAlertConditionString(condition);
- assert "Blah Trait Val Chg".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_METRIC_CHANGED_SHORT,"Blah Trait");
}
public void testPrettyPrintCONTROL() {
AlertCondition condition = createCondition(AlertConditionCategory.CONTROL, "opNameHere", null, null,
OperationRequestStatus.FAILURE.name(), null);
pretty = getPrettyAlertConditionString(condition);
- assert "Operation [opNameHere] has status=[FAILURE]".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_OPERATION,"opNameHere","FAILURE");
pretty = getShortPrettyAlertConditionString(condition);
- assert "Op [opNameHere]=FAILURE".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_OPERATION_SHORT,"opNameHere","FAILURE");
condition = createCondition(AlertConditionCategory.CONTROL, "opNameHere", null, null,
OperationRequestStatus.SUCCESS.name(), null);
pretty = getPrettyAlertConditionString(condition);
- assert "Operation [opNameHere] has status=[SUCCESS]".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_OPERATION,"opNameHere","SUCCESS");
pretty = getShortPrettyAlertConditionString(condition);
- assert "Op [opNameHere]=SUCCESS".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_OPERATION_SHORT,"opNameHere","SUCCESS");
}
public void testPrettyPrintEVENT() {
@@ -303,27 +325,27 @@ public class AlertManagerBeanTest {
AlertCondition condition = createCondition(AlertConditionCategory.RANGE, md.getDisplayName(), "<=", 1.0,
"22.2", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop Value is Between 1.0B and 22.2B, Inclusive".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_INCL,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop Between 1.0B - 22.2B, incl".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_INCL_SHORT,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
condition = createCondition(AlertConditionCategory.RANGE, md.getDisplayName(), ">=", 1.0, "22.2", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop Value is Outside 1.0B and 22.2B, Inclusive".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_INCL,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop Outside 1.0B - 22.2B, incl".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_INCL_SHORT,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
condition = createCondition(AlertConditionCategory.RANGE, md.getDisplayName(), "<", 1.0, "22.2", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop Value is Between 1.0B and 22.2B, Exclusive".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_EXCL,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop Between 1.0B - 22.2B, excl".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_EXCL_SHORT,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
condition = createCondition(AlertConditionCategory.RANGE, md.getDisplayName(), ">", 1.0, "22.2", md);
pretty = getPrettyAlertConditionString(condition);
- assert "Foo Prop Value is Outside 1.0B and 22.2B, Exclusive".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_EXCL,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
pretty = getShortPrettyAlertConditionString(condition);
- assert "Foo Prop Outside 1.0B - 22.2B, excl".equals(pretty) : pretty;
+ check(AlertI18NResourceKeys.ALERT_RANGE_OUTSIDE_EXCL_SHORT,"Foo Prop",String.format("%1.1fB",1d),String.format("%2.1fB",22.2d));
}
private String getPrettyAlertConditionString(AlertCondition condition) {
@@ -392,10 +414,23 @@ public class AlertManagerBeanTest {
}
private String extractCondition(String prettyString) {
+ String cond = AlertI18NFactory.getMessage(AlertI18NResourceKeys.ALERT_CONDITION_PATTERN); // Take i18n into account
//System.out.println(prettyString);
- Pattern pattern = Pattern.compile(" - Cond(?:ition)? 1: (.*)\n"); // short form has " - Cond 1: ...", long form has " - Condition 1: ..."
+ Pattern pattern = Pattern.compile("- " + cond + " 1: (.*)\n"); //en short form has " - Cond 1: ...", long form has " - Condition 1: ..."
Matcher matcher = pattern.matcher(prettyString);
assert matcher.find() : "could not find the condition string";
return matcher.group(1);
}
+
+ private void check(String msg) {
+ String ref = AlertI18NFactory.getMessage(msg);
+ assert ref != null : "Could not find reference message";
+ assert ref.equals(pretty) : pretty;
+ }
+
+ private void check(String msg, Object... args) {
+ String ref = AlertI18NFactory.getMessage(msg,args);
+ assert ref != null : "Could not find reference message";
+ assert ref.equals(pretty) : "Got : >>" + pretty + "<< Expect: >>" + ref + "<<";
+ }
}
commit 4bfd07c96ff3eeca302cb28871d6e7979e9035ec
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Jul 10 18:27:22 2012 +0200
Don't pass the query param in the path.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index a1645d3..e6f9335 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -252,7 +252,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
def.getUnits().getName(),def.getDisplayType().toString());
if (def.getDataType()== DataType.MEASUREMENT) {
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/metric/data/group/{groupId}/{definitionId}?hideEmpty=true");
+ uriBuilder.path("/metric/data/group/{groupId}/{definitionId}");
URI uri = uriBuilder.build(id,def.getId());
Link link = new Link("metric",uri.toString());
schedule.addLink(link);
11 years, 10 months
[rhq] Branch 'lkrejci/modular-scripting' - 4 commits - modules/enterprise
by lkrejci
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java | 27 +++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java | 67 +++++---
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java | 8
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java | 81 ++++++++++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java | 29 +++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java | 28 +--
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java | 10 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java | 11 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java | 2
modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl | 8
modules/enterprise/server/jar/src/main/resources/rest_templates/listMetricDefinitions.ftl | 38 ++++
modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl | 12 +
modules/enterprise/server/jar/src/main/resources/rest_templates/metricDefinition.ftl | 52 ++++++
20 files changed, 341 insertions(+), 46 deletions(-)
New commits:
commit 1e102dc66868f2a07d572e56a560e8c60ac93933
Merge: cdd639d fce34e6
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 15:16:16 2012 +0200
Merge remote-tracking branch 'origin/master' into lkrejci/modular-scripting
commit fce34e60ab6708ed7fc1d59f76fd78970e950aea
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Jul 10 15:10:16 2012 +0200
BZ 838686 - Provide the server status (OperationMode) to unprivileged users.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
index a6fce0c..176b72e 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
@@ -23,25 +23,14 @@ import java.util.Map;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
-import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import org.rhq.core.domain.alert.Alert;
-import org.rhq.core.domain.alert.AlertDefinition;
-import org.rhq.core.domain.criteria.AlertCriteria;
-import org.rhq.core.domain.criteria.AlertDefinitionCriteria;
-import org.rhq.core.domain.criteria.Criteria;
-import org.rhq.core.domain.criteria.ResourceCriteria;
-import org.rhq.core.domain.resource.Resource;
-import org.rhq.core.domain.resource.ResourceCategory;
-import org.rhq.core.domain.util.PageList;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerLocal;
-import org.rhq.enterprise.server.alert.AlertManagerLocal;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerLocal;
-import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.core.domain.cloud.Server;
+import org.rhq.enterprise.server.cloud.instance.ServerManagerLocal;
import org.rhq.enterprise.server.rest.domain.Status;
+import org.rhq.enterprise.server.rest.domain.StringValue;
import org.rhq.enterprise.server.system.SystemInfoManagerLocal;
/**
@@ -55,11 +44,13 @@ public class StatusHandlerBean extends AbstractRestBean implements StatusHandler
@EJB
SystemInfoManagerLocal infoMgr;
+ @EJB
+ ServerManagerLocal serverManager;
@Override
public Response getStatus(HttpHeaders httpHeaders) {
- Map<String,String> statusMap = infoMgr.getSystemInformation (caller);
+ Map<String,String> statusMap = infoMgr.getSystemInformation(caller);
Status status = new Status();
status.setValues(statusMap);
@@ -74,4 +65,11 @@ public class StatusHandlerBean extends AbstractRestBean implements StatusHandler
return builder.build();
}
+
+ @Override
+ public StringValue serverState() {
+ Server server = serverManager.getServer();
+ StringValue sv = new StringValue(server.getOperationMode().name());
+ return sv;
+ }
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
index c03244d..f149d8c 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
@@ -31,6 +31,8 @@ import com.wordnik.swagger.annotations.ApiOperation;
import org.jboss.resteasy.annotations.GZIP;
+import org.rhq.enterprise.server.rest.domain.StringValue;
+
/**
* Return some status information about the system
* @author Heiko W. Rupp
@@ -42,9 +44,15 @@ import org.jboss.resteasy.annotations.GZIP;
public interface StatusHandlerLocal {
@GZIP
- @ApiOperation(value="Retrieve the current configured state of the server along with some runtime information",
+ @ApiOperation(value="Retrieve the current configured state of the server along with some runtime information." +
+ "Caller must have MANAGE_SETTINGS to access this endpoint.",
responseClass = "Map 'values' with map of key-value pairs describing the status")
@GET
@Path("/")
Response getStatus(@Context HttpHeaders httpHeaders);
+
+ @GET
+ @Path("/server")
+ @ApiOperation(value = "Get the operation mode of this server")
+ StringValue serverState();
}
commit 12b6a37abf5be2f7dc0541563e70a8fe94590a08
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Jul 10 12:33:58 2012 +0200
Support metrics for compatible groups.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index 79012a1..615ba6c 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -55,7 +55,10 @@ import org.jboss.cache.TreeCacheMBean;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.domain.resource.group.GroupCategory;
+import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
@@ -78,6 +81,8 @@ public class AbstractRestBean {
TreeCacheMBean treeCache;
@EJB
ResourceManagerLocal resMgr;
+ @EJB
+ ResourceGroupManagerLocal resourceGroupManager;
/**
* Renders the passed object with the help of a freemarker template into a string. Freemarket templates
@@ -397,4 +402,26 @@ public class AbstractRestBean {
*/
return res;
}
+
+ /**
+ * Fetch the group with the passed id
+ *
+ * @param groupId id of the resource group
+ * @param requireCompatible Does the group have to be a compatible group?
+ * @return the group object if found
+ * @throws org.rhq.enterprise.server.rest.StuffNotFoundException if the group is not found (or not accessible by the caller)
+ * @throws IllegalArgumentException if a compatible group is required, but the found one is not a compatible one
+ */
+ protected ResourceGroup fetchGroup(int groupId, boolean requireCompatible) {
+ ResourceGroup resourceGroup;
+ resourceGroup = resourceGroupManager.getResourceGroup(caller, groupId);
+ if (resourceGroup == null)
+ throw new StuffNotFoundException("Group with id " + groupId);
+ if (requireCompatible) {
+ if (resourceGroup.getGroupCategory() != GroupCategory.COMPATIBLE) {
+ throw new IllegalArgumentException("Group with id " + groupId + " is no compatible group");
+ }
+ }
+ return resourceGroup;
+ }
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index 5fa0089..a1645d3 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -22,6 +22,8 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
+import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.GroupDefinition;
@@ -32,7 +34,6 @@ import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
import org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.resource.group.definition.GroupDefinitionManagerLocal;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionAlreadyExistsException;
import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefinitionCreateException;
@@ -41,6 +42,7 @@ import org.rhq.enterprise.server.resource.group.definition.exception.GroupDefini
import org.rhq.enterprise.server.rest.domain.GroupDefinitionRest;
import org.rhq.enterprise.server.rest.domain.GroupRest;
import org.rhq.enterprise.server.rest.domain.Link;
+import org.rhq.enterprise.server.rest.domain.MetricSchedule;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
@@ -54,9 +56,6 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
private final Log log = LogFactory.getLog(GroupHandlerBean.class);
@EJB
- ResourceGroupManagerLocal resourceGroupManager;
-
- @EJB
ResourceManagerLocal resourceManager;
@EJB
@@ -94,7 +93,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
public Response getGroup(int id, @Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
- ResourceGroup group = fetchGroup(id);
+ ResourceGroup group = fetchGroup(id, false);
GroupRest groupRest = fillGroup(group, uriInfo);
@@ -149,7 +148,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
- ResourceGroup resourceGroup = fetchGroup(id);
+ ResourceGroup resourceGroup = fetchGroup(id, false);
resourceGroup.setName(in.getName());
Response.ResponseBuilder builder;
@@ -182,7 +181,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
public Response getResources(int id, @Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
- ResourceGroup resourceGroup = fetchGroup(id);
+ ResourceGroup resourceGroup = fetchGroup(id, false);
Set<Resource> resources = resourceGroup.getExplicitResources();
List<ResourceWithType> rwtList = new ArrayList<ResourceWithType>(resources.size());
@@ -208,7 +207,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
public Response addResource(int id, int resourceId,
@Context Request request, @Context HttpHeaders headers, @Context UriInfo uriInfo) {
- ResourceGroup resourceGroup = fetchGroup(id);
+ ResourceGroup resourceGroup = fetchGroup(id, false);
Resource res = resourceManager.getResource(caller,resourceId);
if (res==null)
throw new StuffNotFoundException("Resource with id " + resourceId);
@@ -230,7 +229,7 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
public Response removeResource(int id, int resourceId,
@Context Request request, @Context HttpHeaders headers, @Context UriInfo uriInfo) {
- ResourceGroup resourceGroup = fetchGroup(id);
+ ResourceGroup resourceGroup = fetchGroup(id, false);
Resource res = resourceManager.getResource(caller,resourceId);
if (res==null)
throw new StuffNotFoundException("Resource with id " + resourceId);
@@ -241,21 +240,38 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
}
- /**
- * Fetch the group with the passed id
- * @param groupId id of the resource group
- * @return the group object if found
- * @throws StuffNotFoundException if the group is not found (or not accessible by the caller)
- */
- private ResourceGroup fetchGroup(int groupId) {
- ResourceGroup resourceGroup;
- resourceGroup = resourceGroupManager.getResourceGroup(caller, groupId);
- if (resourceGroup==null)
- throw new StuffNotFoundException("Group with id " + groupId);
- return resourceGroup;
- }
+ @Override
+ public Response getMetricDefinitionsForGroup(int id, Request request, HttpHeaders headers,
+ UriInfo uriInfo) {
+ ResourceGroup group = fetchGroup(id, true);
+
+ Set<MeasurementDefinition> definitions = group.getResourceType().getMetricDefinitions();
+ List<MetricSchedule> schedules = new ArrayList<MetricSchedule>(definitions.size());
+ for (MeasurementDefinition def : definitions) {
+ MetricSchedule schedule = new MetricSchedule(def.getId(),def.getName(),def.getDisplayName(),false,def.getDefaultInterval(),
+ def.getUnits().getName(),def.getDisplayType().toString());
+ if (def.getDataType()== DataType.MEASUREMENT) {
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/metric/data/group/{groupId}/{definitionId}?hideEmpty=true");
+ URI uri = uriBuilder.build(id,def.getId());
+ Link link = new Link("metric",uri.toString());
+ schedule.addLink(link);
+ }
+ schedules.add(schedule);
+ }
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listMetricDefinitions",schedules));
+ }
+ else {
+ GenericEntity<List<MetricSchedule>> ret = new GenericEntity<List<MetricSchedule>>(schedules) {};
+ builder = Response.ok(ret);
+ }
+ return builder.build();
+ }
private GroupRest fillGroup(ResourceGroup group, UriInfo uriInfo) {
@@ -270,10 +286,15 @@ public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLo
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/{id}");
URI uri = uriBuilder.build(group.getId());
-
Link link = new Link("edit",uri.toASCIIString());
gr.getLinks().add(link);
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/group/{id}/metricDefinitions");
+ uri = uriBuilder.build(group.getId());
+ link = new Link("metricDefinitions",uri.toASCIIString());
+ gr.getLinks().add(link);
+
return gr;
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
index ac77cce..39e0c2a 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -57,6 +57,14 @@ public interface GroupHandlerLocal {
@Context UriInfo uriInfo);
+ @GET
+ @GZIP
+ @Path("{id}/metricDefinitions")
+ @ApiOperation(value = "Get the metric definitions for the compatible group with the passed id")
+ public Response getMetricDefinitionsForGroup(@ApiParam(value = "Id of the group") @PathParam("id") int id,
+ @Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
@POST
@Path("/")
@ApiOperation(value = "Create a new group")
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 7c664b7..163d48d 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -71,10 +71,13 @@ import org.rhq.core.domain.measurement.MeasurementDataTrait;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementSchedule;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.enterprise.server.RHQConstants;
+import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerLocal;
import org.rhq.enterprise.server.measurement.util.MeasurementDataManagerUtility;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.rest.domain.Baseline;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricAggregate;
@@ -99,7 +102,11 @@ public class MetricHandlerBean extends AbstractRestBean implements MetricHandle
@EJB
MeasurementScheduleManagerLocal scheduleManager;
@EJB
+ MeasurementDefinitionManagerLocal definitionManager;
+ @EJB
ResourceManagerLocal resMgr;
+ @EJB
+ ResourceGroupManagerLocal groupMgr;
@PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
EntityManager em;
@@ -158,6 +165,56 @@ public class MetricHandlerBean extends AbstractRestBean implements MetricHandle
return builder.build();
}
+ public Response getMetricDataForGroupAndDefinition(int groupId, int definitionId, long startTime, long endTime,
+ boolean hideEmpty, Request request, HttpHeaders headers) {
+
+ if (startTime==0) {
+ endTime = System.currentTimeMillis();
+ startTime = endTime - EIGHT_HOURS;
+ }
+
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ boolean isHtml = mediaType.equals(MediaType.TEXT_HTML_TYPE);
+
+ fetchGroup(groupId,true); // Make sure the group exists and is compatible
+ MeasurementDefinition definition = definitionManager.getMeasurementDefinition(caller,definitionId);
+ if (definition==null) {
+ throw new StuffNotFoundException("There is no definition with id " + definitionId);
+ }
+
+ MeasurementAggregate aggr = dataManager.getAggregate(caller, groupId, definitionId, startTime, endTime);
+ MetricAggregate res = new MetricAggregate(definitionId, aggr.getMin(),aggr.getAvg(),aggr.getMax());
+ res.setGroup(true);
+
+ List<List<MeasurementDataNumericHighLowComposite>> listList = dataManager.findDataForCompatibleGroup(caller,
+ groupId,definitionId,startTime,endTime,60); // TODO number of points
+
+ if (listList.isEmpty()) {
+ throw new StuffNotFoundException("Data for group with id " + groupId + " and definition " + definitionId);
+ }
+ List<MeasurementDataNumericHighLowComposite> list = listList.get(0);
+ if (!listList.isEmpty()) {
+ fill(res, list,definitionId,hideEmpty,isHtml);
+ }
+
+ CacheControl cc = new CacheControl();
+ int maxAge = (int) (definition.getDefaultInterval() / 1000L)/2; // millis ; half of schedule interval
+ cc.setMaxAge(maxAge); // these are seconds
+ cc.setPrivate(false);
+ cc.setNoCache(false);
+
+ Response.ResponseBuilder builder;
+ if (isHtml) {
+ String htmlString = renderTemplate("metricData", res);
+ builder = Response.ok(htmlString,mediaType);
+ }
+ else
+ builder= Response.ok(res,mediaType);
+ builder.cacheControl(cc);
+
+ return builder.build();
+ }
+
/**
* Get the schedule for the passed schedule id
*
@@ -419,6 +476,30 @@ public class MetricHandlerBean extends AbstractRestBean implements MetricHandle
}
@Override
+ public List<MetricAggregate> getAggregatesForGroup(int groupId, long startTime, long endTime) {
+ long now = System.currentTimeMillis();
+ if (endTime==0)
+ endTime = now;
+ if (startTime==0) {
+ startTime = endTime - EIGHT_HOURS;
+ }
+ ResourceGroup group = fetchGroup(groupId, true);
+
+ Set<MeasurementDefinition> definitions = group.getResourceType().getMetricDefinitions();
+
+ List<MetricAggregate> ret = new ArrayList<MetricAggregate>(definitions.size());
+ for (MeasurementDefinition def : definitions) {
+ if (def.getDataType()==DataType.MEASUREMENT) {
+ MeasurementAggregate aggregate = dataManager.getAggregate(caller, groupId, def.getId(), startTime, endTime);
+ MetricAggregate res = new MetricAggregate(def.getId(), aggregate.getMin(),aggregate.getAvg(),aggregate.getMax());
+ res.setGroup(true);
+ ret.add(res);
+ }
+ }
+ return ret;
+ }
+
+ @Override
public Response updateSchedule(int scheduleId, MetricSchedule in,HttpHeaders httpHeaders) {
if (in==null)
throw new StuffNotFoundException("Input is null"); // TODO other type of exception
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 8ec3d82..4ea9343 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -77,7 +77,7 @@ public interface MetricHandlerLocal {
@ApiParam(value="Start time since epoch.", defaultValue = "End time - 8h") @QueryParam("startTime") long startTime,
@ApiParam(value="End time since epoch.", defaultValue = "Now") @QueryParam("endTime") long endTime,
@ApiParam("Number of buckets - currently fixed at 60") @QueryParam("dataPoints") @DefaultValue("60") int dataPoints,
- @QueryParam("hideEmpty") boolean hideEmpty,
+ @ApiParam(value = "Hide rows that are NaN only", defaultValue = "false") @QueryParam("hideEmpty") boolean hideEmpty,
@Context Request request,
@Context HttpHeaders headers);
@@ -98,10 +98,33 @@ public interface MetricHandlerLocal {
@ApiOperation("Retrieve a list of high/low/average/data aggregate for the resource")
@ApiError(code = 404, reason = NO_RESOURCE_FOR_ID)
List<MetricAggregate> getAggregatesForResource(
- @ApiParam("Resource to query") @PathParam("resourceId") int resourceId,
+ @ApiParam("Id of the resource to query") @PathParam("resourceId") int resourceId,
+ @ApiParam(value = "Start time since epoch.", defaultValue="End time - 8h") @QueryParam("startTime") long startTime,
+ @ApiParam(value = "End time since epoch.", defaultValue = "Now") @QueryParam("endTime") long endTime);
+
+ @GET
+ @Path("data/group/{groupId}")
+ @ApiOperation("Retrieve a list of high/low/average/data aggregate for the group")
+ List<MetricAggregate> getAggregatesForGroup(
+ @ApiParam("Id of the group to query") @PathParam("groupId") int groupId,
@ApiParam(value = "Start time since epoch.", defaultValue="End time - 8h") @QueryParam("startTime") long startTime,
@ApiParam(value = "End time since epoch.", defaultValue = "Now") @QueryParam("endTime") long endTime);
+ @GZIP
+ @GET
+ @Path("data/group/{groupId}/{definitionId}")
+ @ApiOperation(value = "Get the bucketized metric values for the metric definition of the group ")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+ Response getMetricDataForGroupAndDefinition(
+ @ApiParam("Id of the group to query") @PathParam("groupId") int groupId,
+ @ApiParam("Id of the metric definition to retrieve") @PathParam("definitionId") int definitionId,
+ @ApiParam(value = "Start time since epoch.", defaultValue="End time - 8h") @QueryParam("startTime") long startTime,
+ @ApiParam(value = "End time since epoch.", defaultValue = "Now") @QueryParam("endTime") long endTime,
+ @ApiParam(value = "Hide rows that are NaN only", defaultValue = "false") @QueryParam("hideEmpty") boolean hideEmpty,
+ @Context Request request,
+ @Context HttpHeaders headers);
+
+
/**
* Get information about the schedule
* @param scheduleId id of the schedule
@@ -169,7 +192,7 @@ public interface MetricHandlerLocal {
* @param point Datapoint of class NumericDataPoint
* @param headers Injected HTTP headers
* @param uriInfo Injected info about the uri
- * @return
+ * @return Created response
*/
@PUT
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
index 4f17e61..46605cf 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
@@ -25,7 +25,7 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
- * Raw data that could be used to draw a chart
+ * Metric data that could be used to draw a chart
* @author Heiko W. Rupp
*/
@XmlRootElement
@@ -39,6 +39,7 @@ public class MetricAggregate {
List<DataPoint> dataPoints;
long minTimeStamp;
long maxTimeStamp;
+ boolean isGroup = false;
public MetricAggregate() {
dataPoints = new ArrayList<DataPoint>();
@@ -127,6 +128,14 @@ public class MetricAggregate {
this.maxTimeStamp = maxTimeStamp;
}
+ public boolean isGroup() {
+ return isGroup;
+ }
+
+ public void setGroup(boolean group) {
+ isGroup = group;
+ }
+
public static class DataPoint {
long timeStamp;
Double value;
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
index 03a69bb..c88c072 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
@@ -31,7 +31,7 @@
<td>Name</td><td>${var.name}</td>
</tr>
<tr>
- <td>Id</td><td>${var.id}</td>
+ <td>Id</td><td>${var.id?c}</td>
</tr>
<tr>
<td>Category</td><td>${var.category}</td>
@@ -57,4 +57,10 @@
</tr>
</table>
<a href="/rest/1/group/${var.id?c}/resources.html">Resources</a><br/>
+ <#if (var.category?contains("compatible"))>
+ <a href="/rest/1/group/${var.id?c}/metricDefinitions.html">Metric Definitions</a><br/>
+ </#if>
+ <#if (var.dynaGroupDefinitionId >0)>
+ <a href="/rest/1/group/definition/${var.dynaGroupDefinitionId?c}.html">DynaGroup definition</a>
+ </#if>
</html>
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/listMetricDefinitions.ftl b/modules/enterprise/server/jar/src/main/resources/rest_templates/listMetricDefinitions.ftl
new file mode 100644
index 0000000..5c87d5f
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/listMetricDefinitions.ftl
@@ -0,0 +1,38 @@
+<#ftl >
+<#--
+/*
+ * 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.
+ */
+-->
+<#-- @ftlvariable name="var" type="java.util.List<org.rhq.enterprise.server.rest.domain.MetricSchedule>" -->
+<html>
+<#if (var?size>0) >
+<ul>
+ <#-- the next looks odd, but the incoming var is a list -->
+
+ <#list var as var>
+ <li>
+ <#include "metricDefinition.ftl"/>
+ </li>
+ </#list>
+
+</ul>
+<#else>
+ <strong>No Schedules have been set up</strong>
+</#if>
+<html>
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
index fdf2f88..61c9280 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
@@ -33,7 +33,13 @@
</thead>
<tr>
<#assign sched=var.scheduleId/>
- <td>ScheduleId</td><td><a href="/rest/1/metric/schedule/${sched?c}.html">${sched?c}</a></td> </tr>
+ <#if var.isGroup()>
+ <td>DefinitionId</td><td>${sched?c}</a></td>
+ <#else>
+ <td>ScheduleId</td><td><a href="/rest/1/metric/schedule/${sched?c}.html">${sched?c}</a></td>
+ </#if>
+ </tr>
+
<tr>
<td>Min</td><td>
<#if var.min?has_content>
@@ -64,7 +70,9 @@
</td>
</tr>
<tr>
- <td><a align="top" href="javascript:rhq.whisker(${sched?c},'one',400,200)">DataPoints</a></td><td>
+ <#if !var.isGroup()>
+ <td><a align="top" href="javascript:rhq.whisker(${sched?c},'one',400,200)">DataPoints</a></td><td>
+ </#if>
<table>
<thead>
<tr>
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricDefinition.ftl b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricDefinition.ftl
new file mode 100644
index 0000000..df277e1
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricDefinition.ftl
@@ -0,0 +1,52 @@
+<#ftl >
+<#--
+/*
+ * 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.
+ */
+-->
+<#-- @ftlvariable name="var" type="org.rhq.enterprise.server.rest.domain.MetricSchedule" -->
+<html>
+ <table border=1>
+ <tr>
+ <th>Name</th><th>Value</th>
+ </tr>
+ <tr>
+ <td>Id</td><td>${var.scheduleId}</td>
+ </tr>
+ <tr>
+ <td>Internal Name</td><td>${var.scheduleName}</td>
+ </tr>
+ <tr>
+ <td>Name</td><td>${var.displayName}</td>
+ </tr>
+ <tr>
+ <td>Default collection interval (ms)</td><td>${var.collectionInterval}</td>
+ </tr>
+ <tr>
+ <td>Units</td><td>${var.unit}</td>
+ </tr>
+ <tr>
+ <td>Links</td>
+ <td>
+ <#list var.links as link>
+ <li><a href="${link.href}.html">${link.rel}</a> </li>
+ </#list>
+ </td>
+ </tr>
+ </table>
+</html>
\ No newline at end of file
commit 5d33eb1fcb0114ebce98c834bc7e54e89f03eb8c
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Mon Jul 9 08:17:09 2012 -0700
Add GZIP compression to REST reports.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
index f42c190..d106ed6 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/AlertDefinitionLocal.java
@@ -7,12 +7,14 @@ import javax.ws.rs.core.*;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/alertDefinitions")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The Alert definitions report")
public interface AlertDefinitionLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
@ApiOperation(value = "Export the AlertDefinitions in the system")
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
index 1eb4d38..00079e7 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/ConfigurationHistoryLocal.java
@@ -9,12 +9,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/configurationHistory")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The configuration history report")
public interface ConfigurationHistoryLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
StreamingOutput configurationHistory(@Context HttpServletRequest request);
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
index f9dbed3..f3da333 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/DriftComplianceLocal.java
@@ -28,12 +28,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/driftCompliance")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The drift compliance report")
public interface DriftComplianceLocal {
+ @GZIP
@GET
@Produces({"text/csv", "application/xml"})
StreamingOutput generateReport(
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
index 4f09b7e..43f2ee1 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/InventorySummaryLocal.java
@@ -28,6 +28,7 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/inventorySummary")
@Local
@@ -45,6 +46,7 @@ public interface InventorySummaryLocal {
* specified to generate the details version of the report.
* @return An output stream that contains the CSV report.
*/
+ @GZIP
@GET
@Produces({"text/csv"})
StreamingOutput generateReport(
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
index cfda23a..7ee299a 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/PlatformUtilizationLocal.java
@@ -30,12 +30,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/platformUtilization")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The platform utilization report")
public interface PlatformUtilizationLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
StreamingOutput generateReport(@Context HttpServletRequest request);
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
index 6efe5b2..606be2b 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentAlertLocal.java
@@ -11,12 +11,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/recentAlerts")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The recent alerts report")
public interface RecentAlertLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
StreamingOutput recentAlerts(
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
index ec161b0..187ad1d 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/RecentOperationsLocal.java
@@ -11,12 +11,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/recentOperations")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The recent operations report")
public interface RecentOperationsLocal {
+ @GZIP
@GET
@Produces({"text/csv"})
StreamingOutput recentOperations(
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
index 78ab8a1..217920f 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/reporting/SuspectMetricLocal.java
@@ -9,12 +9,14 @@ import javax.ws.rs.core.Context;
import javax.ws.rs.core.StreamingOutput;
import com.wordnik.swagger.annotations.Api;
+import org.jboss.resteasy.annotations.GZIP;
@Path("/suspectMetrics")
@Local
@Api(basePath="http://localhost:7080/coregui/reports", value = "The suspect metrics report")
public interface SuspectMetricLocal {
+ @GZIP
@GET
@Produces("text/csv")
StreamingOutput suspectMetrics(@Context HttpServletRequest request);
11 years, 11 months
[rhq] Branch 'lkrejci/modular-scripting' - 5 commits - modules/enterprise
by lkrejci
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java | 44 --
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/InterfaceSimplifier.java | 109 +++++
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java | 38 ++
modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java | 41 ++
modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java | 185 ++++++++++
modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java | 38 +-
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java | 72 +++
modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider | 1
modules/enterprise/scripting/javascript/pom.xml | 2
modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java | 14
10 files changed, 504 insertions(+), 40 deletions(-)
New commits:
commit cdd639d3a1324412aded642a31d8eb4d88c73115
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:54:35 2012 +0200
Bumping Rhino to 1.7R4.
diff --git a/modules/enterprise/scripting/javascript/pom.xml b/modules/enterprise/scripting/javascript/pom.xml
index 11f3784..6009b1b 100644
--- a/modules/enterprise/scripting/javascript/pom.xml
+++ b/modules/enterprise/scripting/javascript/pom.xml
@@ -21,7 +21,7 @@
<dependency>
<groupId>org.mozilla</groupId>
<artifactId>rhino</artifactId>
- <version>1.7R3</version>
+ <version>1.7R4</version>
</dependency>
</dependencies>
commit 5d58a155a00362b6a5eb4e44ab69471ef577258b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:54:23 2012 +0200
Do not compile the scripts to new classes to save the permgen space.
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
index 0456e94..e8069a6 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
@@ -193,12 +193,19 @@ public class RhinoScriptEngine extends AbstractScriptEngine
}
}
+ protected Context makeContext() {
+ Context cx = super.makeContext();
+ cx.setOptimizationLevel(-1);
+ return cx;
+ }
+
private Object superDoTopCall(final Callable callable,
final Context cx, final Scriptable scope,
final Scriptable thisObj, final Object[] args) {
return super.doTopCall(callable, cx, scope, thisObj, args);
- }
+ }
+
});
}
commit 4344f843a8bdd16454def73be245ada673d259b9
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:53:34 2012 +0200
Setting the require function to NOT be sandboxed (which doesn't make much
difference) and make the require function use the correct scope so that it
can resolve all the variables defined by the script engine.
diff --git a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
index a2dca22..0456e94 100644
--- a/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
+++ b/modules/enterprise/scripting/javascript/src/main/java/org/rhq/scripting/javascript/engine/RhinoScriptEngine.java
@@ -266,7 +266,8 @@ public class RhinoScriptEngine extends AbstractScriptEngine
requireBuilder = new RequireBuilder();
setModuleSourceProvider(moduleSourceProvider);
-
+ requireBuilder.setSandboxed(false);
+
new LazilyLoadedCtor(topLevel, "JSAdapter",
"org.rhq.scripting.javascript.engine.JSAdapter",
false);
@@ -484,7 +485,7 @@ public class RhinoScriptEngine extends AbstractScriptEngine
Context cx = enterContext();
try {
cx.evaluateString(newScope, printSource, "print", 1, null);
- requireBuilder.createRequire(cx, topLevel).install(newScope);
+ requireBuilder.createRequire(cx, newScope).install(newScope);
} finally {
Context.exit();
}
commit 35bad6163b91cc55d2bee67be67f10a6aabdd4bb
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:51:40 2012 +0200
Adding a script source provider able to handle the "file://" scheme.
This is for the scripts to be able to access the file system using absolute
paths.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java
new file mode 100644
index 0000000..531222b
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/FileSystemScriptSourceProvider.java
@@ -0,0 +1,72 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.enterprise.client.script;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.Reader;
+import java.net.URI;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.scripting.ScriptSourceProvider;
+
+/**
+ * @author Lukas Krejci
+ */
+public class FileSystemScriptSourceProvider implements ScriptSourceProvider {
+
+ private static final Log LOG = LogFactory.getLog(FileSystemScriptSourceProvider.class);
+ private static final String SCHEME = "file";
+
+ @Override
+ public Reader getScriptSource(URI location) {
+ String scheme = location.getScheme();
+
+ //return early if we can't handle this URI
+ if (scheme != null && !SCHEME.equals(scheme)) {
+ return null;
+ }
+
+ String path = location.getSchemeSpecificPart();
+
+ if (scheme != null) {
+ // leave out the leading '//';
+ path = path.substring(2);
+ }
+
+ File f = new File(path);
+
+ try {
+ if (f.exists() && f.isFile() && f.canRead()) {
+ return new FileReader(f);
+ }
+ } catch (FileNotFoundException e) {
+ LOG.debug("File '" + f.getAbsolutePath() + "' seems to have disappeared while we were trying to open it.",
+ e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
index 9722a1c..a935c58 100644
--- a/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
+++ b/modules/enterprise/remoting/cli/src/main/resources/META-INF/services/org.rhq.scripting.ScriptSourceProvider
@@ -1 +1,2 @@
org.rhq.enterprise.client.script.SamplesScriptSourceProvider
+org.rhq.enterprise.client.script.FileSystemScriptSourceProvider
commit 5f651047a7d1a147de9ec3f3cabcdba195b42483
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Tue Jul 10 13:50:30 2012 +0200
Fixed the method resolution in the abstract rhq facade proxy.
The old impl assumed the simplified class implements the original interface
which is no longer true. I therefore added some auxiliary annotations on
the simplified class and methods to aid in recovering the original method
even though the simplified interface no longer implements the original
interface.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
index bd37131..5a6d1c5 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/AbstractRhqFacadeProxy.java
@@ -22,7 +22,6 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.rhq.bindings.util.InterfaceSimplifier;
-import org.rhq.core.domain.auth.Subject;
/**
* An abstract {@link InvocationHandler} to help the script users create proxies to actually call the
@@ -50,38 +49,21 @@ public abstract class AbstractRhqFacadeProxy<T extends RhqFacade> implements Inv
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- Class<?>[] interfaces = method.getDeclaringClass().getInterfaces();
- Class<?> originalClass;
- if (interfaces != null && interfaces.length > 0) {
- originalClass = interfaces[0];
- } else {
- originalClass = method.getDeclaringClass();
- }
-
- try {
- // See if this method really exists or if its a simplified set of parameters
- originalClass.getMethod(method.getName(), method.getParameterTypes());
- } catch (NoSuchMethodException e) {
- // If this was not in the original interface it must've been added in the Simplifier... add back the subject argument
- Class<?>[] origParams = method.getParameterTypes();
- Class<?>[] params = new Class<?>[origParams.length + 1];
- params[0] = Subject.class;
- System.arraycopy(method.getParameterTypes(), 0, params, 1, origParams.length);
-
- try {
- method = originalClass.getMethod(method.getName(), params);
- } catch (NoSuchMethodException e2) {
- throw new IllegalArgumentException("Method " + method + " doesn't seem to be present on the interface "
- + originalClass + " neither in its original or simplified form.");
- }
+ Method origMethod = InterfaceSimplifier.getOriginalMethod(method);
+
+ if (origMethod != null) {
+ if (InterfaceSimplifier.isSimplified(method)) {
+ // If this was not in the original interface it must've been added in the Simplifier... add back the subject argument
+ int numArgs = (null == args) ? 0 : args.length;
+ Object[] newArgs = new Object[numArgs + 1];
+ if (numArgs > 0) {
+ System.arraycopy(args, 0, newArgs, 1, numArgs);
+ }
+ newArgs[0] = getRhqFacade().getSubject();
- int numArgs = (null == args) ? 0 : args.length;
- Object[] newArgs = new Object[numArgs + 1];
- if (numArgs > 0) {
- System.arraycopy(args, 0, newArgs, 1, numArgs);
+ args = newArgs;
}
- newArgs[0] = getRhqFacade().getSubject();
- args = newArgs;
+ method = origMethod;
}
return doInvoke(proxy, method, args);
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 dc07d4e..8ce7bc6 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
@@ -18,6 +18,7 @@
*/
package org.rhq.bindings.util;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -69,6 +70,77 @@ public class InterfaceSimplifier {
}
+ /**
+ * Returns the method on the original interface that the simplified interface with given method was generated from
+ * using the {@link #simplify(Class)} method (i.e. this method is kind of reverse to the {@link #simplify(Class)}
+ * method).
+ * <p>
+ * The returned method may or may not have different signature from the supplied method - that depends on whether
+ * the {@link #simplify(Class)} simplified the method or not.
+ *
+ * @param method the potentially simplified method
+ * @return null if the method doesn't come from a simplified class, otherwise a method on the original interface
+ * that the supplied method was generated from.
+ */
+ public static Method getOriginalMethod(Method method) {
+ SimplifiedClass simplifiedClass = method.getDeclaringClass().getAnnotation(SimplifiedClass.class);
+ if (simplifiedClass == null) {
+ return null;
+ } else {
+ SimplifiedMethod simplifiedMethod = method.getAnnotation(SimplifiedMethod.class);
+ Class<?> origClass = simplifiedClass.originalClass();
+
+ if (simplifiedMethod == null) {
+ try {
+ return origClass.getMethod(method.getName(), method.getParameterTypes());
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Inconsisten interface simplification. The non-simplified method "
+ + method + " should have had a counterpart with the exact signature on the interface "
+ + origClass + " but it could not be found.", e);
+ }
+ } else {
+
+ Class<?>[] paramTypes = new Class<?>[method.getParameterTypes().length + 1];
+ paramTypes[0] = Subject.class;
+ System.arraycopy(method.getParameterTypes(), 0, paramTypes, 1, paramTypes.length - 1);
+
+ try {
+ return origClass.getMethod(method.getName(), paramTypes);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalStateException("Inconsistent interface simplification. The simplified method "
+ + method + " should have had a counterpart on the interface " + origClass
+ + " but it couldn't be found.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines whether given class is simplified or not. This method will return true for any class returned from
+ * {@link #simplify(Class)}.
+ *
+ * @param cls the class
+ * @return true if the class object was created by the {@link #simplify(Class)} method, false otherwise.
+ */
+ public static boolean isSimplified(Class<?> cls) {
+ return cls.getAnnotation(SimplifiedClass.class) != null;
+ }
+
+ /**
+ * Determines whether the method (declared on the simplified interface, i.e.
+ * <code>isSimplified(method.getDeclaringClass()</code> returns true) has been "tampered with" by the simplifier or
+ * has been left intact.
+ * <p>
+ * If you want to get the original method that the supplied method corresponds to, use
+ * the {@link #getOriginalMethod(Method)} method.
+ *
+ * @param method the potentially simplified method present on a simplified class
+ * @return true if the method's signature has been modified by the simplifier, false otherwise.
+ */
+ public static boolean isSimplified(Method method) {
+ return method.getAnnotation(SimplifiedMethod.class) != null;
+ }
+
public static Class<?> simplify(Class<?> intf) {
try {
ClassPool classPool = ClassPool.getDefault();
@@ -107,9 +179,10 @@ public class InterfaceSimplifier {
AnnotationsAttribute annotations = (AnnotationsAttribute) originalClassFile
.getAttribute(AnnotationsAttribute.visibleTag);
AnnotationsAttribute newAnnotations = copyAnnotations(annotations, constPool);
- if (newAnnotations != null) {
- newClassFile.addAttribute(newAnnotations);
- }
+
+ //add our @Simplified annotation to the new class
+ newAnnotations = addSimplifiedClassAnnotation(originalClass.getName(), newAnnotations, constPool);
+ newClassFile.addAttribute(newAnnotations);
//copy the generic signature of the class
SignatureAttribute signature = (SignatureAttribute) originalClassFile.getAttribute(SignatureAttribute.tag);
@@ -160,6 +233,9 @@ public class InterfaceSimplifier {
annotations = copyAnnotations(annotations, constPool);
if (simplify) {
+ //add the @SimplifiedMethod to the method annotations
+ annotations = addSimplifiedMethodAnnotation(annotations, constPool);
+
if (signature != null) {
//fun, we need to modify the signature, too, because we have left out the parameter
MethodSignature sig = MethodSignature.parse(signature.getSignature());
@@ -475,4 +551,31 @@ public class InterfaceSimplifier {
return bld.toString();
}
}
+
+ private static AnnotationsAttribute addSimplifiedClassAnnotation(String originalClassName,
+ AnnotationsAttribute annotations, ConstPool constPool) {
+
+ if (annotations == null) {
+ annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+ }
+
+ Annotation simplified = new Annotation(SimplifiedClass.class.getName(), constPool);
+ simplified.addMemberValue("originalClass", new ClassMemberValue(originalClassName, constPool));
+
+ annotations.addAnnotation(simplified);
+
+ return annotations;
+ }
+
+ private static AnnotationsAttribute addSimplifiedMethodAnnotation(AnnotationsAttribute annotations,
+ ConstPool constPool) {
+
+ if (annotations == null) {
+ annotations = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
+ }
+
+ annotations.addAnnotation(new Annotation(SimplifiedMethod.class.getName(), constPool));
+
+ return annotations;
+ }
}
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java
new file mode 100644
index 0000000..50edede
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedClass.java
@@ -0,0 +1,38 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to keep track of the original class on the simplified classes.
+ * Used by the {@link InterfaceSimplifier}.
+ *
+ * @author Lukas Krejci
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target(ElementType.TYPE)
+public @interface SimplifiedClass {
+ Class<?> originalClass();
+}
\ No newline at end of file
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java
new file mode 100644
index 0000000..290b807
--- /dev/null
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/util/SimplifiedMethod.java
@@ -0,0 +1,41 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a method on a simplified class as being simplified.
+ * This is to distinguish the simplified methods on a simplified class
+ * from the non-simplified methods on that class.
+ * <p>
+ * Used by the {@link InterfaceSimplifier}.
+ *
+ * @author Lukas Krejci
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target(ElementType.METHOD)
+public @interface SimplifiedMethod {
+
+}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java
new file mode 100644
index 0000000..d1042df
--- /dev/null
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/client/AbstractRhqFacadeProxyTest.java
@@ -0,0 +1,185 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * 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.bindings.client;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.bindings.util.InterfaceSimplifier;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.server.resource.ResourceManagerRemote;
+
+/**
+ * @author Lukas Krejci
+ */
+@Test
+public class AbstractRhqFacadeProxyTest {
+
+ public interface TestInterface {
+ void method();
+ }
+
+ public static class TestFacade implements RhqFacade {
+
+ private Subject subject;
+
+ @Override
+ public Subject getSubject() {
+ return subject;
+ }
+
+ public void setSubject(Subject subject) {
+ this.subject = subject;
+ }
+
+ @Override
+ public Subject login(String user, String password) throws Exception {
+ return subject;
+ }
+
+ @Override
+ public void logout() {
+ subject = null;
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ return subject != null;
+ }
+
+ @Override
+ public Map<RhqManager, Object> getScriptingAPI() {
+ EnumMap<RhqManager, Object> ret = new EnumMap<RhqManager, Object>(RhqManager.class);
+
+ for (RhqManager m : RhqManager.values()) {
+ Class<?> iface = InterfaceSimplifier.simplify(m.remote());
+ Object proxy = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class<?>[] { iface },
+ new TestProxy(this, m));
+ ret.put(m, proxy);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public <T> T getProxy(Class<T> remoteApiIface) {
+ RhqManager m = RhqManager.forInterface(remoteApiIface);
+ if (m == null) {
+ throw new IllegalArgumentException();
+ }
+
+ return remoteApiIface.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(),
+ new Class<?>[] { remoteApiIface }, new TestProxy(this, m)));
+ }
+
+ }
+
+ public static class InvocationRecord {
+ public Method method;
+ public Object[] args;
+ }
+
+ public static class TestProxy extends AbstractRhqFacadeProxy<TestFacade> {
+
+ private static List<InvocationRecord> pastInvocations = new ArrayList<InvocationRecord>();
+
+ /**
+ * @param facade
+ * @param manager
+ */
+ public TestProxy(TestFacade facade, RhqManager manager) {
+ super(facade, manager);
+ }
+
+ @Override
+ protected Object doInvoke(Object proxy, Method originalMethod, Object[] args) throws Throwable {
+ InvocationRecord inv = new InvocationRecord();
+ inv.method = originalMethod;
+ inv.args = args;
+
+ pastInvocations.add(inv);
+
+ return null;
+ }
+
+ public static List<InvocationRecord> getPastInvocations() {
+ return pastInvocations;
+ }
+
+ public static void clearPastInvocations() {
+ pastInvocations.clear();
+ }
+ }
+
+ public void testInvocationOfSimplifiedMethods() throws Exception {
+ TestProxy.clearPastInvocations();
+
+ TestFacade facade = new TestFacade();
+ Subject subject = new Subject();
+
+ facade.setSubject(subject);
+
+ Object resourceManager = facade.getScriptingAPI().get(RhqManager.ResourceManager);
+
+ Method getResource = resourceManager.getClass().getMethod("getResource", int.class);
+
+ getResource.invoke(resourceManager, 1);
+
+ Assert.assertEquals(TestProxy.getPastInvocations().size(), 1, "Unexpected number of proxy invocations");
+
+ InvocationRecord inv = TestProxy.getPastInvocations().get(0);
+
+ Assert.assertEquals(inv.method, ResourceManagerRemote.class.getMethod("getResource", Subject.class, int.class),
+ "Unexpected method invoked.");
+
+ Assert.assertEquals(subject, inv.args[0], "Unexpected subject passed to the invocation.");
+ Assert.assertEquals(inv.args[1], new Integer(1), "Unexpected resource id passed to the invocation.");
+ }
+
+ public void testProxyRobustAgainstNonSimplifiedMethods() throws Exception {
+ TestProxy.clearPastInvocations();
+
+ Class<?> iface = InterfaceSimplifier.simplify(TestInterface.class);
+
+ Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { iface },
+ new TestProxy(null, null));
+
+ Method charAt = proxy.getClass().getMethod("method");
+
+ charAt.invoke(proxy);
+
+ Assert.assertEquals(TestProxy.getPastInvocations().size(), 1, "Unexpected number of proxy invocations");
+
+ InvocationRecord inv = TestProxy.getPastInvocations().get(0);
+
+ Assert.assertEquals(inv.method, TestInterface.class.getMethod("method"),
+ "Unexpected method invoked.");
+
+ Assert.assertNull(inv.args, "Unexpected number of arguments passed to the invocation.");
+ }
+}
diff --git a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
index 4312153..8c581ce 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/util/InterfaceSimplifierTest.java
@@ -243,7 +243,9 @@ public class InterfaceSimplifierTest {
Class<?> iface = InterfaceSimplifier.simplify(Annotations.class);
Annotation[] annotations = iface.getAnnotations();
- Assert.assertEquals(annotations.length, 1, "UNexpected number of annotations on the 'Annotations' class.");
+ //we add the @SimplifiedClass annotation
+ Assert.assertEquals(annotations.length, Annotations.class.getAnnotations().length + 1,
+ "Unexpected number of annotations on the 'Annotations' class.");
Annotation annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
"Unexpected annotation type on the class 'Annotations");
@@ -284,8 +286,9 @@ public class InterfaceSimplifierTest {
method = iface.getMethod("methodSimplified", int.class);
annotations = method.getAnnotations();
+ //we add the @SimplifiedMethod on the method
Assert
- .assertEquals(annotations.length, 1, "Unexpected number of annotations on the 'methodSimplified' method.");
+ .assertEquals(annotations.length, 2, "Unexpected number of annotations on the 'methodSimplified' method.");
annotation = annotations[0];
Assert.assertEquals(annotation.annotationType(), MyAnnotation.class,
@@ -314,4 +317,35 @@ public class InterfaceSimplifierTest {
.assertEquals(((MyAnnotation) annotation).parameter(), "d",
"Unexpected value of the 'parameter' of the annotation on the parameter p of 'Annotations.methodSimplified(int)'.");
}
+
+ public void testOriginalMethodRetrieval() throws Exception {
+ Class<?> iface = InterfaceSimplifier.simplify(Generics.class);
+
+ Method simplifiedMethod = iface.getMethod("typeParametersSimplified", Type.class, int.class);
+ Method origMethod = InterfaceSimplifier.getOriginalMethod(simplifiedMethod);
+
+ Assert.assertTrue(InterfaceSimplifier.isSimplified(iface),
+ "Unable to determine that the simplified interface was simplified.");
+ Assert.assertTrue(InterfaceSimplifier.isSimplified(simplifiedMethod));
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(String.class), "String class is NOT simplified.");
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(Object.class.getMethod("toString")),
+ "Object.toString() is NOT simplified.");
+
+ Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
+ "Unexpected declaring class of the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes().length, simplifiedMethod.getParameterTypes().length + 1,
+ "Unexpected number of params on the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes()[0], Subject.class,
+ "Unexpected first param of the original method.");
+
+ Method nonSimplifiedMethod = iface.getMethod("typeParameters", Type.class, int.class);
+ origMethod = InterfaceSimplifier.getOriginalMethod(nonSimplifiedMethod);
+
+ Assert.assertFalse(InterfaceSimplifier.isSimplified(nonSimplifiedMethod));
+
+ Assert.assertEquals(origMethod.getDeclaringClass(), Generics.class,
+ "Unexpected declaring class of the original method.");
+ Assert.assertEquals(origMethod.getParameterTypes(), nonSimplifiedMethod.getParameterTypes(),
+ "Unexpected params on the original method.");
+ }
}
11 years, 11 months