Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4071957
Votes 159
Synopsis (reflect) Method.invoke access control does not understand inner class scoping
Category java:classes_lang
Reported Against 1.2 , 1.3 , 1.4 , 1.1.1 , 1.1.3 , 1.1.6 , 1.1.8 , 1.3.1 , 1.4.1
Release Fixed
State 6-Fix Understood, bug
Priority: 4-Low
Related Bugs 4071593 , 4207233 , 4209315 , 4238091 , 4533479 , 4710855 , 4819108 , 4852768 , 6853614 , 4283544
Submit Date 14-AUG-1997
Description


Running the following program, which should succeed, results in an
IllegalAccessException.

import java.lang.reflect.*;

public class TestPrivateAccess extends Object
{
  TPAInnerClass inner = new TPAInnerClass();

  private void privateMethod()
  {System.out.println("private method.");}

  class TPAInnerClass extends Object
  {
    void dynamicInvoke()
    {
      try
	{
	  Method method = 
TestPrivateAccess.class.getDeclaredMethod("privateMethod", new Class[] {});
	  method.invoke(TestPrivateAccess.this, new Object[] {});
	}
      catch (Exception e)
	{e.printStackTrace();}
    }
  }

  public static void main(String[] argv)
  {
    TestPrivateAccess tpa = new TestPrivateAccess();
    tpa.inner.dynamicInvoke();
  }
}


======================================================================
Posted Date : 2005-10-11 21:30:48.0
Work Around





======================================================================
Evaluation
We would like to eventually extend the classfile format to eliminate
access methods; however, we've decided not to do that for 1.2 in favor
of a more sweeping change down the road that would also solve other
problems with inner classes, such as their excessive space consumption.
So this can't be fixed until after a decision has been made about revisiting
the classfile format, which will be well after 1.2.

  xxxxx@xxxxx   1998-04-08

$ java -version
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b10)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b10, mixed mode)
$ java TestPrivateAccess
java.lang.IllegalAccessException: Class TestPrivateAccess$TPAInnerClass can not access a member of class TestPrivateAccess with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:57)
        at java.lang.reflect.Method.invoke(Method.java:317)
        at TestPrivateAccess$TPAInnerClass.dynamicInvoke(TestPrivateAccess.java:18)
        at TestPrivateAccess.main(TestPrivateAccess.java:28)

There have been numerous reports of this problem.  One of the more notable
examples is provided in bug 4207233:

import java.lang.reflect.*;
class R {
    class A {
        A() throws Exception {
            // This works
            method1(); // XXXX
            Class c = R.class;
            Class[] types = {};
            Method m = c.getDeclaredMethod("method1", types);
            Object[] args = {};
            // Why doesn't this work
            m.invoke(R.this, args); // YYYY
        }
    }
    R() throws Exception {
        A a = new A();
    }

    private void method1() {
        System.out.println("method1");
    }

    public static void main(String[] args) throws Exception {
        R r = new R();
    }
}

In this example, we not only see the problem but we are also confronted with the
fact that reflection is doing the reflective access check differently than the
non-reflective case.  This is because the non-reflective case is resolved by
javac where we have more information about member accessibility than is
encoded in the classfile.  It is highly questionable whether we should attempt
to replicate the javac analysis during reflection.  A much better solution is
to update the classfile format to contain the necessary information.

Tiger planning is considering the classfile format changes.

--   xxxxx@xxxxx   2002-04-24
-----------------------------

Another customer has run into similar problem in the context of Java Plugin (ActiveX bridge). See bug 4852768 for details.
There's a fix in the Java Plugin code to workaround this issue. Refer to the suggested fix section of 4852768.
Maybe a similar fix can be applied to the customer's apps. in this case as a workaround for the now while waiting for a complete fix in Tiger.

  xxxxx@xxxxx   2003-05-01
------------------------------

I don't expect to see this fixed before Dolphin at the earliest. As noted above, it requires substantial changes to the class file format and VMs.

  xxxxx@xxxxx   2005-05-05 18:47:22 GMT

It turns out that it is possible to implement this
with limited changes to the JVM.  With the addition of the
enclosing method info attribute in class files in JDK 5,
it is now possible to reconstruct all the required data
required by Method.invoke to check accessibility in
nested classes.  All that would be required from the JVM
is to verify that the enclosing method info attribute
is correct.

  xxxxx@xxxxx   2005-05-06 09:49:20 GMT
A suggested fix has been contributed by java.net member mondo

Contribution forum : https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?forumID=1463&messageID=19397
Posted Date : 2007-04-06 22:52:12.0
Comments
  
  Include a link with my name & email   

Submitted On 25-APR-2002
jleech
I think to workaround this problem Sun needs to go in and
replace all the anonymous classes it uses in its APIs, so
that the APIs can be properly accessed using the reflection 
APIs.  They could do this without too much effort and 
without classfile format changes.  Once they fix 
reflection/inner classes, then they can be allowed to start 
using inner classes again in their API implementations.  Of 
course, telling their developers they're not allowed to use 
inner classes anymore, and further that they have to go 
back and take them out will get them mad, and thus the 
original problem will get fixed as a result.


Submitted On 25-APR-2002
jleech
this problem really has like 51 bug votes instead of the 12 
that show up.  When Sun "closes" bugs by calling them 
duplicates of other bugs, they should roll the votes over 
with them, and email the people who voted for the original 
bugs and indicate the change.


Submitted On 25-APR-2002
thpreusser
I don't think it's politically correct to move this old bug
from a place of reasonable attention at
http://developer.java.sun.com/developer/bugParade/bugs/4071593.html
to this lonely place ... 


Submitted On 26-APR-2002
thpreusser
My speech!


Submitted On 29-APR-2002
jpschewe
They did roll all the votes over.  At least mine got rolled
over and now I se 56 votes for this bug.  However I don't
see how they need class file changes to fix this.  Every
method is accessible from anywhere, it's just that you can't
execute it unless you have the appropriate information. 
That's an easy check, just look at the access of the file
that contains the reflection call.


Submitted On 01-JUL-2002
bkirby
My favorite comment from another "duplicate" bug involves 
Sun's use of anonymous classes when implementing 
interfaces. Try using introspection to iterate over a 
collection (as many libraries/tools would have you do)-- 
YOU LOSE!!! The iterator is implemented as an inner class, 
so you can't invoke such esoteric methods as getNext() 
and next() because they throw IllegalAccessExceptions. I 
guess we could use the oh so efficient and elegant 
toArray() method to access the elements. Ugh. I think I'm 
about to be sick...


Submitted On 01-JUL-2002
bkirby
OK. I've decided to use horrendous wrapper classes to 
deal with my need for reflection and Sun's inner class 
usage. Lots of extra object creation and extra method calls, 
but at least I can continue to make progress towards 
functioning software today. Maybe by the time I revisit the 
issue, the bug will be fixed! Ha ha ha...


Submitted On 01-AUG-2002
jleech
bkirby, I was surprised to find out that you _can_ use
reflection to iterate over a
collection.  The (subtle) secret is to get the class of the
Iterator using Iterator.class
rather than calling getClass() on some iterator object.  


Submitted On 01-OCT-2002
JamesSchriever
Here is one of the simpliest examples of this bug.

public static void main(String[] args) throws Exception {
   ArrayList valueList = new ArrayList();
	   
   Iterator i = valueList.iterator();

   Class iteratorClass = i.getClass();
   //Class iteratorClass = Iterator.class;
   
   Method hasNextMethod = iteratorClass.getMethod
("hasNext", new Class[] {});
   System.out.println(hasNextMethod.invoke(i, new Object[] 
{}));
}

You can fix the code above by uncommenting the line:
//Class iteratorClass = Iterator.class;
and commenting the line above it.

This workaround was suggest by jleech.  This isn't an 
acceptable workaround because most of the time during 
reflection the Interface being implemented is unknown.  After 
all if you knew the Interface you wouldn't be using 
relflection. :)


Submitted On 01-OCT-2002
JamesSchriever
I didn't like the above formatting.  It makes it almost 
impossible to read, so here is the code again:

public static void main(String[] args) 
    throws Exception {

  ArrayList valueList = new ArrayList();

  Iterator i = valueList.iterator();

  Class iteratorClass = i.getClass();

  Method method = 
      iteratorClass.getMethod("hasNext", new Class[]{});

  System.out.println(
     invoke(method, i, new Object[] {})
  );
}

public static Object invoke(
    Method method, Object bean, Object[] args) 
    throws InvocationTargetException, 
	       IllegalAccessException {
  try {
	  
    return method.invoke(bean, args);
	
  } catch (IllegalAccessException ex) {
	  
    Class intfaces[] = 
	  bean.getClass().getInterfaces();
    String mName = method.getName();
    Class[] pTypes = method.getParameterTypes();
	
    for (int i=0; i<intfaces.length; i++) {
      try {
		  
        Method iMethod = 
          intfaces[i].getMethod(mName, pTypes);
		
        return iMethod.invoke(bean, args);
      } catch (NoSuchMethodException ex2) {}
    }
	
    throw ex;
  }	
}


Submitted On 01-OCT-2002
JamesSchriever
I found another piece of reflection that throws an 
IllegalAccessException even with my workaround.  This time it 
is with LinkedHashMap.



public static void main(String[] args) 
    throws Exception {

  LinkedHashMap map = new LinkedHashMap();
  map.put("foo","foo");

  Map.Entry entry = 
    (Map.Entry)map.entrySet().iterator().next();

  Class theClass = entry.getClass();

  Method method = 
    theClass.getMethod("getValue", new Class[]{});

  System.out.println(
    invoke(method, entry, new Object[] {})
  );
}


The Map.Entry for LinkedHashMap doesn't work because the 
Map.Entry is implemented in a super class.

Here is an update to my invoke workaround.  This time I tried 
to invoKe the method in all the Interfaces and Classes in the 
bean and all of its super classes:



public static Object invoke(
    Method method, Object bean, Object[] args) 
    throws InvocationTargetException, 
           IllegalAccessException {

  try {

    return method.invoke(bean, args);

  } catch (IllegalAccessException ex) {


    String mName = method.getName();
    Class[] pTypes = method.getParameterTypes();

    Class currentClass = bean.getClass();
    while(currentClass != null) {        

      try {
        Method iMethod = 
          currentClass.getMethod(mName, pTypes);

        if (!method.equals(iMethod)) {
          return iMethod.invoke(bean, args);
        }

      } catch (NoSuchMethodException ex2) {
      } catch (IllegalAccessException ex2) {}

      Class intfaces[] = currentClass.getInterfaces();

      for (int i=0; i<intfaces.length; i++) {
        try {

          Method iMethod = 
            intfaces[i].getMethod(mName, pTypes);

          return iMethod.invoke(bean, args);

        } catch (NoSuchMethodException ex2) {
        } catch (IllegalAccessException ex2) {}
      }

      currentClass = currentClass.getSuperclass();

    }
    throw ex;
  }  
}



It is too bad that bean.getMethod(...) doesn't return the 
most "public" method. 



Submitted On 01-OCT-2002
JamesSchriever
I need to fix this problem for some code we have, and I came 
up with the following workaround:


public static void main(String[] args) throws Exception {

	ArrayList valueList = new ArrayList();

	Iterator i = valueList.iterator();

	Class iteratorClass = i.getClass();

	Method hasNextMethod = iteratorClass.getMethod
("hasNext", new Class[] {});
	System.out.println(invoke(hasNextMethod, i, new 
Object[] {}));
}

public static Object invoke(Method method, Object bean, 
Object[] args) 
		throws InvocationTargetException, 
IllegalAccessException {
	try {
		return method.invoke(bean, args);
	} catch (IllegalAccessException ex) {
		Class interfaces[] = bean.getClass
().getInterfaces();
		String methodName = method.getName();
		Class[] paramTypes = 
method.getParameterTypes();
		for (int i=0; i<interfaces.length; i++) {
			try {
				Method 
interfaceMethod = interfaces[i].getMethod(methodName, 
paramTypes);
				return 
interfaceMethod.invoke(bean, args);
			} catch 
(NoSuchMethodException ex2) {}
		}
		throw ex;
	}	
}

This only fixes one aspect of the innerclass access bug.  This 
workaround could be incorporated into Method.invoke(). 
However, it would obviously be better if the getMethod(...) 
method returned the Method from the interface in the first 
place. Say that 10 times real fast. :)


Submitted On 03-MAY-2004
jmears
This sounds like it could be done more simply than to 
modify the .class file format.

Instead of modifying this, modify the VM (or its support 
classes) so that if the calling class is an inner class, 
allow access to the private method.  Of course, this is 
what everyone wants, but how do you detect that this is 
the case?

Simple, really.  Expressed in Java, here is a method 
that checks for this condition:

private static boolean isInnerClass(String target, String 
requestor)
{
    return (requestor.getPackage() == target.getPackage
()) &&
        (requestor.getClassLoader() == 
target.getClassLoader()) &&
        (requestor.length() > (target.length() + 1)) &&
        requestor.startsWith(target) &&
        (requestor.charAt(target.length()) == '$');
}

In other words, allow access if the following four things 
are true:

1. The requesting class is in the same package as the 
target class.  This must be done with getPackage in 
case there are 2 separate packages with the same 
name.

2. The requesting class was loaded with the same 
ClassLoader as the target class.  Again, a similar 
security check to the package check.  This might be 
redundant.

3. The beginning of the requesting class name 
(including the package) is the same as the target 
class name followed by a dollar sign.  In other words, if 
the target class is java.util.Map, the requesting class 
must have a name like java.util.Map$Entry.


This procedure of checking access does have a few 
problems, however:

1. It assumes that javac is the compiler - specifically, 
that inner classes have this format of name.  It also 
gives special meaning to the dollar sign character.

2. A class that snuck into another package could be 
able to access private fields it shouldn't be able to this 
way.  This is, however, not a big concern.  Classes 
within the same package already have a certain 
privilege with all other classes in the package, and 
sneaking into a package is a fundamental protection of 
Java's security model (after all, imagine if it were 
possible to become a member of sun.reflect!).  Also, 
as it is now, classes within the same package can 
access any private field of a class that is accessed by 
an inner class because the compiler is generating a 
package-protected accessor method to access those 
fields and methods.  Such methods can be called 
using reflection.


Submitted On 31-MAY-2004
abies
Why just not make the private method package-scope ? Package-scope accessor is created behind the scene by javac anyway, so pretending that it is a private method doesn't change anything. Reflection will then work correctly and you will get what you write, instead of  javac magic.
IMHO, allowing inner classes to access private members is a hack - from very start, they should be allowed to only access package members - or jvm should be modified to implement true inner classes. Anything in between is just a hack, which complicates the language.


Submitted On 15-JUL-2004
kotsikonas
With the new 10g JDBC driver, it is possible more and more people will start running into this problem. Try: 

Class.forName ("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@machine:port:SID", "user", "password");
Method method = conn.getClass().getMethod("prepareStatement", new Class[] { String.class });
method.invoke(conn, new String[] { "SELECT * FROM TABLE" } );        	


Submitted On 29-NOV-2005
There is actually a way to fix the reflection with Oracle 10g  we had the same problem however I would not consider this reflection actually a bug all though the error message was a bit misleading if anything. 

The sample code is below. But essentially with oracle when a connection is created you are given a package protected class so technically it would not be legal to reflect directly to that class. So what you do and maybe this is what the JVM should do is attempt to resolve to a parent class that actually defines the method and is accessible in the current context. 


// Using this actually causes a problem // 
//Class nativeClass = connection.getClass();
Class nativeClass = Class.forName("oracle.jdbc.driver.OracleConnection");
Method m  = nativeClass.getMethod("setDefaultExecuteBatch", new Class[]{int.class});
m.invoke(nativeConnection, new Object[]{new Integer(batchSize)});


Submitted On 29-NOV-2005
So actually with the my previous post about the oracle specific reflection issues. Is that maybe better stated now that I think about it is that Java reflection should adhere to/respect  polymorphisim concepts in OO. 


Submitted On 03-JUL-2006
manabu
Although I submitted the code, I didn't receive any reply.


Submitted On 12-OCT-2006
The bug isn't just limited to enclosed classes.  Top levels are affected too.

package a;
class NotPublic implements Runnable {
public void run() { System.out.println("Run!"); }
}

package a;
public class Factory {
public static Runnable create() { return new NotPublic(); }
}

package b;
import a.*;
public class Bug {
public static void main(String[] args) throws Exception {
Runnable r = Factory.create();
r.run(); //succeeds, obviously
r.getClass().getMethod("run").invoke(r); //fails
}}

> java -version
java version "1.5.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode)

> java b.Bug
Run!
Exception in thread "main" java.lang.IllegalAccessException: Class b.Bug can not access a member of class a.NotPublic with modifiers "public"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Method.invoke(Method.java:578)
        at b.Bug.main(Bug.java:7)

This prevents iteration of of a Map that happens to be implemented by TreeMap by our taglibs.  It's somewhat disconcerting that things can fail depending on what implementation class is used by the backend.  (I thought the point of collections was implementation transparency!)


Submitted On 02-APR-2007
manabu
Although I resubmitted the code via jdk-collaboration last month, I haven't received any reply.


Submitted On 28-JUN-2007
eliasen
How about an update, Sun?  This bug is one of the top-voted bugs and is almost ten years old.  This bug greatly hampers any use of introspection.  And, from the comments here, people are trying to contribute useful fixes and not getting any response.

The "Contribution Forum" link above is apparently not accessible to the general public, either.  After logging in, I get "Your account does not have the "Project Forum - View" 
permission needed for you to access the page you requested in the jdk-collaboration project (view your permissions). Either ask the project administrator for more permission, or log in using a different account." 

However, there's no note at all on who the project administrator is for this nor how to contact them.


Submitted On 30-SEP-2007
here is one more:

package a;
interface NonPub{
int NON_GETTABLE= 1;
}

package a;
public interface I1 extends NonPub{
int GETTABLE=2;
void exec(int n);
}

package b;
public class Test{
public static void main(String[] a){
Class clazz=Class.forName("a.I1");
clazz.getField("GETTABLE").get()
clazz.getField("NON_GETTABLE").get()
}
}

--
there is an obvious bug while checking accesibility 


Submitted On 21-APR-2008
Christoph_W
After 3904 days the bug is still present in 1.6.0 and not fixed!
Maybe some of the subsumed duplicates are understood by Sun developers. But I vote for REOPENING cause understandig ("Fix-Understood") does not really fix the bug.
And nevertheless it IS an error if calling

someObject.someMethod();//succeeds
and
someObject.getClass().getMethod("someMethod").invoke(someObject);//fails!

If I have to write the reflection code, why I can not access methods which are in fact accessible to non-reflection code?
As kotsikonas states "... Java reflection should adhere to/respect polymorphisim concepts in OO"!


Submitted On 31-OCT-2008
arro239
at least for me, this is a workaround:

    private static Method fixJava4071957(Method method) {
        while (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
            try {
                method = method.getDeclaringClass().getSuperclass().getMethod(method.getName(), method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                throw new IllegalStateException(e);
            }
        }
        return method;
    }


Submitted On 03-SEP-2009
thiagoh
See a post in my blog about it.

http://publicclass.wordpress.com/2009/09/03/acessando-metodos-atributos-classes-privadas-reflection/


Submitted On 11-SEP-2009
ssg_
workaround for own code:
method.setAccessible(true) and invoke.



PLEASE NOTE: JDK6 is formerly known as Project Mustang