Remember my problems to integrate OpenOffice.org Bean in an eclipse plugin ?
I just have found a way to solve them (thanks to Bogdan and Romain Guy). The
solution is to create a custom ClassLoader. Let us do it together:


The ClassLoader


The ClassLoader is able to create Class objects from their definition
(.class file). We could create a simple OOoClassLoader from the
URLClassLoader:



public class OOoClassLoader extends URLClassLoader {

public OOoClassLoader(URL[] urls) {

super(urls);

}

@Override

public Class<?> loadClass(String name) throws ClassNotFoundException {

Class clazz;

try {

clazz = findClass(name);

} catch (ClassNotFoundException e) {

clazz = super.loadClass(name);

}

return clazz;

}

@Override

protected String findLibrary(String libname) {

String libraryPath = super.findLibrary(libname);

String ooopath = "/opt/openoffice.org2.0/program/";

File libraryHandle = new File(ooopath+"/lib"+libname+".so");

if (libraryHandle.exists() && libraryHandle.canRead()) {

libraryPath = libraryHandle.getAbsolutePath();

}

return libraryPath;

}

}

In this ClassLoader, we will override the findLibrary method to look
for libraries in the <OOO_HOME>/program directory before to
look in the java.library.path. The loadClass method is
overridden to make the ClassLoader look in it's URLs before asking its
parent ClassLoader. This latter step has to be performed if the class you
want to find is already defined in your classpath.


Using the ClassLoader


Now we have to understand what the JVM do to find a class definition. Let us
consider the following snippet:


MyClass myClass = new MyClass();

The JVM will call a ClassLoader.loadClass("MyClass") method on a
particular ClassLoader. The only thing to remember is that the JVM will call
the ClassLoader that loader the class containing the code. The JVM
implements a ClassLoader in native code and loads the classes in the
classpath: this is the AppClassLoader. The class loader will first
look if it has already loaded MyClass, if not it will delegate
this task to it's parent class loader and finally will try to find the
definition and load it and throw a NoClassDefException if no
definition can be found.


Thus we will create a facade to perform the actions we want to do on the
OpenOffice.org API and we will implement this facade in a OOoLoader jar
which will make all the calls to OpenOffice.org API. The code below, is
using the OOoClassLoader this way, let us analyze it:



  1. We add the urls to OpenOffice.org Jars we want to use.

  2. We add the OOoLoader.jar file containing the code handling the
    OpenOffice.org API

  3. We load the OOoFacade class defined in the OOoLoader.jar
    file using our OOoClassLoader. Note that if the loadClass method
    wasn't overridden, the OOoFacade could have been already loaded by
    the AppClassLoader if the .class file is in the application
    classpath.


  4. We call a IOOoFacade to perform a call to OpenOffice.org API.
    The code of this class will be given some lines below.




public class Foo {

public static void main(String[] args) {

try {

URL[] urls = new URL[6];

urls[0] = new URL("file:///opt/openoffice.org2.0/program/classes/juh.jar");

urls[1] = new URL("file:///opt/openoffice.org2.0/program/classes/jurt.jar");

urls[2] = new URL("file:///opt/openoffice.org2.0/program/classes/officebean.jar");

urls[3] = new URL("file:///opt/openoffice.org2.0/program/classes/ridl.jar");

urls[4] = new URL("file:///opt/openoffice.org2.0/program/classes/unoil.jar");

File file = new File("OOoLoader.jar");

urls[5] = new URL("file://"+file.getAbsolutePath());

OOoClassLoader loader = new OOoClassLoader(urls);

System.out.println(">>> new OOoClassLoader created");

IOOoFacade ooofacade = (IOOoFacade)loader.loadClass("OOoFacade").newInstance();

System.out.println(">>> Created Object type: " + ooofacade.getClass().getName());

System.out.println(">>> OOoFacade created using the OOoClassLoader");

ooofacade.bootstrap();

System.out.println(">>> OpenOffice.org has been bootstrapped");

} catch (Exception e) {

System.err.println(">>> OpenOffice.org hasn't been bootstrapped");

e.printStackTrace();

}

}

}


Here is the code of the OOoFacade that handle the OpenOffice.org API. The
bootstrap method just creates a Frame with the OpenOffice.org Bean
inside.



public class OOoFacade implements IOOoFacade {

public OOoFacade (){

System.out.println(">>> Created using: " +

this.getClass().getClassLoader().getClass().getName());

}

public void bootstrap() throws Exception {

Runtime.getRuntime().exec("/opt/openoffice.org2.0/program/soffice -nologo -nodefault -norestore -accept=pipe,name=chef_Office;urp;StarOffice.NamingService");

Frame f = new Frame();

final OOoBean b = new OOoBean();

f.add(b, BorderLayout.CENTER);

f.pack();

f.setSize(800,600);

f.setVisible(true);

b.loadFromURL("private:factory/swriter", null);

f.validate();

f.addWindowListener(new WindowAdapter(){

public void windowClosing(java.awt.event.WindowEvent e) {

try {

// terminate the OpenOffice.org instance properly

XDesktop desktop = b.getOOoDesktop();

desktop.terminate();

// Stop the OOo connection between the bean and OOo

b.stopOOoConnection();

System.exit(0);

} catch (Exception ex) {

ex.printStackTrace();

System.exit(1);

}

};

});

}


Now there is only to put this code and ideas into a well-packaged Eclipse
plugin.


(Post originally written by Cedric Bosdonnat on the old Nuxeo blogs.)