/* * 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.invocation; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.UndeclaredThrowableException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.transaction.Transaction; import org.jboss.proxy.Interceptor; import org.jboss.proxy.ClientContainer; import org.jboss.system.Registry; import org.jboss.util.id.GUID; import org.jboss.remoting.serialization.IMarshalledValue; import org.jboss.remoting.serialization.SerializationStreamFactory; import org.jboss.serial.objectmetamodel.safecloning.SafeClone; /** * A very simple implementation of it that branches to the local stuff. * * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a> * @author Scott.Stark@jboss.org * @version $Revision: 81179 $ */ public class InvokerInterceptor extends Interceptor implements Externalizable { /** Serial Version Identifier. @since 1.2 */ private static final long serialVersionUID = 2548120545997920357L; /** The value of our local Invoker.ID to detect when we are local. */ private GUID invokerID = Invoker.ID; /** Invoker to the remote JMX node. */ protected Invoker remoteInvoker; /** Static references to local invokers. */ protected static Invoker localInvoker; /** The InvokerProxyHA class */ protected static Class invokerProxyHA; static { try { // Using Class.forName() to avoid security problems in the client invokerProxyHA = Class.forName("org.jboss.invocation.InvokerProxyHA"); } catch (Throwable ignored) { } try { // To guarantee backwards compatibility, we need to make sure this SerializationManager is being used Class.forName("org.jboss.invocation.unified.interfaces.JavaSerializationManager"); } catch (Throwable ignored) { } } /** * Get the local invoker reference, useful for optimization. */ public static Invoker getLocal() { return localInvoker; } /** * Set the local invoker reference, useful for optimization. */ public static void setLocal(Invoker invoker) { localInvoker = invoker; } /** * Exposed for externalization. */ public InvokerInterceptor() { super(); } /** * Returns wether we are local to the originating container or not. * * @return true when we have the same GUID */ public boolean isLocal() { return invokerID.equals(Invoker.ID); } /** * Whether the target is local * * @param invocation the invocation * @return true when the target is local */ public boolean isLocal(Invocation invocation) { // No local invoker, it must be remote if (localInvoker == null) return false; // The proxy was downloaded from a remote location if (isLocal() == false) { // It is not clustered so we go remote if (isClustered(invocation) == false) return false; } // See whether we have a local target return hasLocalTarget(invocation); } /** * Whether we are in a clustered environment<p> * * NOTE: This should be future compatible under any * new design where a prior target chooser interceptor * picks a non HA target than that code being * inside a ha invoker. * * @param invocation the invocation * @return true when a clustered invoker */ public boolean isClustered(Invocation invocation) { // No clustering classes if (invokerProxyHA == null) return false; // Is the invoker a HA invoker? InvocationContext ctx = invocation.getInvocationContext(); Invoker invoker = ctx.getInvoker(); return invoker != null && invokerProxyHA.isAssignableFrom(invoker.getClass()); } /** * Whether there is a local target * * @param invocation * @return true when in the registry */ public boolean hasLocalTarget(Invocation invocation) { return Registry.lookup(invocation.getObjectName()) != null; } /** * The invocation on the delegate, calls the right invoker. * Remote if we are remote, local if we are local. */ public Object invoke(Invocation invocation) throws Exception { // optimize if calling another bean in same server VM if (isLocal(invocation)) return invokeLocal(invocation); else return invokeInvoker(invocation); } /** * Invoke using local invoker * * @param invocation the invocation * @return the result * @throws Exception for any error */ protected Object invokeLocal(Invocation invocation) throws Exception { return localInvoker.invoke(invocation); } /** * Invoke using local invoker and marshalled * * @param invocation the invocation * @return the result * @throws Exception for any error */ protected Object invokeMarshalled(Invocation invocation) throws Exception { MarshalledInvocation mi = new MarshalledInvocation(invocation); MarshalledValue copy = new MarshalledValue(mi); Invocation invocationCopy = (Invocation) copy.get(); // copy the Tx Transaction tx = invocation.getTransaction(); invocationCopy.setTransaction(tx); try { Object rtnValue = localInvoker.invoke(invocationCopy); MarshalledValue mv = new MarshalledValue(rtnValue); return mv.get(); } catch(Throwable t) { MarshalledValue mv = new MarshalledValue(t); Throwable t2 = (Throwable) mv.get(); if( t2 instanceof Exception ) throw (Exception) t2; else throw new UndeclaredThrowableException(t2); } } /** These objects are safe to reuse in callByValue operations */ static final SafeClone safeToReuse = new SafeClone(){ public boolean isSafeToReuse(Object obj) { if (obj==null) { return false; } if (obj instanceof ClientContainer || obj instanceof String || obj instanceof Number || obj instanceof BigDecimal || obj instanceof BigInteger || obj instanceof Byte || obj instanceof Double || obj instanceof Float || obj instanceof Integer || obj instanceof Long || obj instanceof Short) { return true; } else { return false; } } }; protected IMarshalledValue createMarshalledValueForCallByValue(Object value) throws IOException { return SerializationStreamFactory.getManagerInstance().createdMarshalledValue(value); } /** This method is for local calls when using pass-by-value*/ protected Object invokeLocalMarshalled(Invocation invocation) throws Exception { IMarshalledValue value = createMarshalledValueForCallByValue(invocation.getArguments()); MarshalledInvocation invocationCopy = createInvocationCopy(invocation, value); // copy the Tx Transaction tx = invocation.getTransaction(); invocationCopy.setTransaction(tx); try { Object rtnValue = localInvoker.invoke(invocationCopy); IMarshalledValue mv = createMarshalledValueForCallByValue(rtnValue); return mv.get(); } catch(Throwable t) { IMarshalledValue mv = SerializationStreamFactory.getManagerInstance().createdMarshalledValue(t); Throwable t2 = (Throwable) mv.get(); if( t2 instanceof Exception ) throw (Exception) t2; else throw new UndeclaredThrowableException(t2); } } /** It is too expensive to serialize this entire object just to get class isolation and MarshalledValues on local calls. We are creating an in-memory copy without using serialization for that matter. * @throws ClassNotFoundException * @throws IOException */ private MarshalledInvocation createInvocationCopy(Invocation invocation, IMarshalledValue value) throws IOException, ClassNotFoundException { MarshalledInvocation invocationCopy = new MarshalledInvocation(invocation); invocationCopy.setMethod(null); invocationCopy.setMethodHash(MarshalledInvocation.calculateHash(invocation.getMethod())); invocationCopy.setMarshalledArguments(value); invocationCopy.setArguments(null); InvocationContext copyContext = null; if (invocation.getInvocationContext()!=null) { copyContext = (InvocationContext)createMarshalledValueForCallByValue(invocation.getInvocationContext()).get(); } invocationCopy.setInvocationContext(copyContext); Map payLoad = invocation.getPayload(); Map payloadCopy = new HashMap(); if (payLoad!=null && payLoad.size()!=0) { Iterator keys = payLoad.keySet().iterator(); while (keys.hasNext()) { Object currentKey = keys.next(); Object valueSource = payLoad.get(currentKey); payloadCopy.put(currentKey,this.createMarshalledValueForCallByValue(valueSource)); } } invocationCopy.payload = payloadCopy; return invocationCopy; } /** * Invoke using invoker * * @param invocation the invocation * @return the result * @throws Exception for any error */ protected Object invokeInvoker(Invocation invocation) throws Exception { InvocationContext ctx = invocation.getInvocationContext(); Invoker invoker = ctx.getInvoker(); return invoker.invoke(invocation); } /** * Externalize this instance. * * <p> * If this instance lives in a different VM than its container * invoker, the remote interface of the container invoker is * not externalized. */ public void writeExternal(final ObjectOutput out) throws IOException { out.writeObject(invokerID); } /** * Un-externalize this instance. * * <p> * We check timestamps of the interfaces to see if the instance is in the original * VM of creation */ public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { invokerID = (GUID)in.readObject(); } }