HowTo: Quickly resolve what an Sling/OSGi bundle needs.

30 10 2012

Resolving dependencies for an OSGi bundle can be hard at times, especially if working with legacy code. The sure-fire way of finding all the dependencies is to spin the bundle up in an OSGi container, but that requires building the bundle and deploying it. Here is a quick way of doing it with maven, that may at first sound odd.

If your building your bundle with maven, you will be using the BND tool via the maven-bundle-plugin. This analyses all the byte code that is going into the bundle to work out what will cross over the class-loader boundary. BND via the maven-bundle-plugin has a default import rule of ‘*’. ie import everything. If you are trying to control which dependencies are embedded, which are ignored and which are imported, this can be a hinderance. Strange though it sounds, if you remove it life will be easier. BND will immediately report everything that it needs to import that can’t be imported. It will refuse to build which is a lot faster than generating a build that won’t deploy. The way BND reports is also useful. It tells you exactly what it can’t find and this gives you a list of packages to import, ignore or embed. Once you think you have your list of package imports down to a set that you expect to come from other bundles in your container, turn the ‘*’ import back on and away you go.

In maven that means editing the pom.xml eg:

...
 <plugin>
   <groupId>org.apache.felix</groupId>
   <artifactId>maven-bundle-plugin</artifactId>
   <version>2.3.6</version>
   <extensions>true</extensions>
   <configuration>
     <instructions>
       <Import-Package>
         <!-- add ignore packages before the * as required eg. !org.testng.annotations, -->
         * <!-- comment the * out to cause BND to report everything its not been told to import -->
       </Import-Package>
       <Private-Package>
         <!-- add packages that you want to appear as raw classes in the jar as private packages Note, they dont have to source code in the project, they can be anywhere on the classpath for the project, but be careful about resources eg org.apache.sling.commons.cache.infinispan.* -->
       </Private-Package>
       <DynamicImport-Package>sun.misc.*</DynamicImport-Package>
       true</Embed-Transitive>

           <!-- embed dependencies (by artifact ID, including transitives if Embed-Transitive is true) that you dont want exposed to OSGi -->
       </Embed-Dependency>
     </instructions>
   </configuration>
 </plugin>

The OSGi purists will tell us that it’s heresy to embed anything but sometimes with legacy systems it’s just too painful to deal with the classloader issues.

There is probably a better way of doing this, if so, do tell.


				




HowTo: add more than one source directory into a maven build

29 07 2009

There are more project builds that use code generators to convert model files or interface descriptions into code and associated resources. The normal approach is to generate the code every time and place it in the …./target area of the build so that its gets built duging maven’s compile phase. However there are times when you want to generate the code rarely, and have it form part of the code base. If this is the case, then putting it in the man source tree, with all the hand crafted code can be dangerous and confusing. Ideally generated code, even if you keep it for a long time, should really go in its own source tree. eg …/src-generated.

The problem is, by default, maven only allows one source tree per project, and building a jar just for the generated code base might be too painful. There is no standard pom way of adding further source trees into the main maven build, but there is the maven build-helper-maven-plugin, that can amongst other things, inject additional source and resource locations into the build. The example below adds generated sources, tests and resources into a standard build.

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>build-helper-maven-plugin</artifactId>
 <version>1.4</version>
 <executions>
   <execution>
     <id>add-wsdl-source</id>
   <phase>generate-sources</phase>
   <goals>
     <goal>add-source</goal>
   </goals>
   <configuration>
     <sources>
       <source>${basedir}/src-generated/src</source>
     </sources>
   </configuration>
 </execution>
 <execution>
   <id>add-wsdl-test-source</id>
   <phase>generate-sources</phase>
   <goals>
     <goal>add-test-source</goal>
   </goals>
   <configuration>
     <sources>
       <source>${basedir}/src-generated/test</source>
     </sources>
   </configuration>
 </execution>
 <execution>
   <id>add-wsdl-resource</id>
   <phase>generate-sources</phase>
   <goals>
     <goal>add-resource</goal>
   </goals>
   <configuration> 
     <resources>
       <resource>
         <directory>${basedir}/src-generated/resources</directory>
         <targetPath>target/classes</targetPath>
       </resource>
     </resources>
   </configuration>
 </execution>
 </executions>
 </plugin>