You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Apache Wiki <wi...@apache.org> on 2007/01/20 01:05:14 UTC

[Jakarta-commons Wiki] Update of "Betwixt/TipsAndHints/ForwardReferenceChainedBeanCreator" by JesseSweetland

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Jakarta-commons Wiki" for change notification.

The following page has been changed by JesseSweetland:
http://wiki.apache.org/jakarta-commons/Betwixt/TipsAndHints/ForwardReferenceChainedBeanCreator

New page:
When Betwixt encounters an IDREF it looks for the matching bean using the IdStoringStrategy implementation.  If the bean is not found, then control passes from the IdrefChainedBeanCreator to the DerivedChainedBeanCreator and finally the ElementTypeChainedBeanCreator, which ultimately results in the creating of an empty bean that is never populated or linked to the element that is referenced.

This approach inserts a special ChainedBeanCreator that uses CGLIB to create a proxy object.  Method calls on the proxy are queued up until the proxy can be resolved.  The proxy invocation handler stores a reference to the ReadContext (and IdStoringStrategy) so that any time a method is invoked on the proxy it can attempt to resolve the target.

'''IdentityIdStoringStrategy.java'''
{{{
import java.util.*;
import org.apache.commons.betwixt.expression.*;
import org.apache.commons.betwixt.strategy.*;
import net.sf.cglib.proxy.*;


/**
 *
 * @author Jesse Sweetland
 */
public class IdentityIdStoringStrategy extends IdStoringStrategy {
    private class Reference {
        private int _identity;
        private String _id;
        private Object _entity;
        
        public int getIdentity() {
            return _identity;
        }
        
        public String getId() {
            return _id;
        }
        
        public Object getEntity() {
            return _entity;
        }
        
        public Reference(Object entity, String id) {
            _identity = System.identityHashCode(entity);
            _entity = entity;
            _id = id;
        }
    }
    
    private HashMap<Integer, Reference> _identityRefs = new HashMap<Integer, Reference>();
    private HashMap<String, Reference> _idRefs = new HashMap<String, Reference>();
    
    public IdentityIdStoringStrategy() {
        System.out.println("Boo");
    }
    
    public void setReference(Context context, Object bean, String id) {
        Reference ref = new Reference(bean, id);
        _identityRefs.put(ref.getIdentity(), ref);
        _idRefs.put(ref.getId(), ref);
    }
    
    public String getReferenceFor(Context context, Object bean) {
        int identity = System.identityHashCode(bean);
        Reference ref = _identityRefs.get(identity);
        return ref == null ? null : ref.getId();
    }
    
    public Object getReferenced(Context context, String id) {
        Reference ref = _idRefs.get(id);
        return ref == null ? null : ref.getEntity();
    }
    
    public void reset() {
        // DO NOT CLEAR VALUES!  We need these references after
        // unmarshalling has completed to resolve references.
    }
}
}}}

'''ForwardReferenceChainedBeanCreator.java'''
{{{
import net.sf.cglib.proxy.*;
import org.apache.commons.betwixt.*;
import org.apache.commons.betwixt.io.read.*;

/**
 *
 * @author Jesse Sweetland
 */
public class ForwardReferenceChainedBeanCreator implements ChainedBeanCreator {
    public Object create(ElementMapping elementMapping, ReadContext context, BeanCreationChain chain) {
        if(context.getMapIDs()) {
            String idref = elementMapping.getAttributes().getValue("idref");
            if(idref != null){
                context.getLog().trace("Found IDREF");
                Object bean = context.getBean( idref );
                if(bean != null) {
                    if(context.getLog().isTraceEnabled()) {
                        context.getLog().trace("Matched bean " + bean);
                    }
                    return bean;
                }
                
                Class beanClass = null;
                ElementDescriptor descriptor = elementMapping.getDescriptor();
                if(descriptor != null) {
                    // check for polymorphism 
                    if (descriptor.isPolymorphic()) {
                        beanClass = context.getXMLIntrospector().getPolymorphicReferenceResolver()
                            .resolveType(elementMapping, context);
                    }

                    if(beanClass == null) {
                        // created based on implementation class
                        beanClass = descriptor.getImplementationClass();
                    }
                }

                if(beanClass == null) {
                    // create based on type
                    beanClass = elementMapping.getType();
                }
                
                if(beanClass != null) {
                    return Enhancer.create(beanClass, new Class[0], new ForwardReferenceInvocationHandler(beanClass, idref, context));
                }
            }
        }
        return chain.create(elementMapping, context);
    }
}
}}}

'''ForwardReferenceInvocationHandler.java'''
{{{
import java.util.*;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.*;
import org.apache.commons.betwixt.io.read.*;
import org.apache.commons.betwixt.strategy.*;

/**
 *
 * @author jessesw
 */
public class ForwardReferenceInvocationHandler implements InvocationHandler {
    private class QueuedCall {
        public Method method;
        public Object[] args;
        
        public QueuedCall(Method method, Object[] args) {
            this.method = method;
            this.args = args;
        }
    }
    
    private Class _type;
    private String _id;
    private ReadContext _context;
    private IdStoringStrategy _references;
    private List<QueuedCall> _queue = new ArrayList<QueuedCall>();
    private Object _target;
    
    public Object getTarget() {
        if(_target == null) {
            _target = _references.getReferenced(_context, _id);
        }
        return _target;
    }
    
    public ForwardReferenceInvocationHandler(Class type, String id, ReadContext context) {
        _type = type;
        _id = id;
        _context = context;
        _references = context.getIdMappingStrategy();
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object target = getTarget();
        if(target == null) {
            if("getClass".equals(method.getName())) {
                return _type;
            } else if("hashCode".equals(method.getName())) {
                return System.identityHashCode(proxy);
            } else {
                _queue.add(new QueuedCall(method, args));
                return null;
            }
        } else {
            for(QueuedCall call: _queue) {
                call.method.invoke(target, call.args);
            }
            return method.invoke(target, args);
        }
    }
}
}}}

To use, set the custom IdStoringStrategy and insert the chained bean creator:
{{{
BeanReader br = new BeanReader();
ReadConfiguration readCfg = br.getReadConfiguration();
XMLIntrospector xmlIntro = br.getXMLIntrospector();
IntrospectionConfiguration introCfg = xmlIntro.getConfiguration();
BindingConfiguration bindCfg = br.getBindingConfiguration();
BeanCreationChain chain = readCfg.getBeanCreationChain();
bindCfg.setIdMappingStrategy(new IdentityIdStoringStrategy());
((BeanCreationList)chain).insertBeanCreator(1, new ForwardReferenceChainedBeanCreator());
}}}

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org