component equals

12 posts / 0 new
Last post
atao
Offline
Joined: 10/15/2008
component equals

Vincent

What does an equals method for a component defined with a BasicComponentDescriptor?

Pierre

Edited by vvandens on 11/24/2009 - 15:06
vvandens
Offline
Joined: 05/29/2008
Component equals

Hi Pierre,

Components handled by Jspresso J2SE proxies rely on equals/hashCode of java.lang.Object (see BasicComponentInvocationHandler.computeEquals/computeHashCode). Have you faced any problem regarding this implementation ? In fact, I don't see any other option...

 

Regards,

Vincent

atao
Offline
Joined: 10/15/2008
Component equals

Vincent

rely on equals/hashCode of java.lang.Object (see BasicComponentInvocationHandler.computeEquals/computeHashCode)

It's what I suspected.

Have you faced any problem regarding this implementation ?

I'm trying to implement component with special cases (see code below). One of them is not a conversion of a null value. Then I need to check the value of the attributes, not the reference of the instance.

I could do the checking in the interceptor class but I'd like to avoid it. Then the use of equals.

IMH, the checking of reference for component is not a good idea: a component has no identity, it's a value object. If each attribute of two components has same value, then the two components are equals.

In fact, I don't see any other option...

If there is no way to do such a thing from inside the code of computeEquals, what about adding in a service delegate class the methodes

public boolean equals(IComponent c, Object other)

public int hashCode(IComponent c)

Regards

Pierre

 

public interface EventTypeService {

    static final EventType VOID    = new NullEventType();
    static final EventType PAYSLIP = new PayslipEventType();

    final class NullEventType
    extends DefaultComponent
    implements EventType
    {

        @Override public String getName()          {return EMPTY_STRING;}

        @Override public void setName(String name) {}
       
    }   

    final class PayslipEventType
    extends DefaultComponent
    implements EventType
    {

        @Override public String getName()          {return "payslip";}

        @Override public void setName(String name) {}
       
    }   

}

 

public class EventTypeInterceptor
extends EmptyLifecycleInterceptor<EventType> {

    @Override
    public void onLoad(EventType type) {
        if (null == type) {
            type = EventType.VOID;
        }
        else if (EventType.PAYSLIP.equals(type)) {
            type = EventType.PAYSLIP;
        }
    }
   
    @Override
    public boolean onPersist(EventType type, IEntityFactory entityFactory,
              UserPrincipal principal, IEntityLifecycleHandler entityLifecycleHandler)
    {
        return toPersistence(type);
    }
   
    @Override
    public boolean onUpdate(EventType type, IEntityFactory entityFactory,
              UserPrincipal principal, IEntityLifecycleHandler entityLifecycleHandler)
    {
        return toPersistence(type);
    }
   
    private boolean toPersistence(EventType type) {
        if(type == EventType.VOID) {
            type = null;
        }
        return false;
    }
}

 

vvandens
Offline
Joined: 05/29/2008
Component equals

 

Pierre,

I think I get the general idea (using constants for component values), but I think I don't understand the EventTypeInterceptor code (i.e. you change the reference of the parameter passed-in). Well, if I translate correctly, this lifecycle interceptor should be registered on the component-owning class (the one that holds the type reference), right ?

IMH, the checking of reference for component is not a good idea: a component has no identity, it's a value object. If each attribute of two components has same value, then the two components are equals.

I agree with you. But as far as I can remember, I had some problems with Hibernate and Jspresso dirty state detection (but I may be wrong, so let's have a look at it again).

Equality and hashCode can be implemented based on component properties equality (hashCode). Everything is actually accessible from the proxy implementation class, so no technical problem on this side.

 

Do you know that you can even do it yourself, i.e. change the default implementation used by Jspresso for implementing the components J2SE proxy without modifying the Jspresso code itself ? It may be a good scenario to try it out :

  1. Subclass BasicComponentInvocationHandler and ControllerAwareComponentInvocationHandler. Those are the 2 default implementation classes used by Jspresso for components.
  2. Override computeEquals and computeHashCode to implement equality/hashCode based on the component property map. you can use the straightGetProperties() method of the IComponet interface to do so.
  3. Subclass BasicProxyComponentFactory and ControllerAwareProxyComponentFactory and override the createComponentInvocationHandler method to return instances of your subclasses (see 1.) instead of the default ones.
  4. in config.xml, which is the last Spring context file loaded, you can override the standard bean definitions for basicComponentFactory and sessionAwareComponentFactory. Just clone the standard definition from commons.xml and substitute your factory subclasses (see 3.) to the default ones.

Now, each and every component created in your Jspresso app should use your implementation. Same is also available for entities.

Meanwhile, I'm taking a look to the default behaviour.

 

Best,

Vincent

vvandens
Offline
Joined: 05/29/2008
Component equals (missing API)

Pierre,

While trying to implement it, I've come up to the fact that the original proxy also have to be passed to the computeHashCode method (as it is done for the computeEquals method) so that you can leverage the straightGetProperties method. So you can't do it as of now.

vvandens
Offline
Joined: 05/29/2008
SNAPSHOT published

Pierre,

I've implemented the default behaviour as you suggested. The dirty checking problem was linked to something else, so everything runs fine.

I've published a new SNAPSHOT including the fix as well as the computeHashCode API change to align it with computeEquals.

Tell me if it fits.

 

Best,

Vincent

atao
Offline
Joined: 10/15/2008
Component equals

Vincent,

 

I don't understand the EventTypeInterceptor code (i.e. you change the reference of the parameter passed-in). Well, if I translate correctly, this lifecycle interceptor should be registered on the component-owning class (the one that holds the type reference), right ?

 

I haven't tested this code yet. So may be I'm wrong. The idea is to replace any special case value of EventType by a value acceptable by the persistence layer.

I'd like to do it in a unique place.

In the current code, i did it at the level of each attribute of type EventType, e.g. Event.eventType with a interceptor class EventInterceptor.

 

Regards

Pierre

 

 

vvandens
Offline
Joined: 05/29/2008
Responses crossed each other

I haven't tested this code yet. So may be I'm wrong. The idea is to replace any special case value of EventType by a value acceptable by the persistence layer.

I fear that your interceptor won't work. Lifecycle interceptors are designed to change the state of a saved component / entity not its actual reference. Your interceptor doesn't hurt but does nothing (i.e. you only assign parameter another reference, which just doesn't have any effect in java).

 

I'd like to do it in a unique place.

I don't think it's possible that way. We would need something like readResolve / writeReplace that is used in java serialization but applied to the persistence operation. IMHO this would open the door to a lot of problems, particularly regarding the binding.

BTW, why don't you want to use an interceptor on the EventType.name property that takes care of substituting the null and "payslip" strings by final constants (use the IPropertyProcessor.interceptSetter) ? Then checking for equality would make it.

 

HTH,

Vincent

 

atao
Offline
Joined: 10/15/2008
Responses crossed each other

I've published a new SNAPSHOT

Thanks. I'm downloading it.

I don't think it's possible that way.

OK, I think I'll stay with the current code (i.e. interceptor with each attribute using such a type)

to use an interceptor on the EventType.name property that takes care of substituting the null and "payslip" strings by final constants (use the IPropertyProcessor.interceptSetter)

I use it in the current code, to manage special cases in an component attribute between the model and the UI:

- the interceptSetter returns a special case value to the model from the value returned by the UI, e.g. null =>SomeComponent.VOID

- and a modified valueConnector gets a value for the UI from the special case value of the model, e.g. SomeComponent.VOID => null

But:

- it doesn't manage a component attribute with a type using special case values when the component is created or loaded from the persistence layer

- the last job is quite painful to write. Would it be possible to do the job with some kind of getter in IPropertyProcessor?

 

Regards

Pierre

 

vvandens
Offline
Joined: 05/29/2008
Properties transformation

Pierre,

it doesn't manage a component attribute with a type using special case values when the component is created or loaded from the persistence layer

Today, lifecycle interceptors are only triggered for entities. Following your idea, it would make sense that inlined components also get their lifecycle interceptors triggered, right ? So for instance, when an entity is updated to the persistence store, its interceptors get triggered and the interceptors of its inlined components also ? It definitely makes sense.

Would you open a feature request ?

Oups, it's already implemented. So what you say is that onCreate and onLoad don't get triggered for inline components (following their owning entity lifecycle) ?

 

 

and a modified valueConnector gets a value for the UI from the special case value of the model, e.g. SomeComponent.VOID => null

If I understand right, and if I take back your example of null <-> SomeComponent.VOID, you want :

  • UI components to use null for the value binding
  • component getter to return SomeComponent.VOID (never null)
  • component setter to accept both null and SomeComponent.VOID
  • component internal state to use SomeComponent.VOID exclusively (never null), so that the hashCode/equals based on property values works.
  • persistence store to use null exclusively

This seems to me really complicated, but maybe I'm missing the use case behind, and the advantage of such a construct.

BTW, why wouldn't you just declare a (writable but stateless) computed property that takes care of this maping ? e.g. all the internal framework mecanisms (binding, persistence, ...) still use the original property (with null value) and you code your business rules against the computed property (that deals with SomeComponent.VOID and takes care of the mapping between the constants and the actual original values) ? IMHO, it would bring all the benefits you need, i.e. the mapping is written once and kept in one place, is only used for accessors on the component and you don't have to play with Jspresso binding implementation.

 

Would it be possible to do the job with some kind of getter in IPropertyProcessor

The actual implementation doesn't use plain getters for Hibernate to retrieve the state of the entity/component to persist. So it wouldn't be useful. More importantly, I don't feel like opening the door to linking some triggered behaviour to reading a property.

 

HTH,

Vincent

atao
Offline
Joined: 10/15/2008
Properties transformation

Vincent,

Oups, it's already implemented. So what you say is that onCreate and onLoad don't get triggered for inline components (following their owning entity lifecycle) ?

As I understand things:

- a LifeCycleInterceptor can be used to add some custom actions between the model and the persistence layer;

- and a PropertyProcessor can be used to do it between the model and the UI, but only in the direction UI->model

For what I have already tested, this works with components and entities.

If I understand right,

You do, indeed! Sorry for my bad explanation, thought in progess...

  • UI components to use null for the value binding

yes: if a value has never been initialize, I don't want to show any "default" value

  • persistence store to use null exclusively

not always, i.e.: for string it doesn't matter but yes for numeric component like Money ones.

The idea is to keep the state "not still initialized" in the persistence layer.

This seems to me really complicated

Yes, I agree. But ATM it's the only way I have thought of:

- to avoid any null value in the model (Null Object pattern)

- to keep the same behavior in the UI (null == nothing in a field)

BTW, why wouldn't you just declare[...]

Mmmm, good. I'll try it.

Regards

Pierre

 

 

 

 

 

 

 

vvandens
Offline
Joined: 05/29/2008
lifecycle interceptors and property processors

Pierre,

Just for the sake of accuracy Wink:

a LifeCycleInterceptor can be used to add some custom actions between the model and the persistence layer

Right (even if onCreate is not directly linked to the persistence layer, i.e. in-memory instanciation).

and a PropertyProcessor can be used to do it between the model and the UI, but only in the direction UI->model

Not only between UI -> model (even if this is the most common case). Property processors will be triggred every time a modifier on the property is called (so even during a business rule, that is UI-less).

 

keep us informed about your tries.

Best,

Vincet