/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.ejb.plugins.cmp.bridge; import java.lang.reflect.Method; import java.util.Map; import javax.ejb.EJBException; import javax.ejb.FinderException; import org.jboss.ejb.EntityEnterpriseContext; import org.jboss.proxy.compiler.InvocationHandler; /** * EntityBridgeInvocationHandler is the invocation hander used by the CMP 2.x * dynamic proxy. This class only interacts with the EntityBridge. The main * job of this class is to deligate invocation of abstract methods to the * appropriate EntityBridge method. * <p/> * Life-cycle: * Tied to the life-cycle of an entity bean instance. * <p/> * Multiplicity: * One per cmp entity bean instance, including beans in pool. * * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a> * @version $Revision: 81030 $ */ public class EntityBridgeInvocationHandler implements InvocationHandler { private final Class beanClass; private final Map fieldMap; private final Map selectorMap; private EntityEnterpriseContext ctx; /** * Creates an invocation handler for the specified entity. */ public EntityBridgeInvocationHandler(Map fieldMap, Map selectorMap, Class beanClass) { this.beanClass = beanClass; this.fieldMap = fieldMap; this.selectorMap = selectorMap; } public void setContext(EntityEnterpriseContext ctx) { if(ctx != null && !beanClass.isInstance(ctx.getInstance())) { throw new EJBException("Instance must be an instance of beanClass"); } this.ctx = ctx; } public Object invoke(Object proxy, Method method, Object[] args) throws FinderException { // todo find a better workaround // CMP/CMR field bridges are mapped to its abstract method names because of the bug // in reflection introduced in Sun's 1.4 JVM, i.e. when an abstract class C1 extends a super class C2 // and implements interface I and C2 and I both declare method with the same signature M, // C1.getMethods() will contain M twice. // ejbSelect methods are mapped to Method objects instead. Because ejbSelect methods having the same name // might have different signatures. Hopefully, the probability of an ejbSelect method to appear in an interface // is lower. String methodName = method.getName(); BridgeInvoker invoker = (BridgeInvoker) fieldMap.get(methodName); if(invoker == null) { //invoker = (BridgeInvoker) selectorMap.get(methodName); invoker = (BridgeInvoker) selectorMap.get(method); if(invoker == null) { throw new EJBException("Method is not a known CMP field " + "accessor, CMR field accessor, or ejbSelect method: " + "methodName=" + methodName); } } try { return invoker.invoke(ctx, method, args); } catch(RuntimeException e) { throw e; } catch(FinderException e) { throw e; } catch(Exception e) { throw new EJBException("Internal error", e); } } // Inner public interface BridgeInvoker { Object invoke(EntityEnterpriseContext ctx, Method method, Object[] args) throws FinderException, Exception; } public static class FieldGetInvoker implements BridgeInvoker { private final FieldBridge field; public FieldGetInvoker(FieldBridge field) { this.field = field; } public Object invoke(EntityEnterpriseContext ctx, Method method, Object[] args) { // In the case of ejbHome methods there is no context, but ejb home // methods are only allowed to call selectors. if(ctx == null) { throw new EJBException("EJB home methods are not allowed to " + "access CMP or CMR fields: methodName=" + method.getName()); } return field.getValue(ctx); } } public static class FieldSetInvoker implements BridgeInvoker { private final FieldBridge field; public FieldSetInvoker(FieldBridge field) { this.field = field; } public Object invoke(EntityEnterpriseContext ctx, Method method, Object[] args) { // In the case of ejbHome methods there is no context, but ejb home // methods are only allowed to call selectors. if(ctx == null) { throw new EJBException("EJB home methods are not allowed to " + "access CMP or CMR fields: methodName=" + method.getName()); } field.setValue(ctx, args[0]); return null; } } }