You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@jackrabbit.apache.org by Padraic Hannon <pi...@wasabicowboy.com> on 2007/08/17 03:19:02 UTC

dynamic mixins and jcr mapping

I was looking at the default object converter (ObjectConverterImpl) and 
noticed that it dynamically adds the ocm:discriminator mixin type to the 
jcr node on insert. Additionally I have been thinking about dynamic 
languages and looking at Freebase, to make a long story short I am 
thinking about dynamic typing of java objects. My thought was that 
through a typing (or tagging) mechanism one could create objects with 
attribute collections whose values could be validated via jcr if one 
could dynamically assign mixins based on a field in that object.

For example, if you have a vehicle feature you may want to tag it as a 
transmission and an option. Each tag (or type) would carry a set of 
attribute definitions that one would want to see on a web page or in a 
data entry system. If you could inspect the object and look at how it is 
tagged you could write those tags in as mixins when persisting to jcr. 
Then jackrabbit could enforce data types, required fields, etc.

This sort of functionality is a no brainer in dynamically typed systems 
such as javascript, ruby, etc. however is not strictly possible in java 
(or at least is way out of my league) via interfaces or superclasses, 
but could be done via a contract (getTags() getTypes() something like 
that) where a method returns mixin names? Does this make anyone 
immediately repulsed? I could submit a patch or two to jira if anyone is 
interested...

-paddy

Re: dynamic mixins and jcr mapping

Posted by Christophe Lombart <ch...@gmail.com>.
cool this is the fist time that I see a horse which can bark :-)
You are welcome to share your investigation. thanks

Christophe


On 8/29/07, Padraic I. Hannon <pi...@wasabicowboy.com> wrote:
>
> I was playing with cglib over the last few days and found their mixin
> proxy. Using that I was able to do a simple test:
>
> Dog dog = new DogImpl();
> Cat cat = new CatImpl();
>
> Object[] delegates = new Object[2];
> delegates[0] = dog;
> delegates[1] = cat;
> Object mixed = net.sf.cglib.proxy.Mixin.(delegates);
>
> System.out.println(((Dog)mixed).bark());
> System.out.println(((Cat)mixed).meow());
>
> Seems to work fine. You can even add a mixin to a mixin...
>
> Horse horse = new HorseImpl();
> delegates[0] = mixed;
> delegates[1] = horse;
> mixed = net.sf.cglib.proxy.Mixin.(delegates);
>
> System.out.println(((Horse)mixed).neigh());
> System.out.println(((Dog)mixed).bark());
>
> I was thinking that one could create base classes which would be mixin
> implementations and add those during de-serialization from ocm or even
> at runtime to node objects. I'll keep playing and get back with more
> once I have something more concrete.
>
> -paddy
>

Re: dynamic mixins and jcr mapping

Posted by "Padraic I. Hannon" <pi...@wasabicowboy.com>.
For the loading part I ended up doing basically that. Basically I call 
addMixin() on the object (which extends a base class I created). The 
addMixin method keeps track of all objects added in as mixins to 
recreate the mixin each time rather than just nesting them..

public abstract class AbstractJcrModel implements JcrModel {
    private Collection mixins = new HashSet();
 
   <SNIP>

    public Object addMixin(JcrMixin mixin) {
        mixins.add(mixin);       
        Object[] delegates = mixins.toArray();
        delegates = ArrayUtils.add(delegates, 0, this);
        return Mixin.create(delegates);
   }
}

-paddy


Felix Meschberger wrote:
> Hi Paddy,
>
> Wow ! I was about to apologize to not answered to your mails earlier,
> now I have to admit, that I did not realize, what I missed !
>
> This definitely - from my point of view - like a very interesting path
> to go ! Very interested to see the future.
>
> As a side note on loading: You sketch, that you would load a node in the
> traditional way first and then for each mixin, you would create Mixin
> Object. How about first analyzing the node to get the "primary class"
> and all mixin classes to create the final mixin class upfront and then
> loading all the data into the generated class ? This way, you would not
> generate a cascade of mixin classes.... Just off the top of my head :-)
>
> Regards
> Felix
>
> Am Mittwoch, den 29.08.2007, 12:35 -0700 schrieb Padraic I. Hannon:
>   
>> For loading objects, since we now would be relying on interfaces and 
>> proxies the reflectionutils.setnestedproperties would no longer work as 
>> we would need to address methods instead of bean properties. I'll create 
>> a jira ticket if I haven't already to begin tracking code.
>>
>> -paddy
>>     


Re: dynamic mixins and jcr mapping

Posted by Felix Meschberger <fm...@gmail.com>.
Hi Paddy,

Wow ! I was about to apologize to not answered to your mails earlier,
now I have to admit, that I did not realize, what I missed !

This definitely - from my point of view - like a very interesting path
to go ! Very interested to see the future.

As a side note on loading: You sketch, that you would load a node in the
traditional way first and then for each mixin, you would create Mixin
Object. How about first analyzing the node to get the "primary class"
and all mixin classes to create the final mixin class upfront and then
loading all the data into the generated class ? This way, you would not
generate a cascade of mixin classes.... Just off the top of my head :-)

Regards
Felix

Am Mittwoch, den 29.08.2007, 12:35 -0700 schrieb Padraic I. Hannon:
> For loading objects, since we now would be relying on interfaces and 
> proxies the reflectionutils.setnestedproperties would no longer work as 
> we would need to address methods instead of bean properties. I'll create 
> a jira ticket if I haven't already to begin tracking code.
> 
> -paddy


Re: dynamic mixins and jcr mapping

Posted by "Padraic I. Hannon" <pi...@wasabicowboy.com>.
For loading objects, since we now would be relying on interfaces and 
proxies the reflectionutils.setnestedproperties would no longer work as 
we would need to address methods instead of bean properties. I'll create 
a jira ticket if I haven't already to begin tracking code.

-paddy

Re: dynamic mixins and jcr mapping

Posted by "Padraic I. Hannon" <pi...@wasabicowboy.com>.
I have been looking at the ObjectConverterImpl and I think I have a 
strategy for handling mixins that are defined in the mapper but not 
assigned to the nodetype in the mapping file.

For inserts one would get the interfaces on the object and loop through 
them. For each interface one would look for a class descriptor and add 
it to the objectNode one would also get the field, bean, and collection 
descriptors for that mixin's class descriptor and add them to the 
object's class descriptor.

// Scan Object Interfaces for mixins
Class[] interfaces = object.getClass().getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
    ClassDescriptor interfaceDescriptor = 
mapper.getClassDescriptorByClass(interfaces[i]);
    objectNode.addMixin(interfaceDescriptor.getJcrType().trim());
    for (Iterator it = 
interfaceDescriptor.getBeanDescriptors().iterator(); it.hasNext(); ) {
        classDescriptor.addBeanDescriptor((BeanDescriptor)it.next());
    }
    for (Iterator it = 
interfaceDescriptor.getFieldDescriptors().iterator(); it.hasNext(); ) {
        classDescriptor.addFieldDescriptor((FieldDescriptor)it.next());
    }
    for (Iterator it = 
interfaceDescriptor.getCollectionDescriptors().iterator(); it.hasNext(); ) {
        
classDescriptor.addCollectionDescriptor((CollectionDescriptor)it.next());
    }
}

I need to implement it, however, I think directionally this is the right 
way to go (??).

Updates would be very similar to inserts.

For loads we have a more interesting story and somewhere I need to spend 
more time. I think the steps are:

1) Retrieve object and load fields as described in the mapping file
2) Grab mixins from node
3) For mixins on node which are not in the class descriptor
    1) create mixin impl object based on mixin interface name from class 
descriptor + Impl (this way one could have more complex methods than 
just get/set beans)
    2) load fields into mixin impl object
    3) use cglib mixin to create a new object containing the base node + 
mixin
    4) repeat per mixin

Does this sound about right?

-paddy


Re: dynamic mixins and jcr mapping

Posted by "Padraic I. Hannon" <pi...@wasabicowboy.com>.
I was playing with cglib over the last few days and found their mixin 
proxy. Using that I was able to do a simple test:

Dog dog = new DogImpl();
Cat cat = new CatImpl();

Object[] delegates = new Object[2];
delegates[0] = dog;
delegates[1] = cat;
Object mixed = net.sf.cglib.proxy.Mixin.(delegates);

System.out.println(((Dog)mixed).bark());
System.out.println(((Cat)mixed).meow());

Seems to work fine. You can even add a mixin to a mixin...

Horse horse = new HorseImpl();
delegates[0] = mixed;
delegates[1] = horse;
mixed = net.sf.cglib.proxy.Mixin.(delegates);

System.out.println(((Horse)mixed).neigh());
System.out.println(((Dog)mixed).bark());

I was thinking that one could create base classes which would be mixin 
implementations and add those during de-serialization from ocm or even 
at runtime to node objects. I'll keep playing and get back with more 
once I have something more concrete.

-paddy

Re: dynamic mixins and jcr mapping

Posted by Christophe Lombart <ch...@gmail.com>.
Thanks for your mail. All kind of ideas are welcome. I agree with you, the
OCM framework has to provide a nice way to map dynamically mixin types.

You are welcome to create a new jira issue. By this way, we can discuss in
more details.  Please add use cases. That can help to find a solution :-)

Maybe an solution is not something like this (not yet implemented of
course)  - This is just a brain storming. Not sure if it is a good solution.


Transmission transmission =  new Transmission();
transmission.setPath("/mytransmission");
transmission.set...();

ocm.insert(transmission);
ocm.save();

... later

Option option = new Option();
option.setPath("/mytransmission");
option.set...();
ocm.update(option);
ocm.save();


If Option is mapped to a mixin:type and if both objects use the same path,
this should work.  Later, you can make query/retrieve on both types.

Still thinking about that ...

FYI, I would like to drop the ocm:discriminator. It is used when the same
node type is applied on a complete java class hierarchy in order to find the
appropriate descendant class when a new object has to be instantiated. It is
tedious to support it just to solve this issue.


br,
Christophe





On 8/17/07, Padraic Hannon <pi...@wasabicowboy.com> wrote:
>
> I was looking at the default object converter (ObjectConverterImpl) and
> noticed that it dynamically adds the ocm:discriminator mixin type to the
> jcr node on insert. Additionally I have been thinking about dynamic
> languages and looking at Freebase, to make a long story short I am
> thinking about dynamic typing of java objects. My thought was that
> through a typing (or tagging) mechanism one could create objects with
> attribute collections whose values could be validated via jcr if one
> could dynamically assign mixins based on a field in that object.
>
> For example, if you have a vehicle feature you may want to tag it as a
> transmission and an option. Each tag (or type) would carry a set of
> attribute definitions that one would want to see on a web page or in a
> data entry system. If you could inspect the object and look at how it is
> tagged you could write those tags in as mixins when persisting to jcr.
> Then jackrabbit could enforce data types, required fields, etc.
>
> This sort of functionality is a no brainer in dynamically typed systems
> such as javascript, ruby, etc. however is not strictly possible in java
> (or at least is way out of my league) via interfaces or superclasses,
> but could be done via a contract (getTags() getTypes() something like
> that) where a method returns mixin names? Does this make anyone
> immediately repulsed? I could submit a patch or two to jira if anyone is
> interested...
>
> -paddy
>