Onejar and resource loading - java

I have a maven project which I would like to package in an executable jar.
It's using quite a few dependencies, like spring and so on.
It was suggested in a few posts to use OneJar, to avoid a lot of headaches.
This is what I have currently in my pom.xml:
<plugin>
<groupId>org.dstovall</groupId>
<artifactId>onejar-maven-plugin</artifactId>
<version>1.4.4</version>
<executions>
<execution>
<configuration>
<mainClass>com.cool.project.Application</mainClass>
<onejarVersion>0.97</onejarVersion>
<attachToBuild>true</attachToBuild>
<classifier>coolproject</classifier>
</configuration>
<goals>
<goal>one-jar</goal>
</goals>
</execution>
</executions>
</plugin>
In my Spring configuration one of the classes needs to pass the a resource (src/main/resources/coolfile.bin) path to an external library (jsch) method:
String resource = ConfigurationClass.class.getClassLoader().getResource("coolfile.bin").getFile();
jsch.addIdentity(resource);
When I run Application.java from the IDE (eclipse), the entire application loads successfully.
Although when I run mvn clean install, the onejar jar is built under the target folder, but when I try to run it with java -jar coolproject.one-jar.jar the following error is displayed:
...
Caused by: java.io.FileNotFoundException: file:/target/coolproject.one-jar.jar!/main/coolproject.jar!/coolfile.bin (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.jcraft.jsch.IdentityFile.newInstance(IdentityFile.java:83
If I inspect coolproject.one-jar.jar, I can find the coolproject.jar under the main folder, and if I inspect that, I can see coolfile.bin in its root.
So in theory the resource should be found? What am I missing?

Turns out that FileInputStream would not find the path specified by resource.
Luckily jsch provides another method where you can pass the byte array of the file rather than its location:
jsch.addIdentity("coolfile.bin", toByteArray(ConfigurationClass.class.getResourceAsStream("/coolfile.bin")), null, null);

Related

Add vm options to a native image graalvm

Hi i upgraded an old java 8 project to a java 11 to use Graalvm for building native image, first problem was with how should i add external jar files to the project and i did it using a maven plugin, then after compiling and linking the project successfully, the application gave an error JavaFx configuration: classes were loaded from unnamed module. I had solved this problem in the IDE using VM options --module-path javafx-sdk-19/lib --add-modules javafx.fxml,javafx.controls,javafx.graphics,and after adding the runtimeArgs still no luck running the native image.
How can i make the native image use external javafx sdk like in the IDE?
Maven plugin for adding external jars
<plugin>
<groupId>com.googlecode.addjars-maven-plugin</groupId>
<artifactId>addjars-maven-plugin</artifactId>
<version>1.0.2</version>
<executions>
<execution>
<goals>
<goal>add-jars</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>${basedir}/libs</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Gluonfx plugin
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>1.0.15</version>
<configuration>
<mainClass>com.proj.main.Main</mainClass>
<reflectionList>com.proj.main.PaxUtils</reflectionList>
<reflectionList>com.proj.JsonUtil</reflectionList>
<nativeImageArgs>
<arg>+EagerJVMCI</arg>
<arg>-Dgraal.PrintConfiguration=info</arg>
</nativeImageArgs>
</configuration>
</plugin>
Error log
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module #7e0babb1'
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:901)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
at java.lang.Thread.run(Thread.java:829)
at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)
Caused by: java.lang.ExceptionInInitializerError
at org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:164)
at org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2835)
at org.jboss.resteasy.spi.ResteasyProviderFactory.addMessageBodyReader(ResteasyProviderFactory.java:1068)
at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:1841)
at org.jboss.resteasy.spi.ResteasyProviderFactory.registerProvider(ResteasyProviderFactory.java:1769)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.registerProviders(RegisterBuiltin.java:148)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:54)
at org.jboss.resteasy.plugins.providers.RegisterBuiltin.register(RegisterBuiltin.java:40)
at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.getProviderFactory(ResteasyClientBuilder.java:456)
at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.buildOld(ResteasyClientBuilder.java:464)
at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.build(ResteasyClientBuilder.java:496)
at org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder.build(ResteasyClientBuilder.java:50)
at javax.ws.rs.client.ClientBuilder.newClient(ClientBuilder.java:114)
at com.xylo.client.xpos.service.XWebServiceClient.init(XWebServiceClient.java:41)
at com.xylo.client.xpos.service.XWebServiceClient.<init>(XWebServiceClient.java:31)
at com.xylo.client.xpos.POSSettings.fillSettings(POSSettings.java:181)
at com.xylo.client.xpos.main.Main.start(Main.java:45)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:847)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
at java.security.AccessController.executePrivileged(AccessController.java:169)
at java.security.AccessController.doPrivileged(AccessController.java:91)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.oracle.svm.jni.JNIJavaCallWrappers.jniInvoke_VA_LIST_Runnable_run_16403f8d32adb631126daa893e5e80085c5d6325(JNIJavaCallWrappers.java:0)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(GtkApplication.java)
at com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:316)
... 3 more
Caused by: java.lang.IllegalArgumentException: Invalid bundle interface org.jboss.resteasy.resteasy_jaxrs.i18n.Messages (implementation not found)
at org.jboss.logging.Messages.doGetBundle(Messages.java:92)
at org.jboss.logging.Messages.getBundle(Messages.java:59)
at org.jboss.logging.Messages.getBundle(Messages.java:46)
at org.jboss.resteasy.resteasy_jaxrs.i18n.Messages.<clinit>(Messages.java:31)
... 30 more

how to execute Quickfix/J jar?

I'm building a self contained trading simulator using the quickfix/j library. Up untill now I'd been using mvn package, then the intelli J "Run button" to run my program from an entry point within my client-application class. I tried using the java -jar target/.....1.1.0.jar . and get the following error
java -jar Broker/target/Broker-1.0.0.jar
Error: Could not find or load main class Broker.ClientApplication
Caused by: java.lang.NoClassDefFoundError: quickfix/Application
I thought the error might have something to do with my pom file not fetching a dependecy properly. So to make sure I ran the ordermatch example from the quickfix/J github, but i get a similar error.
java -jar /homes/antonga/IdeaProjects/Desktop/quickfixj-parent/quickfixj-examples/ordermatch/target/quickfixj-examples-ordermatch-2.1.1-sources.jar
no main manifest attribute, in /homes/antonga/IdeaProjects/Desktop/quickfixj-parent/quickfixj-examples/ordermatch/target/quickfixj-examples-ordermatch-2.1.1-sources.jar
To be clear using the Intellli J "Run" button inside the main calss works percfectly even for the ordermacth example. From what I gather the command IntelliJ uses is something like this
"/path/to/java/" "-javagent/.../.jar" "/pathtolocalmavenrepo/quickfix-core.jar "/pathtolocalmavenrepo/anotherquickfixdependecy.jar" ....."more quickfix dependency jar files" packagestructure.Main
I don't see why this would work but my execution wouldn't. I can include my pom files and other info if this would help. I'm also using a multi-module maven project but that doesn't seem to the problem.
Turns out I was beeing a noob. The Maven package lifecycle bundles the specified class files without the dependencies into a jar. I needed to create an uber jar with all the necessary bianries, and then run that. See the SO question create excecutable jar with dependencies using maven
What is required is the following:
java -classpath <list-of-all-jars> <main-class>
Where <list-of-all-jars> is a ; (Windows) or : (*nix) separated list of all jars needed to run your program (quickfixj jars, your own jar and any jars needed), and <main-class> is the fully qualified class name of your main class (the class that has the main entry to your application)
You have to create an executable jar.
You can use Maven to do this. In your pom.xml can use maven-assembly-plugin to generate your executable jar
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<archive>
<manifest>
<mainClass>myour.main.Class</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>

How to use Apache Tattletale to analyze duplicate Jar/APIs used in class path

In my project they have used more than 225+ jar files which causing memory issue, while searching on net i come to know Apache Tattletale will analyze and give a report of duplicate classes and JAR/APIs used by the application (Classpath). So i have refereed following links
1) how to use JBoss Tattletale tool
2) Uncover JBoss client jar list with Tattletale
3) Jboss official Documentation
but i didn't get how to execute and run the Tattletale Jar file and my application is not based on maven so i am not using Maven.
I have downloaded tattletale-1.2.0.Beta2.jar file along with jboss-seam-2.3.0.CR1-dist file and used following command
java -Xmx512m -jar tattletale.jar /Java/workspaces/mycoolprojects/projectX output-projectx
but getting following exception
Exception in thread "main" java.lang.NoClassDefFoundError: javassist/NotFoundException
at org.jboss.tattletale.analyzers.Analyzer.getScanner(Analyzer.java:49)
at org.jboss.tattletale.Main.execute(Main.java:608)
at org.jboss.tattletale.Main.main(Main.java:1099)
Caused by: java.lang.ClassNotFoundException: javassist.NotFoundException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 3 more
More over i didn't get what is the use of jboss-seam-2.3.0.CR1-dist file. Meaning i can see lot of jar files and lot of code in there but i don't know how does it help to use tattletale.
In the official documentation they have mentioned jboss-tattletale.properties and how can i set/use that.
I was having the same problem and this solution worked for me too.
(downloaded the latest javaassist jar)
Interestingly, tattletale itself suggests that the tattletale jar contains the
javaassist jar
The below steps worked for me:
download jboss-javassist-javassist-rel_3_22_0_cr1-2-g6a9079a.zip from http://jboss-javassist.github.io/javassist/
extract it to a location
go to that location and copy javassist.jar
go to location where your tattletale-1.2.0.Beta2.jar is present
paste javassist.jar here
open command prompt at this path
run command java -jar tattletale-1.2.0.Beta2.jar path_to_application_archive output_path
I inherited an old Maven project configured to use this plugin and got the same javassist errors. The plugin dependencies may be adjusted as shown to make the errors stop.
<plugin>
<groupId>org.jboss.tattletale</groupId>
<artifactId>tattletale-maven</artifactId>
<version>1.2.0.Beta2</version>
<executions>
<execution>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- This is the location which will be scanned for generating tattletale reports -->
<source>${project.build.directory}/${project.artifactId}/WEB-INF/lib</source>
<!-- This is where the reports will be generated -->
<destination>${project.build.directory}/site/tattletale</destination>
</configuration>
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
</dependencies>
</plugin>

Running app jar file on spark-submit in a google dataproc cluster instance

I'm running a .jar file that contains all dependencies that I need packaged in it. One of this dependencies is com.google.common.util.concurrent.RateLimiter and already checked it's class file is in this .jar file.
Unfortunately when I hit the command spark-submit on the master node of my google's dataproc-cluster instance I'm getting this error:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch$1.<init>(RateLimiter.java:417)
at com.google.common.util.concurrent.RateLimiter$SleepingStopwatch.createFromSystemTimer(RateLimiter.java:416)
at com.google.common.util.concurrent.RateLimiter.create(RateLimiter.java:130)
at LabeledAddressDatasetBuilder.publishLabeledAddressesFromBlockstem(LabeledAddressDatasetBuilder.java:60)
at LabeledAddressDatasetBuilder.main(LabeledAddressDatasetBuilder.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:672)
at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:180)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:205)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:120)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
It seems something happened in the sense of overwriting my dependencies. Already decompiled the Stopwatch.class file from this .jar and checked that method is there. That just happened when I ran on that google dataproc instance.
I did grep on the process executing the spark-submit and I got the flag -cp like this:
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -cp /usr/lib/spark/conf/:/usr/lib/spark/lib/spark-assembly-1.5.0-hadoop2.7.1.jar:/usr/lib/spark/lib/datanucleus-api-jdo-3.2.6.jar:/usr/lib/spark/lib/datanucleus-rdbms-3.2.9.jar:/usr/lib/spark/lib/datanucleus-core-3.2.10.jar:/etc/hadoop/conf/:/etc/hadoop/conf/:/usr/lib/hadoop/lib/native/:/usr/lib/hadoop/lib/*:/usr/lib/hadoop/*:/usr/lib/hadoop-hdfs/lib/*:/usr/lib/hadoop-hdfs/*:/usr/lib/hadoop-mapreduce/lib/*:/usr/lib/hadoop-mapreduce/*:/usr/lib/hadoop-yarn/lib/*:/usr/lib/hadoop-yarn/*
Is there anything I can do to solve this problem?
Thank you.
As you've found, Dataproc includes Hadoop dependencies on the classpath when invoking Spark. This is done primarily so that using Hadoop input formats, file systems, etc is fairly straight-forward. The downside is that you will end up with Hadoop's guava version which is 11.02 (See HADOOP-10101).
How to work around this depends on your build system. If using Maven, the maven-shade plugin can be used to relocate your version of guava under a new package name. An example of this can be seen in the GCS Hadoop Connector's packaging, but the crux of it is the following plugin declaration in your pom.xml build section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>your.repackaged.deps.com.google.common</shadedPattern>
</relocation>
</relocations>
</execution>
</execution>
</plugin>
Similar relocations can be accomplished with the sbt-assembly plugin for sbt, jarjar for ant, and either jarjar or shadow for gradle.

Injecting Maven project information into Swing Application Framework resources?

I have a Maven project using the Swing Application Framework and would like to inject project information from the pom.xml into my application's global resources to avoid duplication.
The base application (provided via netbeans) uses Application.title, Application.version, Application.vendor, Application.description resources etc for Window titles and about box configuration but I can't find a way to set these values programatically at run time and I'm not a maven maven so don't have the skills to inject them at build time.
Anyone have any recommendations on how best to achieve the desired result?
You could try using filtered resources. If you create a property file, say src/main/resources/com/myapp/app.properties that looks like this:
version=${project.version}
name=${project.name}
id=${project.artifactId}
Them you need to enable filtering in your pom.xml:
<build>
<resources>
<resource>src/main/resources</resource>
<filtering>true</filtering>
</resources>
</build>
Now when maven builds your project, it'll expand the property file, and place it on the classpath. Then you can just call getResourceAsStream("/com/myapp/app.properties") to read it into your app.
Whist maven does automatically create a file /META-INF/maven/$groupId/$artifactId/pom.properties, this may not have all the information you need.
You can keep those in separte property file and read it from both pom.xml and your application.
Another option is to read pom.xml file from classpath (mvn will put it in META-INF folder) and parse it from there as plain xml file.
I would go with first option.
I would try using the maven-antrun-plugin. Pass the necessary maven properties to ant and create an ant build script which modifies an application properties file or the spring context configuration directly.
Another way would be to generate a separate properties file with the properties-maven-plugin and then add this properties file to the application bundle names:
For the pom.xml to write application.properties file:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0-alpha-2</version>
<executions>
<execution>
<phase>process-resources</phase>
<goals>
<goal>write-project-properties</goal>
</goals>
</execution>
</executions>
<configuration>
<outputFile>${project.build.outputDirectory}/application.properties</outputFile>
</configuration>
</plugin>
...
</plugins>
...
</build>
For including the application.properties into your application:
public class MyApplication extends SingleFrameApplication
public MyApplication() {
super();
addGeneratedApplicationProperties();
}
private void addGeneratedApplicationProperties() {
ResourceManager resourceManager = getContext().getResourceManager();
getContext().setApplicationClass(MyApplication.class);
List<String> bundleNames = new LinkedList<String>(resourceManager.getApplicationBundleNames());
bundleNames.add(0, "application");
resourceManager.setApplicationBundleNames(bundleNames);
}
...
}
However, I find the maven-filter-solution way more elegant.

Categories

Resources