Where to start java library path from? - java

I'm currently trying to call some C++ code from Java using JNI. To understand how this would work, I followed a tutorial. I have had some hiccups, but now I am almost there. So far, I have successfully created the Java Class, implemented a method in C++, compiled the code from my windows cmd, and created the library. The last thing I'm struggling with and cannot seem to figure out is how to correctly refer to the library when running the code from the command line.
My folder structure is as follows
.
├── src
└── main
└── java
└── com
└── baeldung
└── jni
└── com_baeldung_jni_HelloWorldJNI.cpp
└── com_baeldung_jni_HelloWorldJNI.cpp~
└── com_baeldung_jni_HelloWorldJNI.h
└── com_baeldung_jni_HelloWorldJNI.o
└── HelloWorldJNI.class
└── HelloWorldJNI.java
└── native.dll
The general set-up for running the code is:
java -cp . -Djava.library.path=/NATIVE_SHARED_LIB_FOLDER com.baeldung.jni.HelloWorldJNI
First of all, it took me some time to figure out the folder from which I had to call this in cmd, to not get the error
Error: could not find or load main class com.baeldung.jni.HelloWorldJNI
I solved this issue calling the previous line while being in the src/main/java directory. I have tried different options for the library path, but all returned
Exception in thread "main" java.lang.UnsatisfiedLinkError: no native in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.baeldung.jni.HelloWorldJNI.<clinit>(HelloWorldJNI.java:6)
Any suggestions on how to set the correct relative library path are more than welcome!
Thanks a lot in advance!

Not a direct answer, just some notes what works for me in a similar situation.
My project creates a jar that consists of some java code and a native dll as resource. The following steps are performed so it works in all my use cases:
I have a class that handles resources. The lookup via the classLoader ensures that it also works when packaged in a fat jar.
public class Resources {
private static final Logger LOGGER = Logger.getLogger(Resources.class.getName());
public static <T> InputStream getResourceAsStream(Class<T> clazz, String name) {
final var classLoader = clazz.getClassLoader();
if (classLoader != null) {
final var result = classLoader.getResourceAsStream(name);
if (result != null) {
LOGGER.info("using resource '" + name + "' obtained via the classLoader '" + classLoader.getName() + "'.");
return result;
}
}
LOGGER.info("using resource '" + name + "' obtained via class '" + clazz.getName() + "'.");
return clazz.getResourceAsStream(name);
}
public static <T> void extractResourceToFilesystem(Class<T> clazz, final Path destination, final String resourceFilename) {
if (!Files.isDirectory(destination)) {
throw new IllegalArgumentException("The configuration path '" + destination.toAbsolutePath() + "' must be a directory");
}
if (!Files.isWritable(destination)) {
throw new IllegalArgumentException("The java process must have write permissions in directory '" + destination.toAbsolutePath() + "'");
}
final Path resourcePath = destination.resolve(resourceFilename);
try(final InputStream in = Resources.getResourceAsStream(clazz, "/" + resourceFilename)) {
Files.copy(in, resourcePath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
if (!Files.exists(resourcePath)) {
throw new IllegalArgumentException("The file '" + resourcePath.toAbsolutePath() + "' could not be created.");
}
LOGGER.info("Successfully extracted resource to " + resourcePath.toAbsolutePath());
}
}
The class that uses the native library first writes the dll to the application directory. This directory is always searched from loadLibrary. I use a singleton that is loaded lazily (you could do the initialization in a static block but I need this for reasons that are not important here)
class MyClass {
public native void foo(...);
public native int bar(...);
private MyClass() {
Resources.extractResourceToFilesystem(MyClass.class, Paths.get(""), "myDll.dll");
System.loadLibrary("myDll");
}
private static class LazyHolder {
private final static MyClass INSTANCE = new MyClass();
}
public static MyClass getInstance() {
return LazyHolder.INSTANCE;
}
}

Related

Not able to load rxtx native library with System.load

I'm currently developing on OS X and trying to load the librxtxSerial.jnilib with System.load(), which just doesn't work and always results in
java.lang.UnsatisfiedLinkError: no rxtxSerial in java.library.path thrown while loading gnu.io.RXTXCommDriver
When I place the lib in /Library/Java/Extensions everything works fine.
I have double checked paths and everything, but it just won't work with System.load when I remove the lib from /Library/Java/Extensions.
I want to bundle the jnilib with a distributable jar, that's why I want to load it programmatically.
Does anybody have an idea?
if you have this:
root:
main.jar
libjni.jnilib
And you run your code at the root directory, using command like:
java -jar main.jar
In this case, your loading code should be like:
System.load("libjni.jnilib");
But bot System.loadLibrary(), load is safer than loadLibrary.
It's recommended to pass the absolute path of your jni library to System.load.
I used something like this in my project:
/**
* To load the JNI library
* Created by ice1000 on 2017/1/6.
*
* #author ice1000
*/
#SuppressWarnings("WeakerAccess")
public final class Loader {
public final static String JNI_LIB_NAME;
private static boolean loaded = false;
/*
* maybe it's already loaded, so there should be a check
*/
static {
JNI_LIB_NAME = "libjni";
loadJni();
}
#NotNull
#Contract(pure = true)
private static String libraryName(#NonNls #NotNull String libName) {
String ___ = System.getProperty("os.name");
String fileName;
if (___.contains("Linux"))
fileName = libName + ".so";
else if (___.contains("Windows"))
fileName = libName + ".dll";
else // if (___.get("OSX"))
fileName = libName + ".dylib";
// else fileName = libName;
return new File(fileName).getAbsolutePath();
}
public static void loadJni() {
if (!loaded) {
System.load(libraryName(JNI_LIB_NAME));
loaded = true;
}
}
}
here's my working directory:
root:
javaClasses.jar
libjni.dll
libjni.so
libjni.dylib
Hope this can help you.
Make sure to put your library on LD_LIBRARY_PATH.
Take a look here for lots of JNI related samples written for macOS/Linux.
http://jnicookbook.owsiak.org
I suggest to start with supper simple Hello world app:
http://jnicookbook.owsiak.org/recipe-No-001/
You can take a look there how to develop with JNI for macOS using standard tools.
Hope this helps. Have fun with JNI.

Java reflection: How can I load a class from another project without adding that project or JAR to my classpath?

Here's my situation:
I have a class in project A. I want to load this class in a method in project B without adding project A (or a JAR containing the class) to my project B classpath (I personally have no objection to doing it that way, but I must build this to spec).
Note that I will not know the absolute path to this class (let's call it "MyClass") as project A may be installed various different locations. I will, however, know how to navigate to it based on my current directory, so that is what I have done.
Here is what I have tried:
// Navigate to the directory that contains the class
File pwd = new File(System.getProperty("user.dir"));
File src = pwd.getAbsoluteFile().getParentFile();
File dir = new File(src.getAbsolutePath() + File.separator + "folder" + File.separator + "src");
// dir is the directory that contains all the packages of project A
// Load the class
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] {dir.toURI().toURL()});
try {
classLoader.loadClass("com.blahblahblah.MyClass");
} catch (ClassNotFoundException exception) {
}
This throws a ClassNotFoundException. Am I doing something wrong?
You need to implement a custom classloader, something like this
Class<?> cls = new ClassLoader() {
public java.lang.Class<?> loadClass(String name) throws ClassNotFoundException {
byte[] a = read class bytes from known location
return defineClass(name, b, 0, a.length);
};
}.loadClass("test.MyClass");

How to create a sample java.lang.SecurityException: sealing violation: package .. is sealed

we've an issue with a long-running Java process suddenly spitting out java.lang.SecurityException: sealing violation: package .. is sealed
after overwriting a sealed jar while the JVM is running.
I kind of understand this exception, but in order to reproduce this (and what follows), I'm trying to artificially create a sealed exception and it's not working.
There's a jar file named 'seal.jar' with a META-INF/MANIFEST.MF file like this:
Manifest-Version: 1.0
Sealed: true
This package contains classes A1, A2, A3 like this:
package sealed;
public class A1
{
public A1()
{
System.err.println("Created version 1.0 of " + this.getClass());
}
}
There's a second jar seal-2.0.jar with the same three classes but printing "version 2.0"
Now a third jar file (run.jar) has this content:
import sealed.A1;
import sealed.A2;
import sealed.A3;
public class SealingTest
{
public static void main(String[] args) {
int cnt=0;
try {
while(true) {
if( cnt%3==0 ) {
new A1();
}
else if( cnt%3==1 ) {
new A2();
}
else if( cnt%3==2 ) {
new A3();
}
Thread.sleep(5000);
cnt++;
}
}
catch (Throwable t) {
t.printStackTrace();
}
}
}
And I thought the this code would then create a sealed exception when I do the following:
C:\CodeStreet\FTP>java -cp run.jar;seal-1.0.jar SealingTest
Created 1.0 of class sealed.A1 # now executing: copy seal-2.0.jar seal-1.0.jar
Created 2.0 of class sealed.A2
Created 2.0 of class sealed.A3
Created 1.0 of class sealed.A1
So it looks like A1 is loaded from seal-1.0.jar, but A2 is loaded from the overwritten seal-1.0.jar.
Shouldn't there be a sealing violation because the first class (A1) is loaded from seal-1.0.jar and the second class (A2) from the overwritten file ?
Please try the following:
remove A3.java from seal-1.0.jar, but keep it in seal-2.0.jar;
have both sealed jars in the classpath and run the test program.
That should give you the desired "SecurityException: sealing violation".

"Hello World" with Java annotations

Problem description: Compile 2 jar files independently, without having to include each other on classpath; and at runtime, include both and invoke main() in one jar to print a string on stdout. Parts of the output string have to come from the 2nd jar.
Constraints: The solution cannot be IDE-dependent or use any other jar files.
Answering what have I done so far: My Solution is described below.
Reason to ask the question: I am trying to figure out if/how to use annotations to solve this problem (hopefully in a more elegant manner), but cannot find any suitable documentation or tutorial. I appreciate any pointer(s) for that also.
My Solution: A batch file (on Unix, please change the backslash to forward slash, semicolon to colon and the rem's to #) as follows:
rem Compile and package the testing class (Main) without any library
javac -d target core\*.java
cd target
jar cvf ..\main.jar .\core
cd ..
rem Compile and package the Greeting and Greeted classes as a library
javac -d lib impl\*.java
cd lib
jar cvf ..\mylib.jar .\impl
cd ..
rem Use the two parts above at runtime and execute main() in Main class
java -cp main.jar;mylib.jar core.Main
There are 2 files in the impl directory and 2 in the core, as follows:
/* File: impl/Greeting.java */
package impl;
public class Greeting {
public String getGreeting () {
return "Hello";
}}
/* File: impl/Greeted.java */
package impl;
public class Greeted {
public String getGreeted () {
return "world";
}
/* File: core/Main.java */
package core;
public class Main {
private String greeting = "Learn annotations", greeted = "keep using Java",
// Can read the following 4 values from a configuration file, too
// Trying to see if we can get these using Java annotations
greetingClassName = "impl.Greeting", greetingMethod = "getGreeting",
greetedClassName = "impl.Greeted", greetedMethod = "getGreeted";
public Main () {
try {
MyRunTime runTime = new MyRunTime();
Object gting = runTime.getInstance(greetingClassName),
gted = runTime.getInstance(greetedClassName),
g1Str = runTime.getResponseNoArg (gting, greetingMethod),
g2Str = runTime.getResponseNoArg (gted, greetedMethod);
if (g1Str instanceof String) greeting = (String) g1Str;
if (g2Str instanceof String) greeted = (String) g2Str;
} catch (Exception ex) {
System.err.println ("Error in Library loading: " + ex.getMessage());
}}
public void greet () {
System.out.println (greeting + ", " + greeted + "!");
}
public static void main (String[] args) {
new Main().greet();
}}
/* File: core/MyRunTime.java */
package core;
import java.lang.reflect.*;
public class MyRunTime {
public Object getResponseNoArg (Object anInstance, String methodName) throws
NoSuchMethodException,
IllegalAccessException,
InvocationTargetException {
Method method = anInstance.getClass().getMethod (methodName);
return method.invoke (anInstance);
}
public Object getInstance (String className) throws
ClassNotFoundException,
InstantiationException,
IllegalAccessException {
Class c = Class.forName (className);
return c.newInstance();
}
}
That's it. I would also like not to mess with the ClassLoader unless absolutely necessary. Once I get a handle on how to do this with annotations, I can look into passing arguments and go forward. Thank you for your help.
I wouldn't use Annotations for this - it's probably worth covering what Annotations are and where they are best used.
Instead I would do something like:
Place different files in each jar
A main class to each jar file, which does the same thing: list the files on the classpath
I think that would meet the project requirements.
To list the files on the classpath you could so something like:
public class Main {
public static void main(final String[] args) throws java.lang.Throwable {
final String list = System.getProperty( "java.class.path" );
for (String path : list.split( ";" )) {
java.io.File object = new java.io.File( path );
if ( object.isDirectory() )
for ( String entry : object.list() ) {
java.io.File thing = new java.io.File( entry );
if ( thing.isFile() )
System.out.println( thing );
else if( object.isFile() )
System.out.println( object );
}
}
}
}

Problems with loading resources during execution

Here's the background of the underlying problem, I am collaborating with a group on a project which uses Swt libraries and I am trying to package the software for deployment. As it turns out SWT is very platform/architecture dependent. I would like to be able to package all six jars (linux, mac, win and 32/64-bit) into the same package and use the appropriate library depending on the system. I realize that it is a tough challenge however, switching to Swing (or anything else) isn't really an option right now.
I have found a number of relevant threads (#Aaron Digulla's thread and #mchr's thread) which provided me valuable insights regarding the problem at hand. I have tried to implement the solution proposed by #Alexey Romanov here. With one difference, as the loadSwtJar() method he proposes is not static, I instantiate the object, and immediately following that, run the method before anything else is done to the object.
It appears as the loading procedure doesn't work properly. My reasoning for this statement is as follows:
If all Swt jars are removed from the classpath of the executable jar file, then Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/swt/events/MouseListener is thrown which is caused by: java.lang.ClassNotFoundException: org.eclipse.swt.events.MouseListener
to me this means that the libraries are not found on classpath, am I mistaken?
If swt jars are left on the classpath then the first jar file is used by the system during execution. Meaning if gtk-linux-x86_64 happens to be the first swt jar on the list of jars then the system tries to use that, regardless if the system is win32 or Mac OSX.
I have tried to add some output to see if the loadSwtJar() method is choosing the right jar, and the output seems right on all platforms I have tried, as in the right package is selected (and the files do exist in the runnable jar). But nevertheless the right library is not loaded hence execution errors occur:
Exception in thread "main" java.lang.reflect.InvocationTargetException caused by for ex: Caused by: java.lang.UnsatisfiedLinkError: Cannot load 32-bit SWT libraries on 64-bit JVM
(Note that this is the error I get on my Linux machine if I change the order of appearance of 64-bit and 32 bit swt libraries on the build.xml file)
So, what seems to be the problem here? Am I missing out on some detail, or is it simply not possible to check system properties and load an appropriate library accordingly?
Finally below is an excerpt of my build file, figured it might help finding the source of the problem.
Thanks in advance,
EDIT: After a long debug session with a colleague, the problem is resolved (except an annoying bug regarding Thread management on MacOS as I mentioned here). It involved tweaking with the ANT build as well as the way the main class was written. (The main class, as it turns out, was extending & implementing references from the SWT library which meant that the code wouldn't compile at all, wrapped the main class with another class and loaded the SWT jars from there which seemed to be enough to tackle the problem)
Thanks and regards to everyone who contributed, especially #Aaron. Really appreciated!
Here is a copy of the latest version of my Main class. Let me know if that works for you. I tested it on Linux (32/64bit) and Windows (32bit).
package de.pdark.epen.editor;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import org.apache.commons.lang.SystemUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.util.StatusPrinter;
import de.pdark.epen.exceptions.WikiException;
public class Main
{
public final static String VERSION = "V0.9 (13.05.2010)"; //$NON-NLS-1$
private final static Logger log = LoggerFactory.getLogger (Main.class);
private static final String ORG_ECLIPSE_SWT_WIDGETS_SHELL = "org.eclipse.swt.widgets.Shell"; //$NON-NLS-1$
/**
* #param args
*/
#SuppressWarnings({"nls", "PMD.SystemPrintln"})
public static void main (String[] args)
{
String msg = "Starting ePen "+VERSION;
System.out.println (msg);
log.info (msg);
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory ();
StatusPrinter.print (lc);
int rc = 1;
try
{
Main main = new Main ();
main.run (args);
rc = 0;
}
catch (Throwable t) //NOPMD
{
ExceptionUtils.printRootCauseStackTrace (t);
}
finally
{
System.out.println ("Done.");
log.info ("Exit {}", rc);
System.exit (rc); //NOPMD
}
}
#SuppressWarnings({"nls", "PMD.SystemPrintln", "PMD.SignatureDeclareThrowsException"})
private void run (String[] args) throws Exception
{
if (!SystemUtils.isJavaVersionAtLeast (150))
{
System.out.println ("Version="+SystemUtils.JAVA_VERSION_INT);
throw new WikiException ("Need at least Java 5 but this Java is only "+SystemUtils.JAVA_VERSION);
}
loadSwtJar ();
URLClassLoader cl = (URLClassLoader) getClass().getClassLoader(); //NOPMD
Class<?> c = cl.loadClass ("de.pdark.epen.editor.EPenEditor");
Class<?> shellClass = cl.loadClass (ORG_ECLIPSE_SWT_WIDGETS_SHELL);
Constructor<?> ctor = c.getConstructor (shellClass);
Object obj = ctor.newInstance (new Object[] { null });
Method run = c.getMethod ("run", args.getClass ()); //$NON-NLS-1$
run.invoke (obj, new Object[] { args });
}
#SuppressWarnings({"nls", "PMD"})
private void loadSwtJar ()
{
try {
Class.forName (ORG_ECLIPSE_SWT_WIDGETS_SHELL);
// Already on classpath
return;
} catch (ClassNotFoundException e) {
// Add the JAR
}
String osName = SystemUtils.OS_NAME.toLowerCase ();
String osArch = SystemUtils.OS_ARCH.toLowerCase ();
String swtFileNameOsPart =
osName.contains("win") ? "win32" :
osName.contains("mac") ? "macosx" :
osName.contains("linux") || osName.contains("nix") ? "linux" :
null;
String swtFileNameUiPart =
osName.contains("win") ? "win32" :
osName.contains("mac") ? "cocoa" :
osName.contains("linux") || osName.contains("nix") ? "gtk" :
null;
if (null == swtFileNameOsPart)
{
throw new RuntimeException ("Can't determine name of SWT Jar from os.name=[" + osName + "] and os.arch=["
+ osArch + "]");
}
String swtFileNameArchPart = osArch.contains ("64") ? ".x86_64" : ".x86";
if(".x86".equals(swtFileNameArchPart) && "macosx".equals(swtFileNameOsPart)) {
swtFileNameArchPart = "";
}
String swtFileName = "org.eclipse.swt." + swtFileNameUiPart + "." + swtFileNameOsPart + swtFileNameArchPart + "-3.6.0.jar";
File file = new File ("swt", swtFileName);
if (!file.exists ())
{
throw new RuntimeException ("Can't locate SWT Jar " + file.getAbsolutePath ());
}
try
{
URLClassLoader classLoader = (URLClassLoader) getClass ().getClassLoader ();
Method addUrlMethod = URLClassLoader.class.getDeclaredMethod ("addURL", URL.class);
addUrlMethod.setAccessible (true);
URL swtFileUrl = file.toURI ().toURL ();
log.info ("Adding {} to the classpath", swtFileUrl);
addUrlMethod.invoke (classLoader, swtFileUrl);
}
catch (Exception e)
{
throw new RuntimeException ("Unable to add the swt jar to the class path: " + file.getAbsoluteFile (), e);
}
}
}
You could use Java Web Start as a bootstrap mechanism for your multi platform SWT application. See a corresponding entry in SWT FAQ.
Alternatively, you could put SWT native libraries for each platform into a separate folders and specify them -Djava.library.path in your platform-specific startup script.

Categories