I created a Java code that tries to access the method LoadProject of the class IProjectFactory defined in myAPI.dll. The description of the DLL file says: IProjectFactory is used to load a project file into memory. IProjectFactory is a static class in the myAPI.dll assembly. It exposes the LoadProject method that takes a string containing the path to the file to load, and returns a reference to the resulting IProject. Using the IProject interface you can then manipulate the loaded project in various ways.
import com.sun.jna.Library;
import com.sun.jna.Native;
public class MyClass {
public interface IProjectFactory extends Library {
public Object LoadProject(String fileName);
}
public MyClass() {
//System.loadLibrary("myAPI");
load();
}
void load() {
String fileName = "xxx.sp";
IProjectFactory api = (IProjectFactory) Native.loadLibrary("myAPI",IProjectFactory.class);
try {
Object project = api.LoadProject(fileName);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
After running this code, the following error message has been generated:
Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: Error looking up function 'LoadProject': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:179)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:350)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:330)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.LoadProject(Unknown Source)
Does it mean that myAPI does not contain the class IProjectFactory with the method LoadProject?
You can call functions in native libraries with JNI or JNA. Primitive types are mapped. Certain structures are possible also. Even callback functions are possible. See JNA's mapping table.
What is impossible, by design: Getting a class or interface that was designed for a different runtime environment (like CLR) and use it seamless within the JVM.
So if you have a native procedural/functional library that just returns pointers or primitive types then you can use it quite well.
If you need to work with objects that are returned then you are out of luck. You need to run them in their native environment and find some way of interprocess communication.
Related
I have created a class with the same name and package as the String java class.
package java.lang;
public class String {
public String() {
System.out.println("This is my string ");
}
public void show() {
System.out.println("From show mathod .");
}
}
When I try to invoke this show method it gives error for no such method. It refers to API class. why.?
import java.lang.*;
public class Test {
public static void main(String[] args) {
String str = new String();
str.show();
}
}
I know that I am violating the rule of unique package and class name. But I want to know if I add two jars in my class-path and they have same structure. In such case is there any behaviour defined by Java which class will be loaded.
And why java does not load my class instead of Java API classes? (I also tried this with other api defined classes)
#Please do not give answer like I am violating the rule or work arounds.
Java comes shipped with classes. One of these classes is java.lang.String.
These pre-shipped classes take precedence in the classpath over any class that you might write that have the same package and name.
Whenever you do a new java.lang.String(), regardless if you have a class with the same package and name, the runtime class shipped by Oracle will take precedence.
Since java.lang.String is also a final class, there's no way you will be able to add new methods to it. The best solution is for you to create a new class in a different package or under a different name.
classes can not be redefined after the VM has started and the class has been loaded. As java.lang.String is being used way before your code, it would already be loaded.
I am not sure that you could even achieve this using a byte code enhancement library like javassist
Just tried to write custom ClassLoader to load my class named java.lang.String. Ran out of luck here:
Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:658)
at java.lang.ClassLoader.defineClass(ClassLoader.java:794)
at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
at temp.TempLoader.findClass(TempLoader.java:20)
at temp.TempLoader.main(TempLoader.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Offending code in JRE java.lang.ClassLoader:
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException
("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
It would be interesting to see any "pure Java" solution (not involving out-of-JVM changes to runtime) which can workaround this.
You may wonder how JVM itself loads java.* classes? Well, they loaded by so called bootstrap loader, during initialization of JVM instance. I assume, there's no public API to that classloader.
I don't know what you are trying to do exactly, but I can suggest a different approach.
In java String class is final, so, it can't be extended. Create a wrapper class which has your additional methods and a String field.. Now you can define and call show() on your class (You can't do it on the String directly..) If split() is called on your class, then just delegate the call to String class's split().
expose a method getInstance() in your wrapper . Whenever myWrapper.getInstance() is called, call new String() from there..
I have a web service we'll call service.war. It implements an interface we'll call ServicePluginInterface. During the startup of service.war, it reads in environment variables and uses them to search for a jar (MyPlugin.jar). When it finds that jar, it then uses a second environment variable to load the plugin within the jar. The class that it loads looks like this:
public class MyPlugin implements ServicePluginInterface {...}
The servlet attempts to load the plugin using code like:
try {
if (pluginClass == null) {
plugin = null;
}
else {
ZipClassLoader zipLoader = new ZipClassLoader(Main.class.getClassLoader(), pluginJar);
plugin = (ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
plugin.getAccount(null,null);
}
} catch (Exception e) {
...
}
The trick is that I don't have source or a jar for ServicePluginInterface. Not wanting to give up so easily, I pulled the class files out of the service.war files. By using those class files as dependencies, I was able to build, without compiler warnings, MyPlugin. However, when actually executed by Tomcat, the section of code above generates a runtime exception:
java.lang.ClassCastException: com.whatever.MyPlugin cannot be cast to com.whomever.ServicePluginInterface
As a second point of reference, I am also able to construct a synthetic class loader (separate java executable that uses the same class loading mechanism. Again, since I do not have the original source to ServicePluginInterface, I used the class files from the WAR. This second, synthetic loader, or faux-servlet if you will, CAN load MyPlugin just fine. So I would postulate that the Tomcat JVM seems to be detecting some sort of difference between the classes found inside the WAR, and extracted class files. However, since all I did to extract the class files was to open the WAR as a zip and copy them out, it is hard to imagine what that might be.
Javier made a helpful suggestion about removing the definition of ServicePluginInterface, the problem with that solution was that the ZipClassLoader that the servlet uses to load the plugin out of the jar overrides the ClassLoader findClass function to pull the class out of the JAR like so:
protected Class<?> findClass(String name) throws ClassNotFoundException
{
ZipEntry entry = this.myFile.getEntry(name.replace('.', '/') + ".class");
if (entry == null) {
throw new ClassNotFoundException(name);
}
...
}
The class ZipClassLoader then recursively loads all parent objects and interfaces from the jar. This means that if the plugin jar does not contain the definition for ServicePluginInterface, it will fail.
Classes defined by different class loaders are different:
At run time, several reference types with the same binary name may be
loaded simultaneously by different class loaders. These types may or
may not represent the same type declaration. Even if two such types do
represent the same type declaration, they are considered distinct. JLS
In that case zipLoader returns an instance of MyPlugin that implements the other ServicePluginInterface (is it loaded from the zip too?):
(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
It seems that the application server already has a definition of ServicePluginInterface, then you don't need to redeploy it. It should be enough to add the required files (ServicePluginInterface, etc.) as non-deployed dependecies of your project.
Another approach goes by living with the fact, and accessing methods in ServicePluginInterface via reflection (use the Class object returned by zipLoader, instead of ServicePluginInterface.class).
I am trying to use a library that uses JNI.
I've tried the sample app provided by the developers and it works. So I know it's not a bug in the library.
I assume I'm doing something wrong in the process of importing the library:
copy the .so file into my libs folder (its called libjniRTSP.so)
copy the the jniRTSP.java (summarized below) into my project:
public class jniRTSP {
private volatile static jniRTSP libRTSP = null;
public static jniRTSP getInstance() {
if(null == libRTSP) {
synchronized(jniRTSP.class) {
if(null == libRTSP) {
libRTSP = new jniRTSP();
libRTSP.InitProductList();
// DEBUG
libRTSP.SetDebugView(1);
}
}
}
return libRTSP;
}
static {
try {
System.loadLibrary("jniRTSP");
} catch (Exception e) {
e.printStackTrace();
}
}
public native int GetBrandEnableRecorder();
public native int GetBrandEnableLocal();
public native int GetBrandEnableRemote();
...
then in my onCreate() I try to call one of the methods:
jniRTSP.getInstance().Init(.....)
Which returns the error:
UnsatisfiedLinkError: Native method not found com.myuniquepackage.jniRTSP.InitProductList:()I
UPDATE (FIX): instead of just copying the jniRTSP java file, I copied the whole package that contained it, keeping the same package name. I'm not sure if this fixed it because the package name was the issue, or if because it needed one of the other java files that were in that package. Although I'm pretty sure if it was a missing file, it would complain at compile time.
Fairly certain the package declarations have to be the same inside the C code as the Java code.
So the class jniRTSP should be in the com.myuniquepackage package in Java and have the native method InitProductList declared and the C code should have method declared as Java_com_myuniquepackage_jniRTSP_InitProductList
By moving the class you are probably breaking the link, change the package declaration in Java to match the demo project and see if it works, if it does you can change it back and then change it in the C code which is a bit more time consuming but easy enough.
By running System.loadLibrary("myAPI"), I verified that the DLL file "myAPI.dll" can be successfully loaded into my Eclipse Java project. Now I need to call methods specified inside this DLL file from my Java code. To do this, I added JNA to my Java project. Then I wrote the below-given code snippet that should be able to get instances of classes IProject and ProjectFactory (specified in the DLL file).
I still don't understand how to properly implement this with JNA. I checked different threads, e.g. this one, but the ones I checked don't provide an answer. Any help is highly appreciated. Thanks.
import com.sun.jna.Library;
import com.sun.jna.Native;
public class MyClass {
public interface myAPI extends Library {
//...
}
void LoadProj() {
myAPI api = (myAPI) Native.loadLibrary("myAPI",myAPI.class);
String fileName = "xxx.sp";
IProject project; // this is wrong but shows what I am trying to do
try {
project = ProjectFactory.LoadProject(fileName);
}
catch (Exception ex) {
MessageBox.Show(this, ex.Message, "Load failure");
}
}
}
Not sure what problem you are facing but as a practice your myAPI interface should declare all the methods verbatim with appropriate parameter mapping. I don't see any methods inside your interface.
Please checkout the this link as well as the link mentioned above by #Perception
If there are no Java classes or Java source hidden inside this DLL (which would be ... strange), then it will never work this way. You can't instantiate C# classes or use C# interfaces. MessageBox.Show( isn't Java either, it is Windows Forms code.
My application uses the Standard Widget Toolkit (SWT) for it's GUI. My problem is that the 32-bit SWT library does not work on a 64-bit JVM. But I don't want to make people select the correct architecture when getting the software. So, I want to bundle both the 32-bit and 64-bit libraries, and auto-detect the architecture during runtime. I found out I can get the correct architecture of the JVM like so:
if (System.getProperty("os.arch").contains("64")) {
// ...
}
Now all that's left is to load the jar. But the problem is, all the examples I found require that you manually load the class before using it.
Class.forName("MyClass", false, myClassLoader);
So my question is, is it possible to "register" my class loader, so that I don't have to load classes beforehand?
Update: I created my own child class of URLClassLoader and set it as the default class loader with the command line argument -Djava.system.class.loader; but I get this error:
Error occurred during initialization of VM
java.lang.Error: java.lang.NoSuchMethodException: com.program.LibraryLoader.<init>(java.lang.ClassLoader)
at java.lang.ClassLoader.initSystemClassLoader(Unknown Source)
at java.lang.ClassLoader.getSystemClassLoader(Unknown Source)
I think LibraryLoader.<init> refers to the constructor... but it's there (public LibraryLoader(URI[] urls)).
Update 2: Almost there, the JVM runs now. I added this constructor to make it work:
public LibraryLoader(ClassLoader classLoader) {
super(new URL[0], classLoader);
}
But after adding the jars with addPath() (file:lib/jars/swt.jar), it only produces a NoClassDefFoundError. Yes, I double-checked that the file exists.
You could try to inject your custom class loader by means of the "java.system.class.loader" property (see ClassLoader#getSystemClassLoader). However, I'd recommend to use OSGi and let the framework do the complicated stuff.
As part of the constructor for your custom ClassLoader, call definePackage with the appropriate information, with the URL pointing to the desired jar file.
This example shows that the custom class loader is called when I try to instantiate a class from swing, because I defined my class loader as the loader of that package.
import java.net.URL;
public class junk extends ClassLoader {
byte[] dummy = new byte[0];
public static void main(String[] args) throws Exception {
new junk();
new javax.swing.JPanel();
}
public junk() throws Exception {
definePackage("javax.swing","","","","","","",new URL("file://junk.class"));
}
public Class<?> findClass(String s) throws java.lang.ClassNotFoundException{
Class<?> retVal = super.findClass(s);
System.out.println("delegated responsibility for "+s+" to superclass");
return retVal;
}
public Package getPackage(String s) {
Package retVal = super.getPackage(s);
System.out.println("delegated responsibility for "+s+" to superclass");
return retVal;
}
}
Result:
delegated responsibility for javax.swing to superclass