views:

138

answers:

4

I'm using OSGi for my latest project at work, and it's pretty beautiful as far as modularity and functionality.

But I'm not happy with the development workflow. Eventually, I plan to have 30-50 separate bundles, arranged in a dependency graph - supposedly, this is what OSGi is designed for. But I can't figure out a clean way to manage dependencies at compile time.

Example: You have bundles A and B. B depends on packages defined in A. Each bundle is developed as a separate Java project.

In order to compile B, A has to be on the javac classpath.

Do you:

  1. Reference the file system location of project A in B's build script?
  2. Build A and throw the jar into B's lib directory?
  3. Rely on Eclipse's "referenced projects" feature and always use Eclipse's classpath to build (ugh)
  4. Use a common "lib" directory for all projects and dump the bundle jars there after compilation?
  5. Set up a bundle repository, parse the manifest from the build script and pull down the required bundles from the repository?

No. 5 sounds the cleanest, but also like a lot of overhead.

+2  A: 

Basically, you can use:

  • source dependency (with Eclipse's "referenced projects")
  • binary dependency (using the jar of bundle A)

But since binary dependency is much cleaner, it is also the kind of dependency best managed by a release management framework like maven.
And you can integrate maven in your Eclipse project through m2eclipse.

The Maven plugin to use would then be: maven-bundle-plugin, that you can see in action in:

Consider this more real-world example using Felix' Log Service implementation.
The Log Service project is comprised of a single package: org.apache.felix.log.impl.
It has a dependency on the core OSGi interfaces as well as a dependency on the compendium OSGi interfaces for the specific log service interfaces. The following is its POM file:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.apache.felix</groupId>
  <artifactId>org.apache.felix.log</artifactId>
  <packaging>bundle</packaging>
  <name>Apache Felix Log Service</name>
  <version>0.8.0-SNAPSHOT</version>
  <description>
    This bundle provides an implementation of the OSGi R4 Log service.
  </description>
  <dependencies>
    <dependency>
      <groupId>${pom.groupId}</groupId>
      <artifactId>org.osgi.core</artifactId>
      <version>0.8.0-incubator</version>
    </dependency>
    <dependency>
      <groupId>${pom.groupId}</groupId>
      <artifactId>org.osgi.compendium</artifactId>
      <version>0.9.0-incubator-SNAPSHOT</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <extensions>true</extensions>
        <configuration>
          <instructions>
            <Export-Package>org.osgi.service.log</Export-Package>
            <Private-Package>org.apache.felix.log.impl</Private-Package>
            <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
            <Bundle-Activator>${pom.artifactId}.impl.Activator</Bundle-Activator>
            <Export-Service>org.osgi.service.log.LogService,org.osgi.service.log.LogReaderService</Export-Service>
          </instructions>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
VonC
Binary dependencies are dangerous things to recommend. I helped a customer last week debug a class loader issue caused by having 2 copies of the same binary jar in 2 different bundles. When they tried to pass instances of objects from the binary jar from one bundle to the other there were class cast exception.It is much better to use the import/export package statements as they were intended. In the customer's case the solution was to make a bundle from the binary jar that exported the required packages. Then both customer bundles imported the packages they needed from the new bundle.
James Branigan
Hm. I was kind of hoping to avoid Mavenizing the entire project, but I may not be able to avoid it.
levand
Do you have a follow-up from this question, how did you handle it? I have a very similar problem which I solved via reinventing the Maven wheel. It took a lot of effort to avoid using Maven then ultimately I ended up with a solution that was a subset of Maven that eventually I will have to convert to Maven. The more complex a project dependencies get, the more benefits that Maven provides this coming from someone who really dislikes the complexity of it).
Chris
+5  A: 

My company has 100+ bundle projects and we use Eclipse to manage the dependencies. However, I don't recommend the "Required Plugins" approach to managing the dependencies. Your best bet is to create Plugin Projects. Export just the packages from each project that you want to be visible. Then on the import side do the following:

Open the Manifest editor

Goto the dependencies tab In the bottom left is a section called "Automated Management of Dependencies"

Add any plugins that the current plugin depends on there

Once you have code written, you can click the "add dependencies" link on that tab to auto-compute the imported packages.

If you run from Eclipse, this gets done automatically for you when you execute.

The benefits of this approach is that your built bundles are only using OSGi defined package import/export mechanism, as opposed to something from Eclipse.

If you want to learn more, I'd recommend going to this site and ordering the book. It's excellent.

http://equinoxosgi.org/

James Branigan
That is a much more practical solution (that my maven-based recommendation), integrated with Eclipse PDE. +1
VonC
+2  A: 

There is a 6th option, which I've used for several projects, which is to use a single Eclipse project (not a plugin project, but just an ordinary Java project) and put all source code in there. A build file associated with the project will simply compile all code in a single pass and subsequently create bundles out of the compiled classes (using Bnd from Ant or from the soon to be released BndTools).

This has the downside that it does not honor visibility at development and compile time, but the upside that it's a really simple development model that gives you very fast build and deploy times.

Marcel Offermans
+2  A: 

Well, do what you should have a long time before, separate implementation and API ... ok, this is not always that easy on existing systems but that model has a huge bang for your buck. Once your API is in a separate (much more stable) bundle/jar you can compile the clients and implementations against that bundle/jar.

One of the key qualities of a successful bundle is that it makes as little assumptions about the outside world as possible. This implies you do not have to compile against the bundles you run against in runtime, I have a preference to try hard to not do that. You should only compile against the bundles minimum set of dependencies. If assumptions are made they are explicit as imported packages and the use of services. Well designed OSGi systems attempt to use services for all inter-bundle communications. Not only does this model get rid of class loading issues it also makes your build setup more decoupled.

Unfortunately most code is written as libraries that have a rather wide interface because they hand code lots of the functionality that services provide out of the box like Factories and Listeners. This code has a tight relationship between implementation and API so you have to have the same on the class path during compile and in OSGi. One solution to this problem is to include this kind of code inside the bundle using it (but make sure no objects of this library leak to other bundles). A bit of extra memory consumption but it saves you from some headaches.

So with OSGi, try to create systems relying on services and compile against their service API, not an implementation bundle.

Peter Kriens