Replace access to sun.misc.VM for JDK 11 - java

In OpenJDK 8, it was possible to access sun.misc.VM and call isDirectMemoryPageAligned and maxDirectMemory.
isDirectMemoryPageAligned is used to size correctly the direct memory to allocate, as done by DirectByteBuffer.
maxDirectMemory is used to report memory statistics as well as access giving the value configured for -XX:MaxDirectMemorySize. Internally, it will set a limit to the allowed consumption of direct memory.
Since OpenJDK 9, the class VM has been moved to jdk.internal.misc and is not available unless --add-export java.base/jdk.internal.misc=xyz is used when running the application.
Is there a "right" way to do this ?
I already tried to use ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getMax() as a replacement for maxDirectMemory but it always returned -1 - meaning that the value was not available. It is also possible to access java.nio.Bits#MAX_MEMORY by reflection, but it remains "hackish".
Note that one could be very very dirty and do the following - working for OpenJDK, Zulu and Oracle 11.0.1 - but it is not the target goal of this question.
public static void tryExportInternal() {
final String moduleName = "jdk.internal.misc";
final Module javaLang = Integer.class.getModule();
// Method used only for tests by the JDK...
final Method exporter;
try {
exporter = Module.class.getDeclaredMethod("implAddExports", String.class);
exporter.setAccessible(true);
exporter.invoke(javaLang, moduleName);
} catch (NoSuchMethodException | IllegalAccessException e) {
LOG.log(Level.INFO, "Cannot access internal Module method", e);
} catch (InvocationTargetException e) {
LOG.log(Level.INFO, "Cannot call internal Module method", e);
}
}
In the source code, implAddExports is marked as #apiNote This method is for JDK tests only. :(

This answer comes from the various comments of Alan Bateman to the question.
No, there are no standard API to access the two wanted methods.
Since JDK 6, DirectxxxBuffers are not paged aligned anymore. Thus, accessing VM.isDirectMemoryPageAligned is not needed to reproduce what DirectBuffers do.
About manually memory allocation, being the use-case behind the question, the only API to do direct memory allocation is currently ByteBuffer.allocateDirect, or its JNI alternative NewDirectByteBuffer.
Comment references: 1 2 3

Related

Java 10 Panama Project - port JNI solutions to Panama

I have been reading about the Panama Project recently.
I understand that it will be the next generation replacement to JNI - it will allow java developers to code on the native layer using Java (which is amazing IMHO).
The usage is simple from what I can tell looking at jnr-posix, for example:
public class FileTest {
private static POSIX posix;
#BeforeClass
public static void setUpClass() throws Exception {
posix = POSIXFactory.getPOSIX(new DummyPOSIXHandler(), true);
}
#Test
public void utimesTest() throws Throwable {
// FIXME: On Windows this is working but providing wrong numbers and therefore getting wrong results.
if (!Platform.IS_WINDOWS) {
File f = File.createTempFile("utimes", null);
int rval = posix.utimes(f.getAbsolutePath(), new long[]{800, 200}, new long[]{900, 300});
assertEquals("utimes did not return 0", 0, rval);
FileStat stat = posix.stat(f.getAbsolutePath());
assertEquals("atime seconds failed", 800, stat.atime());
assertEquals("mtime seconds failed", 900, stat.mtime());
// The nano secs part is available in other stat implementations. We really just want to verify that the
// nsec portion of the timeval is passed through to the POSIX call.
// Mac seems to fail this test sporadically.
if (stat instanceof NanosecondFileStat && !Platform.IS_MAC) {
NanosecondFileStat linuxStat = (NanosecondFileStat) stat;
assertEquals("atime useconds failed", 200000, linuxStat.aTimeNanoSecs());
assertEquals("mtime useconds failed", 300000, linuxStat.mTimeNanoSecs());
}
f.delete();
}
}
// ....
// ....
// ....
}
My question is this - having worked with JNI, and knowing how cumbersome it is, will there be a solution for porting existing JNI solutions to the Panama format?
IE - go over the generated (via the deprecated javah) C header file and given implementation in C of the header file, identify functions which can be replaced by the Panama API, then generate a java output file?
Or will existing JNI solutions need to be refactored by hand?
Additional links :
OpenJDK: Panama
Working with Native Libraries in Java
JEP 191: Foreign Function Interface thanks to a comment made by Holger
The JNI format is as follows:
Java -> JNI glue-code library -> Native code
One of the goals of project panama is to remove this middle layer and get:
Java -> Native code
The idea is that you can use a command line tool to process a native header (.h) file to generate a Java interface for calling the native code, and the JDK code will do the rest at runtime as far as connecting the 2 together goes.
If your current JNI code does a lot of stuff in this glue-code layer, then that might have to be re-written on the Java side when porting to panama. (this depends on how much could be done automatically by the used interface extraction tool).
But if you are using something like JNA or JNR then moving to panama should be relatively easy, since those 2 have very similar APIs, where you bind an interface to a native library as well.
But questions like:
will there be a solution for porting existing JNI solutions to the Panama format?
Are difficult to answer, since nobody can predict the future. I feel that there are enough differences between panama and JNI that an automatic 1-to-1 conversion between the 2 will probably not be possible. Though if your glue-code is not doing much besides forwarding arguments then the interface extraction tool will probably be able to do all the work for you.
If you're interested you could take a look at the early access builds of panama that started shipping recently: https://jdk.java.net/panama/
Or watch a recent talk about it: https://youtu.be/cfxBrYud9KM

Accessing 64-bit Registry in 32-bit application

Please do not mark this question as duplicate!
I'm searching for a solution in java - not C# - and used the WinRegistry class.
I wrote a program that can readout a registry key. Now the problem: the java application is 32bit and I want to read the reg-keys from a windows 7 64bit-system. With this code windows will redirect my 32bit program to the 32bit section of the 64bit-registry (compare the real path with the comment in the code - Wow6432Node!).
// only access to "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run"
value = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", "Citrix Login Service");
I deleted the try-catch-block so you are able to focus the real problem more better ;).
I solved this now - thanks goes to Petrucio who posted this solution in 2012: read/write to Windows Registry using Java.
E.g. - Read Operation:
try {
String value = WinRegistry.readString(WinRegistry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", "TestValue", WinRegistry.KEY_WOW64_64KEY);
System.out.println(value);
} catch (Exception ex) {
ex.printStackTrace();
}
I hope that is usefully for someone.

Another odd NoClassDefFoundError on WhiteSpaceProcessor

I'm strugelling with an odd problem for days now.
Only one of the users of my webapp get an NoClassDefFoundError when trying to use some functionallity. This is the stacktrace:
java.lang.NoClassDefFoundError: com/sun/xml/bind/WhiteSpaceProcessor
at com.sun.xml.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:105)
at com.foo.bar.webservice.generated.GetLoginsRequest_JaxbXducedAccessor_panelId.parse(TransducedAccessor_field_Integer.java:32)
at com.sun.xml.bind.v2.runtime.unmarshaller.StructureLoader.startElement(StructureLoader.java:166)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:406)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:384)
at com.sun.xml.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:35)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:101)
at com.sun.xml.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:224)
at com.sun.xml.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:107)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:289)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:272)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:106)
at org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:424)
On a strange way WhiteSpaceProcessor can't be found while it is on the classpath.
I used tattletale to look at the possitions of the usage of the classes:
WhiteSpaceProcessor only exist once on the classpath:
DatatypeConverterImpl only exist once on the classpath
I'm stuck on the fact that the exact war on a different environment is working perfect.
working environment:
Windows machine
Tomcat 5.5.28
Java 5 (jdk1.5.0.22)
none working environment:
Linux machine
Tomcat 5.5.??
Java 5 (jdk1.5.0.22)
I hope somebody can sent me in the right direction.
tomcat server is already restarted
Did you use tattletale on the working or non-working machine?
Perhaps the failing environment contains some jar file in jre/lib/ext (or a similar extensions directory), and that's being used in preference to a "lower down" version?
EDIT: Just to go into a bit more detail about the situations in which NoClassDefFoundError can be thrown, it's worth reading the JVM spec, chapter 5. It talks about three situations:
The resource corresponding to the class can't be found at all
The resource is found, but doesn't correspond to the right class (although in that case I'd expect a message including "wrong name")
You're using a version of Java earlier than 1.2, and the class file has an unsupported major/minor version number. (This situation now throws UnsupportedClassVersionError.)
Also read section 2.17.5: it states that if the class is in an "erroneous state" (e.g. previously initialization failed, or there was a bytecode verification failure) then NoClassDefFoundError will be thrown.
Now, if the static initializer of the class fails then the first caller sees an ExceptionInInitializerError - but the second caller sees NoClassDefFoundError. Here's a short but complete program to demontrate this:
class Foo {
static {
if (true) {
throw new RuntimeException();
}
}
static void foo() {
}
}
public class Test {
public static void main(String[] args) {
try {
Foo.foo();
} catch (Throwable t) {
System.out.println("First exception: " + t);
}
try {
Foo.foo();
} catch (Throwable t) {
System.out.println("Second exception: " + t);
}
}
}
Now unless something in your system is suppressing the ExceptionInInitializerError, I'd expect to see that in the log before NoClassDefFoundError if that were the problem. I still think it's more likely that your failing system is loading one class in an extension classloader which then can't find the ShiteSpaceProcessor class.
NoClassDefFoundError does not mean that the class file cannot be found in the classpath. It means that the class cannot be loaded. This is generally due to an error during initialization, or, more often, a version mismatch in JAR files on which the class depends.
Eg, you probably compiled against XYZ package version 1.2 and your user has XYZ version 1.1 installed.

Is -Djava.library.path=... equivalent to System.setProperty("java.library.path", ...)

I load an external library that is placed in ./lib. Are these two solutions to set the java.library.path equivalent?
Set path in console when executing jar:
java -Djava.library.path=./lib -jar myApplication.jar
Set path in the code before loading library:
System.setProperty("java.library.path", "./lib");
If they are equivalent, why in the second solution can Java not find the library while the first one is ok?
If not, is there a way the set the path in the code?
Although it is not well documented, the java.library.path system property is a "read-only" property as far as the System.loadLibrary() method is concerned. This is a reported bug but it was closed by Sun as opposed to getting fixed. The problem is that the JVM's ClassLoader reads this property once at startup and then caches it, not allowing us to change it programatically afterward. The line System.setProperty("java.library.path", anyVal); will have no effect except for System.getProperty() method calls.
Luckily, someone posted a workaround on the Sun forums. Unfortunately, that link no longer works but I did find the code on another source. Here is the code you can use to work around not being able to set the java.library.path system property:
public static void addDir(String s) throws IOException {
try {
// This enables the java.library.path to be modified at runtime
// From a Sun engineer at http://forums.sun.com/thread.jspa?threadID=707176
//
Field field = ClassLoader.class.getDeclaredField("usr_paths");
field.setAccessible(true);
String[] paths = (String[])field.get(null);
for (int i = 0; i < paths.length; i++) {
if (s.equals(paths[i])) {
return;
}
}
String[] tmp = new String[paths.length+1];
System.arraycopy(paths,0,tmp,0,paths.length);
tmp[paths.length] = s;
field.set(null,tmp);
System.setProperty("java.library.path", System.getProperty("java.library.path") + File.pathSeparator + s);
} catch (IllegalAccessException e) {
throw new IOException("Failed to get permissions to set library path");
} catch (NoSuchFieldException e) {
throw new IOException("Failed to get field handle to set library path");
}
}
WARNING: This may not work on all platforms and/or JVMs.
Generally speaking, both approaches have the same net effect in that the system property java.library.path is set to the value ./lib.
However, some system properties are only evaluated at specific points in time, such as the startup of the JVM. If java.library.path is among those properties (and your experiment seems to indicate that), then using the second approach will have no noticeable effect except for returning the new value on future invocations of getProperty().
As a rule of thumb, using the -D command line property works on all system properties, while System.setProperty() only works on properties that are not only checked during startup.
you can add three lines
System.setProperty("java.library.path", "/path/to/libs" );
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
and also import java.lang.reflect.Field
It's ok to solve the problem
This is an addendum to this answer to Jesse Webb's amazing answer above: https://stackoverflow.com/a/6408467/257299
For Java 17:
import jdk.internal.loader.NativeLibraries;
final Class<?>[] declClassArr = NativeLibraries.class.getDeclaredClasses();
final Class<?> libraryPaths =
Arrays.stream(declClassArr)
.filter(klass -> klass.getSimpleName().equals("LibraryPaths"))
.findFirst()
.get();
final Field field = libraryPaths.getDeclaredField("USER_PATHS");
final MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
final VarHandle varHandle = lookup.findVarHandle(Field.class, "modifiers", int.class);
varHandle.set(field, field.getModifiers() & ~Modifier.FINAL);
Since package jdk.internal.loader from module java.base is not normally accessible, you will need to add "exports" and "opens" to both the compiler and JVM runtime args.
--add-exports=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens=java.base/java.lang.reflect=ALL-UNNAMED
Read more here:
--add-exports: https://stackoverflow.com/a/53647605/257299
--add-opens: https://stackoverflow.com/a/61663667/257299
Remove final modifier on Java12+: https://stackoverflow.com/a/56043252/257299

NoClassDefFoundError while accessing GraphicsEnvironment.getLocalGraphicsEnvironment on Tomcat

I have an application which is running on tomcat, one of the methods is, creating a simple thumbnail from an jpeg image. The functions works fine offline and a week ago also on tomcat. But now i get the following error:
java.lang.NoClassDefFoundError
java.lang.Class.forName0(Native Method)
java.lang.Class.forName(Class.java:164)
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:68)
java.awt.image.BufferedImage.createGraphics(BufferedImage.java:1141)
eval.impl.ImageEval.getThumbnail(ImageEval.java:155)
eval.impl.ImageServlet.doGet(ImageServlet.java:79)
javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
I don't think that i have change anything what should influence this (actually i didn't change the function at all according to the svn repository), so it must be a library problem. But i can't figure out what is missing.
Here are the actual lines from the getThumbnail function, where the error occures:
BufferedImage thumbImage = new BufferedImage(thumbWidth,
thumbHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = thumbImage.createGraphics();
graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.drawImage(simage, 0, 0, thumbWidth, thumbHeight, null);
[edit] I decided to update the problem description a little.
Yes it seems that he can not find some class from java.awt or one related to that. But they do exist on the server in the jvm. Java headless mode doesn't solve the problem.
In another project the exact same code, but inside an axis2 webservice on this server is working fine.
[/edit]
It seems like you've change the configuration of Tomcat.
Either you've changed to a l{0,1}[iu]n[iu]x box or installed on a virtual machine with different security control than the one where you test it.
Apparently the
GraphicsEnvironment.getLocalGraphicsEnvironment()
Is trying to access the property: java.awt.graphicsenv
Which may return null or some non existing class name which is then loaded and throws the ClassNotFoundException. 1
The solution seems to be specifying the "java.awt.headless" property.
This is a similar question: java.awt.Color error
Try this search , it shows similar situations as your.
I remember there was something in the sun bugs database too.
Post the solution when you find it!
1.GraphicsEnvironment.java
EDIT
It is not eclipse!!
In my original post there is a link to the source code of the class which is throwing the exception.
Since I looks like you miss it, I'll post it here for you:
public static synchronized GraphicsEnvironment getLocalGraphicsEnvironment() {
if (localEnv == null) {
// Y O U R E R R O R O R I G I N A T E S H E R E !!!
String nm = (String) java.security.AccessController.doPrivileged
(new sun.security.action.GetPropertyAction
("java.awt.graphicsenv", null));
try {
// long t0 = System.currentTimeMillis();
localEnv =
(GraphicsEnvironment) Class.forName(nm).newInstance();
// long t1 = System.currentTimeMillis();
// System.out.println("GE creation took " + (t1-t0)+ "ms.");
if (isHeadless()) {
localEnv = new HeadlessGraphicsEnvironment(localEnv);
}
} catch (ClassNotFoundException e) {
throw new Error("Could not find class: "+nm);
} catch (InstantiationException e) {
throw new Error("Could not instantiate Graphics Environment: "
+ nm);
} catch (IllegalAccessException e) {
throw new Error ("Could not access Graphics Environment: "
+ nm);
}
}
return localEnv;
}
That's what gets executed.
And in the original post which you don't seem to have read, I said the code is accessing the property "java.awt.graphicsenv"
If that other project using axis doesn't have the same problem it may be because it may be running in a different tomcat configuration or the axis library allowed the access to that property. But we cannot be sure. That's pure speculation. So why don't you test the following and see what gets printed:
String nm = (String) java.security.AccessController.doPrivileged
(new sun.security.action.GetPropertyAction
("java.awt.graphicsenv", null));
System.out.println("java.awt.graphicsenv = " + nm );
It it prints null then you now what the problem is. You don't have that property in your system, or the security forbids you do use it.
It is very hard to tell you from here: "Go and edit file xyz and add : fail = false" So you have to do your work and try to figure out what's the real reason.
Start by researching what's the code being executed is ( which I have just posted ) and follow by understand what it does and how does all that "AccessController.doPrivileged" works. (You may use Google + StackOverflow for that).
We had a similar issue and after much trouble shooting it was identified to be related to the java.awt.headless property. The issue was resolved by explicitly setting the JVM option to
-Djava.awt.headless=true
It was running a week ago, and now it is not.
THEREFORE, YOU CHANGED SOMETHING BETWEEN "working" and "not working".
Go back to the working config (if you can), and rigorously track what you changed. If you don't have a backup of the working config, then meticulously go back through what you've done between working and non-working until you find what you changed.
It may not be code - it could be a config file, etc.
Best of luck,
-R
Is this server running java in server mode - I hear that doesn't load in the AWT classes.
If you are deploying this on *nix, and you don't have an X window system running anymore, that could explain it. Even if you do, if you aren't exporting the DISPLAY system variable to the process that starts the JVM, or if you are but it is not actually valid, it could cause such an issue.
That would at least explain why you didn't change any configuration in tomcat, but still have a problem.
If your NoClassDefFoundError has no message at all, then this means two things:
The JVM has already tried and failed to load a class. Usually, this means the JVM was unable to complete static initialization for that class, i.e. assign values to any static fields and run any static { } blocks. Often, this is because the classes necessary to do this static initialization are missing.
You're using Java 5, not Java 6. (In Java 6, you get a 'Could not initialize class xyz' message instead.)
The problem class appears to be the one whose name is the value of the system property java.awt.graphicsenv. I would start by finding out the value of this property. What happens when you try to instantiate this class?
Since you're getting NoClassDefFoundError from inside the AWT code, it looks like Java is failing to load the X Windows libraries. Note that even if you're running in headless mode ($DISPLAY not pointing to an X Windows server), AWT still needs some subset of the X11 libraries in order to render images. See, for example, this reference:
http://javatechniques.com/blog/linux-x11-libraries-for-headless-mode
If something stopped working and your Java code didn't change, it's possible that the X11 libraries got moved or uninstalled on your machine, or that for some other reason your LD_LIBRARY_PATH environment variable doesn't point to them anymore.

Categories

Resources