Elastic Path Commerce Development

Adding third party jars to Cortex API

Adding third party jars to Cortex API

This section describes how to add third party jars as OSGi dependencies to Cortex.

Determining if a Bundle is OSGi Compliant

Before adding a bundle to Cortex, you must first determine whether it is OSGi compliant or requires work to become so.

There are two ways to determine if a bundle is OSGi compliant. Ideally, a bundle's MANIFEST.MF provides the information required, but if it does not, loading the bundle and building the Cortex webapp can provide information on a bundle's compliance.

Determining Compliance via a Bundle's MANIFEST.MF file

The easiest way to determine if a bundle is OSGi compliant is by inspecting its META-INF/MANIFEST.MF file. Non-OSGi compliant jars don't have bundle key/value tags inside the manifest, while OSGi compliant jars do.

A non OSGi compliant dependency manifest
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: hardy
Build-Jdk: 1.5.0_20
An OSGi compliant manifest
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven Bundle Plugin
Built-By: hen
Build-Jdk: 1.5.0_22
Implementation-Build: UNKNOWN_BRANCH@r??????; 2011-11-09 22:58:07-0800
Implementation-Title: Commons Lang
Implementation-Vendor: The Apache Software Foundation
Implementation-Vendor-Id: org.apache
Implementation-Version: 3.1
Specification-Title: Commons Lang
Specification-Vendor: The Apache Software Foundation
Specification-Version: 3.1
X-Compile-Source-JDK: 1.5
X-Compile-Target-JDK: 1.5
Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt
Include-Resource: templates/release-notes.vm=src/main/resources/templa
 tes/release-notes.vm,META-INF/LICENSE.txt=LICENSE.txt,META-INF/NOTICE
 .txt=NOTICE.txt
Bnd-LastModified: 1320908300777
Export-Package: org.apache.commons.lang3.event;version="3.1",org.apach
 e.commons.lang3.mutable;version="3.1",org.apache.commons.lang3.tuple;
 version="3.1",org.apache.commons.lang3.time;version="3.1",org.apache.
 commons.lang3.concurrent;version="3.1",org.apache.commons.lang3.text.
 translate;version="3.1",org.apache.commons.lang3.text;version="3.1",o
 rg.apache.commons.lang3.math;version="3.1",org.apache.commons.lang3;v
 ersion="3.1",org.apache.commons.lang3.exception;version="3.1",org.apa
 che.commons.lang3.reflect;version="3.1",org.apache.commons.lang3.buil
 der;version="3.1"
Bundle-Version: 3.1.0
Bundle-Name: Commons Lang
Bundle-Description: Commons Lang, a package of Java utility classes fo
 r the  classes that are in java.lang's hierarchy, or are considered t
 o be so  standard as to justify existence in java.lang.
Private-Package: templates
Bundle-DocURL: http://commons.apache.org/lang/
Bundle-Vendor: The Apache Software Foundation
Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.apache.commons.lang3
Tool: Bnd-1.15.0

Determining Compliance via Console Output

Adding a non-OSGi compliant jar to Cortex causes a number of errors and exceptions at runtime, and prevents the webapp from starting up. Errors may range from BundleExceptions to error messaging reading "could not start bundle...", "unresolved requirement..." or more. An example of a runtime error due to a non-OSGi compliant jar is below:

org.osgi.framework.BundleException: Unable to resolve com.elasticpath.rest.integration.epcommerce.ep-resource-orders-epcommerce [181](R 181.0): missing requirement [com.elasticpath.rest.integration.epcommerce.ep-resource-orders-epcommerce [181](R 181.0)] osgi.wiring.package; (osgi.wiring.package=com.does.not.exist) Unresolved requirements: [[com.elasticpath.rest.integration.epcommerce.ep-resource-orders-epcommerce [181](R 181.0)] osgi.wiring.package; (osgi.wiring.package=com.does.not.exist)]
at org.apache.felix.framework.Felix.resolveBundleRevision(Felix.java:4114) ~[org.apache.felix.framework-5.2.0.jar:na]
at org.apache.felix.framework.Felix.startBundle(Felix.java:2111) ~[org.apache.felix.framework-5.2.0.jar:na]
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:977) ~[org.apache.felix.framework-5.2.0.jar:na]
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:964) ~[org.apache.felix.framework-5.2.0.jar:na]
at org.apache.felix.webconsole.internal.core.BundlesServlet.doPost(BundlesServlet.java:365) ~[na:na]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:650) [tomcat-embed-core-7.0.65.jar:7.0.65]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.felix.webconsole.internal.servlet.OsgiManager.service(OsgiManager.java:567) [org.apache.felix.webconsole-4.2.12.jar:na]
at org.apache.felix.webconsole.internal.servlet.OsgiManager$3.run(OsgiManager.java:465) [org.apache.felix.webconsole-4.2.12.jar:na]
at java.security.AccessController.doPrivileged(Native Method) [na:1.8.0_131]
at org.apache.felix.webconsole.internal.servlet.OsgiManager.service(OsgiManager.java:461) [org.apache.felix.webconsole-4.2.12.jar:na]
at org.apache.felix.http.base.internal.handler.ServletHandler.doHandle(ServletHandler.java:339) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.ServletHandler.handle(ServletHandler.java:300) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.ServletPipeline.handle(ServletPipeline.java:93) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:50) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:84) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at com.elasticpath.rest.relos.rs.authentication.springoauth2.filter.OAuth2TokenAuthenticationFilter.doFilter(OAuth2TokenAuthenticationFilter.java:81) [ep-rs-authentication-spring-oauth2-0-SNAPSHOT.jar:na]
at org.apache.felix.http.base.internal.handler.FilterHandler.doHandle(FilterHandler.java:108) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:80) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at com.elasticpath.rest.relos.rs.authentication.web.filter.HeadersRemoverFilter.doFilter(HeadersRemoverFilter.java:107) [ep-rs-authentication-resource-0-SNAPSHOT.jar:na]
at org.apache.felix.http.base.internal.handler.FilterHandler.doHandle(FilterHandler.java:108) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:80) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:84) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at com.elasticpath.rest.relos.rs.filter.CharsetEncodingFilter.doFilter(CharsetEncodingFilter.java:43) [ep-rs-jersey-integration-0-SNAPSHOT.jar:na]
at org.apache.felix.http.base.internal.handler.FilterHandler.doHandle(FilterHandler.java:108) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:80) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:84) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at com.elasticpath.rest.relos.rs.filter.ExceptionTranslatingFilter.doFilter(ExceptionTranslatingFilter.java:45) [ep-rs-jersey-integration-0-SNAPSHOT.jar:na]
at org.apache.felix.http.base.internal.handler.FilterHandler.doHandle(FilterHandler.java:108) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.handler.FilterHandler.handle(FilterHandler.java:80) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.InvocationFilterChain.doFilter(InvocationFilterChain.java:46) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.HttpFilterChain.doFilter(HttpFilterChain.java:31) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.FilterPipeline.dispatch(FilterPipeline.java:76) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.dispatch.Dispatcher.dispatch(Dispatcher.java:49) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at org.apache.felix.http.base.internal.DispatcherServlet.service(DispatcherServlet.java:67) [org.apache.felix.http.bridge-2.3.2.jar:2.3.2]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.felix.http.proxy.ProxyServlet.service(ProxyServlet.java:60) [org.apache.felix.http.proxy-2.3.2.jar:2.3.2]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625) [tomcat-embed-core-7.0.65.jar:7.0.65]
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) [tomcat-embed-core-7.0.65.jar:7.0.65]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_131]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_131]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-7.0.65.jar:7.0.65]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_131]
            

To identify specific OSGi issues, use the Apache Felix console to examine a project's bundles. For more information on how to access Apache Felix and use it, see Apache Felix Web Console. In particular, a bundle's status as listed in the Felix web console can offer insight: bundles with an Active state have started correctly, and bundles non-Active states may have had issues. Manually re-deploying specific non-Active bundles produces more console output which can help identify specifically why a bundle may have issues starting up.

Adding OSGi Compliant Jars

If the jar is OSGi compliant, add it to commerce-extensions/ext-cortex-webapp's pom.xml file. During the Webapp's start up, this third party jar is included in Cortex and run as an OSGi bundle.

To add the third party jar to Cortex:
  1. With a text editor, open commerce-extensions/ext-cortex-webapp's pom.xml file.
  2. Add the jar as a dependency using the following declaration:
    <dependencies>
    ...
            <dependency>
                <groupId>com.third.party.group</groupId>
                <artifactId>third-party-jar</artifactId>
    			<version>1.0.0</version>
            </dependency>
    ...
    </dependencies>
  3. Add the jar as an artifact item using the following declaration:
    <plugin>
    	<groupId>org.apache.maven.plugins</groupId>
    	...
            <artifactItems>
    		...
    		<artifactItem>
                    	<groupId>com.third.party.group</groupId>
                            <artifactId>third-party-jar</artifactId>
                    </artifactItem>
    		...
            </artifactItems>
    	...
    </plugin>
    
    
  4. Rebuild commerce-extensions/ext-cortex-webapp using mvn clean install, and run the Cortex Webapp.

Adding non-OSGi Compliant Jars

Occasionally you will have to add non-OSGi compliant jars to Cortex to support additional functionality. This section details strategies for using non-OSGi compliant bundles in an OSGi environment.

Finding an OSGi Compliant Version of the Jar

In general, this is the preferred method for handing non-OSGi compliant jars, and should be used whenever possible.

Find an OSGi compliant version of the third party jar. There are several sources to finding it; two good sources are listed below.

Wrap the jar using bndtools

If there is no OSGi compliant version of the jar you require, you can create an OSGi compliant jar using bndtools wrap command.

You can download bndtools here:

http://bnd.bndtools.org/

BND's wrap command takes an existing jar, guesses the right headers for the jar's manifest, and creates a new OSGi bundle jar.

To wrap an existing jar, run the following from the command line:

 bnd wrap osgi.jar *.jar

Use OSGi Framework Properties

By using Cortex's OSGi framework properties, you can set OSGi to export packages of your non-OSGi compliant jars and include them in your Cortex API framework. In general, this method should only be used if the above methods did not work.

The OSGi framework properties we need to modify are:
  • org.osgi.framework.bootdelegation - allows you to specify, at a global framework level, a list of packages to implicitly make available to all bundles in the framework. There is no need to declare the classes in the Import-Package headers of the bundles using them.
  • org.osgi.framework.system.packages.extra - allows you to add to the list of packages exported by the System Bundle (bundle id 0). Once exported, the packages are then available to the other bundles in the framework, but only if they are explicitly imported in the bundle's Import-Package headers.
To add the jar to Cortex's OSGi framework:
  1. With a text editor, open ext-cortex-webapp/src/main/resources/spring/applicationContext.xml
  2. Add the third party jar to the org.osgi.framework.bootdelegation list:
    ext-cortex-webapp applicationContext.xml
    ...
    <bean name="osgiFrameworkProperties" class="java.util.HashMap">
            <constructor-arg>
                <map>
    ...
    				<entry key="org.osgi.framework.bootdelegation"
                            value="org.w3c.dom.ranges,
                                    org.w3c.dom.traversal,
                                    org.w3c.dom.bootstrap,
                                    org.w3c.dom.events,
                                    org.w3c.dom.ls,
                                    javax.xml.parsers,
                                    sun.reflect,
                                    org.apache.xerces.jaxp,
                                    javax.inject,
                                    javax.net.ssl,
                                    javax.validation,
                                    javax.validation.bootstrap,
                                    javax.validation.spi,
                                    javax.validation.metadata,
                                    javax.management,
                                    oracle.sql"/>
                </map>
            </constructor-arg>
    ...
    </bean>
  3. Add the third party jar to the org.osgi.framework.system.packages.extra list:
    ext-cortex-webapp applicationContext.xml
    ...
    <bean name="osgiFrameworkProperties" class="java.util.HashMap">
        <constructor-arg>
            <map>
    ...
                <entry key="org.osgi.framework.system.packages.extra" value="
                    javax.inject;version=1.0,
                    javax.jms;version=1.1.1,
                    javax.validation;version=1.1.0,
                    javax.validation.bootstrap,
                    javax.validation.constraints,
                    javax.validation.groups,
                    javax.validation.metadata;version=1.1.0,
                    javax.validation.spi,
                    javax.servlet;javax.servlet.descriptor;javax.servlet.http;version=3.0,
                    javax.servlet.annotation;version=3.0,
                    sun.misc,
                    org.w3c.dom.traversal,
                    org.w3c.dom.ranges,
                    org.slf4j;org.slf4j.spi;org.slf4j.helpers;version=1.7.12
                    "/>
    ...
    </bean>
  4. Add the jar artifact as a dependency in ext-cortex-webapp's pom.xml file:
    <dependencies>
    ...
            <dependency>
                <groupId>com.third.party.group</groupId>
                <artifactId>third-party-jar</artifactId>
    			<version>1.0.0</version>
            </dependency>
    ...
    </dependencies>
  5. Rebuild ext-cortex-webapp using mvn clean install, and run theCortex Webapp.
Note: General Rule for Declaring Bundles

If your bundle has an Import-Package declaration for a jar, simply declaring the bundle in org.osgi.framework.bootdelegation will not be enough since it only makes the package available in the class loader. You must also declare the package in the org.osgi.framework.system.packages.extra for the bundle to be differentiated from the system bundle.

The general rule is use org.osgi.framework.bootdelegation when you don't have control over the manifest of the problematic bundle and you don't mind exposing the package to every other bundle. Use org.osgi.framework.system.packages.extra when a bundle imports a package and you expect the system bundle (not another bundle) to provide it.