Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternative solutions for using native APIs: JVM_LoadClass0, JVM_AllocateNewArray and JVM_AllocateNewObject

Tags:

java

c++

c

jvm

java-9

As in Java 9 some native APIs were removed for being deprecated, I didn't manage to find alternative solutions to replace them. I am a C++ developer, with little experience in java. The native APIs that I am using are: JVM_LoadClass0, JVM_AllocateNewObject and JVM_AllocateNewArray.

My source code in Java is:

protected Class resolveClass(MercObjectStreamClass v) throws IOException, ClassNotFoundException
{
    /* Resolve by looking up the stack for a non-zero class
     * loader. If not found use the system loader.
     */
    String name = v.getName();
    try
    {
        //we are using the loadClass0 method which calls the native JVM_LoadClass0
        //JVM_LoadClass0 is deprecated and we need to replace the call
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if(loader == null) {
          return loadClass0(null,name);
        }

      Class scriptCls =  loader.loadClass(scriptClassname);
        return loadClass0(scriptCls,name);
    }
    catch (ClassNotFoundException ex)
    {
        Class cl = (Class)primClasses.get(name);
        if (cl != null)
            return cl;
        else
            throw ex;
    }
}
private native Class loadClass0(Class cl, String classname) throws ClassNotFoundException;

And then the native code is just a simple call to JVM_LoadClass0:

JNIEXPORT jclass JNICALL
Java_mercio_MercObjectInputStream_loadClass0(JNIEnv * env,
    jobject this,
    jclass curClass,
    jstring currClassName)
{
    return JVM_LoadClass0(env, this, curClass, currClassName);
}

The native part is similar to the other APIs.

Can someone suggest an alternative to this approach?

like image 934
istik Avatar asked Feb 06 '18 11:02

istik


2 Answers

I’ve looked up the source code of JVM_LoadClass0 and found the following comment

// Load a class relative to the most recent class on the stack  with a non-null
// classloader.
// This function has been deprecated and should not be considered part of the
// specified JVM interface.

The purpose of the additional currClass parameter can only guessed from the actual code, but apparently “the most recent class on the stack” is only used if currClass is not null, otherwise the loader of currClass is used.

This raises some questions about the intention of the resolveClass method.

  • In a normal environment, a custom class like your MercObjectInputStream should not be loaded by the bootstrap loader, hence, the most recent class on the stack with a non-null classloader should be your own MercObjectInputStream class. Using your own class’ loader for resolving a class is quiet easy, you can simply call Class.forName(name).

  • You are probing Thread.currentThread().getContextClassLoader() which defaults to ClassLoader.getSystemClassLoader() and is rarely set to null. So the behavior of probing the classes on stack seems to be a rare corner case anyway.

  • When the context loader is not null, you are not using it to load the class, but rather do loader.loadClass(scriptClassname); return loadClass0(scriptCls,name);, loading a scriptClassname first and use the result’s loader to resolve name. If that’s intentional, you can use Class<?> scriptCls = loader.loadClass(scriptClassname); return Class.forName(name, true, scriptCls.getClassLoader()); to achieve the same without native methods.

You can do exactly the same as the original code in Java 9 without native code using

protected Class resolveClass(MercObjectStreamClass v)
                                       throws IOException, ClassNotFoundException
{
    String name = v.getName();
    try
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if(loader != null)
            loader = loader.loadClass(scriptClassname).getClassLoader();
        else
            loader = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
                        .walk(s -> s.map(sf -> sf.getDeclaringClass().getClassLoader())
                                    .filter(Objects::nonNull)
                                    .findFirst().orElse(null));

        return Class.forName(name, true, loader);
    }
    catch (ClassNotFoundException ex)
    {
        Class cl = (Class)primClasses.get(name);
        if (cl != null)
            return cl;
        else
            throw ex;
    }
}

but, as said, you should first rethink what the method actually is supposed to do.

like image 175
Holger Avatar answered Sep 29 '22 13:09

Holger


This doc page at oracle.com contains complete list of JNI functions that are available in Java 9.

By looking at the old JNI functions you mentioned, I guess you may be particularly interested in:

  • class operations (DefineClass(), FindClass(), ...)
  • object operations (AllocObject(), NewObject(), ...)
  • array operations (NewObjectArray(), GetArrayLength(), ...)
like image 23
Mateusz Grzejek Avatar answered Sep 29 '22 12:09

Mateusz Grzejek