Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8298011

Unexpected reflection failure for dynamically creating virtual threads

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      macos openjdk-19.0.1+10

      A DESCRIPTION OF THE PROBLEM :
      As an implementer of < https://abcl.org >, I am trying to code a pure-Java solution that will run on openjdk8, openjdk11, openjdk17, and openjdk19 that will allow the launching of virtual threads when present and enabled in the underlying JDK runtime. As such, I have to use the java.lang.reflect API to perform runtime introspection to call the virtual thread creation present as a preview in openjdk19 if it present.

      Using the ABCL JSS mechanism, I can get the following pure Common Lisp solution to work, so there must be something special about the module of the runtime classloader that I don't understand. Shouldn't "--add-opens java.base/java.lang=ALL-UNNAMED" allow such access?

      (let ((runnable
              (java:jinterface-implementation "java.lang.Runnable" "run" (lambda ())))
            (thread-factory
              (java:jcall "factory"
                          (java:jcall "ofVirtual" (java:jnew "java.lang.Thread")))))
        (java:jcall "newThread" thread-factory runnable))






      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Compile and run the standalone example with the following JVM options:

      --enable-preview --add-opens java.base/java.lang=ALL-UNNAMED

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Process should just exit normally.
      ACTUAL -
      Dec 01, 2022 8:29:47 AM org.not.Reflect main
      SEVERE: null
      java.lang.IllegalAccessException: class org.not.Reflect cannot access a member of class java.lang.ThreadBuilders$VirtualThreadBuilder (in module java.base) with modifiers "public"
      at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:420)
      at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709)
      at java.base/java.lang.reflect.Method.invoke(Method.java:569)
      at org.not.Reflect.main(Reflect.java:28)

      Debugging under Netbeans, the following code fails for the same
      package condition: =currentClass= is =org.not.reflect=
      whereas =memberClass= is
      =java.lang.ThreadBuilders$VirtualThreadBuilder=.

      #+begin_src java
            if (!Modifier.isPublic(getClassAccessFlags(memberClass))) {
                  isSameClassPackage = isSameClassPackage(currentClass, memberClass);
                  gotIsSameClassPackage = true;
                  if (!isSameClassPackage) {
                      return false;
                  }
              }
      #+end_src


      ---------- BEGIN SOURCE ----------
      package org.not;

      import java.lang.reflect.InvocationTargetException;
      import java.lang.reflect.Method;
      import java.util.logging.Level;
      import java.util.logging.Logger;

      public class Reflect {

          static Object virtualThreadFactory;
          static Method newThread;
          
          public static void main(String[] args) {
              try {
                  Class c = Class.forName("java.lang.Thread");
                  Method ofVirtualMethod = c.getMethod("ofVirtual");
                  Object threadBuilderOfVirtual = ofVirtualMethod.invoke(null);
                  Class factoryMethodClass = threadBuilderOfVirtual.getClass();
                  Method factoryMethod = factoryMethodClass.getMethod("factory");
                  Class virtualThreadFactoryClass = Class.forName("java.lang.ThreadBuilders$VirtualThreadFactory");
                  virtualThreadFactory = factoryMethod.invoke(virtualThreadFactoryClass, (Object[]) null);
                  newThread = virtualThreadFactory.getClass().getMethod("newThread");
              } catch (ClassNotFoundException ex) {
                  Logger.getLogger(Reflect.class.getName()).log(Level.SEVERE, null, ex);
              } catch (NoSuchMethodException ex) {
                  Logger.getLogger(Reflect.class.getName()).log(Level.SEVERE, null, ex);
              } catch (SecurityException ex) {
                  Logger.getLogger(Reflect.class.getName()).log(Level.SEVERE, null, ex);
              } catch (IllegalAccessException ex) {
                  Logger.getLogger(Reflect.class.getName()).log(Level.SEVERE, null, ex);
              } catch (InvocationTargetException ex) {
                  Logger.getLogger(Reflect.class.getName()).log(Level.SEVERE, null, ex);
              }
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      None known.

      FREQUENCY : always


            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: