Question

I am facing a problem that is a combination of "plain" Java, Equinox, and the communication between the two. I have read other related questions (here, here, and there as well as other Web sites, such as this one and that one) but could not find a satisfying (or working!) solution.

I have a set of plug-ins: P1, P2, and P3. P1 export some classes, which are used by P2. P2 additionally exports other classes and interfaces, which are used by P3. In particular, P2 defines and exports interface MyInterface and its implementation class MyInterfaceImpl implements MyInterface. P3 is an application and, thus, contains a class Launcher implements IApplication and defines the public Object start(IApplicationContext) method. Everything compiles fine. When I run the Launcher as an Eclipse application from Eclipse, it runs fine. The launcher uses MyInterface and MyInterfaceImpl fine.

Now, I programmatically run my application using the following (simple) code, which seems to be a "okay" way of running an Equinox/Eclipse application, according to various posts:

void callApplication() {
    final String[] args =
        new String[] {
            "-application",
            "Launcher",
            ... };
    EclipseStarter.run(args, null);
}

Again, this piece of code works fine and I can "see" my application run and produce its expected results.

Now, here comes my problem: I would like to obtain an instance of MyInterfaceImpl built by my application, running in Equinox, back inside the "outer" Java code. My naïve solution was to return this object in the public Object start(IApplicationContext) method and modify my calling code as follows:

MyInterface callApplication() {
    final String[] args =
        new String[] {
            "-application",
            "Launcher",
            ... };
    return (MyInterface) EclipseStarter.run(args, null);
}

But this naïve solution does not work. I receive a java.lang.ClassCastException: MyInterfaceImpl cannot be cast to MyInterface. Thinking of it, it makes sense because, at the end of callApplication(), I have two "versions" of the pairs {MyInterface, MyInterfaceImpl}: one from the "outer" Java code, loaded by the JVM class-loader, and another from Equinox, loaded by Equinox class-loader. I modified my code to print the class-loaders of the returned object and of MyInterface:

final Object o = EclipseStarter.run(args, null);
System.out.println(o.getClass().getClassLoader());
System.out.println(MyInterface.class.getClassLoader());

and, indeed, I obtained:

org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader@7e66458c[P2:1.5.0(id=413)]
sun.misc.Launcher$AppClassLoader@1efc3d2

Note that the class-loader of the returned object is said to come from P2, as expected, because P2 is the plug-in that defines and exports MyInterface and MyInterfaceImpl.

I tried different ways to get "compatible" pairs of {MyInterface, MyInterfaceImpl} but with no luck so far (I always get the same java.lang.ClassCastException: MyInterfaceImpl cannot be cast to MyInterface):

  • I tried setting the properties -Dorg.osgi.framework.bootdelegation=* -Dorg.osgi.framework.system.packages.extra=MyInterface,MyInterfaceImpl.

  • I also tried intercepting the DefaultClassLoader in the start() method.

So, my question is: Is there a way for a Java program, which uses some classes defined in a project, which is also a plug-in and which export these classes, to exchange instances of these classes with an instance of Eclipse started programmatically? Thanks!

Was it helpful?

Solution

Ah, this one is tricky. I would suggest rearchitecting your application and use plugins for everything, but that does not answer your question.

Now, what you want to do is first, delete the interface definition form P2 and add it to your original (non-OSGi) JAR, preferably in a package on its own, let's say com.example.api. Then, add -Dorg.osgi.framework.system.packages.extra=com.example.api; also add imports in P2 and P3 for that package.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top