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 ?
> >> >>>>>>>>>
> >> >>>>>>>>>
> >> >>>>>>
> >> >>>>>>
> >> >>>>>
> >> >>
> >> >
> >>
> >
>