Tuesday, December 15, 2015

A new interpreter for EASE (3): Loading native classes

In this tutorial we will teach our interpreter to load all kinds of classes from Eclipse and dedicated URLs. So we will have a closer look on the classloader, but don't worry - it will not get too complicated.

Our BeanShell interpreter is already capable of calling java code, so we only need to make sure that its classloader will have access to all desired resources.

Read all tutorials from this series.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online. 

Step 1: Access java classes from eclipse

Our BeanShell interpreter already accepts calls to JRE classes like
java.lang.System.out.println("Hello world");
However if we try to access eclipse classes like
org.eclipse.jface.resource.JFaceColors
    Sourced file: inline evaluation of: ``org.eclipse.jface.resource.JFaceColors;'' :
    Class or variable not found: org.eclipse.jface.resource.JFaceColors
We get an exception indicating that the class cannot be found. This is a classloader issue as the interpreter uses the bundle classloader from our org.eclipse.ease.lang.beanshell bundle. We could add dependencies to jface to solve the problem, but how would we introduce dependencies to all plug-ins that might be used by a customer?

Fortunately eclipse has a solution for this. We may allow plug-ins to access classes from all exported packages out there without  adding a dedicated dependency by defining
Eclipse-BuddyPolicy: global
in the MANIFEST.MF file. This comes with a penalty on class loading performance but allows to access all available classes.

Add this setting to the manifest of org.eclipse.ease.lang.beanshell. Afterwards switch to the BeanShellEngine and add following line to setupEngine():
fInterpreter.setClassLoader(getClass().getClassLoader());
This line changes the classloader of the BeanShell with the one from the org.eclipse.ease.lang.beanshell bundle.

Now go and try to load some eclipse classes:
org.eclipse.jface.resource.JFaceColors
    Class Identifier: org.eclipse.jface.resource.JFaceColors

Step 2: Load classes from external URLs

EASE allows to register external jar files and make them available to scripting. This mechanism  is provided by a module - a concept we will introduce in the next tutorial. For now we will make all necessary preparations. EASE provides a generic classloader with all necessary functionality. Go to your BeanShellEngine and modify the code as follows:
public class BeanShellEngine extends AbstractScriptEngine {

 private DelegatingJarClassLoader fClassLoader;

 @Override
 protected boolean setupEngine() {
  fInterpreter = new Interpreter();

  fClassLoader = new DelegatingJarClassLoader(getClass().getClassLoader());
  fInterpreter.setClassLoader(fClassLoader);

  [...]
 }

 @Override
 public void registerJar(final URL url) {
  if (fClassLoader != null)
   fClassLoader.registerURL(url);
 }
}
The DelegatingClassLoader will take care of registered URLs. For everything else its parent classloader will be used. We cannot test this currently, so you need to trust me on this until the next tutorial.

No comments:

Post a Comment