Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using JavaCompiler in an OSGi Bundle

I'm in the process of refactoring a Java application to use OSGi. One feature of the application is on-the-fly Java compilation using javax.tools.JavaCompiler. In the original application this process worked by feeding the compiler the existing classpath, like so.

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
String[] options = {"-classpath", System.getProperty("java.class.path")};
DiagnosticListener<JavaFileObject> listener = new DiagnosticListener<JavaFileObject>() {...};
StandardJavaFileManager fileManager = compiler.getStandardFileManager(listener, null, null);
Iterable<? extends JavaFileObject> fileObjects = fileManager.getFileObjects(sourceFile);
CompilationTask task = compiler.getTask(null, fileManager, listener, Arrays.asList(options), null, fileObjects);
task.call();

However, this won't work in an OSGi bundle since the classpath no longer contains the needed paths. In the refactored OSGi version of the application, the compiler needs access to classes that are within the same bundle as the above code, as well as classes from other bundles. How do I make the compiler aware of these classes?

I've thought of two possible solutions:

  1. Give the compiler the classloader used by the bundle containing the above code since it is aware of all the necessary classes. However, this doesn't seem like a viable solution from what I've read here and here.
  2. Build the classpath using the physical locations of the installed bundles. I've looked at org.osgi.framework.Bundle.getLocation() but I'm not sure if this would be a reliable solution. The paths I get back (at least when deploying within Eclipse) are relative and I'm not sure if they'd be safe to use across all platforms and situations.

Does option two above seem possible? Is there a better solution?

like image 674
arena_dev Avatar asked Jun 23 '11 16:06

arena_dev


People also ask

What is an OSGi bundle?

In OSGi, a single component is called a bundle. Logically, a bundle is a piece of functionality that has an independent lifecycle – which means it can be started, stopped and removed independently. Technically, a bundle is just a jar file with a MANIFEST. MF file containing some OSGi-specific headers.

How to run OSGi bundle in Eclipse?

To run your bundles, right click and choose Run as ->OSGi Framework (or debug as). You can tweak which bundles are included in the runtime configuration, and what arguments are used. You may for example want to add -console . You can also create an application for export, which will give you a config.

Why do we need OSGi?

OSGi facilitates creating and managing modular Java components (called bundles) that can be deployed in a container. As a developer, you use the OSGi specification and tools to create one or more bundles. OSGi defines the lifecycle for these bundles. It also hosts them and supports their interactions in a container.

How does OSGi work?

How does OSGi work? OSGi is a set of specifications that define a dynamic component system for Java. These specifications allow for a development model in which an application is composed of several components, and then packed into bundles. These components communicate locally and through the network via services.


1 Answers

I've created a working example on GitHub.

It is not option 1 or 2, it creates a custom JavaFileManager which looks through all bundles and retrieves their resources.

Things to take into consideration:

  • It uses the JSR 199 compiler API, but it works only on the OpenJDK/Sun compiler, the Eclipse JDT compiler seems broken in this respect.
  • I've only tested on Equinox, I haven't used any equinox specific code, so it should work on other implementations.
  • It isn't optimized, so it might be slow and/or memory hungry.
  • It does register a bundle listener so it will flush its class cache when a bundle which offers a certain package resolves or unresolves
  • It is not very deterministic for split packages, I think.
  • It uses the BundleWiring API, which was introduced in OSGi 4.3, so it won't work on older OSGi implementations of OSGi (Karaf 2.x for example)

I should mention Technology Excruciation, his example helped me along tremendously.

like image 58
Frank Lee Avatar answered Oct 01 '22 03:10

Frank Lee