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

(reflect) Method.invoke throws IllegalAccessException on inner class public meth

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P3 P3
    • None
    • 1.4.1
    • core-libs



      Name: nt126004 Date: 02/14/2003


      FULL PRODUCT VERSION :
      java version "1.4.1_01"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
      Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)


      FULL OPERATING SYSTEM VERSION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      1) Using an object of an inner class, which defines public
      methods cannot be invoked via "Method.invoke(...)", *if*
      used from a class not belonging to the package.

      2) The public methods of the very same object *can* be used
      from a class not belonging to the package, if not using
      reflection.

      3) Studying the language specification, either the compiler
      *and* reflection allow usage of public members of inner
      classes (e.g. if the object was returned by getter method),
      or *both* do not allow for it, irrespectible of whether the
      using class belongs to the same package or not.

        To make this more understandable: if one receives an object
      with e.g. the Enumeration interface implemented for it, then
      one needs to be able to use those public methods. This is
      the case, if one uses e.g. Hashtable (or the subclass
      Properties) and wishes to enumerate the keys. Now, this
      works for compiled Java programs, but using reflection
      instead, does *not* work, but throws
      "IllegalAccessException" instead. Obviously, if the compiler
      does not cause that exception invoking the same public
      methods via Method.invoke(...) must not throw that exception
      either.

      Hence the classification of a bug for this inconsistent
      behaviour.


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. compile the three enclosed (short) programs, note that
         two of them (RgfCase.java and RgfUseCase1.java) belong
         to the same package "rgfTest" (so they should reside
         in such a subdirectory)

      2. the third program "RgfUseCase2.java" is *exactly*
         the same program as "RgfUseCase1.java", but with no
         package statement (hence this class does not belong
         to the above package)

      3. make sure that the compiled Java programs of "rgfTest"
         package are retrievable and enter:

           java RgfUseCase1

         You see that using both, the compiled and the reflection
         version of accessing the public methods of the received
         object of the inner class in "RgfCase.java" work.

      4. make sure that the compiled Java programs of "rgfTest"
         package are retrievable and enter:

           java RgfUseCase2

         You see, that *only* the compiled version works without
         a problem, but not the reflection version of accessing
         the public methods of the received object of the inner
         class in "RgfCase.java" work.

      Either both in 4.) should work or none!

      (Again, I did not find a place in the Java specification
      where the contrary would be stated explicitly or implicitly.)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      - Expected results: if using reflection to invoke methods at
      runtime, by default all methods should be invocable that can
      be invoked with a compiled version.

      Hence, it should be possible to invoke public methods on
      received (and hence accessible!) objects via reflection, for
      which implementors arbitrarily used inner classes (e.g. to
      distinguish the implementation of interfaces from the outer
      class as is the case e.g. Hashtable and Hashtable@Enumerator).

      - Actual Results: whenever one invokes a *PUBLIC* method on
      a received object, which is an instance of an inner class,
      erroneously the "IllegalAccessException" is thrown!



      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      As you can see,

      1) the correct method is found (see signature),
      2) the exception is raised by Method.invoke(), which is a native method.

      -------------------- cut here -----------------------
             9/14: [hasMoreElements]

      bean=[java.util.Hashtable$Enumerator@c23c12], args=[[Ljava.lang.Object;@c23de8],
      m=[public boolean java.util.Hashtable$Enumerator.hasMoreElements()]
      EngineUtils: in last catch:
             e=java.lang.IllegalAccessException: java/util/Hashtable$Enumerator
      EngineUtils: trace stack:
      - - - - - - - - - - - -
      java.lang.IllegalAccessException: java/util/Hashtable$Enumerator
             at java.lang.reflect.Method.invoke(Native Method)
             at com.ibm.bsf.util.EngineUtils.callBeanMethod(EngineUtils.java:160)
             at com.ibm.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:1339)
      - - - - - - - - - - - -
      com.ibm.bsf.BSFException: method invocation failed: java.lang.IllegalAccessExcep
      tion: java/util/Hashtable$Enumerator
             at com.ibm.bsf.util.EngineUtils.callBeanMethod(EngineUtils.java:170)
             at com.ibm.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:1339)
      "invoke": got exception [method invocation failed:
      java.lang.IllegalAccessException: java/util/Hashtable$Enumerator].
      Exception in thread "main" com.ibm.bsf.BSFException: "invoke": object
      'java.util.Hashtable$Enumerator@c23c12' - method [hasMoreElements], method not
      found!
             at com.ibm.bsf.engines.rexx.RexxAndJava.javaCallBSF(RexxAndJava.java:1349)
        263 *-* call BSF "invoke", "java.util.Hashtable$Enumerator@c23c12",
      "HASMOREELEMENTS"
        263 *-* interpret code -- execute this dynamically created Rexx string

          9 *-* do while enum~hasMoreElements -- loop over enumeration
      Error 40 running e:\rony\dev\bsf\bsf-2_2\lib\com\ibm\bsf\engines\rexx\BSF.cls
      line 263: Incorrect call to routine
      Error 40.1: External routine "BSF" failed
      -------------------- cut here -----------------------

      The documentation of Method.invoke() states that: "If this Method object
      enforces Java language access control and the underlying method is inaccessible,
      the invocation throws an IllegalAccessException."

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      ----------------- cut here -----------------
      // ###@###.###, Wirtschaftsuniversitaet Wien/Universitaet
      Augsburg, 2003-02-13

      package rgfTest;

      public class RgfCase
      {
          String [] info=null;
          int myCount=2;

          public RgfCase()
          {
              info=new String[myCount];
              info[0]="Hi, there.";
              info[1]="How do we do, today?";
          }

          public String toString ()
          {
              String t="";
              for (int i=0; i<myCount; i++) { t=t+"["+info[i]+"] "; }
              return t;
          }

          public java.util.Enumeration getEnumeration() { return new Enumerator(this); }

          class Enumerator implements java.util.Enumeration {
              int count=0;
              RgfCase obj=null;
              Enumerator (RgfCase r) { obj=r; }

              public boolean hasMoreElements() { return count < obj.myCount; }

              public Object nextElement() { return obj.info[count++]; }
          }
      }
      ----------------- cut here -----------------


      ----------------- cut here -----------------
      // ###@###.###, Wirtschaftsuniversitaet Wien/Universitaet
      Augsburg, 2003-02-13
      package rgfTest;

      public class RgfUseCase1 // same package as "RgfTest"
      {
          public static void main (String args[]) {
              rgfTest.RgfCase r=new rgfTest.RgfCase(); // create an instance
              System.out.println("r.toString=["+r+"]");
              java.util.Enumeration e=r.getEnumeration();
              System.out.println("\nNow using an enumeration:");
              while (e.hasMoreElements()) {
                  System.out.println("\t"+e.nextElement());
              }

              System.out.println("\nNow using reflection to access a public method of
      the inner class:");
              Class ec=e.getClass();
              java.lang.reflect.Method [] em=ec.getMethods(); // get the inner class
      methods

              int i;
              Object o;
              for (i=0; i<em.length; i++) {
                  if ("hasMoreElements".equalsIgnoreCase(em[i].getName())) break;
              }

              if (i<em.length) {
                  System.out.println("\tfound: ["+em[i]+"].\n\tnow invoking the method:");
                  try {
                      o=em[i].invoke(e, null);
                      System.out.println("\tresult from invoking the method: ["+o+"]
      (should be 'false')");
                  }
                  catch (java.lang.Exception exc)
                  {
                      System.err.println("caught exception: "+exc);
                      exc.printStackTrace();
                      return;
                  }
              }

          }
      }
      ----------------- cut here -----------------

      ----------------- cut here -----------------
      // ###@###.###, Wirtschaftsuniversitaet Wien/Universitaet
      Augsburg, 2003-02-13
      /* NOT part of the package ('package rgfTest;') anymore! */


      public class RgfUseCase2 // same package as "RgfTest"
      {
          public static void main (String args[]) {
              rgfTest.RgfCase r=new rgfTest.RgfCase(); // create an instance
              System.out.println("r.toString=["+r+"]");
              java.util.Enumeration e=r.getEnumeration();
              System.out.println("\nNow using an enumeration:");
              while (e.hasMoreElements()) {
                  System.out.println("\t"+e.nextElement());
              }

              System.out.println("\nNow using reflection to access a public method of
      the inner class:");
              Class ec=e.getClass();
              java.lang.reflect.Method [] em=ec.getMethods(); // get the inner class
      methods

              int i;
              Object o;
              for (i=0; i<em.length; i++) {
                  if ("hasMoreElements".equalsIgnoreCase(em[i].getName())) break;
              }

              if (i<em.length) {
                  System.out.println("\tfound: ["+em[i]+"].\n\tnow invoking the method:");
                  try {
                      o=em[i].invoke(e, null);
                      System.out.println("\tresult from invoking the method: ["+o+"]
      (should be 'false')");
                  }
                  catch (java.lang.Exception exc)
                  {
                      System.err.println("caught exception: "+exc);
                      exc.printStackTrace();
                      return;
                  }
              }

          }
      }
      ----------------- cut here -----------------

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

      CUSTOMER WORKAROUND :
      A workaround for 1.2, 1.3, 1.4, but *not* for 1.1 is as follows:

      - use AccessibleObject.setAccessible(true) on the methods
      (members), you wish to access

      side-effect: using this removes *any* access control (hence
      I check whether the method has the 'public' modifier set,
      before invoking setAccessible(true) on the method object).
      (Review ID: 181276)
      ======================================================================

            iris Iris Clark
            nthompsosunw Nathanael Thompson (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: