Include and use native lib files (.so, .dylib) in "executable JAR" - spring-boot

I have a multimodule Spring Boot project with Maven. I am using spring-boot-maven-plugin to package this application into an executable JAR. This JAR is deployed to PCF (Pivotal Cloud Foundry).
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>my.AppMain</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
Now I would like to use PDFTron library - they provided me with a JAR ,.so and .dylib files (.dll too but I dont need those). They said that the JAR has to be in the same directory as the so and dylib files in order to work properly.
I tried to use this library in the project but I cant make it work. I am running out of ideas, I believe I have read almost all possible posts to this topic.
I have tried:
load each of this files to maven repo, include each one as (dylib, so and pdfnet.jar), but it resulted in error message: Caused by: java.lang.UnsatisfiedLinkError: no PDFNetC in java.library.path:
put all the files (dylib, so and pdfnet.jar) into the src/main/resources, include just provided PDFNet.jar as but it resulted in error message: Caused by: java.lang.UnsatisfiedLinkError: no PDFNetC in java.library.path:
Only way that worked was when I used maven-dependency-plugin to unpack the PDFTron dependencies into the target directory and update the -Djava.library.path to point at this created directory - but this can be done only at my local machine, because PDFTron dependencies were unpacked at "package" phase. To the PCF we are depolying only the executable JAR, so there is no target directory at PCF, so this was not an "production ready" option.
My question is, how can I include these files into this "executable JAR" so it will work properly ? Is there a way to unpack them at runtime ? Or do I have to use other plugin than spring-boot-maven-plugin ?
I can modify the -Djava.library.path at PCF, but I would like to avoid deploying another zip/jar/folder along with this JAR (basically I want to avoid changing the CI/CD deploy script as it is not under my control - and right now we are deploying only the executable JAR)
I am using java version "11.0.8" and spring-boot version "2.2.8.RELEASE"

You only need two files, per-platform.
PDFNet.jar is for all platforms.
libPDFNetC.so for Linux
PDFNetC.dll for Windows
PDFNetC.dylib for macOS
You can see in the samples, there are RuntTest.[sh|exe] files that show the settings that you can use. For example
#!/bin/sh
TEST_NAME=AddImageTest
javac -cp ../../../Lib/PDFNet.jar *.java
java -Djava.library.path=../../../Lib -cp .:../../../Lib/PDFNet.jar $TEST_NAME
So -Djava.library.path= points to the folder containing libPDFNetC.so and/or PDFNetC.dll.
It sounds like you have deployment specific issue with your tech stack, but I assume there is a way, same as how you would deploy any images/fonts or other resources. Though maybe your tech stack puts some restrictions on where java.library.path can point to, but that should be in the documentation supplied by what you are using.

Related

Maven: How to confine the application output to the target directory

I have a java application that uses maven for build management.
When I run the generated application jar with
java -jar myjar
the output files generated by the application end up in the projects root directory. So if I execute the jar in /my/project/dir and create a filewriter to write to logs/mylog
The resulting file ends up in
/my/project/dir/logs/mylog
Exactly as expected.
HOWEVER:
When maven surefire plugin executes the unit tests, the files end up in the module directory.
Say that i compile a maven project in /my/module.
The compiled files end up in /my/module/target/classes.
When maven executes these classes, through unit tests, the output of the same classes ends up in
/my/module/logs/mylog
I would like the files to end up in the target dir like
/my/module/target/logs/mylog
As this is where the class files reside.
So I am looking for a way to configure maven surefire to define the java classes' root directory to point to target instead of the module dir.
EDIT:
I have found this post:
Maven: change the directory in which tests are executed
That seems to attempt a fix to my problem. However, if i set the workingdirectory to my target dir, the tests can no longer find my resources, even if they are copied from the modules ${basedir} to ${basedir}/target
You should set your application working dir to ./target (or in a Maven property way: ${project.build.directory})
Solution:
By default, the maven surefire plugin executes its tests in the modules main directory.
In order to avoid this, set the workingdirectory of the plugin to the target directory.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>...</version>
<configuration>
<workingDirectory>${project.build.directory}</workingDirectory>
<basedir>${project.build.directory}</basedir>
</configuration>
</plugin>
Note that in case of tests that rely on some resources with root in the modules basedir, you need to copy these over. The easiest way i can find is with the resource plugin.
<build>
<resources>
<resource>
<directory>dependency</directory>
<targetPath>${project.build.directory}/dependency</targetPath>
</resource>
...
</build>
I can't tell you exactly how to do what you need to do because there is no real information
I can give you a hint about what to look at using.
Maven profiles can change the source, and behavior of Maven when they are selectively enabled.
Profiles are specifically designed to do just what you want to do.

how to deploy Maven dependencies automatically into JBoss as OSGI bundles?

I have a project that deploys an standalone OSGí Apache ServiceMix application. It has tons of dependencies and it is built with Maven. Now I want to deploy this application into a JBoss AS. I found an interesting Maven plugin called jboss-as-maven-plugin (org.jboss.as.plugins) to deploy anything. I use maven-bundle-plugin (org.apache.felix) to construct my bundles and it works fine, but when I deploy the project bundles, the deployment fails because dependencies are not satisfied.
How can I automatically bundle and deploy all the dependency tree with a Maven goal? Is it possible? My project has dozens of dependencies declared on the pom.xml and some of them are other projects in my workspace.
Currently the only solution to this I know are the Karaf features. You can create a feature file out of your pom dependencies.
I found that jboss seems to support subsystems. That may help to specify the bundles required to run your application. It does not seem to be the OSGi subsystem spec but for jboss this may already help. For OSGi spec 5 there is the standardized subsystem spec which may provide a standard way to do this across containers.
If jboss supports OBR (OSGi bundle repository) then you can limit the number of dependencies you have to specify.
If your application do not use OSGi per see, you may consider packing your application as a WAR which is deployable in JBoss.
Then you would need to use web.xml to bootstrap your application, such as using a Spring XML file.
There is a Camel example as a WAR here: http://camel.apache.org/servlet-tomcat-example.html
You can autoinstall your bundles with org.apache.sling plugin
<plugin>
<groupId>org.apache.sling</groupId>
<artifactId>maven-sling-plugin</artifactId>
<executions>
<execution>
<id>install-bundle</id>
<goals>
<goal>install</goal>
</goals>
</execution>
</executions>
<configuration>
<slingUrl>http://localhost:8181/system/console/install</slingUrl>
<user>karaf</user>
<password>karaf</password>
</configuration>
</plugin>
you can find detailed pom.xml from Adobe website :https://docs.adobe.com/docs/en/cq/5-6-1/developing/developmenttools/how-to-build-aem-projects-using-apache-maven.html
or http://www.cqblueprints.com/tipsandtricks/build-and-deploy-osgi/build-deploy-osgi-1.html

ClassNotFoundException Netbeans Maven Library wrapper

I was able to follow the tutorial here and convert it to a Maven application.
I got the loading part right, but when I try to use it, it can't find the appropriate swt classes (When ran from windows 64). I did it on a Maven project. Here's the relevant part of the respective pom for the Windows 64 module:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>nbm-maven-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<!-- To have the jar plugin pickup the nbm generated manifest -->
<useDefaultManifestFile>true</useDefaultManifestFile>
<moduleType>normal</moduleType>
swt.windows.64/1</codeNameBase>
<publicPackages>
<publicPackage>org.eclipse.swt</publicPackage>
</publicPackages>
</configuration>
</plugin>
I translates to org.eclipse.swt.*. It seems to be a Maven module issue since it already tries to load from correct module.
Caused: java.lang.ClassNotFoundException: org.eclipse.swt.SWT starting from ModuleCL#25e0dee5[djproject.core] with possible defining loaders [ModuleCL#6783113b[swt.windows.64]] and declared parents [ModuleCL#32821bf7[org.netbeans.api.annotations.common]]
Caused: java.lang.NoClassDefFoundError: org/eclipse/swt/SWT
at chrriis.dj.nativeswing.swtimpl.core.SWTNativeInterface.initialize_(SWTNativeInterface.java:213)
at chrriis.dj.nativeswing.swtimpl.NativeInterface.initialize(NativeInterface.java:71)
at chrriis.dj.nativeswing.swtimpl.core.SWTNativeInterface.open_(SWTNativeInterface.java:337)
at chrriis.dj.nativeswing.swtimpl.NativeInterface.open(NativeInterface.java:100)
at synamicd.windows64.support.Windows64BrowserProvider.initialize(Windows64BrowserProvider.java:25)
Any help is more than welcomed!
Edit:
Code is available here.
when I try to build the application at the bitbucket location, it's build order suggests that none of the swt modules are used in the app - http://screencast.com/t/LMxIPy6A6ZUL

Maven: Force Jersey to use specific artifact version

I have a Maven repository where I load Jena TDB 0.9.3 (which depends on Jena ARQ 2.9.3), Jersey 1.8 and RMOnto 1.0. The point is, as you expected, to do some analysis on semantic datasets.
It looks like RMOnto has ARQ 2.8.7 built in, as in "hardwired". There isn't any explicit dependency in its pom file, yet the jar file contains a ARQ.class. It's very tricky because you won't notice it with Maven Enforcer Plugin and the like.
It looks like this causes Jersey to use RMOnto's ARQ version instead of the one defined in pom.xml. Here is a minimal example. When you run the test (checks whether or not ARQ.VERSION equals 2.9.3), it succeeds. When you build the project and deploy it on a Tomcat 7, you should see 2.8.7 as output.
Is this behaviour expected and why?
How could one force Jersey to use ARQ 2.9.3?
In case it's not possible, could one isolate RMOnto to use 2.8.7 while the rest of the source uses 2.9.3?
Thanks in advance!
You should define the ARQ 2.9.3 first in the dependencies list. By doing that you force your build to use that specific version. The dependency order is relevant when choosing what artifact to use.
Update
OK, I understand what the problem is.
The RMOnto jar is obviously shaded according to the pom: http://semantic.cs.put.poznan.pl/maven/put/semantic/RMOnto/1.0/RMOnto-1.0.pom.
Tomcat 7 loads the jars in WEB-INF/lib in an undefined order. This means that even if you define ARQ 2.9.3 to be first in your dependencies it will not be the case when the application is run in Tomcat. http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
Good thing is that Tomcat always look in WEB-INF/classes before WEB-INF/lib for dependencies.
So what you can do as a work around is to make sure that the ARQ 2.9.3 version is added to the WEB-INF/classes folder. This can be done using the maven-dependency-plugin:
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.apache.jena</groupId>
<artifactId>jena-arq</artifactId>
<version>2.9.3</version>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<excludes>**/META-INF/</excludes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Your war as well as your exploded war will now contain all the classes from ARQ 2.9.3 in the WEB-INF/classes folder. They will be loaded before any jar-file that is in WEB-INF/lib folder.
NB: I have not tested this on Tomcat but I cannot see that it would not work.
NB2: This is a hack. Best thing would be to remove the ARQ packages out of the RMOnto jar.
You should file a defect report against RMOnto. Hard-wiring library code into a jar, instead of including it as a dependency you can manage in the POM, is definitely a bad idea that the code maintainer should fix.
If the files have been copied directly to the RMOnto .jar, the behaviour is expected.
In that case, I'd say the best bet is to hardcode it away, aka remove the ARQ files directly from the package. Opening up the RMOnto-1.0.jar package one can see arq files in the arq folder. What you'd need to do is open up the jar file (it's just a .zip), remove the ARQ files from there, store the edited RMOnto package in your version control / repository and refer to the edited package from there. Also, you'd need to add excludes statement to your pom for the old version of ARC and keep the dependency to the new version.
If you feel like it, it would be also good practice to remove the other dependencies that haven't been mentioned in the RMOnto's pom file, then add them in the RMOnto pom file (and rebuild, if you have the source code). This way Maven mechanism would be aware of them. The file seems to contain a lot of dependencies like this, which will cause headaches in the future.

how to let maven war plugin to create multiple war file

i used selenium-mave-plugin for integration test, which require the war file named: project.artifactId-version(say: myproj-0.1-SNAPSHOT.war) while the default war created by maven-war-plugin is project.artifactId.war(say myproj-SNAPSHOT.war).
in order to let selenium plugin, i override the maven-war-plugin in that selenium profile as:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.1-beta-1</version>
<configuration>
<warName>${project.artifactId}-${project.version}</warName>
</configuration>
</plugin>
now when i build the project, it failed at rpm:rpm, complaining:
source location ..../myProj.war does not exist
my question is if it's possible to create 2 war files: myProj.war and myProj-0.1-SNAPSHOT.war so both rpm and selenium plugins are happy? Thanks
For rpm plugin, please make sure you use the location directive. If you need further help, please post your full pom.xml.
As for selenium, it doesn't really need to know where your .war file resides. Only the web application server needs to know. Sadly, you didn't provide information in which phase of maven the "does not exist" error occured. So I can only guess it's while starting jetty, tomcat or another web application server.
You should run your full build (including tests) with: mvn clean verify integration-test rpm:prm.

Resources