How can I programmatically get the JNA version being used at runtime? - java

I'm trying to print the JNA version being used to my logs, at runtime.
How do I get the version of JNA through code at runtime?

The current JNA version is written at build time to a constant VERSION in the appropriately-named Version interface. That interface is package private, but is implemented by the Native class, making the constant publicly available from Native. (Linters may complain.) So you can simply do:
import com.sun.jna.Native;
public class Test {
public static void main(String[] args) {
System.out.println("JNA Version: " + Native.VERSION);
}
}
Output:
JNA Version: 5.6.0
You can also get the version of the native bits, which follow a different numbering scheme than the overall project version (which is incremented with new mappings that don't change the compiled native portions), but may be relevant in some contexts:
System.out.println("JNA Native Version: " + Native.VERSION_NATIVE);
The Native class also exposes a boolean isCompatibleVersion() method which you can use to check whether JNA is at least the specified version or higher.

Related

reflection between java versions

Let’s say that in hypothetical Java version X.1 we have a class in the standard library (or third-party with backwards compatible API and version tied to Java version)
public class String {
private final byte[] bytes;
//...
}
and in Java X.2 it has changed internally
public class String {
private final char[] chars;
//...
}
We also have a class that accesses String class field, which is a part of a plugin for a server
public class Accessor {
public static Field getField() throws ReflectiveOperationException {
return String.class.getDeclaredField(“bytes”);
}
Server runs on Java X.2, plugin was compiled using Java X.1 and loaded at runtime
What will happen? Which String class will the Accessor see? If it will be X.1 then what happens if server and plugin share a String?
If it will be X.2 is there a way to force a specific version at compile-time or in the package configuration, or at least determine from which Java version the currently visible String class comes from?
If we talk about java.lang.String class or any other class from jvm then it is usually a part of a JVM where the code is run. And in your case you'll get version x.2.
But if we talk about custom compiled class from a 3rd party library then it depends on the target version that was specified during compilation of that class. In your case it might be version 1.x.

Java - Inject java agent in to running jvm

Basically, I am trying to write something that lists every class loaded by the JVM. What I wrote works, but it only works for the jvm it is running on.
I crafted a java agent to dynamically inject into another JVM, but then realized I don't actually know how to inject it.
How do I actually send this agent into another JVM?
Is it possible?
Dynamic agents need to declare an agentmain(String, Instrumentation) method which is executed upon attachment within the target VM. You can use the tools.jar dependency which is (until Java 9) only included in a JDK but not a JRE. You can however bundle your agent program with a JDK and attach to JVMs from there.
The biggest pitfall is that the API differs for different VMs; you can however use a library like byte-buddy-agent which contains different implementations for different VMs. An attachment can be done using:
ByteBuddyAgent.attach("my.jar", "my-pid");
This attaches the agent contained in my.jar onto the Java process with id my-id.
Agents can be injected with HotSpot Attach API.
Run the following snippet with $JAVA_HOME/lib/tools.jar on the class path.
VirtualMachine vm = VirtualMachine.attach(PID);
try {
vm.loadAgent(agentJar);
} finally {
vm.detach();
}
Alternatively you may do this with my command-line jattach utility:
$ jattach PID load instrument false /path/to/agent.jar
Note that in order to support dynamic attach, your Java agent should have agentmain method and Agent-Class property in MANIFEST.MF.
As far as I understand from the comment, you are interested in something that can inspect remote JVM from within another Java process. If it is the case, then you need a Serviceability Agent rather than Java Agent.
Serviceability Agent API allows you to attach to another JVM process, to read its memory, to reconstruct VM structures and to inspect remote objects in reflection-like manner.
Here is a sample tool to list all classes loaded by a remote JVM:
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;
public class ListRemoteClasses extends Tool {
public static void main(String[] args) {
new ListRemoteClasses().execute(args);
}
#Override
public void run() {
VM.getVM().getSystemDictionary().classesDo(klass -> {
String className = klass.getName().asString().replace('/', '.');
System.out.println(className);
});
}
}
How to run it:
java -cp $JAVA_HOME/lib/sa-jdi.jar:. ListRemoteClasses PID
It's hard to provide assistance without looking at the content that you've written but this is just to notify that there is a class named as Instrumentation interface (public interface Instrumentation) from java.lang.instrument package that provides services needed to instrument Java programming language code.
One such method provided by this class is getInitiatedClasses which returns an array containing all the classes that are loaded.
Look at the documentation here
getInitiatedClasses
Class[] getInitiatedClasses(ClassLoader loader)
Returns an array of all classes for which loader is an initiating loader. If the
supplied loader is null, classes initiated by the bootstrap class
loader are returned.
Parameters: loader - the loader whose initiated class list will be returned
Returns: an array containing all the classes for which loader is an initiating loader, zero-length if there are none

Registering multiple .dll libraries into a single java class using JNA

First of all some context, to thoroughly explain the methods I've already tried:
I'm working in a java-based programming platform on windows which provides access to custom java functions with several other extensions. Within the source code of this modelling platform, there is a class "CVODE" which grants access to native library "cvode" to import the functionality of a C++ library CVODE.
//imports
public class CVODE {
static {
Native.register("cvode");
}
public static native int ... //methods
}
I created shared libraries from the CVODE library, which resulted in 2 files: sundials_cvode.dll and sundials_nvecserial.dll.
Adding the first library to my java path obviously resulted in
Unexpected Exception UnsatisfiedLinkError: Unable to load library 'cvode': The specified module could not be found.
as the names were not compatible. Therefore I changed the name of sundials_cvode.dll to cvode.dll and retried. Resulting in an error indicating that not all methods are present in the library sundials_cvode.dll:
Unexpected Exception UnsatisfiedLinkError: Error looking up function 'N_VDestroy_Serial': The specified procedure could not be found.
This convinces me that the library is being found and loaded correctly, but not all methods are available. Examining the dll's in question led me to the conclusion that the CVODE class requires functions from both the sundials_cvode.dll and sundials_nvecserial.dll libraries. Therefore I tried changing the platform source-code to
public class CVODE {
static {
Native.register("sundials_cvode");
Native.register("sundials_nvecserial");
}
public static native int ... //methods
}
which still results in
Unexpected Exception UnsatisfiedLinkError: Error looking up function 'N_VNew_Serial': The specified procedure could not be found.
I have confirmed this method is present in both the class file and in the dll:
So I can only guess the error results from calling the Native.register() twice. resulting in the 2nd library not being loaded or an error down the way. I'd appreciate some insight in what I'm doing wrong or how I can gain a better overview of what's going wrong.
As far as I know, you can only load one dll per class, i.e. split the classes into two, each providing the methods the particular dll provides.
See also here: https://stackoverflow.com/a/32630857/1274747

undefined method - geoBoundingBoxQuery(String location)

I am trying to create a geoBoundingBoxQuery("pin.location") in Java for Elastic Search. Eclipse is not able to resolve imports for this method although the Elastic Search Java API documentation specifically suggests to use this method.
What class do I need to import which has this method?
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-geo-queries.html#java-query-dsl-geo-bounding-box-query
geoBoundingBoxQuery("pin.location") is defined as a static method in class org.elasticsearch.index.query.QueryBuilders, so you can use it this way:
QueryBuilder q = QueryBuilders.geoBoundingBoxQuery("pin.location");
Elasticsearch documentation is assuming you're using a static import like:
import static org.elasticsearch.index.query.QueryBuilders.geoBoundingBoxQuery;
so you don't need qualify the method with class name:
QueryBuilder q = geoBoundingBoxQuery("pin.location");
For anyone who is having the same issue, I have found the answer.
Its the version of Elastic search java API that I am using is the issue. The documentation is for version 2.1 whereas I am using an older version. For the older version, you need to do the following:
FilterBuilders.geoBoundingBoxFilter("pin.location")

Dynamically loading class during runtime

I'm developing a Java application for both Windows and Mac OSX. There are some platform-dependent pieces of code. Rather than splitting the source code into two separate branches, the application will establish the platform during runtime and then execute the proper code.
This has worked like a charm. But I'm now using the following code which should only be executed on computers running Mac OSX:
package abc.extension;
import com.apple.eawt.Application;
import abc.Globals;
public class osx {
public static void setApplicationLook() {
Application application = Application.getApplication();
application.setDockIconImage(Globals.iconImage);
}
}
The above code works perfectly fine. But as it imports a class that is only available on Mac OSX, the application will not compile or run on other platforms.
I believe the solution would be to dynamically import com.apple.eawt.Application in the function setApplicationLook().
As that function will only be called on computers running Mac OSX, this will not raise any errors on other platforms.
But how would one go about doing this?
You can either;
make sure every class which you need to compile is available when you compile the program, regardless of the platform you are on or
use the reflection library to call the method so it doesn't need to be available at compile time.
Using reflections instead you can do
// import com.apple.eawt.Application;
Class applicationClass = Class.forName("com.apple.eawt.Application"):
// Application application = Application.getApplication();
Object application = applicationClass.getMethod("getApplication()")
.invoke(null); // static method.
// application.setDockIconImage(Globals.iconImage);
appplicationClass.getMethod("setDockIconImage", Image.class)
.invoke(application, Globals.iconImage);
If you want to avoid reflection, class loading behavior is defined in the specs:
T is a class and an instance of T is created.
T is a class and a static method declared by T is invoked.
A static field declared by T is assigned.
A static field declared by T is used and the field is not a constant variable (§4.12.4).
T is a top level class (§7.6), and an assert statement (§14.10) lexically nested within T (§8.1.3) is executed.
...
A class or interface will not be initialized under any other circumstance.
As long as you have the Mac libraries (maybe even a stub) at compile time, you do not need to worry if you have them at runtime unless you execute a method in a class that uses them.
Took me a while trying combinations of getMethod and random code, but I got there.
public static void setApplicationLook() {
Class aClass = classLoader.loadClass("com.apple.eawt.Application");
Method getApplication = aClass.getMethod("getApplication", null);
Object application = getApplication.invoke(null);
Method setDockIconImage = aClass.getMethod("setDockIconImage", Image.class);
setDockIconImage.invoke(application, Globals.iconImage);
}

Categories