we would like to integrate the Jython interpreter into our Eclipse RCP based solution and we need to access the OSGi bundles (e.g. everything from Activator.getContext().getBundles() ) from there.
How could I pass these bundles to a Jython PythonInterpreter object python.path property, so I can import these classes from the Jython code?
(I get similar error messages when I try to import packages e.g.:
from org.eclipse.chemclipse.msd.converter.chromatogram import ChromatogramConverterMSD)
ImportError: cannot import name ChromatogramConverterMSD
I managed to find a solution after working on this issue for days.
First we need a ClassLoader which can load OSGi bundles if necessary:
public class JythonClassLoader extends ClassLoader {
private final Bundle bundle;
public JythonClassLoader(ClassLoader parent, Bundle bundle) {
super(parent);
this.bundle = bundle;
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// System.out.println("findClass " + name);
try {
return super.findClass(name);
} catch(ClassNotFoundException e) {
Class<?> loadClass = bundle.loadClass(name);
if(loadClass != null)
return loadClass;
throw e;
}
}}
Then the PythonInterpreter needs to know about this ClassLoader. The best is to set up the environment for the PythonInterpreter before. The following class does the job:
public class JythonEnvironment {
private final Bundle bundle;
public JythonEnvironment(Bundle bundle) {
this.bundle = bundle;
}
public PySystemState getPySystemState() {
Properties properties = new Properties();
String jythonPath = net.openchrom.thirdpartylibraries.jython.Activator.getJythonPath();
properties.setProperty("python.home", jythonPath);
properties.setProperty("python.cachedir.skip", "true");
Properties systemProperties = System.getProperties();
PySystemState.initialize(systemProperties, properties, new String[]{""});
PySystemState pySystemState = new PySystemState();
JythonClassLoader classLoader = new JythonClassLoader(getClass().getClassLoader(), bundle);
pySystemState.setClassLoader(classLoader);
return pySystemState;
}
public PythonInterpreter getInterpreter() {
return new PythonInterpreter(null, getPySystemState());
}
public PythonInterpreter getInterpreter(OutputStream outStream, OutputStream errStream) {
PythonInterpreter interpreter = new PythonInterpreter(null, getPySystemState());
interpreter.setErr(outStream);
interpreter.setOut(errStream);
return interpreter;
}}
The JythonEnvironment class needs to know about the bundle. The best if that one can be received through the Activator.
JythonEnvironment environment = new JythonEnvironment(Activator.getDefault().getBundle());
Hope this answer will save time, if someone else needs to integrate Jython into an Eclipse RCP based solution.
Related
I'm using the following code to dynamically load a class from an external package:
Object plugin = null;
String packageName = "unipd.elia.deltapluginaccelerometersensor";
String className = "unipd.elia.deltapluginaccelerometersensor.AccelerometerPlugin";
try {
PackageManager packageManager = getPackageManager();
ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
DexFile df = new DexFile(appInfo.sourceDir);
ClassLoader cl = getClassLoader();
Class classToInvestigate = df.loadClass(className, cl);
plugin = classToInvestigate.newInstance();
} catch (Exception e) {
System.out.println("EXCEPTION");
}
The problem is the following: if the class I try to load is a "simple" class everything goes fine, but if it implents an interface the loading fails. Here's an example of a class that fails to load:
package unipd.elia.deltapluginaccelerometersensor;
public class AccelerometerPlugin implements IDeltaPlugin {
#Override
public String getPluginName() {
return "Accelerometer Sensor Plugin";
}
#Override
public String getPluginID() {
return "delta.plugin.accelerometersensor";
}
#Override
public String getPluginAuthor() {
return "Me";
}
}
And the interface it implements:
public interface IDeltaPlugin {
String getPluginName();
String getPluginID();
String getPluginAuthor();
}
Basically the call to loadClass() returns null if the class implements the interface. Remove the "implements" statement and it loads fine.
What am I doing wrong?
Well, turns out the problem was that the interface in question was in an Android Library project shared by both the app that was doing the loading and the apk where the class to load was defined.
The ClassLoader then gets confused because it finds two copies of the same interface in the classpath.
The solution: turn the shared library into a JAR library and in the plugin apk import it in "provided" (rather than "compile") mode. This way only one copy of the interface exists (the one in the apk that is doing the classloading)
I created a maven project with JADE framework as a dependency but this framework requires different commands to execute the jar than ordinary java applications.
Build:
javac –classpath <JADE-classes> Class_name.java
Run:
java –classpath <JADE-classes> jade.Boot <agent-local-name>:<fully-qualified-agent-class>
Where <fully-qualified-agent-class> is package_name.Class_name
or
java –cp lib\jade.jar jade.Boot [options] [AgentSpecifierlist]
Is it possible to build a runnable jar using maven plugins so I just type java -jar myjar.jar instead of the command above?
Would mvn eclipse:eclipse command change build parameters of the eclipse project after editing the pom.xml file?
There isn't any such plugin available for JADE because it is not widely used framework and anyone hasn't bothered to develop a plugin for it. But there is a workaround to run it the conventional way, but this would only work if you already know your <fully-qualified-agent-class> names. what you can do is write a class that extends Thread and from that Thread's run() method invoke the JADE framework agent by passing the <fully-qualified-agent-class> as arguments. Refer to an example below.
jadeBootThread.java
public class jadeBootThread extends Thread {
private final String jadeBoot_CLASS_NAME = "jade.Boot";
private final String MAIN_METHOD_NAME = "main";
//add the <agent-local-name>:<fully-qualified-agent-class> name here;
// you can add more than one by semicolon separated values.
private final String ACTOR_NAMES_args = "Agent1:com.myagents.agent1";
private final String GUI_args = "-gui";
private final Class<?> secondClass;
private final Method main;
private final String[] params;
public jadeBootThread() throws ClassNotFoundException, SecurityException, NoSuchMethodException {
secondClass = Class.forName(jadeBoot_CLASS_NAME);
main = secondClass.getMethod(MAIN_METHOD_NAME, String[].class);
params = new String[]{GUI_args, ACTOR_NAMES_args};
}
#Override
public void run() {
try {
main.invoke(null, new Object[]{params});
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
ex.printStacktrace();
}
}
}
Now you can invoke this thread from your main method or any other way by creating runnable jar file with eclipse plugin etc.
A better solution for this :
public class Example03 {
public static void main(String[] args){
String host;
int port;
String platform = null; //default name
boolean main = true;
host = "localhost";
port = -1; //default-port 1099
Runtime runtime = Runtime.instance();
Profile profile = null;
AgentContainer container = null;
profile = new ProfileImpl(host, port, platform, main);
// create container
container = runtime.createMainContainer(profile);
// create the agents
try {
AgentController agent1 = container.createNewAgent(
"Agent1",
routing.TransportAgent.class.getName(),
args);
AgentController agent2 = container.createNewAgent(
"Agent2",
routing.TransportAgent.class.getName(),
args);
// create the GUI
AgentController rma = container.createNewAgent("rma", "jade.tools.rma.rma", null);
// start the agents
agent1.start();
agent2.start();
rma.start();
} catch(StaleProxyException e) {
throw new RuntimeException(e);
}
}
}
I'm pretty new to groovy, and scripting in java generally, and I really
hope there is a simple solution for my problem.
In our application, the users can execute groovy scripts which they write
themselves, and we need to control what those scripts can and can not do.
I read a lot of stuff about sandboxing groovy, but either I am looking at
wrong places or I am overlooking the obvious.
To make it simple, I have a small example which demonstrates the problem.
This is my class loader which should prevent java.lang.System from being
loaded and available to scripts:
public class MyClassLoader extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("java.lang.System")) {
throw new ClassNotFoundException("Class not found: " + name);
}
return super.loadClass(name);
}
}
And this is a simple program that tries to call System.currentTimeMillis():
public static void main(String[] args) {
String code = "java.lang.System.currentTimeMillis();";
ClassLoader classLoader = new MyClassLoader();
Thread.currentThread().setContextClassLoader(classLoader);
GroovyShell shell = new GroovyShell();
Script script = shell.parse(code);
Object result = script.run();
log.debug(result);
}
MyClassLoader throws exceptions for java.lang.SystemBeanInfo
and java.lang.SystemCustomizer, but the code executes.
Same thing happens if I use javax.script classes:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("Groovy");
Object o = engine.eval(code);
log.debug(o);
And if I try it with JavaScript engine, it works as expected (just replace
"Groovy" with "JavaScript" in the above example).
Can anyone help me with this? BTW, I'm using groovy-all-1.8.8.jar, with
jdk1.7.0_55.
Thanks
I can recommend Groovy Sandbox for this purpose. In contrast to SecureASTCustomizer it will check if an execution is allowed dynamically at runtime. It intercepts every method call, object allocations, property/attribute access, array access, and so on - and you thus have a very fine grained control on what you allow (white-listing).
Naturally the configuration on what is allowed is very important. For example you may want to allow using Strings and use methods like substring, but probably not the execute method on String, which could be exploited with something like 'rm -R ~/*'.execute().
Creating a configuration that is really safe is a challenge, and it is more difficult the more you allow.
Downside of the Groovy Sandbox is that the code must run with the interceptor registered and you will have a performance penalty during execution.
This image [1] shows an example from a project where we used Groovy Sandbox for Groovy code entered by the user. The code is run to valide the script - so if the statement there would actually be executed as part of it, the application would have exited before I could do the screenshot ;)
Perhaps you'd be interested in using a SecureASTCustomizer in conjunction with a CompilerConfiguration. If you are concerned with security, an explicit white list might be better than a black list.
def s = new SecureASTCustomizer()
s.importsWhiteList = [ 'a.legal.Klass', 'other.legal.Klass' ]
def c = new CompilerConfiguration()
c.addCompilationCustomizers(s)
def sh = new GroovyShell(c)
Take a look at that class, it contains a lot of options that are ready to use.
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
public class SandboxGroovyClassLoader extends ClassLoader {
public SandboxGroovyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (name.startsWith("java.lang.System"))
return null;
return super.loadClass(name);
}
#Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (name.startsWith("java.lang.System"))
return null;
return super.loadClass(name, resolve);
}
static void runWithGroovyClassLoader() throws Exception {
System.out.println("Begin runWithGroovyClassLoader");
String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class<?> scriptClass = groovyClassLoader.parseClass(code);
Object scriptInstance = scriptClass.newInstance();
Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
System.out.println(result);
groovyClassLoader.close();
System.out.println("End runWithGroovyClassLoader");
}
static void runWithSandboxGroovyClassLoader() throws Exception {
System.out.println("Begin runWithSandboxGroovyClassLoader");
ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);
String code = "def hello_world() { java.lang.System.currentTimeMillis(); };";
GroovyClassLoader groovyClassLoader = new GroovyClassLoader(classLoader);
Class<?> scriptClass = groovyClassLoader.parseClass(code);
Object scriptInstance = scriptClass.newInstance();
Object result = scriptClass.getDeclaredMethod("hello_world", new Class[] {}).invoke(scriptInstance, new Object[] {});
System.out.println(result);
groovyClassLoader.close();
System.out.println("End runWithSandboxGroovyClassLoader");
}
static void runWithSandboxGroovyShellClassLoader() throws Exception {
System.out.println("Begin runWithSandboxGroovyShellClassLoader");
String code = "java.lang.System.currentTimeMillis();";
ClassLoader parentClassLoader = SandboxGroovyClassLoader.class.getClassLoader();
SandboxGroovyClassLoader classLoader = new SandboxGroovyClassLoader(parentClassLoader);
Thread.currentThread().setContextClassLoader(classLoader);
GroovyShell shell = new GroovyShell();
Script script = shell.parse(code);
Object result = script.run();
System.out.println(result);
System.out.println("End runWithSandboxGroovyShellClassLoader");
}
public static void main(String[] args) throws Exception {
runWithGroovyClassLoader();
runWithSandboxGroovyClassLoader();
runWithSandboxGroovyShellClassLoader();
}
}
Is it what you want ?
I have recently started working on OSGi framework. I have one bundle named Bundle-A. I want to call one of the methods in Bundle-A jar from my main application.
I have loaded and installed Bundle-A from my main application. Below is my code for my main application where I am installing Bundle-A.
private void initializeModelFramework() {
try {
FileUtils.deleteDirectory(new File("felix-cache"));
FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
Framework framework = frameworkFactory.newFramework(new HashMap<String, String>());
framework.start();
BundleContext bundleContext = framework.getBundleContext();
modulesNameVersionHolder.put("Bundle-A", "1.0.0");
List<Bundle> installedBundles = new LinkedList<Bundle>();
String basePath = "C:\\ClientTool\\LocalStorage";
for (Map.Entry<String, String> entry : modulesNameVersionHolder.entrySet()) {
String version = entry.getValue();
final String filename = name + Constants.DASH + version + Constants.DOTJAR;
final String localFilename = GoldenModulesConstants.FILE_PROTOCOL + basePath+ File.separatorChar + filename;
installedBundles.add(bundleContext.installBundle(localFilename));
}
for (Bundle bundle : installedBundles) {
bundle.start();// this will start bundle A
}
// After starting the Bundle-A, now I need to call one of the methods in Bundle-A
for(int i=0; i<=10; i++) {
//call processingEvents method of Bundle-A class GoldenModelFramework
}
} catch (Exception e) {
e.printStackTrace();
}
Now the Bundle-A has been started up. Below is my Activator class for Bundle-A.
public class Activator implements BundleActivator {
private static final String BUNDLE_VERSION_KEY = "Bundle-Version";
private static Logger s_logger = Logger.getLogger(Activator.class.getName());
#Override
public void start(BundleContext context) throws Exception {
final Bundle bundle = context.getBundle();
final String bundleName = bundle.getSymbolicName();
final String bundleVersion = (String) bundle.getHeaders().get(BUNDLE_VERSION_KEY);
System.out.println(bundleName+" - "+bundleVersion);
}
#Override
public void stop(BundleContext context) throws Exception {
System.out.println("Bye.!");
}
}
And below is the class I have in Bundle-A jar. I need to call processingEvents method from my above main application code as soon as Bundle-A has been started.
public class GoldenModelFramework {
private static final Logger LOGGER = Logger.getLogger(GoldenModelFramework.class.getName());
private static final long checkingAfterEveryXMinutes = 15L;
public GoldenModelFramework() {
// following the traditions
}
public static void processingEvents(final String item) {
for (BundleRegistration.HolderEntry entry : BundleRegistration.getInstance()) {
final String response = entry.getPlugin().process(item);
System.out.println(response);
}
}
}
I am not sure what's the right way to do it? I know one way is add the dependency of this Bundle-A in my main application pom.xml file as I am using maven based project. But I don't think so that's the right way to do it. Because ultimately, I am going to have some more bundles so there should be some other way around for this which I am not aware of.
Should I am supposed to use ServiceListener or ServiceTracker here? Any simple example basis on my code will help me understand much better. Thanks.
I hope the question is clear enough. I am trying to call one of the methods in Bundle-A after it has been loaded up and installed.
You have several choices:
Import the package dynamically
You can use DynamicImport-Package instead of Import-Package. In this case bundle-A does not have to be active when the main bundle starts. Although this works I do not recommend this solution as I do not like DynamicImport-Package. In this case of course bundle A has to be a dependency of the main bundle.
Using reflection
You can call the method you want with reflection like the following (draft example):
Class<GoldenModelFramework> clazz = bundleA.loadClass("GoldenModelFramework");
Method m = clazz.getMethod("processingEvents", String.class);
m.execute(null, myParam);
This is a bit better, however this solution is still a bit foggy, I would not say this is a clean code.
Using interface and OSGi service
The cleanest way would need a bit of refactoring. In this case you should create an interface and in the Activator of bundle A you should register a service based on that interface. In the main bundle you should use a service tracker to catch that service and call the method on it.
In case you really want to make your method processEvent static, the registered service object (that is based on the interface) should simply call the static method inside.
Not to have the necessity to add bundle A as a dependency to the main bundle the interface should be taken into a third bundle that is the dependency of both, the main and A bundle.
Although this solution seems to be the most complex I would suggest this one.
An example:
Create an interface and put it to a new bundle like goldenframework-api.
public interface GoldenModelFrameworkOSGi {
void processingEvents(final String item);
}
The goldenframework-api will be a dependency of main bundle and bundle-A. The main bundle will use it, while bundle-A will implement it.
Here is how bundle A implements it:
public class GoldenFrameworkOSGiImpl {
public void processingEvents(final String item) {
GoldenModelFramework.processEvents(item);
}
}
Create an Activator class in bundle-A (I will leave out your code in that activator to have less typing):
public class Activator {
private ServiceRegistration goldenFrameworkSR;
#Override
public void start(BundleContext context) {
goldenFrameworkSR = context.registerService(GoldenFrameworkOSGi.class, new GoldenFrameworkOSGi(), new HashTable());
}
#Override
public void stop(BundleContext context) {
goldenFrameworkSR.unregister();
}
}
As you know the code of Bundle-A you can cheat a bit. When bundle-A is in Active state you can be sure that the service you need is registered. However, in the future you should think in working based on events (like using a ServiceTracker). I mean this will be a bad practice :) :
ServiceReference sr = context.getServiceReference(GoldenServiceOSGi.class);
GoldenServiceOSGi gs = context.getService(sr);
gs.processEvents(...);
context.ungetService(sr);
This might solve your problem for now and you can continue with your work. However, please consider reading a book like "OSGi in Action" to have a feeling about OSGi bundle and service lifecycles so you may re-design your framework.
I have just gotten started with an Eclipse RCP application, it is basically just one of the provided "hello world" samples.
When the application boots up, I would like to look at my command-line parameters and start some services according to them. I can get the command-line parameters in IApplication.start:
public Object start(IApplicationContext context) {
String[] argv = (String[])
context.getArguments().get(IApplicationContext.APPLICATION_ARGS)));
}
But how do I get the BundleContext, so that I can register services? It does not seem to be in the IApplicationContext.
Just came across this doing a web search, and thought I'd promote the new standard OSGi R4.2 way (as provided by Equinox shipped with Eclipse 3.5). If you don't have an activator, and don't want to create one just to cache the bundle context, you can use FrameworkUtil.getBundle. Modifying the previous example:
import org.osgi.framework.FrameworkUtil;
public class ExportClassDigestApplication implements IApplication {
public Object start(IApplicationContext context) throws Exception {
context.applicationRunning();
BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
.getBundleContext();
}
}
Tricky internal way:
InternalPlatform.getDefault().getBundleContext()
could do it.
You will find an example in this class
public class ExportClassDigestApplication implements IApplication {
public Object start(IApplicationContext context) throws Exception {
context.applicationRunning();
List<ExtensionBean> extensionBeans = ImpCoreUtil.loadExtensionBeans("com.xab.core.containerlaunchers");
for (ExtensionBean bean : extensionBeans) {
ILauncher launcher = (ILauncher) bean.getInstance();
launcher.start();
}
ClassFilter classFilter = new ClassFilter() {
public boolean isClassAccepted(Class clz) {
return true;
}
};
PrintWriter writer = new PrintWriter( new File( "C:/classes.csv"));
Bundle[] bundles = InternalPlatform.getDefault().getBundleContext().getBundles();
Proper way:
Every plug-in has access to its own bundle context.
Just make sure your plug-in class overrides the start(BundleContext) method. You can then save it to a place classes in your plug-in can easily access
Note the bundle context provided to a plug-in is specific to it and should never be shared with other plug-ins.