ok, so a slight new twist has entered the mix.  i quickly realized that we also 
need to deal with the fact that some pojo methods actually return other pojos 
or collecitons of pojos which need to wrapped as well.

i think i've got a solution for this already worked out, but in the spirit of 
collaboration i thought i'd ask for thoughts from others before settling.  
here's how it works ...

xdoclet is run and generates a <POJO>Wrapper class for all pojos specified.  
developers must tag each method that is going to be exposed with one of the 
lines below ...

/** 
 * @roller.wrapPojoMethod type="simple"
 * @roller.wrapPojoMethod type="pojo"
 * @roller.wrapPojoMethod type="pojo-collection" 
class="org.roller.pojos.pojoclass"
 */

a "simple" wrapped method just returns the same type of object as the original 
method, so this is meant for most methods that return Strings, ints, etc.  a 
"pojo" wrapped method identifies a method that returns another pojo and this 
gives xdoclet a chance to alter the method code so that it can return a wrapped 
pojo instead of the original pojo.  a "pojo-collection" is a method which 
returns some collection of pojos.  xdoclet handles "pojo-collection" methods by 
inserting code that iterates through the contents of the collection and wraps 
each object.

here is some example output from each of the 3 wrap types ...

    // define a simple wrapped method
    public java.lang.String getId()
    {
        return this.pojo.getId();
    }


    // this method returns another pojo, so we need to wrap that pojo as well
    public org.roller.pojos.wrapper.WeblogCategoryDataWrapper getCategory()
    {
        return new 
org.roller.pojos.wrapper.WeblogCategoryDataWrapper(this.pojo.getCategory());
    }


    // this method returns a collection of pojos, so we need to wrap the 
collection contents
    public java.util.Collection getEntryAttributes()
    {
        // first get the collection
        java.util.Set initialCollection = this.pojo.getEntryAttributes();

        // iterate through and wrap
        // we force the use of an ArrayList because it should be good enough to 
cover
        // for any Collection type we encounter.
        java.util.ArrayList wrappedCollection = new 
java.util.ArrayList(initialCollection.size());
        java.util.Iterator it = initialCollection.iterator();
        int i = 0;
        while(it.hasNext()) {
            wrappedCollection.add(i, new 
org.roller.pojos.wrapper.EntryAttributeDataWrapper((org.roller.pojos.EntryAttributeData)
 it.next()));
            i++;
        }

        return wrappedCollection;
    }

so, what do people think?  is that good enough?  does anyone have any other 
ideas on how this might be done better?

-- Allen


On Wed, 2005-07-13 at 07:28, Anil Gangolli wrote:
> Allen:
> 
> You raise some good points.
> 
> I'm ok with using XDoclet to generate the wrappers directly, and also 
> recognize that there may be more issues with either 
> implementation as you get down to the details.  I'm happy to leave that to 
> your judgement.
> 
> I think we all want low maintenance of the wrapping code.  The dynamic proxy 
> + X-Doclet-generated restriction interfaces idea was 
> just a means to achieve this, and your suggestion of moving the delegation 
> write into the X-doclet generated code should be fine 
> too.
> 
> --a.
> 
> 
> ----- Original Message ----- 
> From: "Allen Gilliland" <[EMAIL PROTECTED]>
> To: "roller-dev" <[email protected]>
> Sent: Tuesday, July 12, 2005 3:16 PM
> Subject: Re: velocity context cleanup
> 
> 
> >I was just thinking about this a little more and I am actually starting to 
> >think that the dynamic proxy might not be the better 
> >approach.  If we are going to have xdoclet auto generate the restricted 
> >interfaces, then why not have it generate wrapper classes 
> >instead?
> >
> > The big bonus I see with the wrapper classes is that we get compile time 
> > validation that a wrapper exists for the pojo and that 
> > the coder has applied the right wrapper.  With the dynamic proxy you could 
> > easily call ctx.put("obj", 
> > VelocityWrapper.wrap(myPojo)) but you don't find out if there is something 
> > wrong until runtime.
> >
> > My other slight uneasyness with the proxy solution (and this is mainly a 
> > pet peeve) is that it makes me uncomfortable when a class 
> > implements an interface that is not generated until build time.  Auto 
> > generated wrapper classes isn't a whole lot better, but I 
> > still prefer to have important core code like pojos to not be reliant on 
> > auto generated code if possible.
> >
> > I am still okay with the proxy solution if that's what everyone else wants, 
> > but my personal preference is to avoid mucking with 
> > things at runtime when we don't really need to.
> >
> > -- Allen
> >
> >
> > On Sun, 2005-07-10 at 08:27, Anil Gangolli wrote:
> >> An additional suggestion: the "restriction interfaces" (see below) could 
> >> be constructed using XDoclet, so that we could just tag 
> >> the
> >> bean methods to be exposed.  One has to write a suitable XDoclet template 
> >> for it, though.
> >>
> >> --a.
> >>
> >>
> >>
> >> ----- Original Message ----- 
> >> From: "Dave Johnson" <[EMAIL PROTECTED]>
> >> To: <[email protected]>
> >> Sent: Friday, July 08, 2005 3:40 PM
> >> Subject: Re: velocity context cleanup
> >>
> >>
> >> >
> >> > +1 on dynamic proxies!
> >> >
> >> > - Dave
> >> >
> >> >
> >> > On Jul 6, 2005, at 11:17 AM, Anil Gangolli wrote:
> >> >
> >> >>
> >> >> Yes, I meant using the dynamic proxy facilities introduced in Java 1.3 
> >> >> (see, e.g.
> >> >> http://java.sun.com/j2se/1.3/docs/guide/reflection/proxy.html).
> >> >>
> >> >> I apologize for the terseness.  Here's a more complete description.
> >> >>
> >> >> Note that the success of this approach willl hinge on the reflection of 
> >> >> the proxy class seen by Velocity as being the 
> >> >> restricted
> >> >> interface desired to be exposed.  Reading "Proxy Class Properties" in 
> >> >> the above document, this should be the case.
> >> >>
> >> >> (1) Define a simple invocation handler class whose invoke() method just 
> >> >> does an m.invoke(obj) on the passed in params,
> >> >>     and just unwraps InvocationTargetException to throw back any 
> >> >> originating Throwable it contains.
> >> >>     See the examples in the doc above.
> >> >>
> >> >> (2) Define your restriction interfaces whose names can be derived by 
> >> >> convention from the name of the associated POJO
> >> >>     (like org.roller.pojos.Template -> 
> >> >> org.roller.presentation.velocity.wrappers.Template or TemplateWrapper). 
> >> >>  You must
> >> >>     define an interface to use a dynamic proxy.
> >> >>
> >> >> (3) Define a single proxy factory method wrap() that generates a proxy. 
> >> >>  [With the following example code it would be
> >> >> VelocityWrapper.wrap(thePojo)].  You just call this and place the 
> >> >> result into the Velocity context.
> >> >>
> >> >> Here is APPROXIMATE pseudo-code for illustration.  Note: this won't 
> >> >> compile for sure without a createWrapperFromPojoName() 
> >> >> method
> >> >> defined. You can get the intent anyway.
> >> >>
> >> >>    public class VelocityWrapper {
> >> >>
> >> >>        public static Object wrap(Object pojo) {
> >> >>             // Determine the wrapper interface class name from the pojo 
> >> >> class name
> >> >>             String restrictionInterfaceName = 
> >> >> createWrapperNameFromPojoName(pojo.getClass().getName());
> >> >>             // Get that class an return a proxy instance with the 
> >> >> invocation handler below.
> >> >>             Class restrictionInterface = 
> >> >> pojo.getClass().getClassLoader().loadClass(restrictionInterfaceName);
> >> >>             SimpleInvocationHandler handler = new 
> >> >> SimpleInvocationHandler(pojo);
> >> >>             return 
> >> >> Proxy.newProxyInstance(pojo.getClass().getClassLoader,
> >> >>                                                           new Class[] { 
> >> >>  restrictionInterface },
> >> >>                                                           handler);
> >> >>         }
> >> >>
> >> >>        public static class SimpleInvocationHandler implements 
> >> >> InvocationHandler {
> >> >>                private Object theWrappedPojo;
> >> >>
> >> >>                SimpleInvocationHandler(Object pojo) {
> >> >>                   // When constructed, remember the instance we were 
> >> >> constructed with
> >> >>                   theWrappedPojo = pojo;
> >> >>                }
> >> >>
> >> >>                public Object invoke(Object proxy, method m, Object[] 
> >> >> args) throws Throwable {
> >> >>                    try {
> >> >>                        return m.invoke(theWrappedPojo, args);
> >> >>                    } catch (InvocationTargetException e) {
> >> >>                        // rethrow the original exception to make the 
> >> >> wrapping transparent
> >> >>                        throw e.getTargetException();
> >> >>                    }
> >> >>                }
> >> >>        }
> >> >>    }
> >> >>
> >> >>
> >> >>
> >> >>
> >> >>
> >> >> ----- Original Message ----- From: "Rudman Max" <[EMAIL PROTECTED]>
> >> >> To: <[email protected]>
> >> >> Sent: Tuesday, July 05, 2005 10:07 PM
> >> >> Subject: Re: velocity context cleanup
> >> >>
> >> >>
> >> >>> I think he is talking about dynamic proxy facilities provided by  
> >> >>> java.lang.reflect.Proxy. Instead of creating a wrapper 
> >> >>> class
> >> >>> for each POJO you could deposit a dynamically proxy class in the 
> >> >>> Velocity  context constructed with InvocationHandler
> >> >>> implementation which  blocks all method calls unless they begin with 
> >> >>> "set/is". I personally think this a really good idea.
> >> >>>
> >> >>> Max
> >> >>>
> >> >>> On Jul 6, 2005, at 12:50 AM, Allen Gilliland wrote:
> >> >>>
> >> >>>> I'm not sure what you mean by dynamic proxy.  Could you give more  
> >> >>>> info.
> >> >>>>
> >> >>>> -- Allen
> >> >>>>
> >> >>>>
> >> >>>> Anil Gangolli wrote:
> >> >>>>
> >> >>>>
> >> >>>>>
> >> >>>>> Just a quick note, and I admit I haven't followed the latest  
> >> >>>>> discussion, but if the wrappers are merely restrictions by a
> >> >>>>> specified interface, it seems like a single dynamic proxy could  
> >> >>>>> implement all of them.
> >> >>>>>
> >> >>>>> --a.
> >> >>>>>
> >> >>>>> ----- Original Message ----- From: "Allen Gilliland"  <[EMAIL 
> >> >>>>> PROTECTED]>
> >> >>>>> To: <[email protected]>
> >> >>>>> Sent: Tuesday, July 05, 2005 3:31 PM
> >> >>>>> Subject: Re: velocity context cleanup
> >> >>>>>
> >> >>>>>
> >> >>>>>
> >> >>>>>> agreed.  so the convention will be ...
> >> >>>>>>    org.roller.presentation.velocity.wrappers.<POJO Class>Wrapper
> >> >>>>>>
> >> >>>>>> will act as a wrapper class for a <POJO Class> normally found in  
> >> >>>>>> org.roller.pojos
> >> >>>>>>
> >> >>>>>> -- Allen
> >> >>>>>>
> >> >>>>>>
> >> >>>>>> Lance Lavandowska wrote:
> >> >>>>>>
> >> >>>>>>
> >> >>>>>>> Ooops, you caught me not paying sufficient attention, even whilst I
> >> >>>>>>> was typing out the package name!  Hmm, I think I like  
> >> >>>>>>> o.r.p.v.wrappers
> >> >>>>>>> better, less confusion with the "real" "pojos".
> >> >>>>>>>
> >> >>>>>>> On 7/5/05, Allen Gilliland <[EMAIL PROTECTED]> wrote:
> >> >>>>>>>
> >> >>>>>>>
> >> >>>>>>>> i can do that, but org.roller.presentation.velocity.pojos *is*  a 
> >> >>>>>>>> new
> >> >>>>>>>> sub-package.  maybe org.roller.presentation.velocity.wrappers  
> >> >>>>>>>> would be
> >> >>>>>>>> more clear?
> >> >>>>>>>>
> >> >>>>>>>> -- Allen
> >> >>>>>>>>
> >> >>>>>>>>
> >> >>>>>>>> Lance Lavandowska wrote:
> >> >>>>>>>>
> >> >>>>>>>>
> >> >>>>>>>>
> >> >>>>>>>>> Just one suggestion, put the wrappers in a sub-package, perhaps
> >> >>>>>>>>> org.roller.presentation.velocity.pojos.wrappers ?
> >> >>>>>>>>>
> >> >>>>>>>>>
> >> >>>>>>
> >> >>>>>>
> >> >>>>>
> >> >>
> >> >
> >>
> > 
> 

Reply via email to