Thursday, 12 November 2015

Creating OSGi Bundles

Problem: I have a third party Java library that I need to use in my CQ component.  It’s available as a standard jar file. I want to drop this in a lib folder somewhere and have access to the classes. But AEM doesn’t support libraries this way. How do I import and use this code?
Solution: AEM manages libraries as OSGi bundles.  OSGi bundles are essentially the same thing as standard jar files, but they contain extra metadata. This metadata relates to versioning and dependencies, and is stored in a jar’s \META-INF\MANIFEST.MF file.  AEM requires this metadata, so in order to use a third-party jar library in AEM, you must add that to the library’s jar file, turning it into what’s called an OSGi bundle.
How: Our case study will be a Java API for a third-party mailing list manager:  Ecwid’s MailChimp API.
Note that if all you want is a quick step-by-step solution, without images or explanations, seehere.
Step 0: Since the source code for our third party library happens to be stored in git, and happens to be compiled with maven, we’ll need to install clients for each.  I’ll be using the windows command line clients with cygwin.
Step 1:  Build and compile the library
mailchimp-step-1
git clone https://github.com/Ecwid/ecwid-mailchimp.git
Note that the URL was simply copied from “clone URL” field on the repository page linked above
cd ecwid-mailchimp
mvn clean package dependency:copy-dependencies -DskipTests=true
Note that I skipped the tests because they break without some API key configuration (which we can do later) and copy the dependencies for a reason I’ll explain in the next step.
Step 2: Figure out how to create a OSGi bundle jar file
There are many ways to build a jar as an OSGi bundle.  Here’s what we will not be doing
a) Manually writing up the MANIFEST.MF file.  This is untenable for even slightly complicated OSGi bundles because the file format is so fickle.  And mistakes don’t yield friendly errors.
b) Use a Maven OSGi plugin and modify the project poms.  Configuring this the first time can be nearly as complicated as manually building the manifest file.
c) Use bndtools to create one.  This is a good tool but is kind of indiscriminate into what it includes as dependencies.  This brings us to our next problem.
Problem: OSGi bundles won’t load unless their dependencies are loaded as OSGi bundles.
“Ideal” Solution: Track down each of the dependencies and recompile those as OSGi bundles.  Do the same for each of their dependencies, all the way down the chain.  In an ideal world, all jars would be OSGi capable.  But few libraries are currently packaged this way, and we’re not going to spend this kind of time on code that’s not ours.
Quick and Dirty Solution: Package the main jar and all its dependency jars as a single, combined OSGi bundle.  (Note how we explicitly copied those dependency jars in the maven command above).  There are downsides, and this defeats some of the points to OSGi dependency management, but it’s the most practical solution for moderately complex libraries.
Why are we downloading a specialized version of Eclipse for something seemingly unrelated to what we’re doing?  Because it has a fantastic OSGi builder and interface!  Download and install the latest version somewhere.  Then open it.
Note that you can also just add “Eclipse Plug-in Devlopment Environment” to your existing Eclipse environment.
Step 3: Create OSGi project in Eclipse
New -> Other -> Plugin Development -> Plug-in from Existing JAR Archives
Add External
Select all the jars in the target folder (target\ecwid-mailchimp-2.0.0.1-SNAPSHOT.jar and target\dependencies\*.jar)
Set a project name
Check "Analyze library contents and add dependencies"
Select "an OSGi framework"
Select "standard"
Uncheck "Unzip the JAR..."
Finish
Step 4: Export the project as an OSGi bundle
Right click on project -> Export
Plug-in Development -> Deployable plug-ins and fragments
Select a directory
Click Finish
Open that directory -> Plugin folder
Copy the jar file
Step 5: Install the OSGi bundle into CQ
Open CRXDE
Navigate to apps\(project)\install
Paste jar file
Step 6: Verify OSGi bundle deployed in CQ
Open CQ OSGi Console
OSGi -> Bundles
Type "mailchimp" (or whatever project name was) into search bar and click "Apply Filter"
Use arrow to expand description of bundle
Scroll through imported packages
Note anything that is highlighted in red because it's unresolvable
Go back into eclipse and the OSGi project
Click "dependencies"
Remove the project dependencies under Imported Packages
Step 7: Redeploy OSGi bundle
Export OSGi bundle again from Eclipse as in previous step
Open CRXDE and delete the old OSGi bundle
Copy-paste the new one in
Verify no issues and an Active status within CQ's OSGi console
Step 8: Use it in a component.
Here’s some sample code from a component jsp that consumes this library, along with its position relative to the OSGi jar.
structure
<%@include file="/apps/cardinal/global.jsp"%>
<%@ page import="com.ecwid.mailchimp.*,
com.ecwid.mailchimp.method.list.*" %><%
%>
<%
String apikey = currentStyle.get("apikey", "null");
String listid = properties.get("listid", "null");
MailChimpClient mailChimpClient = new MailChimpClient();
ListsMethod listsMethod = new ListsMethod();
listsMethod.apikey = apikey;
ListsMethodFilters listsMethodFilters = new ListsMethodFilters();
listsMethodFilters.list_id = listid;
listsMethod.filters = listsMethodFilters;
ListsResult listsResult = mailChimpClient.execute(listsMethod);
String listUrl = listsResult.data.get(0).subscribe_url_short;
%>
Conclusion:
Adding third party code to CQ the “right way” isn’t necessarily trivial, but using some standard tools can be done without too much overhead. Suggestions and questions are welcome; based on feedback I may be appending or following up on this topic with more details and explanations.

No comments :

Post a comment