1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.invocation;
23
24 import java.io.Externalizable;
25 import java.io.IOException;
26 import java.io.ObjectInput;
27 import java.io.ObjectOutput;
28 import java.lang.reflect.UndeclaredThrowableException;
29
30 import javax.transaction.Transaction;
31
32 import org.jboss.invocation.Invocation;
33 import org.jboss.invocation.Invoker;
34
35 import org.jboss.proxy.Interceptor;
36
37 import org.jboss.system.Registry;
38 import org.jboss.util.id.GUID;
39
40 /**
41 * A very simple implementation of it that branches to the local stuff.
42 *
43 * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
44 * @author Scott.Stark@jboss.org
45 * @version $Revision: 62554 $
46 */
47 public class InvokerInterceptor
48 extends Interceptor
49 implements Externalizable
50 {
51 /** Serial Version Identifier. @since 1.2 */
52 private static final long serialVersionUID = 2548120545997920357L;
53
54 /** The value of our local Invoker.ID to detect when we are local. */
55 private GUID invokerID = Invoker.ID;
56
57 /** Invoker to the remote JMX node. */
58 protected Invoker remoteInvoker;
59
60 /** Static references to local invokers. */
61 protected static Invoker localInvoker;
62
63 /** The InvokerProxyHA class */
64 protected static Class invokerProxyHA;
65
66 static
67 {
68 try
69 {
70 // Using Class.forName() to avoid security problems in the client
71 invokerProxyHA = Class.forName("org.jboss.invocation.InvokerProxyHA");
72 }
73 catch (Throwable ignored)
74 {
75 }
76 }
77
78 /**
79 * Get the local invoker reference, useful for optimization.
80 */
81 public static Invoker getLocal()
82 {
83 return localInvoker;
84 }
85
86 /**
87 * Set the local invoker reference, useful for optimization.
88 */
89 public static void setLocal(Invoker invoker)
90 {
91 localInvoker = invoker;
92 }
93
94 /**
95 * Exposed for externalization.
96 */
97 public InvokerInterceptor()
98 {
99 super();
100 }
101
102 /**
103 * Returns wether we are local to the originating container or not.
104 *
105 * @return true when we have the same GUID
106 */
107 public boolean isLocal()
108 {
109 return invokerID.equals(Invoker.ID);
110 }
111
112 /**
113 * Whether the target is local
114 *
115 * @param invocation the invocation
116 * @return true when the target is local
117 */
118 public boolean isLocal(Invocation invocation)
119 {
120 // No local invoker, it must be remote
121 if (localInvoker == null)
122 return false;
123
124 // The proxy was downloaded from a remote location
125 if (isLocal() == false)
126 {
127 // It is not clustered so we go remote
128 if (isClustered(invocation) == false)
129 return false;
130 }
131
132 // See whether we have a local target
133 return hasLocalTarget(invocation);
134 }
135
136 /**
137 * Whether we are in a clustered environment<p>
138 *
139 * NOTE: This should be future compatible under any
140 * new design where a prior target chooser interceptor
141 * picks a non HA target than that code being
142 * inside a ha invoker.
143 *
144 * @param invocation the invocation
145 * @return true when a clustered invoker
146 */
147 public boolean isClustered(Invocation invocation)
148 {
149 // No clustering classes
150 if (invokerProxyHA == null)
151 return false;
152
153 // Is the invoker a HA invoker?
154 InvocationContext ctx = invocation.getInvocationContext();
155 Invoker invoker = ctx.getInvoker();
156 return invoker != null && invokerProxyHA.isAssignableFrom(invoker.getClass());
157 }
158
159 /**
160 * Whether there is a local target
161 *
162 * @param invocation
163 * @return true when in the registry
164 */
165 public boolean hasLocalTarget(Invocation invocation)
166 {
167 return Registry.lookup(invocation.getObjectName()) != null;
168 }
169
170 /**
171 * The invocation on the delegate, calls the right invoker.
172 * Remote if we are remote, local if we are local.
173 */
174 public Object invoke(Invocation invocation)
175 throws Exception
176 {
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) throws Exception
192 {
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) throws Exception
204 {
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 {
215 Object rtnValue = localInvoker.invoke(invocationCopy);
216 MarshalledValue mv = new MarshalledValue(rtnValue);
217 return mv.get();
218 }
219 catch(Throwable t)
220 {
221 MarshalledValue mv = new MarshalledValue(t);
222 Throwable t2 = (Throwable) mv.get();
223 if( t2 instanceof Exception )
224 throw (Exception) t2;
225 else
226 throw new UndeclaredThrowableException(t2);
227 }
228 }
229
230 /**
231 * Invoke using invoker
232 *
233 * @param invocation the invocation
234 * @return the result
235 * @throws Exception for any error
236 */
237 protected Object invokeInvoker(Invocation invocation) throws Exception
238 {
239 //Specify that it is inter-vm on the invocation
240 invocation.setInterVM(Boolean.TRUE);
241
242 InvocationContext ctx = invocation.getInvocationContext();
243 Invoker invoker = ctx.getInvoker();
244 return invoker.invoke(invocation);
245 }
246
247 /**
248 * Externalize this instance.
249 *
250 * <p>
251 * If this instance lives in a different VM than its container
252 * invoker, the remote interface of the container invoker is
253 * not externalized.
254 */
255 public void writeExternal(final ObjectOutput out)
256 throws IOException
257 {
258 out.writeObject(invokerID);
259 }
260
261 /**
262 * Un-externalize this instance.
263 *
264 * <p>
265 * We check timestamps of the interfaces to see if the instance is in the original
266 * VM of creation
267 */
268 public void readExternal(final ObjectInput in)
269 throws IOException, ClassNotFoundException
270 {
271 invokerID = (GUID)in.readObject();
272 }
273 }