001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.bridge;
023:
024: import java.lang.reflect.Method;
025: import java.util.Map;
026:
027: import javax.ejb.EJBException;
028: import javax.ejb.FinderException;
029:
030: import org.jboss.ejb.EntityEnterpriseContext;
031: import org.jboss.proxy.compiler.InvocationHandler;
032:
033: /**
034: * EntityBridgeInvocationHandler is the invocation hander used by the CMP 2.x
035: * dynamic proxy. This class only interacts with the EntityBridge. The main
036: * job of this class is to deligate invocation of abstract methods to the
037: * appropriate EntityBridge method.
038: * <p/>
039: * Life-cycle:
040: * Tied to the life-cycle of an entity bean instance.
041: * <p/>
042: * Multiplicity:
043: * One per cmp entity bean instance, including beans in pool.
044: *
045: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
046: * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
047: * @version $Revision: 57209 $
048: */
049: public class EntityBridgeInvocationHandler implements InvocationHandler {
050: private final Class beanClass;
051: private final Map fieldMap;
052: private final Map selectorMap;
053: private EntityEnterpriseContext ctx;
054:
055: /**
056: * Creates an invocation handler for the specified entity.
057: */
058: public EntityBridgeInvocationHandler(Map fieldMap, Map selectorMap,
059: Class beanClass) {
060: this .beanClass = beanClass;
061: this .fieldMap = fieldMap;
062: this .selectorMap = selectorMap;
063: }
064:
065: public void setContext(EntityEnterpriseContext ctx) {
066: if (ctx != null && !beanClass.isInstance(ctx.getInstance())) {
067: throw new EJBException(
068: "Instance must be an instance of beanClass");
069: }
070: this .ctx = ctx;
071: }
072:
073: public Object invoke(Object proxy, Method method, Object[] args)
074: throws FinderException {
075: // todo find a better workaround
076: // CMP/CMR field bridges are mapped to its abstract method names because of the bug
077: // in reflection introduced in Sun's 1.4 JVM, i.e. when an abstract class C1 extends a super class C2
078: // and implements interface I and C2 and I both declare method with the same signature M,
079: // C1.getMethods() will contain M twice.
080: // ejbSelect methods are mapped to Method objects instead. Because ejbSelect methods having the same name
081: // might have different signatures. Hopefully, the probability of an ejbSelect method to appear in an interface
082: // is lower.
083:
084: String methodName = method.getName();
085:
086: BridgeInvoker invoker = (BridgeInvoker) fieldMap
087: .get(methodName);
088: if (invoker == null) {
089: //invoker = (BridgeInvoker) selectorMap.get(methodName);
090: invoker = (BridgeInvoker) selectorMap.get(method);
091:
092: if (invoker == null) {
093: throw new EJBException(
094: "Method is not a known CMP field "
095: + "accessor, CMR field accessor, or ejbSelect method: "
096: + "methodName=" + methodName);
097: }
098: }
099:
100: try {
101: return invoker.invoke(ctx, method, args);
102: } catch (RuntimeException e) {
103: throw e;
104: } catch (FinderException e) {
105: throw e;
106: } catch (Exception e) {
107: throw new EJBException("Internal error", e);
108: }
109: }
110:
111: // Inner
112:
113: public interface BridgeInvoker {
114: Object invoke(EntityEnterpriseContext ctx, Method method,
115: Object[] args) throws FinderException, Exception;
116: }
117:
118: public static class FieldGetInvoker implements BridgeInvoker {
119: private final FieldBridge field;
120:
121: public FieldGetInvoker(FieldBridge field) {
122: this .field = field;
123: }
124:
125: public Object invoke(EntityEnterpriseContext ctx,
126: Method method, Object[] args) {
127: // In the case of ejbHome methods there is no context, but ejb home
128: // methods are only allowed to call selectors.
129: if (ctx == null) {
130: throw new EJBException(
131: "EJB home methods are not allowed to "
132: + "access CMP or CMR fields: methodName="
133: + method.getName());
134: }
135:
136: return field.getValue(ctx);
137: }
138: }
139:
140: public static class FieldSetInvoker implements BridgeInvoker {
141: private final FieldBridge field;
142:
143: public FieldSetInvoker(FieldBridge field) {
144: this .field = field;
145: }
146:
147: public Object invoke(EntityEnterpriseContext ctx,
148: Method method, Object[] args) {
149: // In the case of ejbHome methods there is no context, but ejb home
150: // methods are only allowed to call selectors.
151: if (ctx == null) {
152: throw new EJBException(
153: "EJB home methods are not allowed to "
154: + "access CMP or CMR fields: methodName="
155: + method.getName());
156: }
157:
158: field.setValue(ctx, args[0]);
159: return null;
160: }
161: }
162: }
|