001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.core.ivm;
017:
018: import java.io.ObjectStreamException;
019: import java.lang.reflect.Method;
020: import java.rmi.AccessException;
021: import java.rmi.RemoteException;
022: import java.util.HashMap;
023: import java.util.Map;
024: import java.util.List;
025: import java.util.ArrayList;
026:
027: import javax.ejb.AccessLocalException;
028: import javax.ejb.EJBAccessException;
029: import javax.ejb.EJBException;
030: import javax.ejb.EJBHome;
031:
032: import org.apache.openejb.DeploymentInfo;
033: import org.apache.openejb.InterfaceType;
034: import org.apache.openejb.ProxyInfo;
035: import org.apache.openejb.core.ServerFederation;
036: import org.apache.openejb.core.entity.EntityEjbHomeHandler;
037: import org.apache.openejb.core.stateless.StatelessEjbHomeHandler;
038: import org.apache.openejb.core.stateful.StatefulEjbHomeHandler;
039: import org.apache.openejb.spi.ApplicationServer;
040: import org.apache.openejb.util.LogCategory;
041: import org.apache.openejb.util.Logger;
042: import org.apache.openejb.util.proxy.ProxyManager;
043:
044: public abstract class EjbHomeProxyHandler extends BaseEjbProxyHandler {
045: public static final Logger logger = Logger.getInstance(
046: LogCategory.OPENEJB, "org.apache.openejb.util.resources");
047:
048: private final Map<String, MethodType> dispatchTable;
049:
050: private static enum MethodType {
051: CREATE, FIND, HOME_HANDLE, META_DATA, REMOVE
052: }
053:
054: public EjbHomeProxyHandler(DeploymentInfo deploymentInfo,
055: InterfaceType interfaceType, List<Class> interfaces) {
056: super (deploymentInfo, null, interfaceType, interfaces);
057: dispatchTable = new HashMap<String, MethodType>();
058: dispatchTable.put("create", MethodType.CREATE);
059: dispatchTable.put("getEJBMetaData", MethodType.META_DATA);
060: dispatchTable.put("getHomeHandle", MethodType.HOME_HANDLE);
061: dispatchTable.put("remove", MethodType.REMOVE);
062:
063: if (interfaceType.isHome()) {
064: Class homeInterface = deploymentInfo
065: .getInterface(interfaceType);
066: Method[] methods = homeInterface.getMethods();
067: for (Method method : methods) {
068: if (method.getName().startsWith("create")) {
069: dispatchTable.put(method.getName(),
070: MethodType.CREATE);
071: } else if (method.getName().startsWith("find")) {
072: dispatchTable
073: .put(method.getName(), MethodType.FIND);
074: }
075: }
076: }
077:
078: }
079:
080: public void invalidateReference() {
081: throw new IllegalStateException(
082: "A home reference must never be invalidated!");
083: }
084:
085: protected static EjbHomeProxyHandler createHomeHandler(
086: DeploymentInfo deploymentInfo, InterfaceType interfaceType,
087: List<Class> interfaces) {
088: switch (deploymentInfo.getComponentType()) {
089: case STATEFUL:
090: return new StatefulEjbHomeHandler(deploymentInfo,
091: interfaceType, interfaces);
092: case STATELESS:
093: return new StatelessEjbHomeHandler(deploymentInfo,
094: interfaceType, interfaces);
095: case CMP_ENTITY:
096: case BMP_ENTITY:
097: return new EntityEjbHomeHandler(deploymentInfo,
098: interfaceType, interfaces);
099: default:
100: throw new IllegalStateException(
101: "Component type does not support rpc interfaces: "
102: + deploymentInfo.getComponentType());
103: }
104: }
105:
106: public static Object createHomeProxy(DeploymentInfo deploymentInfo,
107: InterfaceType interfaceType) {
108: return createHomeProxy(deploymentInfo, interfaceType, null);
109: }
110:
111: public static Object createHomeProxy(DeploymentInfo deploymentInfo,
112: InterfaceType interfaceType, List<Class> objectInterfaces) {
113: if (!interfaceType.isHome())
114: throw new IllegalArgumentException(
115: "InterfaceType is not a Home type: "
116: + interfaceType);
117:
118: try {
119: EjbHomeProxyHandler handler = createHomeHandler(
120: deploymentInfo, interfaceType, objectInterfaces);
121:
122: List<Class> proxyInterfaces = new ArrayList<Class>(2);
123:
124: Class homeInterface = deploymentInfo
125: .getInterface(interfaceType);
126: proxyInterfaces.add(homeInterface);
127: proxyInterfaces.add(IntraVmProxy.class);
128:
129: return ProxyManager.newProxyInstance(proxyInterfaces
130: .toArray(new Class[] {}), handler);
131: } catch (Exception e) {
132: throw new RuntimeException("Can't create EJBHome stub"
133: + e.getMessage(), e);
134: }
135: }
136:
137: public Object createProxy(Object primaryKey) {
138: try {
139:
140: InterfaceType objectInterfaceType = this .interfaceType
141: .getCounterpart();
142:
143: EjbObjectProxyHandler handler = newEjbObjectHandler(
144: getDeploymentInfo(), primaryKey,
145: objectInterfaceType, this .getInterfaces());
146:
147: List<Class> proxyInterfaces = new ArrayList<Class>(handler
148: .getInterfaces().size() + 1);
149:
150: proxyInterfaces.addAll(handler.getInterfaces());
151: proxyInterfaces.add(IntraVmProxy.class);
152:
153: return ProxyManager.newProxyInstance(proxyInterfaces
154: .toArray(new Class[] {}), handler);
155:
156: } catch (IllegalAccessException iae) {
157: throw new RuntimeException(
158: "Could not create IVM proxy for "
159: + getInterfaces().get(0), iae);
160: }
161: }
162:
163: protected abstract EjbObjectProxyHandler newEjbObjectHandler(
164: DeploymentInfo deploymentInfo, Object pk,
165: InterfaceType interfaceType, List<Class> interfaces);
166:
167: protected Object _invoke(Object proxy, Class interfce,
168: Method method, Object[] args) throws Throwable {
169:
170: if (logger.isInfoEnabled()) {
171: logger.info("invoking method " + method.getName() + " on "
172: + deploymentID);
173: }
174:
175: String methodName = method.getName();
176:
177: try {
178: java.lang.Object retValue;
179: MethodType operation = dispatchTable.get(methodName);
180:
181: if (operation == null) {
182: retValue = homeMethod(interfce, method, args, proxy);
183: } else {
184: switch (operation) {
185: /*-- CREATE ------------- <HomeInterface>.create(<x>) ---*/
186: case CREATE:
187: retValue = create(interfce, method, args, proxy);
188: break;
189: case FIND:
190: retValue = findX(interfce, method, args, proxy);
191: break;
192: /*-- GET EJB METADATA ------ EJBHome.getEJBMetaData() ---*/
193: case META_DATA:
194: retValue = getEJBMetaData(method, args, proxy);
195: break;
196: /*-- GET HOME HANDLE -------- EJBHome.getHomeHandle() ---*/
197: case HOME_HANDLE:
198: retValue = getHomeHandle(method, args, proxy);
199: break;
200: /*-- REMOVE ------------------------ EJBHome.remove() ---*/
201: case REMOVE: {
202: Class type = method.getParameterTypes()[0];
203:
204: /*-- HANDLE ------- EJBHome.remove(Handle handle) ---*/
205: if (javax.ejb.Handle.class.isAssignableFrom(type)) {
206: retValue = removeWithHandle(interfce, method,
207: args, proxy);
208: } else {
209: /*-- PRIMARY KEY ----- EJBHome.remove(Object key) ---*/
210: retValue = removeByPrimaryKey(interfce, method,
211: args, proxy);
212: }
213: break;
214: }
215: default:
216: throw new RuntimeException(
217: "Inconsistent internal state: value "
218: + operation + " for operation "
219: + methodName);
220: }
221: }
222:
223: if (logger.isDebugEnabled()) {
224: logger.debug("finished invoking method "
225: + method.getName() + ". Return value:"
226: + retValue);
227: } else if (logger.isInfoEnabled()) {
228: logger.info("finished invoking method "
229: + method.getName());
230: }
231:
232: return retValue;
233:
234: /*
235: * The ire is thrown by the container system and propagated by
236: * the server to the stub.
237: */
238: } catch (RemoteException re) {
239: if (interfaceType.isLocal()) {
240: throw new EJBException(re.getMessage(),
241: (Exception) re.detail);
242: } else {
243: throw re;
244: }
245:
246: } catch (org.apache.openejb.InvalidateReferenceException ire) {
247: Throwable cause = ire.getRootCause();
248: if (cause instanceof RemoteException
249: && interfaceType.isLocal()) {
250: RemoteException re = (RemoteException) cause;
251: Throwable detail = (re.detail != null) ? re.detail : re;
252: cause = new EJBException(re.getMessage(),
253: (Exception) detail);
254: }
255: throw cause;
256: /*
257: * Application exceptions must be reported dirctly to the client. They
258: * do not impact the viability of the proxy.
259: */
260: } catch (org.apache.openejb.ApplicationException ae) {
261: Throwable exc = (ae.getRootCause() != null) ? ae
262: .getRootCause() : ae;
263: if (exc instanceof EJBAccessException) {
264: if (interfaceType.isBusiness()) {
265: throw exc;
266: } else {
267: if (interfaceType.isLocal()) {
268: throw (AccessLocalException) new AccessLocalException(
269: exc.getMessage()).initCause(exc);
270: } else {
271: throw new AccessException(exc.getMessage(),
272: (Exception) exc);
273: }
274: }
275:
276: }
277: throw exc;
278: /*
279: * A system exception would be highly unusual and would indicate a sever
280: * problem with the container system.
281: */
282: } catch (org.apache.openejb.SystemException se) {
283: if (interfaceType.isLocal()) {
284: throw new EJBException(
285: "Container has suffered a SystemException",
286: (Exception) se.getRootCause());
287: } else {
288: throw new RemoteException(
289: "Container has suffered a SystemException", se
290: .getRootCause());
291: }
292: } catch (org.apache.openejb.OpenEJBException oe) {
293: if (interfaceType.isLocal()) {
294: throw new EJBException("Unknown Container Exception",
295: (Exception) oe.getRootCause());
296: } else {
297: throw new RemoteException(
298: "Unknown Container Exception", oe
299: .getRootCause());
300: }
301: } catch (Throwable t) {
302: logger.debug("finished invoking method " + method.getName()
303: + " with exception:" + t, t);
304: throw t;
305: }
306: }
307:
308: /*-------------------------------------------------*/
309: /* Home interface methods */
310: /*-------------------------------------------------*/
311:
312: protected Object homeMethod(Class interfce, Method method,
313: Object[] args, Object proxy) throws Throwable {
314: checkAuthorization(method);
315: return container.invoke(deploymentID, interfce, method, args,
316: null);
317: }
318:
319: protected Object create(Class interfce, Method method,
320: Object[] args, Object proxy) throws Throwable {
321: ProxyInfo proxyInfo = (ProxyInfo) container.invoke(
322: deploymentID, interfce, method, args, null);
323: assert proxyInfo != null : "Container returned a null ProxyInfo: ContainerID="
324: + container.getContainerID();
325: return createProxy(proxyInfo.getPrimaryKey());
326: }
327:
328: protected abstract Object findX(Class interfce, Method method,
329: Object[] args, Object proxy) throws Throwable;
330:
331: /*-------------------------------------------------*/
332: /* EJBHome methods */
333: /*-------------------------------------------------*/
334:
335: protected Object getEJBMetaData(Method method, Object[] args,
336: Object proxy) throws Throwable {
337: checkAuthorization(method);
338: IntraVmMetaData metaData = new IntraVmMetaData(
339: getDeploymentInfo().getHomeInterface(),
340: getDeploymentInfo().getRemoteInterface(),
341: getDeploymentInfo().getPrimaryKeyClass(),
342: getDeploymentInfo().getComponentType());
343: metaData.setEJBHome((EJBHome) proxy);
344: return metaData;
345: }
346:
347: protected Object getHomeHandle(Method method, Object[] args,
348: Object proxy) throws Throwable {
349: checkAuthorization(method);
350: return new IntraVmHandle(proxy);
351: }
352:
353: public org.apache.openejb.ProxyInfo getProxyInfo() {
354: return new org.apache.openejb.ProxyInfo(getDeploymentInfo(),
355: null, getDeploymentInfo().getInterfaces(interfaceType),
356: interfaceType);
357: }
358:
359: protected Object _writeReplace(Object proxy)
360: throws ObjectStreamException {
361: /*
362: * If the proxy is being copied between bean instances in a RPC
363: * call we use the IntraVmArtifact
364: */
365: if (IntraVmCopyMonitor.isIntraVmCopyOperation()) {
366: return new IntraVmArtifact(proxy);
367: /*
368: * If the proxy is referenced by a stateful bean that is being
369: * passivated by the container we allow this object to be serialized.
370: */
371: } else if (IntraVmCopyMonitor.isStatefulPassivationOperation()) {
372: return proxy;
373: /*
374: * If the proxy is being copied between class loaders
375: * we allow this object to be serialized.
376: */
377: } else if (IntraVmCopyMonitor.isCrossClassLoaderOperation()) {
378: return proxy;
379: /*
380: * If the proxy is serialized outside the core container system,
381: * we allow the application server to handle it.
382: */
383: } else {
384: ApplicationServer applicationServer = ServerFederation
385: .getApplicationServer();
386: return applicationServer.getEJBHome(this .getProxyInfo());
387: }
388: }
389:
390: protected Object removeWithHandle(Class interfce, Method method,
391: Object[] args, Object proxy) throws Throwable {
392:
393: IntraVmHandle handle = (IntraVmHandle) args[0];
394: Object primKey = handle.getPrimaryKey();
395: EjbObjectProxyHandler stub;
396: try {
397: stub = (EjbObjectProxyHandler) ProxyManager
398: .getInvocationHandler(handle.getEJBObject());
399: } catch (IllegalArgumentException e) {
400:
401: stub = null;
402: }
403:
404: container.invoke(deploymentID, interfce, method, args, primKey);
405:
406: /*
407: * This operation takes care of invalidating all the EjbObjectProxyHanders associated with
408: * the same RegistryId. See this.createProxy().
409: */
410: if (stub != null) {
411: invalidateAllHandlers(stub.getRegistryId());
412: }
413: return null;
414: }
415:
416: protected abstract Object removeByPrimaryKey(Class interfce,
417: Method method, Object[] args, Object proxy)
418: throws Throwable;
419: }
|