I'm currently trying to deploy an XFA form rendering solution to Karaf. All my code works nicely when testing locally (no OSGi), but when I try to deploy to Karaf I get various errors. I believe the root cause here is that thge XFAWorker jars are not compiled as OSGi bundles. So I used the Karaf wrap protocol to import the bundles, but am still not succeeding. I've tried quite a few things and will try to document them as fully as possible below. However, it's super long to describe the various roadblocks I rand into, so in simple terms, the summary of my questions is:
How does one go about using iText XFA Worker in an OSGi container? (Karaf/Felix preferably)
Herewith the full sad story of what I tried:
First, the piece of code that requires the XFA Worker jars:
private byte[] flattenXfa(byte[] sourceXfaBytes) throws RuntimeException {
// Create an output stream for the flattened doc
ByteArrayOutputStream flattened = new ByteArrayOutputStream();
try {
Document document = new Document();
PdfReader reader = new PdfReader(sourceXfaBytes);
PdfWriter writer = PdfWriter.getInstance(document, flattened);
XFAFlattener flattener = new XFAFlattener(document, writer);
flattener.flatten(reader);
document.close();
} ... various catches
}
This method is called (via another class) on the start method of my bundle Activator, so that means I try to render (and flatten) the PDF on bundle start-up. This is really just for testing purposes - 've created a totally seperate project that really just has the little bundle and rendering code, to make sure nothing else can cause trouble.
Here's the start method of my activator (just in case it's important)
public void start(BundleContext context) {
System.out.println("Starting the bundle");
System.out.println("Rendering PDF");
Renderer rend = new Renderer();
String templatePath = "Path/to/xfa/file";
String renderPayload = "<form1><Name>MyName</Name><Surname>MySurname</Surname></form1>";
String xfaPdfPath = "Path/to/output/interactive/form";
String flatPdfPath = "Path/to/output/flat/pdf";
String licenseFilePath = "Path/to/itext/license/file";
boolean flatten = true;
rend.renderDoc(flatten, templatePath, renderPayload, xfaPdfPath, flatPdfPath, licenseFilePath);
}
As you can see, I've got a boolean flag that allows me to skip the flattenXfa method under certain circumstances.
In my Maven POM, I'm using the following dependencies:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xfaworker</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.11</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>itext-licensekey</artifactId>
<version>1.0.4</version>
</dependency>
Take note: the itextpdf and xmlworker jars are available as OSGi bundles. Only the xfaworker and itext-licensekey cause issues
As stated earlier, when I run the code outside of OSGi it works fine - it renders and flattens the PDF like a champion.
When I try to run from OSGi it gives various issues, depending on my approach. So here's a trace of the things I've tried. I'll explain what problems I get with each:
Try 1: Use the wrap protocol to install the non-OSGi bundles.
After starting Karaf I run the following commands in sequence, i.e. simulating what my eventual feature file will do:
bundle:install -s mvn:com.itextpdf/itextpdf/5.5.11
bundle:install -s mvn:com.itextpdf.tool/xmlworker/5.5.11
bundle:install -s wrap:mvn:com.itextpdf.tool/xfaworker/5.5.11
bundle:install -s wrap:mvn:com.itextpdf.tool/itext-licensekey/1.0.4
bundle:install mvn:com.testing/xfa-test/0.0.1-SNAPSHOT (I specifically don't start it yet)
When I now start my bundle I get the following issue:
Starting the bundle
Rendering PDF
java.lang.NoClassDefFoundError: com/itextpdf/tool/xml/html/CssAppliersAware
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.defineClass(BundleWiringImpl.java:2370)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.findClass(BundleWiringImpl.java:2154)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1542)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2018)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.apache.felix.framework.BundleWiringImpl.getClassByDelegation(BundleWiringImpl.java:1415)
at org.apache.felix.framework.BundleWiringImpl.searchImports(BundleWiringImpl.java:1595)
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1525)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2018)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.testing.xfa.Renderer.flattenXfa(Renderer.java:123)
at com.testing.xfa.Renderer.renderDoc(Renderer.java:52)
at com.testing.xfa.Activator.start(Activator.java:34)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:697)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2226)
at org.apache.felix.framework.Felix.startBundle(Felix.java:2144)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
at org.apache.karaf.bundle.command.Start.executeOnBundle(Start.java:38)
at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:64)
at org.apache.karaf.bundle.command.BundlesCommand.execute(BundlesCommand.java:54)
at org.apache.karaf.shell.impl.action.command.ActionCommand.execute(ActionCommand.java:83)
at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:67)
at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:87)
at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:480)
at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:406)
at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:182)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:119)
at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:94)
at org.apache.karaf.shell.impl.console.ConsoleSessionImpl.run(ConsoleSessionImpl.java:274)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: com.itextpdf.tool.xml.html.CssAppliersAware not found by wrap_mvn_com.itextpdf.tool_xfaworker_5.5.11 [61]
at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1574)
at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:79)
at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:2018)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 35 more
Error executing command: Error executing command on bundles:
Error starting bundle 65: Activator start error in bundle xfa-test [65].
For interest's sake, if I set the flatten attribute to false (i.e. I just render the interactive form without flattening) then it works fine, even in OSGi. That scenario, however, does not use the XFA Worker jar.
Try 2 - Use the Private-Package configuration of the maven-bundle-plugin
From the error message in Try 1, it seems that the issue has something to do with bundle wiring. So then I thought: Why not simply embed all the non-OSGi stuff directly in my bundle as private packages
So I edit the maven plugin as follows:
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven-bundle-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Version>${project.version}</Bundle-Version>
<Bundle-Activator>com.testing.xfa.Activator</Bundle-Activator>
<Export-Package>
com.testing.xfa*;version=${project.version}
</Export-Package>
<Import-Package>
*
</Import-Package>
<Private-Package>com.itextpdf.*</Private-Package>
</instructions>
</configuration>
</plugin>
Now when I install and try to start my bundle I get the following (typical OSGi) message:
Error executing command: Error executing command on bundles:
Error starting bundle 66: Unable to resolve xfa-test [66](R 66.0): missing requirement [xfa-test [66](R 66.0)] osgi.wiring.package; (osgi.wiring.package=org.antlr.v4.runtime) Unresolved requirements: [[xfa-test [66](R 66.0)] osgi.wiring.package; (osgi.wiring.package=org.antlr.v4.runtime)]
Which leads me down a trial-and-error rabbit hole of adding more and more private packages to the bundle until my Private-Package tags looks like this:
<Private-Package>com.itextpdf.*;org.antlr.*;org.abego.treelayout.*</Private-Package>
At this stage I get the following error (and it doesn't go away, even if I add "org.apache.jcp.*" to the list of private packages
Error executing command: Error executing command on bundles:
Error starting bundle 69: Unable to resolve xfa-test [69](R 69.0): missing requirement [xfa-test [69](R 69.0)] osgi.wiring.package; (osgi.wiring.package=org.apache.jcp.xml.dsig.internal.dom) Unresolved requirements: [[xfa-test [69](R 69.0)] osgi.wiring.package; (osgi.wiring.package=org.apache.jcp.xml.dsig.internal.dom)]
Try 3 - Install all the bundles that cause issues that Private-Package doesn't solve.
This was another trial-and-error rabbit hole, but my approach was to: - Keep all non-osgi jars in my private packages tag - Remove the wrapped jars altogether (since they are now embedded in my bundle) - Add the following Karaf bundles, one by one:
bundle:install -s mvn:com.itextpdf/itextpdf/5.5.11
bundle:install -s mvn:com.itextpdf.tool/xmlworker/5.5.11
bundle:install -s mvn:org.apache.directory.studio/org.apache.commons.codec/1.8
bundle:install -s mvn:org.apache.santuario/xmlsec/2.0.2
bundle:install -s mvn:org.bouncycastle/bcprov-jdk15on/1.56
bundle:install -s mvn:org.bouncycastle/bcpkix-jdk15on/1.56
bundle:install -s mvn:org.bouncycastle/bcprov-jdk15/1.45
bundle:install -s wrap:mvn:org.apache.xmlbeans/xmlbeans/2.6.0
In between the addition of each bundle, I try to start my own and check what errors I get. Wash, rinse, repeat. After an hour or two I've got a Private Packahe tag that looks like this:
<Private-Package>com.itextpdf.*;org.antlr.*;org.abego.treelayout.*;org.bouncycastle.*;org.mozilla.*</Private-Package>
At this stage Karaf no longer throws osgi wiring exceptions, but gives me the following NullPointerException
karaf@root()> start 87
Starting the bundle
Rendering PDF
java.lang.NullPointerException
at com.itextpdf.tool.xml.xtra.xfa.positioner.Positioner.<init>(Positioner.java:190)
at com.itextpdf.tool.xml.xtra.xfa.positioner.SubFormPositioner.<init>(SubFormPositioner.java:50)
at com.itextpdf.tool.xml.xtra.xfa.pipe.FormBuilder.buildSubForm(FormBuilder.java:215)
at com.itextpdf.tool.xml.xtra.xfa.pipe.FormBuilder.buildSubForm(FormBuilder.java:152)
at com.itextpdf.tool.xml.xtra.xfa.pipe.FormBuilder.getFormDom(FormBuilder.java:566)
at com.itextpdf.tool.xml.xtra.xfa.XFAFlattener.flatten(XFAFlattener.java:901)
at com.itextpdf.tool.xml.xtra.xfa.XFAFlattener.flatten(XFAFlattener.java:491)
at com.itextpdf.tool.xml.xtra.xfa.XFAFlattener.flatten(XFAFlattener.java:359)
at com.itextpdf.tool.xml.xtra.xfa.XFAFlattener.flatten(XFAFlattener.java:329)
at com.itextpdf.tool.xml.xtra.xfa.XFAFlattener.flatten(XFAFlattener.java:300)
at com.testing.xfa.Renderer.flattenXfa(Renderer.java:124)
at com.testing.xfa.Renderer.renderDoc(Renderer.java:52)
at com.testing.xfa.Activator.start(Activator.java:35)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:697)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:2226)
at org.apache.felix.framework.Felix.startBundle(Felix.java:2144)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:998)
at org.apache.karaf.bundle.command.Start.executeOnBundle(Start.java:38)
at org.apache.karaf.bundle.command.BundlesCommand.doExecute(BundlesCommand.java:64)
at org.apache.karaf.bundle.command.BundlesCommand.execute(BundlesCommand.java:54)
at org.apache.karaf.shell.impl.action.command.ActionCommand.execute(ActionCommand.java:83)
at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:67)
at org.apache.karaf.shell.impl.console.osgi.secured.SecuredCommand.execute(SecuredCommand.java:87)
at org.apache.felix.gogo.runtime.Closure.executeCmd(Closure.java:480)
at org.apache.felix.gogo.runtime.Closure.executeStatement(Closure.java:406)
at org.apache.felix.gogo.runtime.Pipe.run(Pipe.java:108)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:182)
at org.apache.felix.gogo.runtime.Closure.execute(Closure.java:119)
at org.apache.felix.gogo.runtime.CommandSessionImpl.execute(CommandSessionImpl.java:94)
at org.apache.karaf.shell.impl.console.ConsoleSessionImpl.run(ConsoleSessionImpl.java:274)
at java.lang.Thread.run(Thread.java:745)
Error executing command: Error executing command on bundles:
Error starting bundle 87: Activator start error in bundle xfa-test [87].
At which point I believe I'm doing something VERY WRONG, so I'm hoping to get some assistance here at SO.
Any pointers would be appreciated