Calling Eclipse/OSGi Plug-in APIs from outside OSGi – part three

Previously I have tried to explain how it’s possible to call Eclipse APIs from outside Eclipse. It was all a bit painful.

I decided to automate the process as much as possible. I’ve now got a “Run in OSGI” API which enables you to write code like this:


package com.macrobug.testOutside;

import com.macrobug.osgiWrapper.OsgiWrapper;
import com.macrobug.testinside.IResults;
import com.macrobug.testinside.MyAPI;

public class Main {

  public static String resultsHolder;
  
  public static void main(String[] args) {
    try {
      OsgiWrapper.runInOsgi(MyRunnable.class.getName(),"/Users/adriantaylor/Desktop/testInsideExport/eclipse","com.macrobug.testinside");
      System.out.println("Results holder is "+resultsHolder);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  
  public static class MyRunnable implements Runnable {
    public void run() {
      MyAPI myapi = new MyAPI();
      IResults results = myapi.doFirstApi();
      resultsHolder = results.getResults();
    }
  }
}

Note that MyAPI is an API exposed by a normal Eclipse plug-in, whilst this code is running in a normal Java VM outside of Eclipse.

The code works by creating two new class loaders. Firstly, it fabricates an entire new Eclipse plug-in which depends upon the packages you want. It steals the class loader from that plug-in; this class loader has access to the APIs in those packages. It then creates a new class loader which delegates some requests to that class loader, but also allows access to whatever classes can be loaded via the normal means.

Finally it asks that new class loader to load the runnable, and then runs it.

The code is here if you’re interested.

But I’m not sure I’ll ever use it, or advocate its use to my customers. It’s still just too horrendous. The point of this whole exercise is to allow my customers, who may have arbitrarily complex Java systems involving their own wacky class loaders (e.g. in a Java web serving environment) to embed my code and use my APIs, even if I use OSGi internally. But this solution won’t work for that: it will only work where the existing system is simple.

So I am still not sure what to do. I really, really, want to use OSGi inside my projects but that makes it virtually impossible to embed them into an existing complex Java world. It’s daft.

3 Responses to “Calling Eclipse/OSGi Plug-in APIs from outside OSGi – part three”

  1. Peter Kriens says:

    The OSGi is developing RFC 132, which is addressing this issue.

    http://www.osgi.org/Specifications/Drafts

    Also take a look at the embedding felix pages on the Felix website.

    http://felix.apache.org/site/launching-and-embedding-apache-felix.html

    The felix model was the role model for the OSGi RFC 132 specification.

  2. Thanks Peter!

    Another interesting link, linked from the Felix page, is this:
    http://code.google.com/p/transloader/
    This was written for the exact purpose I am describing. It takes a tree of objects loaded by one ClassLoader and makes a copy which will work in another ClassLoader. Remarkable!

    Meanwhile I’ll have a look into RFC 132 and Felix.

  3. So, upon consideration of Felix (and therefore presumably RFC132) here’s how I’d need to do it.

    Summary of the problem: I have a simple Java ‘library’ which wishes to use OSGi internally to load and use plug-ins. That library has an API which may be used by any number of driver ‘applications’, from a simple command-line tool up to arbitrarily complex existing Java web systems. (Not forgetting Eclipse GUIs).

    If my understanding is correct of that ’embedding Felix’ page: with Felix, my ‘library’ would NOT be an OSGi plug-in. It would be a normal Java JAR. That means the driver applications (except Eclipse) could access its APIs freely without any nasty class loading shenanigens. Great! Of course, to embed this into Eclipse, I’d have to create an extra plug-in which wrapped this normal JAR. That’s fine.

    Within this JAR, it could create an embedded Felix OSGi framework to load the plug-ins it needs. It would do this using Felix.

    That all sounds great, but I have three reservations.

    Firstly, I’m not familiar with any aspects of OSGi other than simple plug-in loading, and I appear to do that in an Eclipse-specific way. I use org.eclipse.core.runtime.Platform.getExtensionRegistry().getExtensionPoint(…).getConfigurationElements() and then IConfigurationElement.createExecutableExtension in order to instantiate the plug-ins I want. I suspect if I were to switch to another OSGi framework, I’d have to learn to do things a different way since these are Eclipse-specific APIs, and OSGi in general seems much more focused around service provision. Still, I’m sure there’s a way, and it’s my problem :-)

    Secondly, the general advice I see from OSGi experts is “make everything an OSGi bundle!” In the above scenario, I’d actually be taking something which is currently a bundle – my library – and making it a plain old Java library (POJL?) However, I think I’ve come to the conclusion that there’s no other way I can expose the APIs to people outside of OSGi, without using things like ‘transloader’, so that’s no surprise.

    Finally, though: this presents a problem when I want my system to be used as an Eclipse plug-in. It means that I’d have an embedded plug-in system inside my plug-in, and I wouldn’t be able to use any of Eclipse’s extensive facilities to create and distribute those plug-in plug-ins. (For example, update sites, the update manager, maybe even PDE to create the plug-ins, etc.) That’s unacceptable, and I’d need to find a solution.

    I’m hoping that when I find the solution to the first point, and work out the generic OSGi APIs I should be using, it will transpire that there is some ‘extension registry’ interface which is implemented by both Equinox (inside Eclipse) and Felix (outside Eclipse). The user of my library will have to pass some instance of this interface to the library when creating it. If they’re working inside Eclipse, that’s easy to obtain; if they’re working outside, they’ll have to initialise Felix themselves. Not ideal but not awful. I’m not sure if it’s even possible yet! (The plug-ins themselves could hopefully use Import-Package, and would therefore get the main ‘library’ code from the Eclipse plug-in or the Felix ‘system bundle’ depending on what was available).

    In short: it looks like this could solve the problem, but I’ll need to do some research into non-Equinox-specific ways of loading plug-ins and then find out if those ways can also be used within Equinox. And then find out what other problems pop up!

    Thanks again Peter for the useful comment.