Hi Klotz,
 
Thanks for the detailed investigation and for proposing this workaround. 
 
I have fixed the Handler class in both SVN 1.1 branch and SVN trunk.
 
Best regards,
Jerome Louvel
--
Restlet ~ Founder and Lead developer ~  <http://www.restlet.org/> 
http://www.restlet.org
Noelios Technologies ~ Co-founder ~  <http://www.noelios.com> 
http://www.noelios.com
 

 
  _____  

De : Leigh L. Klotz, Jr. [mailto:[email protected]] 
Envoye : mardi 3 fevrier 2009 20:11
A : [email protected]
Cc : [email protected]
Objet : WadlApplication and non-public Resources in Restlet 1.1.1 give 
IllegalAccessException



As reported earlier, I'm using WadlApplication and WadlResource in Restlet 
1.1.1 and Sun JDK 1.6.

I've found that the non-public classes extending Resource cause WadlApplication 
to fail in Java reflection attempting to invoke
method "allowGet()", due to Sun bug  
<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533479>
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4533479 which is marked as a 
duplicate of a similar issue at
<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957> 
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957

Details:

class MyResource extends SomeWadlResource {
  public MyResource(Context context, Request request, Response response);
  public allowGet() { return true; }
  public Representation represent() { ... }
}

Note that class MyResource is not public, but allowGet() is public.  I've made 
MyResource package protected to prevent direct
invocation of Resources by other packages.  

Curiously, I have not seen any problem with normal Restlet invocations, only 
with WadlApplication's use.  I haven't examined why
this is the case, but it's possible there may be other bugs due to this issue.

The error I get is

org.restlet/org/java/Handler.java:
344            if (method != null) {
345                try {
346 =>                 result = method.invoke(this, args);
347                } catch (Exception e) {
348                    getLogger().log(
349                            Level.WARNING,
350                            "Couldn't invoke the handle method for \"" + 
method
351                                    + "\"", e);
352                }

java.lang.IllegalAccessException: Class org.restlet.Handler can not access a 
member of class MyResource with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.reflect.Method.invoke(Method.java:588)
at org.restlet.Handler.invoke(Handler.java:346)

 
The invocation is from getAllowedMethods(), in WadlResource:
    public void describe(String path, ResourceInfo info) {
        info.setPath(path);
 
        // Introspect the current resource to detect the allowed methods
        final List<Method> methodsList = new ArrayList<Method>();
        methodsList.addAll(getAllowedMethods());
 
 





The issue appears to be that java.lang.reflect.Method does not allow access to 
public methods of package-protected classes, whereas
regular method invocation does.  In other words, doing resource.allowGet() 
would be legal, but doing the same call by reflection
gives the IllegalAccessError.
 
The Sun bug report discussion centers on a possible future change in classfile 
format to provide more information from the compiler
for reflection; however, there is a simple workaround listed in Bug 4533479 and 
I've described it below.

Recommended Fix:

In bug 4533479, user arro239 posted workaround code for obtaining the 
superclass's java.lang.reflect.Method of the same name and
signature and using it instead, and walking the tree upwards to find a public 
class or interface.  That fix works in this case.  

There may be other possible fixes that I haven't explored, so I'd like to ask 
for anyone with experience in this area to make
suggestions.

Below is a recommended change to org.restlet/org/restlet/Handler.java using 
arro239's sample.  

    /**
     * Updates the set of methods with the ones allowed by this resource
     * instance.
     * 
     * @param allowedMethods
     *            The set to update.
     */
    private void updateAllowedMethods(Set<Method> allowedMethods) {
        for (final java.lang.reflect.Method orig_classMethod : getClass()
                .getMethods()) {
            final java.lang.reflect.Method classMethod = 
fixJava4071957(orig_classMethod);
            if (classMethod.getName().startsWith("allow")
                    && (classMethod.getParameterTypes().length == 0)) {
                if ((Boolean) invoke(classMethod)) {
                    final Method allowedMethod = Method.valueOf(classMethod
                            .getName().substring(5));
                    allowedMethods.add(allowedMethod);
                }
            }
        }
    }

    // from  <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957> 
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957
    private static java.lang.reflect.Method 
fixJava4071957(java.lang.reflect.Method method) {
        while 
(!java.lang.reflect.Modifier.isPublic(method.getDeclaringClass().getModifiers()))
 {
            try {
                method = 
method.getDeclaringClass().getSuperclass().getMethod(method.getName(), 
method.getParameterTypes());
            } catch (NoSuchMethodException e) {
                throw new IllegalStateException(e);
            }
        }
        return method;
    }


Leigh.

------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=1118916

Reply via email to