How to Include Third-Party Jars in Your Component (Aspire 2)

From wiki.searchtechnologies.com
Jump to: navigation, search

For Information on Aspire 3.1 Click Here

NOTE: If you need to call a small number of methods from a JAR file, you may find Calling out to a 3rd Party JAR from Groovy useful.

The process of including third-party JARs into your bundle is probably the most difficult and tricky part of creating new OSGi bundles.

The issue is that OSGi likes to know all of the java packages (note: packages, not classes) that your bundle requires, as well as all of the java packages that your bundle produces.

There is an automatic tool which scans through all of your code looking for packages that it requires (including requirements of all of your embedded JARs). This tool is the Felix Maven Plugin the documentation of which can be found here. Note that the aspire-stage-archetype automatically uses this tool, and that Maven automatically downloads it and installs it when building your component.

The maven-bundle-plugin does a lot of useful things (creates the manifest, copies JARs, copies classes, constructs the JAR, etc.), so it's an essential part of the build process.

The problem is that the plug-in will return errors whenever there appears to be a mismatch - i.e. a package required by your code which is not imported. This is, unfortunately, a very common occurrence because many programmers are very lazy about their "import" statements and the code included in their distributions. The upshot of all this is that the plugin will often say "your package needs this" when, in fact, it doesn't (primarily because it is unreachable code).

How do you know that it is safe to ignore these "improperly required" packages? Basically, if your component passes its unit tests, then you are guaranteed to not need any of the additional packages stated by the bundle program.

Preparations

Generally, the aspire-stage-archetype sets everything up automatically for you, so things should go (relatively) smoothly. But first check the following in your project's "pom.xml" file:

  1. The "aspire-test" dependency has scope=provided.
  2. The "JUnit" dependency has scope=provided.
  3. The "maven-bundle-plugin" plugin is listed in your pom.xml as one of the plug-ins in the <build> section of your pom.xml file.
  4. That the plugin/configuration/instructions section of your maven-bundle-plugin contains sections for Bundle-Activator, Private-Package, and Import-Package.
  5. Make sure it says <packaging>bundle</packaging>. This goes after the <artifactId> tag.

General Rules

  • If you are using classes or interfaces that are shared with other Aspire components:
    • The other component must be a “provided” dependency to your component
    • The Java packages containing those classes must be inside “ImportPackages” in your POM
  • If you are using classes or interfaces from 3rd-party JARs which are NOT shared
    • The 3rd-party JARs should be “compiled” dependencies
    • The packages should be listed inside “ImportPackages”, but with a “!” (not sign) to indicate that they are NOT imported
    • You should use “embed dependencies” in your POM

Your Third-Party JARs Should be Specified as Maven Dependencies

Your third-party JARs should be specified as dependencies of your project in the "dependency" section of your Maven pom.xml file. Eclipse actually provides quite a nice editor for specifying dependencies in a pom.xml file. The scope for all dependent JARs must be "compile" and cannot be "provided" (only Aspire services and Aspire components can have "provided" dependencies in Aspire).

This means, of course, that you will need to know the "groupId" and the "artifactId" for all of your third-party JARs. This information can usually be located by browsing your third-party JAR's web site, or one of the public repository centers:

However, if it is impossible to locate a maven repository which contains your third-party JAR, then you will need to load the JAR into your local repository manually. This can be done with a Maven command, for example:

 mvn install:install-file -DgroupId={JAR's group-id} -DartifactId={JAR's artifact-id} -Dversion={JAR's version number} -Dfile={filename/location of the JAR to load} -DgeneratePom=true -Dpackaging=jar

For example:

 mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.0.1B -Dfile=jta-1.0.1B.jar -DgeneratePom=true -Dpackaging=jar

And then make sure that the JAR's groupId and artifactId (and version) are specified as a dependency in your pom.xml file.

If the Third Party JAR Is Not in a Public Repository

You can download the third-party JAR from the owner's website, and then install it into the Search Technologies repository. This is done using the following command:

 mvn deploy:deploy-file -DgroupId={GROUPID} -DartifactId={ARTIFACTID} -Dversion={VERSIONNUM} -Dpackaging=jar -Dfile={PATH-TO-JAR-FILE} -DrepositoryId=stPublic -Durl=http://repository.searchtechnologies.com/artifactory/simple/community-public/

This makes building our components which depend on these third-party JAR files easier to build.

For example, I added the ROME libraries to our repository using the following:

 mvn deploy:deploy-file -DgroupId=rome -DartifactId=rome -Dversion=1.0 -Dpackaging=jar -Dfile=rome-1.0.jar -DrepositoryId=stPublic -Durl=http://repository.searchtechnologies.com/artifactory/simple/community-public/

And now, anyone building the RSS feeder will no longer have to manually install the ROME libraries onto their own local repository. Instead, Maven will automatically down load it from the Search Technologies repository.

Creating the Bundle

Now we're ready to try and create the bundle. Note that it is almost guaranteed to return errors which will need to be corrected. Do not fret.

Run the Maven command (with the working directory set to your component's directory) to create your bundle:

 mvn clean package

NOTE: Make sure you see the following in your output:

 [INFO] [bundle:bundle]

If not, this means that your <packaging> is not correct. See "preparations" above.

To build without running the unit tests (typically much faster):

 mvn -Dmaven.test.skip=true clean package

Embedding Dependencies

If you get the following error, you will need to go through the following procedure to include your third-party JARs into your bundle:

 [ERROR] Error building bundle com.searchtechnologies:aspire-extract-text:bundle:0.0.1-SNAPSHOT : Unresolved references to...

This process can be time-consuming and frustrating, so be warned.

1. First, embed dependencies of the JARs which are specified in your POM as dependencies. For example:

 <Embed-Dependency>tika</Embed-Dependency>
 <Embed-Transitive>true</Embed-Transitive> 

In the above example, "tika" is the artifact name of the dependency that I want to embed. The above should be added to the plugin/configuration/instructions section of the maven-bundle-plugin in your project's pom.xml file.

If you have multiple dependencies to include, specify them all separated by commas.

If you have lots and lots of dependencies, you could say "*" for Embed-Dependency. For example:

 <Embed-Dependency>*</Embed-Dependency>
 <Embed-Transitive>true</Embed-Transitive> 

2. Then, run the "mvn clean package" command again (you'll probably want to run "mvn -Dmaven.test.skip=true clean package" to skip the unit tests).

3. Once you've done this, all dependencies are included. However, there will still likely be many java packages which can not be found by the maven bundle plug-in.

If maven didn't include the JARs for these packages automatically, then clearly these packages are not required for actual execution of the software. What happens is that these packages represent code in your dependent libraries which import other packages that are never used (spurious imports or unreachable code).

Unfortunately, the maven bundle plug-in doesn't know the difference. It believes that this code is required, and so it prints an ERROR.

The only way around this is to list these packages in the <Import-Package> directive, with a bang (i.e. "!") before them. You can use wildcards to eliminate whole groups of them.

For example:

  <Import-Package>com.searchtechnologies.aspire.services,org.osgi.framework,org.w3c.dom,
           org.osgi.service.http,org.osgi.service.cm,javax.*,org.w3c.dom,org.w3c.dom.*,
           org.osgi.util.tracker,org.xml.*,org.osgi.service.log,
      !android.*,!com.sun.*,!dalvik.*,!junit.*,!org.apache.avalon.*,!org.apache.log,
      !org.apache.lucene.*,!org.apache.tools.*,!org.apache.xml.resolver*,
      !org.apache.xmlbeans.impl.*,!org.gjt.*,!org.jaxen.*,!org.relaxng.*,!sun.io</Import-Package>

Notice that the packages listed at the beginning of <Import-Package> are ones that I actually need. It's the ones at the end (with the "!") which are called out by the 3rd party libraries, but which (according to the Maven dependency hierarchy) are never actually needed for normal operations.

Go through all of the unresolved references and add each one to the end of the Import-Package statement with the "!" character before it. Notice that you can lump some of them together with wildcards if that's helpful.

After some time, and lots of executing of the "mvn" command, eventually should have a complete Import-Package statement and your bundle will be created.

Verifying Your Bundle

Open up the bundle .JAR file and verify the following:

  1. Does it have your ComponentFactory.xml at the top level?
  2. Does it have a META-INF/MANIFEST.MF file?
    • Are the contents of the manifest correct? (Imports, Exports, etc.)
  3. Does it have your classes in it?
  4. Does it have the com.searchtechnologies.aspire.framework classes in it? (It should.)
  5. Does it have the com.searchtechnologies.aspire.services classes in it? (It should not.)
  6. Does it contain the 3rd party JARs at the top level, embedded in your JAR?

Problems You May Discover Running Your Bundle in Apache Felix

First, try running your bundle in the application.

If your bundle installs but does not start, try installing the bundle by hand within the Felix console. For example:

 -> install file:bundles/aspire/...
 .
 .
 .
 -> start 12
 org.osgi.framework.BundleException: Unresolved constraint in bundle 12: package; (package=javax.jms)

Notice the unresolved constraint above. This is something which will need to be added to your POM as an imported package. Change your pom.xml file, add the package to the Import-Package statement, re-build your bundle JAR, and then try again.

If You Have the Following Execution Errors when Starting Your Bundle

>> Provider for javax.xml.transform.TransformerFactory cannot be found

>> Provider for javax.xml.parsers.SAXParserFactory cannot be found

>> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl cannot be cast to javax.xml.transform.sax.SAXTransformerFactory

>> loader constraint violation: when resolving method "javax.xml.transform.sax.SAXResult.<init>(Lorg/xml/sax/ContentHandler;)V" the class loader (instance of org/apache/felix/framework/searchpolicy/ModuleImpl$ModuleClassLoader) of the current class, com/searchtechnologies/aspire/framework/SimpleDigester, and the class loader (instance of <bootloader>) for resolved class, javax/xml/transform/sax/SAXResult, have different Class objects for the type org/xml/sax/ContentHandler used in the signature

>> Caused by: javax.xml.transform.TransformerException: Source object passed to {0} has no contents. at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(Unknown Source)

>> Class not found: x.y.z.CLASS

>> Unable to load class

If you have thes execution errors when starting your bundle, add the following to your <Import-Package> description:

 javax.xml.parsers,javax.xml.transform,javax.xml.transform.sax,org.xml.sax,javax.xml.transform.dom,javax.xml.namespace,org.xml.sax.ext,

NOTE: These should be included even if the maven-bundle-plugin reports warnings that they're not needed.

Version Errors

For a currently unknown reason, Maven sometimes adds a "version" to the packaged imports when it builds the bundle. You'll typically notice this when your bundle fails to load with the following exception:

 com.searchtechnologies.aspire.services.AspireException: Unable to install bundle file:/C:/Users/sdenny/.m2/repository/com/searchtechnologies/aspire-fast-qpl/1.2-SNAPSHOT/aspire-fast-qpl-1.2-SNAPSHOT.jar from repository MavenRepository[C:\Users\sdenny/.m2/repository].
       at com.searchtechnologies.aspire.application.ComponentBundleInfo.load(ComponentBundleInfo.java:156)
       at com.searchtechnologies.aspire.application.RepositoryManager.load(RepositoryManager.java:170)
       at com.searchtechnologies.aspire.application.AspireApplicationImpl.loadFactoryBundle(AspireApplicationImpl.java:394)
       at com.searchtechnologies.aspire.application.ComponentManagerImpl.registerComponents(ComponentManagerImpl.java:238)
       .
       .
       at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
       at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
      Caused by: org.osgi.framework.BundleException: Unresolved constraint in bundle com.searchtechnologies.aspire-fast-qpl [24]: Unable to resolve 24.0: missing requirement [24.0] osgi.wiring.package; (&(osgi.wiring.package=groovy.lang)(version>=2.0.0))
 
       at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:3826)
       at org.apache.felix.framework.Felix.startBundle(Felix.java:1868)
       at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:944)
       at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:931)
       at com.searchtechnologies.aspire.application.ComponentBundleInfo.load(ComponentBundleInfo.java:152)
       ... 42 more

If you examine the bundle (using winrar or similar)and look in the META-INF/MANIFEST.MF file, you'll see the imports have a version specified:

 Import-Package: com.searchtechnologies.aspire.services,com.searchtechnologies.aspire.services.xml,groovy.lang;version="2.0",javax.xml.parsers,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,org.osgi.framework,org.osgi.service.log,org.osgi.util.tracker,org.w3c.dom,org.w3c.dom.bootstrap,org.w3c.dom.ls,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers

Unless the packages were exported with this version, then the import cannot find them. In Aspire we don't add versions to exported packages, so the easiest way to work around this issue is to suppress the addition of the version number to the import. This can be done by specifying the import in the following manner:

 <Import-Package>
   groovy.lang;version=!,

Other Hints and Tricks

If you make changes to the framework, you'll likely need to re-run "mvn install" on the framework before those changes will be visible in your project.

You can find more information here:

http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html

Also, check out the BND documentation page for additional options (Maven bundle plugin is based on BND, so the syntax for specifying imports/exports is the same):

http://www.aqute.biz/Bnd/Bnd

Specifically, look at the "Import Package" section:

http://www.aqute.biz/Bnd/Format#import-package

APPENDIX: Packages provided by the System Bundle

Note that you will need to import these with <Import-Package> if you use them, or if any of your 3rd party JARs use them.

javax.accessibility,version=1.6.0
javax.activation,version=1.6.0
javax.activity,version=1.6.0
javax.annotation,version=1.6.0
javax.annotation.processing,version=1.6.0
javax.crypto,version=1.6.0
javax.crypto.interfaces,version=1.6.0
javax.crypto.spec,version=1.6.0
javax.imageio,version=1.6.0
javax.imageio.event,version=1.6.0
javax.imageio.metadata,version=1.6.0
javax.imageio.plugins.bmp,version=1.6.0
javax.imageio.plugins.jpeg,version=1.6.0
javax.imageio.spi,version=1.6.0
javax.imageio.stream,version=1.6.0
javax.jws,version=1.6.0
javax.jws.soap,version=1.6.0
javax.lang.model,version=1.6.0
javax.lang.model.element,version=1.6.0
javax.lang.model.type,version=1.6.0
javax.lang.model.util,version=1.6.0
javax.management,version=1.6.0
javax.management.loading,version=1.6.0
javax.management.modelmbean,version=1.6.0
javax.management.monitor,version=1.6.0
javax.management.openmbean,version=1.6.0
javax.management.relation,version=1.6.0
javax.management.remote,version=1.6.0
javax.management.remote.rmi,version=1.6.0
javax.management.timer,version=1.6.0
javax.naming,version=1.6.0
javax.naming.directory,version=1.6.0
javax.naming.event,version=1.6.0
javax.naming.ldap,version=1.6.0
javax.naming.spi,version=1.6.0
javax.net,version=1.6.0
javax.net.ssl,version=1.6.0
javax.print,version=1.6.0
javax.print.attribute,version=1.6.0
javax.print.attribute.standard,version=1.6.0
javax.print.event,version=1.6.0
javax.rmi,version=1.6.0
javax.rmi.CORBA,version=1.6.0
javax.rmi.ssl,version=1.6.0
javax.script,version=1.6.0
javax.security.auth,version=1.6.0
javax.security.auth.callback,version=1.6.0
javax.security.auth.kerberos,version=1.6.0
javax.security.auth.login,version=1.6.0
javax.security.auth.spi,version=1.6.0
javax.security.auth.x500,version=1.6.0
javax.security.cert,version=1.6.0
javax.security.sasl,version=1.6.0
javax.sound.midi,version=1.6.0
javax.sound.midi.spi,version=1.6.0
javax.sound.sampled,version=1.6.0
javax.sound.sampled.spi,version=1.6.0
javax.sql,version=1.6.0
javax.sql.rowset,version=1.6.0
javax.sql.rowset.serial,version=1.6.0
javax.sql.rowset.spi,version=1.6.0
javax.swing,version=1.6.0
javax.swing.border,version=1.6.0
javax.swing.colorchooser,version=1.6.0
javax.swing.event,version=1.6.0
javax.swing.filechooser,version=1.6.0
javax.swing.plaf,version=1.6.0
javax.swing.plaf.basic,version=1.6.0
javax.swing.plaf.metal,version=1.6.0
javax.swing.plaf.multi,version=1.6.0
javax.swing.plaf.synth,version=1.6.0
javax.swing.table,version=1.6.0
javax.swing.text,version=1.6.0
javax.swing.text.html,version=1.6.0
javax.swing.text.html.parser,version=1.6.0
javax.swing.text.rtf,version=1.6.0
javax.swing.tree,version=1.6.0
javax.swing.undo,version=1.6.0
javax.tools,version=1.6.0
javax.transaction,version=1.6.0
javax.transaction.xa,version=1.6.0
javax.xml,version=1.6.0
javax.xml.bind,version=1.6.0
javax.xml.bind.annotation,version=1.6.0
javax.xml.bind.annotation.adapters,version=1.6.0
javax.xml.bind.attachment,version=1.6.0
javax.xml.bind.helpers,version=1.6.0
javax.xml.bind.util,version=1.6.0
javax.xml.crypto,version=1.6.0
javax.xml.crypto.dom,version=1.6.0
javax.xml.crypto.dsig,version=1.6.0
javax.xml.crypto.dsig.dom,version=1.6.0
javax.xml.crypto.dsig.keyinfo,version=1.6.0
javax.xml.crypto.dsig.spec,version=1.6.0
javax.xml.datatype,version=1.6.0
javax.xml.namespace,version=1.6.0
javax.xml.parsers,version=1.6.0
javax.xml.soap,version=1.6.0
javax.xml.stream,version=1.6.0
javax.xml.stream.events,version=1.6.0
javax.xml.stream.util,version=1.6.0
javax.xml.transform,version=1.6.0
javax.xml.transform.dom,version=1.6.0
javax.xml.transform.sax,version=1.6.0
javax.xml.transform.stax,version=1.6.0
javax.xml.transform.stream,version=1.6.0
javax.xml.validation,version=1.6.0
javax.xml.ws,version=1.6.0
javax.xml.ws.handler,version=1.6.0
javax.xml.ws.handler.soap,version=1.6.0
javax.xml.ws.http,version=1.6.0
javax.xml.ws.soap,version=1.6.0
javax.xml.ws.spi,version=1.6.0
javax.xml.xpath,version=1.6.0
org.ietf.jgss,version=1.6.0
org.omg.CORBA,version=1.6.0
org.omg.CORBA.DynAnyPackage,version=1.6.0
org.omg.CORBA.ORBPackage,version=1.6.0
org.omg.CORBA.TypeCodePackage,version=1.6.0
org.omg.CORBA.portable,version=1.6.0
org.omg.CORBA_2_3,version=1.6.0
org.omg.CORBA_2_3.portable,version=1.6.0
org.omg.CosNaming,version=1.6.0
org.omg.CosNaming.NamingContextExtPackage,version=1.6.0
org.omg.CosNaming.NamingContextPackage,version=1.6.0
org.omg.Dynamic,version=1.6.0
org.omg.DynamicAny,version=1.6.0
org.omg.DynamicAny.DynAnyFactoryPackage,version=1.6.0
org.omg.DynamicAny.DynAnyPackage,version=1.6.0
org.omg.IOP,version=1.6.0
org.omg.IOP.CodecFactoryPackage,version=1.6.0
org.omg.IOP.CodecPackage,version=1.6.0
org.omg.Messaging,version=1.6.0
org.omg.PortableInterceptor,version=1.6.0
org.omg.PortableInterceptor.ORBInitInfoPackage,version=1.6.0
org.omg.PortableServer,version=1.6.0
org.omg.PortableServer.CurrentPackage,version=1.6.0
org.omg.PortableServer.POAManagerPackage,version=1.6.0
org.omg.PortableServer.POAPackage,version=1.6.0
org.omg.PortableServer.ServantLocatorPackage,version=1.6.0
org.omg.PortableServer.portable,version=1.6.0
org.omg.SendingContext,version=1.6.0
org.omg.stub.java.rmi,version=1.6.0
org.omg.stub.javax.management.remote.rmi,version=1.6.0
org.osgi.framework,version=1.4.0
org.osgi.framework.hooks.service,version=1.4.0
org.osgi.service.packageadmin,version=1.2.0
org.osgi.service.startlevel,version=1.1.0
org.osgi.service.url,version=1.0.0
org.osgi.util.tracker,version=1.3.3
org.w3c.dom,version=1.6.0
org.w3c.dom.bootstrap,version=1.6.0
org.w3c.dom.css,version=1.6.0
org.w3c.dom.events,version=1.6.0
org.w3c.dom.html,version=1.6.0
org.w3c.dom.ls,version=1.6.0
org.w3c.dom.ranges,version=1.6.0
org.w3c.dom.stylesheets,version=1.6.0
org.w3c.dom.traversal,version=1.6.0
org.w3c.dom.views,version=1.6.0
org.w3c.dom.xpath,version=1.6.0
org.xml.sax,version=1.6.0
org.xml.sax.ext,version=1.6.0
org.xml.sax.helpers,version=1.6.0