i've played around the example code and craig's diagnosis seems to be 
correct (as usual :)

the Method.getMethod used by digester returns the inner implementation 
which is only accessible within the package rather than the public 
interface implementation.

this problem can be fixed - in my test harness - by using the 
PropertyUtils.getAccessibleMethod to find an accessible version of the 
method. unfortunately, this method is currently private in PropertyUtils. 
this is a method which is needed by PropertyUtils but doesn't really 
belong in there (it's a general method finder method rather than a 
property finder method).

maybe the right thing to do would be to add a new class (in beanutils) - 
MethodUtils? - which would contain the general method-related methods from 
PropertyUtils and which exposed them as public methods. other utility 
method-related reflection methods could be added later. the public 
interface for PropertyUtils could be maintained but the private 
implementations would be moved into this new class. digester could them 
use the improved reflection methods in this new class.

i'm willing to create patches if this plan is acceptable.

- robert


On Thursday, December 6, 2001, at 02:04 AM, Donnie Hale wrote:

> See below...
>
> Donnie
>
>
>> -----Original Message-----
>> From: craigmcc@localhost [mailto:craigmcc@localhost]On Behalf Of Craig
>> R. McClanahan
>> Sent: Wednesday, December 05, 2001 1:41 AM
>> To: Jakarta Commons Developers List
>> Subject: RE: Digester yielding IllegalAccessException in CallMethodRule,
>> SetNextRule
>>
>>
>>
>>
>> On Tue, 4 Dec 2001, Donnie Hale wrote:
>>
>>> Date: Tue, 4 Dec 2001 22:44:09 -0500
>>> From: Donnie Hale <[EMAIL PROTECTED]>
>>> Reply-To: Jakarta Commons Developers List
>> <[EMAIL PROTECTED]>
>>> To: Jakarta Commons Developers List <[EMAIL PROTECTED]>
>>> Subject: RE: Digester yielding IllegalAccessException in CallMethodRule,
>>>      SetNextRule
>>>
>>> Craig,
>>>
>>> Thanks for the prompt response. If I understand your second
>> case, I tried
>>> it, and it worked as I wanted:
>>>
>>> // Callback.java
>>> public class Callback implements Reader.WebXmlFileDigestion
>>> {
>>>     public Callback(Driver d)
>>>     {
>>>         driver_ = d;
>>>     }
>>>
>>>     public void parseError(Throwable t) throws Exception
>>>     {
>>>         t.printStackTrace();
>>>     }
>>>
>>>     public void setServletMapping(
>>>         String servletName, String urlPattern)
>>>     {
>>>         driver_.addServletMapping(servletName, urlPattern);
>>>     }
>>>
>>>     Driver driver_;
>>> }
>>>
>>> Then, in Driver.java, I used
>>>
>>>         reader.digestWebXmlFile(new Callback(this));
>>>
>>> instead of passing the anonymous implementing class.
>>>
>>
>> OK, so we know it definitely has something to do with visibility of the
>> called class.
>>
>>> I'll have to see if I have Sun's 1.3 JDK around to try. If not,
>> it could be
>>> a while, as I only have IDSL access. :)
>>>
>>> One more, related, thing, since you're in the know.
>>
>> I better be, since I wrote Digester :-).
>>
>>> Is it possible to
>>> implement an interface with methods which have package scope though the
>>> interface methods are public? I don't care that some unknown
>> future entity
>>> implements the interface; but I don't want the methods in the
>> implementation
>>> of it that I care about to be publicly accessible. My copy of
>> the language
>>> spec is at work...
>>>
>>
>> I'm sure it's feasible to implement access via reflection (which is what
>> Digester uses) to methods defined public through an interface - we ran
>> into a similar case in BeanUtils and solved it be looking up an
>> appropriate java.lang.reflect.Method object correctly.  I'm not sure what
>> happens when you shadow a package-private method with a public one -- 
>> will
>> have to check on that.  However, AFAIK, the class itself has to be public
>> for any of this to work.
>
> Is the requirement of the class being public an artifact of doing this
> through reflection. If not, I don't understand. As an example, I've got 
> this
> code in an app:
>
>         File dir = new File(dirName);
>         String[] fileList = dir.list(
>             new FilenameFilter()
>             {
>                 public boolean accept(java.io.File file, java.lang.String
> name)
>                 {
>                     return name.toLowerCase().endsWith(fileSuffix);
>                 }
>             });
>
> Clearly, the File.list method needs an impl of FilenameFilter on which to
> make callbacks. I'm providing it one, anonymously. I'm sure, though, that
> the callback is direct - not via reflection. This works find for me.
>
> I guess that's my main source of confusion about the whole thing.
>
>
>
>>
>>> Thanks much,
>>>
>>> Donnie
>>>
>>
>> Craig
>>
>>
>>>
>>> -----Original Message-----
>>> From: craigmcc@localhost [mailto:craigmcc@localhost]On Behalf Of Craig
>>> R. McClanahan
>>> Sent: Tuesday, December 04, 2001 10:08 PM
>>> To: Jakarta Commons Developers List
>>> Subject: Re: Digester yielding IllegalAccessException in CallMethodRule,
>>> SetNextRule
>>>
>>>
>>> Hi Donnie,
>>>
>>> IIRC, Digester only knows how to call public methods of public
>> classes --
>>> and an anonymous inner class isn't public.
>>>
>>> Could you try two things for me?
>>> * Try this under the Sun JDK just to see if the behavior is different
>>> * Try this where the driver class is a regular public class,
>>>   rather than an inner class
>>>
>>> That will help us narrow down where the difficulty is.
>>>
>>> Craig
>>>
>>>
>>> On Tue, 4 Dec 2001, Donnie Hale wrote:
>>>
>>>> Date: Tue, 4 Dec 2001 22:15:20 -0500
>>>> From: Donnie Hale <[EMAIL PROTECTED]>
>>>> Reply-To: Jakarta Commons Developers List
>> <[EMAIL PROTECTED]>
>>>> To: [EMAIL PROTECTED]
>>>> Subject: Digester yielding IllegalAccessException in CallMethodRule,
>>>>      SetNextRule
>>>>
>>>> I searched the list archives and didn't see anything directly
>> on-topic.
>>>> Sorry if I missed it. Also, this is likely a normal Java
>> issue, but I'm
>>> not
>>>> getting it. :(
>>>>
>>>> Here's a very pared down example which reproduces my problem exactly:
>>>>
>>>> // Driver.java
>>>> public class Driver
>>>> {
>>>>     public static void main(String[] args)
>>>>     {
>>>>         try
>>>>         {
>>>>             Driver driver = new Driver();
>>>>             driver.parseWebApp(new Reader());
>>>>         }
>>>>         catch (Exception e)
>>>>         {
>>>>             e.printStackTrace();
>>>>         }
>>>>     }
>>>>
>>>>     public void Driver()
>>>>     {
>>>>     }
>>>>
>>>>     public void parseWebApp(Reader reader) throws Exception
>>>>     {
>>>>         reader.digestWebXmlFile(
>>>>             new Reader.WebXmlFileDigestion()
>>>>             {
>>>>                 public void parseError(Throwable t) throws Exception
>>>>                 {
>>>>                     t.printStackTrace();
>>>>                 }
>>>>
>>>>                 public void setServletMapping(
>>>>                     String servletName, String urlPattern)
>>>>                 {
>>>>                     addServletMapping(servletName, urlPattern);
>>>>                 }
>>>>             }
>>>>         );
>>>>     }
>>>>
>>>>     public void addServletMapping(String servletName, String
>> urlPattern)
>>>>     {
>>>>         System.out.println("Mapping read: " + servletName + " => " +
>>>> urlPattern);
>>>>     }
>>>> }
>>>>
>>>> // Reader.java
>>>> import java.io.InputStream;
>>>> import java.io.IOException;
>>>> import java.net.URL;
>>>> import org.xml.sax.Attributes;
>>>> import org.xml.sax.SAXException;
>>>> import org.apache.commons.digester.Digester;
>>>> import org.apache.commons.digester.Rule;
>>>>
>>>> public class Reader
>>>> {
>>>>     public static interface WebXmlFileDigestion
>>>>     {
>>>>         public void parseError(Throwable t) throws Exception;
>>>>
>>>>         public void setServletMapping(String servletName, String
>>>> urlPattern);
>>>>     }
>>>>
>>>>     public void Reader()
>>>>     {
>>>>     }
>>>>
>>>>     public void digestWebXmlFile(WebXmlFileDigestion callback)
>>>>         throws Exception
>>>>     {
>>>>         // Prepare a Digester to scan the web application deployment
>>>> descriptor
>>>>         Digester digester = new Digester();
>>>>         digester.push(callback);
>>>>         digester.setDebug(1);
>>>>         digester.setNamespaceAware(true);
>>>>         digester.setValidating(false);
>>>>
>>>>         // Register our local copy of the DTDs that we can find
>>>>         for (int i = 0; i < registrations_.length; i += 2)
>>>>         {
>>>>             URL url =
>> this.getClass().getResource(registrations_[i+1]);
>>>>             if (url != null)
>>>>             {
>>>>                 digester.register(registrations_[i], url.toString());
>>>>             }
>>>>         }
>>>>
>>>>         // Configure the processing rules that we need
>>>>         digester.addCallMethod("web-app/servlet-mapping",
>>>>             "setServletMapping", 2);
>>>>
>> digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
>>>>
>> digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);
>>>>
>>>>         InputStream input= null;
>>>>         try
>>>>         {
>>>>             input = this.getClass().getResourceAsStream("web.xml");
>>>>             digester.parse(input);
>>>>         }
>>>>         catch (Throwable e)
>>>>         {
>>>>             callback.parseError(e);
>>>>         }
>>>>         finally
>>>>         {
>>>>             if (input != null)
>>>>             {
>>>>                 input.close();
>>>>             }
>>>>         }
>>>>     }
>>>>
>>>>     protected static final String registrations_[] =
>>>>     {
>>>>         "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",
>>>>         "/org/apache/struts/resources/web-app_2_2.dtd",
>>>>         "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",
>>>>         "/org/apache/struts/resources/web-app_2_3.dtd"
>>>>     };
>>>> }
>>>>
>>>> The object on which the method call is to be invoked is an
>> instance of an
>>>> anonymous inner class which implements a public (nested)
>> interface. All
>>> the
>>>> methods on the interface and its anonymous implementation are
>> public. All
>>>> the methods on Driver and Reader are public. Yet when I run
>> this, I get:
>>>>
>>>> java.lang.IllegalAccessException: Driver$1
>>>>         at java.lang.reflect.Method.invoke(Native Method)
>>>>         at
>>>>
>> org.apache.commons.digester.CallMethodRule.end(CallMethodRule.java:308)
>>>>         at
>>>> org.apache.commons.digester.Digester.endElement(Digester.java:757)
>>>>         at
>>>> org.apache.xerces.parsers.SAXParser.endElement(SAXParser.java:1403)
>>>>         at
>>>>
>>>
>> org.apache.xerces.validators.common.XMLValidator.callEndElement(XM
>> LValidator
>>>> .java:1480)
>>>>         at
>>>>
>>>
>> org.apache.xerces.framework.XMLDocumentScanner$ContentDispatcher.d
>> ispatch(XM
>>>> LDocumentScanner.java:1149)
>>>>         at
>>>>
>>>
>> org.apache.xerces.framework.XMLDocumentScanner.parseSome(XMLDocume
>> ntScanner.
>>>> java:381)
>>>>         at
>>> org.apache.xerces.framework.XMLParser.parse(XMLParser.java:1081)
>>>>         at
>> org.apache.commons.digester.Digester.parse(Digester.java:1206)
>>>>         at Reader.digestWebXmlFile(Reader.java:53)
>>>>         at Driver.parseWebApp(Driver.java:23)
>>>>         at Driver.main(Driver.java:9)
>>>>
>>>> I'm guessing this has something to do with the "Driver$1" being the
>>>> classname. But it sure seems like I've done stuff like this
>> before, just
>>>> possibly not through reflection. Do I have to go to the
>> extreme of making
>>>> the interface implementation non-anonymous to have any hope of getting
>>> this
>>>> to work?
>>>>
>>>> BTW, I'm using IBM's JDK 1.3 on Win2K.
>>>>
>>>> Thanks much,
>>>>
>>>> Donnie
>>>>
>>>>
>>>> --
>>>> To unsubscribe, e-mail:
>>> <mailto:[EMAIL PROTECTED]>
>>>> For additional commands, e-mail:
>>> <mailto:[EMAIL PROTECTED]>
>>>>
>>>>
>>>
>>>
>>> --
>>> To unsubscribe, e-mail:
>>> <mailto:[EMAIL PROTECTED]>
>>> For additional commands, e-mail:
>>> <mailto:[EMAIL PROTECTED]>
>>>
>>>
>>>
>>> --
>>> To unsubscribe, e-mail:
>> <mailto:[EMAIL PROTECTED]>
>>> For additional commands, e-mail:
>> <mailto:[EMAIL PROTECTED]>
>>>
>>>
>>
>>
>> --
>> To unsubscribe, e-mail:
>> <mailto:[EMAIL PROTECTED]>
>> For additional commands, e-mail:
>> <mailto:[EMAIL PROTECTED]>
>>
>>
>
>
> --
> To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED].
> org>
> For additional commands, e-mail: <mailto:[EMAIL PROTECTED].
> org>
>


--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to