| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.openejb.core.ivm; |
| |
| import org.apache.openejb.ApplicationException; |
| import org.apache.openejb.BeanContext; |
| import org.apache.openejb.InterfaceType; |
| import org.apache.openejb.InvalidateReferenceException; |
| import org.apache.openejb.OpenEJBException; |
| import org.apache.openejb.OpenEJBRuntimeException; |
| import org.apache.openejb.ProxyInfo; |
| import org.apache.openejb.SystemException; |
| import org.apache.openejb.async.AsynchronousPool; |
| import org.apache.openejb.core.ServerFederation; |
| import org.apache.openejb.core.ThreadContext; |
| import org.apache.openejb.loader.SystemInstance; |
| import org.apache.openejb.spi.ApplicationServer; |
| import org.apache.openejb.spi.SecurityService; |
| import org.apache.openejb.util.LogCategory; |
| import org.apache.openejb.util.Logger; |
| |
| import javax.ejb.AccessLocalException; |
| import javax.ejb.EJBAccessException; |
| import javax.ejb.EJBLocalObject; |
| import javax.ejb.EJBObject; |
| import java.io.ObjectStreamException; |
| import java.lang.reflect.Method; |
| import java.rmi.AccessException; |
| import java.rmi.RemoteException; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Callable; |
| |
| public abstract class EjbObjectProxyHandler extends BaseEjbProxyHandler { |
| |
| private static final Logger logger = Logger.getInstance(LogCategory.OPENEJB, "org.apache.openejb.util.resources"); |
| static final Map<String, Integer> dispatchTable; |
| |
| static { |
| dispatchTable = new HashMap<String, Integer>(); |
| dispatchTable.put("getHandle", 1); |
| dispatchTable.put("getPrimaryKey", 2); |
| dispatchTable.put("isIdentical", 3); |
| dispatchTable.put("remove", 4); |
| dispatchTable.put("getEJBHome", 5); |
| dispatchTable.put("getEJBLocalHome", 6); |
| } |
| |
| public EjbObjectProxyHandler(final BeanContext beanContext, final Object pk, final InterfaceType interfaceType, final List<Class> interfaces, final Class mainInterface) { |
| super(beanContext, pk, interfaceType, interfaces, mainInterface); |
| } |
| |
| public abstract Object getRegistryId(); |
| |
| @Override |
| public Object _invoke(final Object p, final Class interfce, final Method m, final Object[] a) throws Throwable { |
| Object retValue = null; |
| Throwable exc = null; |
| |
| final String methodName = m.getName(); |
| try { |
| if (logger.isDebugEnabled()) { |
| logger.debug("EjbObjectProxyHandler: invoking method " + methodName + " on " + deploymentID + " with identity " + primaryKey); |
| } |
| Integer operation = dispatchTable.get(methodName); |
| if (operation != null) { |
| if (operation == 3) { |
| if (m.getParameterTypes()[0] != EJBObject.class && m.getParameterTypes()[0] != EJBLocalObject.class) { |
| operation = null; |
| } |
| } else { |
| operation = m.getParameterTypes().length == 0 ? operation : null; |
| } |
| } |
| if (operation == null || !interfaceType.isComponent()) { |
| retValue = businessMethod(interfce, m, a, p); |
| } else { |
| switch (operation) { |
| case 1: |
| retValue = getHandle(m, a, p); |
| break; |
| case 2: |
| retValue = getPrimaryKey(m, a, p); |
| break; |
| case 3: |
| retValue = isIdentical(m, a, p); |
| break; |
| case 4: |
| retValue = remove(interfce, m, a, p); |
| break; |
| case 5: |
| retValue = getEJBHome(m, a, p); |
| break; |
| case 6: |
| retValue = getEJBLocalHome(m, a, p); |
| break; |
| default: |
| throw new OpenEJBRuntimeException("Inconsistent internal state"); |
| } |
| } |
| |
| return retValue; |
| |
| /* |
| * The ire is thrown by the container system and propagated by |
| * the server to the stub. |
| */ |
| } catch (final InvalidateReferenceException ire) { |
| invalidateAllHandlers(getRegistryId()); |
| exc = ire.getRootCause() != null ? ire.getRootCause() : new RemoteException("InvalidateReferenceException: " + ire); |
| throw exc; |
| /* |
| * Application exceptions must be reported dirctly to the client. They |
| * do not impact the viability of the proxy. |
| */ |
| } catch (final ApplicationException ae) { |
| exc = ae.getRootCause() != null ? ae.getRootCause() : ae; |
| if (exc instanceof EJBAccessException) { |
| if (interfaceType.isBusiness()) { |
| throw exc; |
| } else { |
| if (interfaceType.isLocal()) { |
| throw new AccessLocalException(exc.getMessage()).initCause(exc.getCause()); |
| } else { |
| throw new AccessException(exc.getMessage()); |
| } |
| } |
| |
| } |
| throw exc; |
| |
| /* |
| * A system exception would be highly unusual and would indicate a sever |
| * problem with the container system. |
| */ |
| } catch (final SystemException se) { |
| try { |
| invalidateReference(); |
| } catch (final IllegalStateException ignore) { |
| logger.debug("Tried to invalidate reference before processing system error: " +ignore.getMessage()); |
| } |
| exc = se.getRootCause() != null ? se.getRootCause() : se; |
| logger.debug("The container received an unexpected exception: ", exc); |
| throw new RemoteException("Container has suffered a SystemException", exc); |
| } catch (final OpenEJBException oe) { |
| exc = oe.getRootCause() != null ? oe.getRootCause() : oe; |
| logger.debug("The container received an unexpected exception: ", exc); |
| throw new RemoteException("Unknown Container Exception", oe.getRootCause()); |
| } finally { |
| if (logger.isDebugEnabled()) { |
| if (exc == null) { |
| String ret = "void"; |
| if (null != retValue) { |
| try { |
| ret = retValue.toString(); |
| } catch (final Exception e) { |
| ret = "toString() failed on (" + e.getMessage() + ")"; |
| } |
| } |
| logger.debug("EjbObjectProxyHandler: finished invoking method " + methodName + ". Return value:" + ret); |
| } else { |
| logger.debug("EjbObjectProxyHandler: finished invoking method " + methodName + " with exception " + exc); |
| } |
| } |
| } |
| } |
| |
| protected Object getEJBHome(final Method method, final Object[] args, final Object proxy) throws Throwable { |
| checkAuthorization(method); |
| return getBeanContext().getEJBHome(); |
| } |
| |
| protected Object getEJBLocalHome(final Method method, final Object[] args, final Object proxy) throws Throwable { |
| checkAuthorization(method); |
| return getBeanContext().getEJBLocalHome(); |
| } |
| |
| protected Object getHandle(final Method method, final Object[] args, final Object proxy) throws Throwable { |
| checkAuthorization(method); |
| return new IntraVmHandle(proxy); |
| } |
| |
| @Override |
| public ProxyInfo getProxyInfo() { |
| return new ProxyInfo(getBeanContext(), primaryKey, getInterfaces(), interfaceType, getMainInterface()); |
| } |
| |
| @Override |
| protected Object _writeReplace(final Object proxy) throws ObjectStreamException { |
| /* |
| * If the proxy is being copied between bean instances in a RPC |
| * call we use the IntraVmArtifact |
| */ |
| if (IntraVmCopyMonitor.isIntraVmCopyOperation()) { |
| return new IntraVmArtifact(proxy); |
| /* |
| * If the proxy is referenced by a stateful bean that is being |
| * passivated by the container we allow this object to be serialized. |
| */ |
| } else if (IntraVmCopyMonitor.isStatefulPassivationOperation()) { |
| return proxy; |
| /* |
| * If the proxy is being copied between class loaders |
| * we allow this object to be serialized. |
| */ |
| } else if (IntraVmCopyMonitor.isCrossClassLoaderOperation()) { |
| return proxy; |
| /* |
| * If the proxy is serialized outside the core container system, |
| * we allow the application server to handle it. |
| */ |
| } else if (!interfaceType.isRemote()) { |
| |
| return proxy; |
| |
| } else { |
| final ApplicationServer applicationServer = ServerFederation.getApplicationServer(); |
| if (interfaceType.isBusiness()) { |
| return applicationServer.getBusinessObject(this.getProxyInfo()); |
| } else { |
| return applicationServer.getEJBObject(this.getProxyInfo()); |
| } |
| } |
| } |
| |
| protected abstract Object getPrimaryKey(Method method, Object[] args, Object proxy) throws Throwable; |
| |
| protected abstract Object isIdentical(Method method, Object[] args, Object proxy) throws Throwable; |
| |
| protected abstract Object remove(Class interfce, Method method, Object[] args, Object proxy) throws Throwable; |
| |
| protected Object businessMethod(final Class<?> interfce, final Method method, final Object[] args, final Object proxy) throws Throwable { |
| final BeanContext beanContext = getBeanContext(); |
| |
| if (beanContext.isAsynchronous(method)) { |
| |
| final SecurityService securityService = SystemInstance.get().getComponent(SecurityService.class); |
| |
| if(null == securityService){ |
| throw new OpenEJBRuntimeException("SecurityService has not been initialized"); |
| } |
| |
| Object stateTmp = securityService.currentState(); |
| final boolean associate; |
| if (stateTmp == null) { |
| stateTmp = ClientSecurity.getIdentity(); |
| associate = stateTmp != null; |
| } else { |
| associate = false; |
| } |
| final Object securityState = stateTmp; |
| |
| final ThreadContext threadContext = ThreadContext.getThreadContext(); |
| final AsynchronousPool asynchronousPool = beanContext.getModuleContext().getAppContext().getAsynchronousPool(); |
| |
| return asynchronousPool.invoke(new Callable<Object>() { |
| @Override |
| public Object call() throws Exception { |
| final Object threadState; |
| if (associate) { |
| //noinspection unchecked |
| securityService.associate(securityState); |
| threadState = null; |
| } else { |
| threadState = securityService.currentState(); |
| securityService.setState(securityState); |
| } |
| |
| final ThreadContext oldCtx; // ensure context is the same as for the caller |
| if (threadContext != null) { |
| oldCtx = ThreadContext.enter(new ThreadContext(threadContext)); |
| } else { |
| oldCtx = null; |
| } |
| |
| try { |
| return synchronizedBusinessMethod(interfce, method, args); |
| } catch (final ApplicationException ae) { |
| |
| logger.error("EjbObjectProxyHandler: Asynchronous call to '" + interfce.getSimpleName() + "' on '" + method.getName() + "' failed", ae); |
| |
| throw ae; |
| } finally { |
| if (threadContext != null) { |
| ThreadContext.exit(oldCtx); |
| } |
| if (!associate) { |
| securityService.setState(threadState); |
| } else { |
| securityService.disassociate(); |
| } |
| } |
| } |
| }, method.getReturnType() == Void.TYPE); |
| } else { |
| return synchronizedBusinessMethod(interfce, method, args); |
| } |
| } |
| |
| protected Object synchronizedBusinessMethod(final Class<?> interfce, final Method method, final Object[] args) throws OpenEJBException { |
| return container.invoke(deploymentID, interfaceType, interfce, method, args, primaryKey); |
| } |
| |
| public static Object createProxy(final BeanContext beanContext, final Object primaryKey, final InterfaceType interfaceType, final Class mainInterface) { |
| return createProxy(beanContext, primaryKey, interfaceType, null, mainInterface); |
| } |
| |
| public static Object createProxy(final BeanContext beanContext, final Object primaryKey, InterfaceType interfaceType, final List<Class> interfaces, final Class mainInterface) { |
| if (!interfaceType.isHome()) { |
| interfaceType = interfaceType.getCounterpart(); |
| } |
| final EjbHomeProxyHandler homeHandler = EjbHomeProxyHandler.createHomeHandler(beanContext, interfaceType, interfaces, mainInterface); |
| return homeHandler.createProxy(primaryKey, mainInterface); |
| } |
| |
| } |