I develop and distribute a Java Swing app that uses Apache Batik and JavaCV. I've updated it through java 1.6, 7 and 8. Installers for macOS, Windows and Linux are built with Javapackager. Java 8 is end of support in Jan '19 and I can't find a solution to package and distribute at Java 11, the new LTS version.
Neither JavaCV or Batik produce modular jars but I have managed to repackage them to the point where I can compile and produce a runnable jar at Java 11, app works fine but I can't package it for distribution. I was planning dropping to Java 10 to use javapackager and bundle the 11 runtime from there but it uses jlink to produce a custom runtime, jlink fails because JavaCV and Batik aren't modular. jdeps won't produce a module-info.class to patch in and make them modular due to unsatisfied references in Batik and JavaCV, even though my app works fine without them.
As a result I'm going to have to leave the code base at Java 8, and ship it even though no longer supported.
I know there's a call to create a replacement for javapackager but that won't be there until way after Java 8 is deprecated. And I'd still need Batik and JavaCV projects to refactor and build modular jars to produce a custom runtime.
Can anyone offer any other solution please? Have I missed something? Thanks.
Update: jpackage is now included since JDK 14. The following answer is still applicable for older JDK versions, though.
I've had the same issue. I wanted to use JDK 11, but according to JDK-8212780 JEP 343 is scheduled for JDK 13, so we'll need to wait a little longer. Packaging "native" self-contained applications under Java 8 - 10 wasn't a problem, since the packager was included (at least in the Oracle JDK).
Today I found out about this email and figured I'd like to give it a try: You can see my sample project on github.com/skymatic/javafx11-test, which I successfully packaged using the back-ported jpackager from the JDK development branch.
What I did here:
Created a new HelloWorld project using OpenJDK 11 and OpenJFX 11.
Downloaded the packager and invoke it from the Maven build (note it needs to reside with the JDK and you need to set JAVA_HOME for it to work...)
Bonus: I used jdeps to find out the dependency of my non-modular jar and set the --add-modules parameter to produce a smaller runtime image
Of course it is even easier for modular projects: In this commit to my sample project you can see that I used jpackager's parameters for the module path and main module instead of the classpath and main class.
Here is a link to a GitHub template showing how to use jlink, jpackage and GitHub Actions to produce a JavaFX app and native macOS, Windows, and Linux installers with a small JVM:
https://github.com/wiverson/maven-jpackage-template
Here is a (WIP) version that uses Swing and an embedded Spring Boot server:
https://github.com/wiverson/desktop-spring-boot
So the only solution was to write a wrapper app that uses ProcessBuilder to launch the original apps jar.
Advantage in this is that link can be used to produce the minimal required runtime. Place the jar in the bin directory, then use FPM (https://github.com/jordansissel/fpm) to create an installer.
Sample code for the wrapper
package xyz.arwhite.dslauncher;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class DrumScoreLauncher {
public static void main(String[] args) {
String installHome = System.getProperty("java.home");
String installBin = installHome + File.separator + "bin" + File.separator;
System.out.println("Launching Drum Score Editor from "+installHome);
List<String> cmdLine = new ArrayList<String>();
cmdLine.add(installBin + "java");
cmdLine.add("-jar");
cmdLine.add(installBin + "DrumScoreEditor.jar");
for ( int i = 0; i < args.length; i++ )
cmdLine.add(args[i]);
try {
ProcessBuilder p = new ProcessBuilder(cmdLine);
p.inheritIO();
p.start();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Exiting launcher");
}
}
Related
When a make a simple test program with Logitech's LED SDK and run it, I get an UnsatisfiedLinkError with the message:
C:\Users\cwa38\AppData\Local\Temp\LogitechLedJNI.dll4587977834410494064: Can't find dependent libraries
What I have done so far:
I downloaded the SDK from https://www.logitechg.com/en-us/innovation/developer-lab.html
I extracted it
I made a new Java project in NetBeans
I added the logiled.jar file to my new project as a dependency
I made a new class and copied the simple test program from the documentation (code is below)
I clicked "run" and got the UnsatisfiedLinkError described above
I see that the SDK download comes with some .dll files and a .lib file, but I have no idea what to do with these files. The included documentation makes absolutely no mention of them.
What am I doing wrong?
Here is my code:
import com.logitech.gaming.LogiLED;
public class Main {
public static void main(String[] args) {
LogiLED.LogiLedInit();
LogiLED.LogiLedSetLighting(100, 0, 0);
LogiLED.LogiLedShutdown();
}
}
And here is the full documentation from Logitech (yes, it really is this short):
The following steps show how to make the Logitech SDK work with a Java program. Please adapt the steps to your game for things to work.
Steps
Copy the SDK’s Lib\logiled.jar to your project’s directory.
Configure your project’s Java Build Path to include the logiled.jar.
Call the functions from the JNI wrapper in your Java code as follows:
import com.logitech.gaming.LogiLED;
LogiLED.LogiLedInit();
LogiLED.LogiLedSetLighting(red,blue,green);
LogiLED.LogiLedShutdown();
Compile and run your program.
The full stack trace for the error I get is below:
Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\Users\cwa38\AppData\Local\Temp\LogitechLedJNI.dll4587977834410494064: Can't find dependent libraries
at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
at java.base/jdk.internal.loader.NativeLibraries$NativeLibraryImpl.open(NativeLibraries.java:384)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:228)
at java.base/jdk.internal.loader.NativeLibraries.loadLibrary(NativeLibraries.java:170)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2389)
at java.base/java.lang.Runtime.load0(Runtime.java:755)
at java.base/java.lang.System.load(System.java:1953)
at com.logitech.gaming.LogiLED.<clinit>(LogiLED.java:205)
at javaapplication1.JavaApplication1.main(JavaApplication1.java:20)
C:\Users\cwa38\AppData\Local\NetBeans\Cache\12.4\executor-snippets\run.xml:111: The following error occurred while executing this line:
C:\Users\cwa38\AppData\Local\NetBeans\Cache\12.4\executor-snippets\run.xml:68: Java returned: 1
I tried to run your project in my netbeans but failed with
project com.cwa380:KeyColors:jar:1.0: logitech:gaming:jar:9.00 was not found in https://repo.maven.apache.org/maven2 during a previous attempt. This failure was cached in the local repository and resolution is not reattempted until the update interval of central has elapsed or updates are forced
then I created a new project from ant, as long as you are using this logitech jar which is not really in maven repository, you dont need to have maven project.
then I added the logiled.jar as image below
good news when I run the code I get no exception. output below
and when I try to use code completion netbeans lists the functions nicely.
here are my versions of tools
Product Version: Apache NetBeans IDE 12.4
Java: 17; OpenJDK 64-Bit Server VM 17+35-2724
Runtime: OpenJDK Runtime Environment 17+35-2724
System: Windows 10 version 10.0 running on amd64; Cp1252; en_US (nb)
User directory: C:\Users\ozkan\AppData\Roaming\NetBeans\12.4
Cache directory: C:\Users\ozkan\AppData\Local\NetBeans\Cache\12.4
in github I found https://github.com/larsgrefer/logi-led you may contact with Lars(#larsgrefer), looks like he knows how to use logitech SDK.
here is my project https://github.com/ozkanpakdil/spring-examples/tree/master/JavaApplication1 make sure you have added logiled jar in correct path to libraries
I am using Hot Code Replace feature when Tomcat is running from eclipse and it works great.
But, how can I do this manually when Tomcat is running outside eclipse?
After some searching, I have found that I need to use an agent like HotswapAgent. But, they are using this agent with modified JDK called DCEVM. I don't want to use modified JDK. I want to achieve the same thing with OpenJDK.
I know that modification will be limited to method body only but, that's not a problem for me. How can I achieve the exact same thing eclipse is doing for Hot Code Replace for an externally running Tomcat without using IDE?
Edit : Eclipse example is just to clarify what I want to achieve. I do not want to use eclipse at all. I just want to do Hot Code Replace in an application running in Tomcat.
Yes, it's possible to perform Hot Code Replace in a running JVM. This involves several steps.
Prepare (compile) the new version of classes you want to replace. Let's say, you want to replace org.pkg.MyClass, and the new version of this class is located at /new/path/org/pkg/MyClass.class
Create a Java Agent that uses Instrumentation API to redefine the given class. Here is how the simplest agent may look like:
import java.lang.instrument.*;
import java.nio.file.*;
public class HotCodeReplace {
public static void agentmain(String args, Instrumentation instr) throws Exception {
Class oldClass = Class.forName("org.pkg.MyClass");
Path newFile = Paths.get("/new/path/org/pkg/MyClass.class");
byte[] newData = Files.readAllBytes(newFile);
instr.redefineClasses(new ClassDefinition(oldClass, newData));
}
}
Compile the above agent and pack it into .jar with the following MANIFEST.MF
Agent-Class: HotCodeReplace
Can-Redefine-Classes: true
The command to create HotCodeReplace.jar:
jar cvfm HotCodeReplace.jar MANIFEST.MF HotCodeReplace.class
Load the agent .jar into the target JVM. This can be done with Attach API or simply with jattach utility:
jattach <pid> load instrument false /path/to/HotCodeReplace.jar
More about Java agents »
The below error compelled me to dig into the build process of a cap file in the command line without using the IDE. So now, I can build a cap file from the command line using java/javac series of commands. But I have this one applet which successfully created a cap file if built via eclipse IDE but I am encountering an error when i try to build in the command line. I am also having same error when I tried in a correctly setup gradle build settings/environment. This is the error:
[ant:convert] [ INFO: ] Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
[ant:convert]
[ant:convert]
[ant:convert] warning: You did not supply export file for the previous minor version of the package
[ant:convert] [ INFO: ] conversion completed with 1 errors and 1 warnings.
[ant:convert] error: Class org/dx/tools/TestApplet, specified in -applet option, is abstract.
Take note, this is a working and tested applet in combination with other applets that uses this one. And this builds in the eclipse IDE.
Also I am able to generate the .class file. The problem is during the convertion of the class file to cap file.
Here is how it look like:
package org.dx.tools;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
import javacardx.apdu.ExtendedLength;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.Util;
import javacard.framework.APDU;
import javacard.framework.APDUException;
import javacard.framework.Applet;
import javacard.framework.AppletEvent;
public abstract class TestApplet extends Applet implements AppletEvent,
ExtendedLength {
...
}
This .cap is one among five others. The others are not abstract, but inherits from this one. Since it build in eclipse IDE, I can actually build the other cap files by taking the output of the IDE had produce. First, I jar the classes since as I said I can create the classes and feed to java conversion command to build the other caps, and I also use the TestApplet.exp that the IDE generated.
UPDATES: 2019/11/17
Here is the actual java command options that is able to build from .class to .cap. I took this as exactly from the Eclipse IDE conversion log.
If an answer can confirm this is a current gradle limitation then I will accept this answer. If answer can show what is the correct gradle settings to make it work then I will accept this answer. Thanks.
This is very likely due to linking to a newer or older version of a library, while targeting a different Java Card version during your build process. Note that the converter creates pre-linked code, so it expects to link against the correct versions.
This is different from Java SE, where the classes can be linked at runtime and the location of methods and fields doesn't matter, as they are created during runtime.
I've resolved the issue by removing the applet {} block in build.gradle. As the particular cap being built is an abstract class, it does not have an applet aid.
I'm using netbeans 7.4 / jdk 1.7u51
I downloaded the jar for JNA from official site, in version 4.0.0.
I have an internally developped DLL whose interface is in plain C, which loads perfectly well with ctypes in python. This dll is compiled in release with visual 2010, whose runtime are in path.
D:\fl006\Downloads>dir D:\deploy\SpotLight\spotlight-1488\PasanBusLibrary.dll
Directory of D:\deploy\SpotLight\spotlight-1488
29.01.2014 11:13 1'690'112 PasanBusLibrary.dll
I tried to load it in java with jna:
public interface CLibrary extends Library {
(...snip...)
void pasanIpcInitializeLibrary(String xClient, String xBusName, int xTimeout);
void pasanIpcTerminateLibrary();
}
public static void main(String[] args) {
NativeLibrary.addSearchPath("PasanBusLibrary","D:\\deploy\\SpotLight\\spotlight-1488");
CLibrary Bus = (CLibrary) Native.loadLibrary("PasanBusLibrary",CLibrary.class);
(... snip ...)
This is basically an out of the book standard dll load, from a custom location.
When activating jna debug, I see the following :
run:
Looking in classpath from sun.misc.Launcher$AppClassLoader#714a8f44 for /com/sun/jna/win32-x86-64/jnidispatch.dll
Found library resource at jar:file:/D:/code/perso/TestWrapperBus/jna-4.0.0.jar!/com/sun/jna/win32-x86-64/jnidispatch.dll
Looking for library 'PasanBusLibrary'
Adding paths from jna.library.path: null
Trying D:\deploy\SpotLight\spotlight-1488\PasanBusLibrary.dll
Adding system paths: []
Trying D:\deploy\SpotLight\spotlight-1488\PasanBusLibrary.dll
Looking for lib- prefix
Trying libPasanBusLibrary.dll
Looking in classpath from sun.misc.Launcher$AppClassLoader#714a8f44 for PasanBusLibrary
Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'PasanBusLibrary': Native library (win32-x86-64/PasanBusLibrary.dll) not found in resource path ([file:/D:/code/perso/TestWrapperBus/jna-4.0.0.jar, file:/D:/code/perso/TestWrapperBus/jna-platform-4.0.0.jar, file:/D:/code/perso/TestWrapperBus/build/classes/])
at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:271)
at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:398)
at com.sun.jna.Library$Handler.<init>(Library.java:147)
at com.sun.jna.Native.loadLibrary(Native.java:412)
at com.sun.jna.Native.loadLibrary(Native.java:391)
at testwrapperbus.TestWrapperBus.main(TestWrapperBus.java:39)
It looks it looks through the location I gave and somehow discards it. I tried different folders and I got same behaviour, there is no obvious file system right issue (dll is RW from all users)
Any clue on what I'm missing, I'm kind of stuck currently...
EDIT
if I load "msvcrt" this is working nice to cll printf
my dll has some dependencies, all of them hosted in c:\windows\system32 (standard runtime, dynamically linked)
My dll is a win32 compilation while I use a win64 JDK / JRE. Of course, when dealing with pure java, we don't care but loading native library needs to match.
I tried running from command line on a 32 bits JRE7 and it worked, so I'm pretty sure that installing JDK for win32 in my netbeans or recompiling my dll in 64 bits will solve the issue.
Thanks to this answer : Trying to use DLL from Java (JNA). Unable to load library exception for having put me on the righteous path
I am creating a program along the lines of Codingbat.com.
During Runtime, it needs to compile code, and then execute it. This has all been handled.
Currently, I am forced to use the JavacTool, which requires it to be packed alongside.
I have 2 basic questions:
1) How can I stop the ToolProvider.getSystemJavaCompiler() from returning null when ran from an executable jar?
2) If the above is not possible, is there a way to add the jar of com.sun.tools.javac.api.JavacTool; without having it as a referenced library, so that it acts like a regular import?
Thanks for answering this, if you would like, I could upload the Jar with the referenced library, and the jar without it.
Just to be clear, the one with the referenced library works, but it is way to large, and slower then the jar that is ran through eclipse, that uses the JavaCompiler, not the JavacTool
Thanks
Edit:
I am pretty sure this is possible with java as I have seen it before, yet forget where and how.
I suspect it's just a problem of which version of Java you run. If you run the version which comes with the JRE, it won't have the tools available. If you run the version which comes with the JDK, it will.
As an example, here's a short but complete program:
import javax.tools.*;
public class Test
{
public static void main(String[] args)
{
System.out.println(ToolProvider.getSystemJavaCompiler());
}
}
Running it with the JRE version of java.exe on my laptop:
c:\Users\Jon\Test>"\Program Files\java\jre7"\bin\java Test
null
And now with the JDK:
c:\Users\Jon\Test>"\Program Files\Java\jdk1.7.0"\bin\java Test
com.sun.tools.javac.api.JavacTool#441944ae
So try explicitly specifying the a Java binary associated with the JDK.
So you already know that all your users will have a JDK installed and you know how to find the classpath of the JDK when you're in your Java program. You don't need to load a DLL. You need to load the com.sun.tools.javac.api.JavacTool class in tools.jar. See How to load a jar file at runtime on how to load tools.jar