views:

552

answers:

1

I'm using some off-the-shelf OSGi bundles in my application and would like to repackage them together with additional packages that are not yet OSGi compatible into a new bundle.

Case in point is EclipseLink, which is available as several OSGi bundles, most of which are optional, depending on what you want to do. I want to pick those bundles that are relevant for me, add database drivers (for example the MySQl JDBC connector) and repackage them into a new bundle that is easier to deploy.

I'm using the maven-bundle-plugin from Apache Felix. I set up a new Maven project without source code, added the four eclipselink and the mysql connector as dependencies and tried the following:

  • use the <Embed-Dependency> and <Embed-Transitive> instructions to include all dependencies in one bundle. Problem: Optional dependencies from the eclipselink bundles (for example, javax.mail.internet) become required as the plugin rewrites the manifest. The original bundles contain "resolution=optional" in the manifest and thus work well without.
  • use the manifest goal of the plugin and a jar-with-dependencies assembly, but that gives me basically the same result, only with more work.
  • used the bundleall goal of the plugin, which is not quite what I want, because it creates separate bundles again. Even worse, because now these bundles don't have their dependencies inside.

I'm going to face similar issues with Struts 2. I'm not going to be obsessive about this, and just go with a whole bunch of separate third-party bundles, but if I can package them more neatly, I would really like to. I'm aware that a point of OSGi is modularity, so creating big bundles kind of defeats that, but I feel that if your modules are tightly coupled anyway, you might as well put them into a single bundle.

Of course, I could manually tweak the manifests, but I definitely don't want to.

+1  A: 

As omerkudat says, this is probably not a good practice to encourage, but as you have your reasons, this is a way you could do a poor-man's merge.

Assuming you are handling the OSGi manifest yourself, you only really need to get all the classes from the bundles and jars into the target/classes directory before the package phase.

You can do this with either of the dependency plugin's unpack-dependencies or unpack goals. I'd use the unpack-dependencies if you want to process all the project dependencies (or those following a certain naming patter or in a certain groupId) and the unpack goal if you want to have fine control over the artifacts to be unpacked (at the expense of a verbose POM). I'll assume unpack in my example. Each unpack is output to the project's outputDirectory (i.e. target/classes).

Note this will overwrite duplicate artifacts from each package in the order they're downloaded, so the manifests will be clobbering each other. To ensure your artifacts are managed correctly, I would bind the unpack goal to an early phase so that your src/main/resources are copied on top of the unpacked contents and not overwritten. In the sample below this phase is generate-resources, so it will happen after your local compile. If you need to overwrite any of the classes, use an earlier phase to unpack the dependencies such as generate-sources

My sample below unpacks the contents of junit-3.8.1 and commons-io 1.4 (just the first two dependencies I had declarations for) into target/classes before the project's resources are copied there. Note that the versions are defined in my dependencies section. If you haven't got the bundles/jars declared as dependencies you'll need to declare the version in the artifactItem as well.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>unpack</id>
      <phase>generate-resources</phase>
      <goals>
        <goal>unpack</goal>
      </goals>
      <configuration>
        <artifactItems>
          <artifactItem>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <overWrite>false</overWrite>
            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
          </artifactItem>
          <artifactItem>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <overWrite>false</overWrite>
            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
          </artifactItem>
        </artifactItems>
        <overWriteReleases>false</overWriteReleases>
        <overWriteSnapshots>true</overWriteSnapshots>
      </configuration>
    </execution>
  </executions>
</plugin>
Rich Seller
Hm, OK, this could work. Thanks. Not exactly beautiful, but I guess that's what I asked for.
Hanno Fietz
If all of your dependencies need to be bundled you can use the unpack-dependencies goal instead (http://maven.apache.org/plugins/maven-dependency-plugin/examples/unpacking-project-dependencies.html). This involves less ugly configuration, but it's still a hack
Rich Seller