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.invocation;
023:
024: import java.io.Externalizable;
025: import java.io.IOException;
026: import java.io.ObjectInput;
027: import java.io.ObjectOutput;
028: import java.lang.reflect.UndeclaredThrowableException;
029: import java.math.BigDecimal;
030: import java.math.BigInteger;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: import javax.transaction.Transaction;
036:
037: import org.jboss.proxy.Interceptor;
038: import org.jboss.proxy.ClientContainer;
039:
040: import org.jboss.system.Registry;
041: import org.jboss.util.id.GUID;
042: import org.jboss.remoting.serialization.IMarshalledValue;
043: import org.jboss.remoting.serialization.SerializationStreamFactory;
044: import org.jboss.serial.objectmetamodel.safecloning.SafeClone;
045:
046: /**
047: * A very simple implementation of it that branches to the local stuff.
048: *
049: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
050: * @author Scott.Stark@jboss.org
051: * @version $Revision: 57209 $
052: */
053: public class InvokerInterceptor extends Interceptor implements
054: Externalizable {
055: /** Serial Version Identifier. @since 1.2 */
056: private static final long serialVersionUID = 2548120545997920357L;
057:
058: /** The value of our local Invoker.ID to detect when we are local. */
059: private GUID invokerID = Invoker.ID;
060:
061: /** Invoker to the remote JMX node. */
062: protected Invoker remoteInvoker;
063:
064: /** Static references to local invokers. */
065: protected static Invoker localInvoker;
066:
067: /** The InvokerProxyHA class */
068: protected static Class invokerProxyHA;
069:
070: static {
071: try {
072: // Using Class.forName() to avoid security problems in the client
073: invokerProxyHA = Class
074: .forName("org.jboss.invocation.InvokerProxyHA");
075: } catch (Throwable ignored) {
076: }
077:
078: try {
079: // To guarantee backwards compatibility, we need to make sure this SerializationManager is being used
080: Class
081: .forName("org.jboss.invocation.unified.interfaces.JavaSerializationManager");
082: } catch (Throwable ignored) {
083: }
084:
085: }
086:
087: /**
088: * Get the local invoker reference, useful for optimization.
089: */
090: public static Invoker getLocal() {
091: return localInvoker;
092: }
093:
094: /**
095: * Set the local invoker reference, useful for optimization.
096: */
097: public static void setLocal(Invoker invoker) {
098: localInvoker = invoker;
099: }
100:
101: /**
102: * Exposed for externalization.
103: */
104: public InvokerInterceptor() {
105: super ();
106: }
107:
108: /**
109: * Returns wether we are local to the originating container or not.
110: *
111: * @return true when we have the same GUID
112: */
113: public boolean isLocal() {
114: return invokerID.equals(Invoker.ID);
115: }
116:
117: /**
118: * Whether the target is local
119: *
120: * @param invocation the invocation
121: * @return true when the target is local
122: */
123: public boolean isLocal(Invocation invocation) {
124: // No local invoker, it must be remote
125: if (localInvoker == null)
126: return false;
127:
128: // The proxy was downloaded from a remote location
129: if (isLocal() == false) {
130: // It is not clustered so we go remote
131: if (isClustered(invocation) == false)
132: return false;
133: }
134:
135: // See whether we have a local target
136: return hasLocalTarget(invocation);
137: }
138:
139: /**
140: * Whether we are in a clustered environment<p>
141: *
142: * NOTE: This should be future compatible under any
143: * new design where a prior target chooser interceptor
144: * picks a non HA target than that code being
145: * inside a ha invoker.
146: *
147: * @param invocation the invocation
148: * @return true when a clustered invoker
149: */
150: public boolean isClustered(Invocation invocation) {
151: // No clustering classes
152: if (invokerProxyHA == null)
153: return false;
154:
155: // Is the invoker a HA invoker?
156: InvocationContext ctx = invocation.getInvocationContext();
157: Invoker invoker = ctx.getInvoker();
158: return invoker != null
159: && invokerProxyHA.isAssignableFrom(invoker.getClass());
160: }
161:
162: /**
163: * Whether there is a local target
164: *
165: * @param invocation
166: * @return true when in the registry
167: */
168: public boolean hasLocalTarget(Invocation invocation) {
169: return Registry.lookup(invocation.getObjectName()) != null;
170: }
171:
172: /**
173: * The invocation on the delegate, calls the right invoker.
174: * Remote if we are remote, local if we are local.
175: */
176: public Object invoke(Invocation invocation) throws Exception {
177: // optimize if calling another bean in same server VM
178: if (isLocal(invocation))
179: return invokeLocal(invocation);
180: else
181: return invokeInvoker(invocation);
182: }
183:
184: /**
185: * Invoke using local invoker
186: *
187: * @param invocation the invocation
188: * @return the result
189: * @throws Exception for any error
190: */
191: protected Object invokeLocal(Invocation invocation)
192: throws Exception {
193: return localInvoker.invoke(invocation);
194: }
195:
196: /**
197: * Invoke using local invoker and marshalled
198: *
199: * @param invocation the invocation
200: * @return the result
201: * @throws Exception for any error
202: */
203: protected Object invokeMarshalled(Invocation invocation)
204: throws Exception {
205: MarshalledInvocation mi = new MarshalledInvocation(invocation);
206: MarshalledValue copy = new MarshalledValue(mi);
207: Invocation invocationCopy = (Invocation) copy.get();
208:
209: // copy the Tx
210: Transaction tx = invocation.getTransaction();
211: invocationCopy.setTransaction(tx);
212:
213: try {
214: Object rtnValue = localInvoker.invoke(invocationCopy);
215: MarshalledValue mv = new MarshalledValue(rtnValue);
216: return mv.get();
217: } catch (Throwable t) {
218: MarshalledValue mv = new MarshalledValue(t);
219: Throwable t2 = (Throwable) mv.get();
220: if (t2 instanceof Exception)
221: throw (Exception) t2;
222: else
223: throw new UndeclaredThrowableException(t2);
224: }
225: }
226:
227: /** These objects are safe to reuse in callByValue operations */
228: static final SafeClone safeToReuse = new SafeClone() {
229: public boolean isSafeToReuse(Object obj) {
230: if (obj == null) {
231: return false;
232: }
233:
234: if (obj instanceof ClientContainer || obj instanceof String
235: || obj instanceof Number
236: || obj instanceof BigDecimal
237: || obj instanceof BigInteger || obj instanceof Byte
238: || obj instanceof Double || obj instanceof Float
239: || obj instanceof Integer || obj instanceof Long
240: || obj instanceof Short) {
241: return true;
242: } else {
243: return false;
244: }
245: }
246: };
247:
248: protected IMarshalledValue createMarshalledValueForCallByValue(
249: Object value) throws IOException {
250: return SerializationStreamFactory.getManagerInstance()
251: .createdMarshalledValue(value);
252: }
253:
254: /** This method is for local calls when using pass-by-value*/
255: protected Object invokeLocalMarshalled(Invocation invocation)
256: throws Exception {
257:
258: IMarshalledValue value = createMarshalledValueForCallByValue(invocation
259: .getArguments());
260: MarshalledInvocation invocationCopy = createInvocationCopy(
261: invocation, value);
262:
263: // copy the Tx
264: Transaction tx = invocation.getTransaction();
265: invocationCopy.setTransaction(tx);
266:
267: try {
268: Object rtnValue = localInvoker.invoke(invocationCopy);
269: IMarshalledValue mv = createMarshalledValueForCallByValue(rtnValue);
270: return mv.get();
271: } catch (Throwable t) {
272: IMarshalledValue mv = SerializationStreamFactory
273: .getManagerInstance().createdMarshalledValue(t);
274: Throwable t2 = (Throwable) mv.get();
275: if (t2 instanceof Exception)
276: throw (Exception) t2;
277: else
278: throw new UndeclaredThrowableException(t2);
279: }
280: }
281:
282: /** It is too expensive to serialize this entire object just to get class isolation
283: and MarshalledValues on local calls.
284: We are creating an in-memory copy without using serialization for that matter.
285: * @throws ClassNotFoundException
286: * @throws IOException */
287: private MarshalledInvocation createInvocationCopy(
288: Invocation invocation, IMarshalledValue value)
289: throws IOException, ClassNotFoundException {
290:
291: MarshalledInvocation invocationCopy = new MarshalledInvocation(
292: invocation);
293: invocationCopy.setMethod(null);
294: invocationCopy.setMethodHash(MarshalledInvocation
295: .calculateHash(invocation.getMethod()));
296: invocationCopy.setMarshalledArguments(value);
297: invocationCopy.setArguments(null);
298:
299: InvocationContext copyContext = null;
300: if (invocation.getInvocationContext() != null) {
301: copyContext = (InvocationContext) createMarshalledValueForCallByValue(
302: invocation.getInvocationContext()).get();
303: }
304: invocationCopy.setInvocationContext(copyContext);
305:
306: Map payLoad = invocation.getPayload();
307: Map payloadCopy = new HashMap();
308:
309: if (payLoad != null && payLoad.size() != 0) {
310:
311: Iterator keys = payLoad.keySet().iterator();
312: while (keys.hasNext()) {
313: Object currentKey = keys.next();
314: Object valueSource = payLoad.get(currentKey);
315:
316: payloadCopy
317: .put(
318: currentKey,
319: this
320: .createMarshalledValueForCallByValue(valueSource));
321: }
322: }
323:
324: invocationCopy.payload = payloadCopy;
325:
326: return invocationCopy;
327: }
328:
329: /**
330: * Invoke using invoker
331: *
332: * @param invocation the invocation
333: * @return the result
334: * @throws Exception for any error
335: */
336: protected Object invokeInvoker(Invocation invocation)
337: throws Exception {
338: InvocationContext ctx = invocation.getInvocationContext();
339: Invoker invoker = ctx.getInvoker();
340: return invoker.invoke(invocation);
341: }
342:
343: /**
344: * Externalize this instance.
345: *
346: * <p>
347: * If this instance lives in a different VM than its container
348: * invoker, the remote interface of the container invoker is
349: * not externalized.
350: */
351: public void writeExternal(final ObjectOutput out)
352: throws IOException {
353: out.writeObject(invokerID);
354: }
355:
356: /**
357: * Un-externalize this instance.
358: *
359: * <p>
360: * We check timestamps of the interfaces to see if the instance is in the original
361: * VM of creation
362: */
363: public void readExternal(final ObjectInput in) throws IOException,
364: ClassNotFoundException {
365: invokerID = (GUID) in.readObject();
366: }
367: }
|