reflection between java versions - java

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.

Related

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

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.

The opens directive in Java 9

I'm reading the draft of the Java 9 specification but this phrase is not clear to me:
The opens directive specifies the name of a package to be opened by the current module. This makes public and protected types in the package, and their public and protected members, be accessible to code in other modules at run time only. It also makes all types in the package, and all their members, be accessible via the reflection libraries of the Java SE Platform.
If the opens makes public and protected accessible at runtime only, what does meaning that all types in the packages area accessible via reflection?
I don't understand the difference between runtime and reflection.
It seems like the opened package makes accessible only public and protected at runtime (via reflection?) and also other packages not specified with type and members accessible vie reflection (also private...).
Let's say you write some code that uses a public class from a library.
import somelibrary.somepackage.SomeClass; // <-- public class from a library.
public class Main {
public static void main(String[] args) {
SomeClass.doSomething();
}
}
You then compile this code, and it compiles fine, since the class you're using is public.
Now, in the next version of the library, the package is added to a module, but not exported. That means that if you try to run your code with this new module on the runtime module path, it would throw an exception because you're trying to access an encapsulated package.
In order to make your code work again, you could use the command line option to open this module to your code, so that it can continue to use the encapsulated package.
Alternatively, the creator of the library could add opens somepackage; to the module definition of the library. That would allow you to run your code using this new version, but not compile with it. I.e. the public and protected members are only accessible at runtime, but there is no reflection involved.
The same goes for when you extend a class, and want to access a protected member of a super class that is in the encapsulated package.
But the opens directive does not change the fact that, if in the next version of a library, a method or field is made private, that you get an IllegalAccessError if you try to use it:
class SomeClass { // <-- the class in the library
public static void doSomething() {
System.out.println("doSomething"); // contrived example code
}
}
...
public class Main {
public static void main(String... args) throws Exception {
SomeClass.doSomething(); // this call compiles fine,
}
}
Then, in the next version of the library doSomething is made private:
private static void doSomething() {...}
And re-compiled. But if you try to run the old version of Main with the new version of SomeClass you get an IllegalAccessError.
In short, opens only works for members that are still public or protected in the new version of the library.
However, in the case of reflection, you can always access a private member, by using setAccessible(true):
public static void main(String... args) throws Exception {
Method m = SomeClass.class.getDeclaredMethod("doSomething");
m.setAccessible(true);
m.invoke(null); // works Fine
}
So in the case of reflection, opens would also make encapsulated private members accessible again.
A package opened by a module, may be qualified or unqualified.
The opens directive in a module declaration declares a package to be
open to allow all types in the package, and all their members, not
just public types and their public members to be reflected on by APIs
that support private access or a way to bypass or suppress default
Java language access control checks.
--Documentation

Subclass obtained from Serviceloader throws AbstractMethodError

I am trying to make a Java app that can load plugins implementing an abstract class and am having an AbstractMethodError with the instances generated from ServiceLoader. The code is a bit heavy so I've made a simplification below.
First, I have an abstract class:
package stuff.TheAbstractClass;
public abstract class TheAbstractClass implements ClassInterface{
//Stuff happens
}
Which implements the below interface:
package stuff.ClassInterface;
public interface ClassInterface {
public String getClassName();
}
I have a service provider NotAbstractClass which extends TheAbstractClass and states so in a meta-inf/services folder:
package anotherstuff.NotAbstractClass;
public final class NotAbstractClass extends TheAbstractClass implements ClassInterface{
private String name = "Silent Bob";
#Override
public String getClassName() { return name; }
}
Then on the main application (which is actually a plugin inside another application), I want to find all classes which extend TheAbstractClass:
package stuff.TheApp;
import java.util.ServiceLoader;
public class TheApp {
private String name;
public final static TheApp INSTANCE = new TheApp();
private TheApp() {
ServiceLoader<TheAbstractClass> serviceLoader =
ServiceLoader.load(TheAbstractClass.class);
for (TheAbstractClass class: serviceLoader) {
name = class.getClassName;
}
}
My application does find NotAbstractClass. I know this since, in the for loop, I can do class.getName() and it'll give me anotherstuff.NotAbstractClass) but gives the error:
java.lang.AbstractMethodError: stuff.TheAbstractClass.getClassName()Ljava/lang/String;
I'm stumped. Any suggestion? Thank you, Pedro
According to the API for AbstractMethodError you get this:
Thrown when an application tries to call an abstract method. Normally,
this error is caught by the compiler; this error can only occur at run
time if the definition of some class has incompatibly changed since
the currently executing method was last compiled.
Just by looking at your code and your comment I see that this could only have happened at runtime.
If that is the case then:
some class has incompatibly changed since the currently executing
method was last compiled
I've tested your logic after some adjustments in a Java compatible form and I had no problems. The only thing that seems to be happening is a change in any of the subclasses of TheAbstractClass.
Another thing I did was to declare the dependencies using the dependency files in: resources/META-INF/services:
file: <full-package>.TheAbstractClass
content: <full-package>.NotAbstractClass
After this I had no problems.
It seems the issue wasn't in the code, but in the IDE (IntelliJ). I deleted all previously packaged jars and made new jars without changing anything and it magically worked... So it's an IDE bug, and not a language issue!
Thanks to #Joao and #hotzst for taking time to read however.
Best, Pedro

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);
}

Determining whether library class is used from GWT app or regular Java

I'm writing a couple of library classes that I am sharing between several projects. Some of these projects are plain-old Java and others are GWT applications. For some of these classes the exact implementation is different whether they need to run in GWT or in Java (Let's not get into exactly why, but just as one of many examples, Date.getMonth is deprecated in Java, but the Calendar replacement isn't available in GWT).
Is there a way to mark certain sections of code as pertaining to one or the other scenario?
I looked at using deferred binding and class-replacement in GWT, but that requires instantiation of classes using GWT.create() which isn't available for a plain-old Java app and will therefore lead to compile errors.
Found a solution that works beautifully: the <super-source> tag in my library's .gwt.xml file!
Basically, I have two versions of the following EnvironmentFlags class in my library. One in the actual library that is used by Java located in folder "lib":
my.library.EnvironmentFlags looks like this:
package my.library;
public class EnvironmentFlags {
public static final boolean IS_GWT = false;
public static final boolean IS_DEV_MODE = false;
}
And then a file in the folder "super/my/library" that looks like this:
package my.library;
import com.google.gwt.core.client.GWT;
public class EnvironmentFlags {
public static final boolean IS_GWT = true;
public static final boolean IS_DEV_MODE = !GWT.isProdMode();
}
Now the magic: The .gwt.xml file of my library looks like this:
<module>
<source path='lib' />
<super-source path='super' />
</module>
This leads to plain-old Java using the first version of the EnvironmentFlags class, which simply sets both flags to false, while the GWT compiler replaces the source of that class with the second version loaded from the super-source directory, which sets the GWT flag to true and the DEV_MODE flag to whatever it gets from GWT.
Now, in my code I can simply use the following:
if (EnvironmentFlags.IS_GWT) {
// Do GWT stuff
} else {
// Do plain-old Java stuff
}
and both, the Java and the GWT compiler should drop the respective unreachable/unneeded code from the compiled result, i.e. no run-time overhead needed.
PS: The IS_DEV_MODE flag doesn't actually have anything to do with my original question. I just included it as a freebie which allows me to have my code act differently (more verbose, for example) depending on whether I am testing or deploying my GWT app.
Sounds like you could use the static GWT.isClient() which returns true if your code is running in GWT environment (Dev or Production) or false elsewhere. You'll have to include gwt-user.jar in your server classpath. For example, running the following in a JVM:
import com.google.gwt.core.shared.GWT;
public class Main {
public static void main(String[] args) {
System.out.println(GWT.isClient() ? "Running client-side."
: "Running server-side.");
}
}
Will produce Running server-side. in your console.

Categories