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.ejb3;
023:
024: import java.lang.reflect.Method;
025: import java.util.HashMap;
026: import java.util.Hashtable;
027: import java.util.Map;
028:
029: import javax.ejb.EJBObject;
030: import javax.ejb.Handle;
031: import javax.ejb.LocalHome;
032: import javax.ejb.RemoteHome;
033: import org.jboss.aop.AspectManager;
034: import org.jboss.aop.Dispatcher;
035: import org.jboss.aop.MethodInfo;
036: import org.jboss.aop.advice.Interceptor;
037: import org.jboss.aop.joinpoint.Invocation;
038: import org.jboss.aop.joinpoint.InvocationResponse;
039: import org.jboss.aop.util.MethodHashing;
040: import org.jboss.aspects.asynch.FutureHolder;
041: import org.jboss.ejb3.interceptor.InterceptorInfoRepository;
042: import org.jboss.ejb3.remoting.IsLocalInterceptor;
043: import org.jboss.ejb3.stateful.StatefulContainerInvocation;
044: import org.jboss.logging.Logger;
045: import org.jboss.serial.io.MarshalledObjectForLocalCalls;
046:
047: /**
048: * Comment
049: *
050: * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
051: * @version $Revision: 61280 $
052: */
053: public abstract class SessionContainer extends EJBContainer {
054: @SuppressWarnings("unused")
055: private static final Logger log = Logger
056: .getLogger(SessionContainer.class);
057:
058: protected ProxyDeployer proxyDeployer;
059: protected Map clusterFamilies = new HashMap();
060:
061: public class InvokedMethod {
062: public InvokedMethod(boolean localInterface, Method method) {
063: isLocalInterface = localInterface;
064: this .method = method;
065: }
066:
067: public boolean isLocalInterface;
068: public Method method;
069: }
070:
071: public abstract Object createLocalProxy(Object id) throws Exception;
072:
073: public abstract Object createRemoteProxy(Object id)
074: throws Exception;
075:
076: protected ThreadLocalStack<InvokedMethod> invokedMethod = new ThreadLocalStack<InvokedMethod>();
077:
078: public SessionContainer(ClassLoader cl, String beanClassName,
079: String ejbName, AspectManager manager,
080: Hashtable ctxProperties,
081: InterceptorInfoRepository interceptorRepository,
082: Ejb3Deployment deployment) {
083: super (Ejb3Module.BASE_EJB3_JMX_NAME + ",name=" + ejbName,
084: manager, cl, beanClassName, ejbName, ctxProperties,
085: interceptorRepository, deployment);
086: proxyDeployer = new ProxyDeployer(this );
087: }
088:
089: public Class getInvokedBusinessInterface() {
090: InvokedMethod method = invokedMethod.get();
091: if (method == null)
092: throw new IllegalStateException(
093: "getInvokedBusinessInterface() being invoked outside of a business invocation");
094: if (method.method == null)
095: throw new IllegalStateException(
096: "getInvokedBusinessInterface() being invoked outside of a business invocation");
097: if (method.isLocalInterface)
098: return method.method.getDeclaringClass();
099: Class[] remoteInterfaces = ProxyFactoryHelper
100: .getRemoteInterfaces(this );
101: for (Class intf : remoteInterfaces) {
102: try {
103: intf.getMethod(method.method.getName(), method.method
104: .getParameterTypes());
105: return intf;
106: } catch (NoSuchMethodException ignored) {
107: // continue
108: }
109: }
110: throw new IllegalStateException(
111: "Unable to find geInvokedBusinessInterface()");
112: }
113:
114: @Override
115: public void instantiated() {
116: super .instantiated();
117: proxyDeployer.initializeRemoteBindingMetadata();
118: proxyDeployer.initializeLocalBindingMetadata();
119: }
120:
121: @Override
122: public void processMetadata(DependencyPolicy dependencyPolicy) {
123: super .processMetadata(dependencyPolicy);
124: }
125:
126: public void start() throws Exception {
127: super .start();
128: // So that Remoting layer can reference this container easily.
129: Dispatcher.singleton.registerTarget(getObjectName()
130: .getCanonicalName(), this );
131: proxyDeployer.start();
132: }
133:
134: public Map getClusterFamilies() {
135: return clusterFamilies;
136: }
137:
138: public void stop() throws Exception {
139: try {
140: proxyDeployer.stop();
141: } catch (Exception ignore) {
142: log.trace("Proxy deployer stop failed", ignore);
143: }
144: try {
145: Dispatcher.singleton.unregisterTarget(getObjectName()
146: .getCanonicalName());
147: } catch (Exception ignore) {
148: log.trace("Dispatcher unregister target failed", ignore);
149: }
150: super .stop();
151: }
152:
153: protected void createMethodMap() {
154: super .createMethodMap();
155: try {
156: RemoteHome home = (RemoteHome) resolveAnnotation(RemoteHome.class);
157: if (home != null) {
158: Method[] declaredMethods = home.value().getMethods();
159: for (int i = 0; i < declaredMethods.length; i++) {
160: long hash = MethodHashing
161: .methodHash(declaredMethods[i]);
162: advisedMethods.put(hash, declaredMethods[i]);
163: }
164:
165: declaredMethods = javax.ejb.EJBObject.class
166: .getMethods();
167: for (int i = 0; i < declaredMethods.length; i++) {
168: long hash = MethodHashing
169: .methodHash(declaredMethods[i]);
170: advisedMethods.put(hash, declaredMethods[i]);
171: }
172: }
173:
174: LocalHome localHome = (LocalHome) resolveAnnotation(LocalHome.class);
175: if (localHome != null) {
176: Method[] declaredMethods = localHome.value()
177: .getMethods();
178: for (int i = 0; i < declaredMethods.length; i++) {
179: long hash = MethodHashing
180: .methodHash(declaredMethods[i]);
181: advisedMethods.put(hash, declaredMethods[i]);
182: }
183:
184: declaredMethods = javax.ejb.EJBLocalObject.class
185: .getMethods();
186: for (int i = 0; i < declaredMethods.length; i++) {
187: long hash = MethodHashing
188: .methodHash(declaredMethods[i]);
189: advisedMethods.put(hash, declaredMethods[i]);
190: }
191: }
192: } catch (Exception e) {
193: throw new RuntimeException(e);
194: }
195: }
196:
197: protected boolean isHomeMethod(Method method) {
198: if (javax.ejb.EJBHome.class.isAssignableFrom(method
199: .getDeclaringClass()))
200: return true;
201: if (javax.ejb.EJBLocalHome.class.isAssignableFrom(method
202: .getDeclaringClass()))
203: return true;
204: return false;
205: }
206:
207: protected boolean isEJBObjectMethod(Method method) {
208: if (method.getDeclaringClass().getName().equals(
209: "javax.ejb.EJBObject"))
210: return true;
211:
212: if (method.getDeclaringClass().getName().equals(
213: "javax.ejb.EJBLocalObject"))
214: return true;
215:
216: return false;
217: }
218:
219: protected boolean isHandleMethod(Method method) {
220: if (method.getDeclaringClass().getName().equals(
221: "javax.ejb.Handle"))
222: return true;
223:
224: return false;
225: }
226:
227: public static InvocationResponse marshallException(
228: Invocation invocation, Throwable exception,
229: Map responseContext) throws Throwable {
230: if (!invocation.getMetaData().hasTag(
231: IsLocalInterceptor.IS_LOCAL))
232: throw exception;
233:
234: InvocationResponse response = new InvocationResponse();
235: response.setContextInfo(responseContext);
236:
237: response.addAttachment(IsLocalInterceptor.IS_LOCAL_EXCEPTION,
238: new MarshalledObjectForLocalCalls(exception));
239:
240: return response;
241: }
242:
243: public static InvocationResponse marshallResponse(
244: Invocation invocation, Object rtn, Map responseContext)
245: throws java.io.IOException {
246: InvocationResponse response;
247: // marshall return value
248: if (rtn != null
249: && invocation.getMetaData().hasTag(
250: IsLocalInterceptor.IS_LOCAL)) {
251: response = new InvocationResponse(
252: new MarshalledObjectForLocalCalls(rtn));
253: } else {
254: response = new InvocationResponse(rtn);
255: }
256: response.setContextInfo(responseContext);
257: return response;
258: }
259:
260: /**
261: * Invoke a method on the virtual EJB bean. The method must be one of the methods defined in one
262: * of the business interfaces (or home interface) of the bean.
263: *
264: * TODO: work in progress
265: *
266: * @param factory the originating end point
267: * @param id unique identifier (primary key), can be null for stateless
268: * @param method the business or home method to invoke
269: * @param args the arguments for the method
270: * @param provider for asynchronous usage
271: */
272: public Object invoke(ProxyFactory factory, Object id,
273: Method method, Object args[], FutureHolder provider)
274: throws Throwable {
275: ClassLoader oldLoader = Thread.currentThread()
276: .getContextClassLoader();
277: pushEnc();
278: try {
279: long hash = MethodHashing.calculateHash(method);
280: MethodInfo info = (MethodInfo) methodInterceptors.get(hash);
281: if (info == null) {
282: throw new RuntimeException(
283: "Could not resolve beanClass method from proxy call: "
284: + method.toString());
285: }
286:
287: Method unadvisedMethod = info.getUnadvisedMethod();
288:
289: if (unadvisedMethod != null
290: && isHomeMethod(unadvisedMethod)) {
291: return invokeHomeMethod(factory, info, args);
292: } else if (unadvisedMethod != null
293: && isEJBObjectMethod(unadvisedMethod)) {
294: return invokeEJBObjectMethod(factory, id, info, args);
295: }
296:
297: // FIXME: Ahem, stateful container invocation works on all.... (violating contract though)
298: EJBContainerInvocation nextInvocation = new StatefulContainerInvocation(
299: info, id);
300: nextInvocation.setAdvisor(this );
301: nextInvocation.setArguments(args);
302:
303: // allow a container to supplement information into an invocation
304: nextInvocation = populateInvocation(nextInvocation);
305:
306: ProxyUtils.addLocalAsynchronousInfo(nextInvocation,
307: provider);
308: try {
309: invokedMethod.push(new InvokedMethod(true, method));
310: return nextInvocation.invokeNext();
311: } finally {
312: invokedMethod.pop();
313: }
314: } finally {
315: Thread.currentThread().setContextClassLoader(oldLoader);
316: popEnc();
317: }
318: }
319:
320: /**
321: * TODO: work in progress (refactor both invokeHomeMethod's, localHomeInvoke)
322: */
323: private Object invokeHomeMethod(ProxyFactory factory,
324: MethodInfo info, Object args[]) throws Exception {
325: Method unadvisedMethod = info.getUnadvisedMethod();
326: if (unadvisedMethod.getName().equals("create")) {
327: Class[] initParameterTypes = {};
328: Object[] initParameterValues = {};
329: if (unadvisedMethod.getParameterTypes().length > 0) {
330: initParameterTypes = unadvisedMethod
331: .getParameterTypes();
332: initParameterValues = args;
333: }
334:
335: Object id = createSession(initParameterTypes,
336: initParameterValues);
337:
338: Object proxy = factory.createProxy(id);
339:
340: return proxy;
341: } else if (unadvisedMethod.getName().equals("remove")) {
342: if (args[0] instanceof Handle)
343: removeHandle((Handle) args[0]);
344: else
345: destroySession(args[0]);
346:
347: return null;
348: } else {
349: throw new IllegalArgumentException("illegal home method "
350: + unadvisedMethod);
351: }
352: }
353:
354: /**
355: * Create session to an EJB bean.
356: *
357: * @param initParameterTypes the parameter types used by the home's create method
358: * @param initParameterValues the arguments for the home's create method
359: * @return the identifier of the session
360: */
361: abstract protected Object createSession(Class initParameterTypes[],
362: Object initParameterValues[]);
363:
364: /**
365: * Destroy a created session.
366: *
367: * @param id the identifier of the session
368: */
369: protected void destroySession(Object id) {
370: throw new RuntimeException("NYI");
371: }
372:
373: protected Object invokeEJBObjectMethod(ProxyFactory factory,
374: Object id, MethodInfo info, Object args[]) throws Exception {
375: Method unadvisedMethod = info.getUnadvisedMethod();
376: if (unadvisedMethod.getName().equals("getEJBHome")) {
377: return factory.createHomeProxy();
378: }
379: if (unadvisedMethod.getName().equals("getPrimaryKey")) {
380: return id;
381: }
382: if (unadvisedMethod.getName().equals("isIdentical")) {
383: // object has no identity
384: if (id == null)
385: return false;
386:
387: EJBObject bean = (EJBObject) args[0];
388:
389: Object primaryKey = bean.getPrimaryKey();
390: if (primaryKey == null)
391: return false;
392:
393: boolean isIdentical = id.equals(primaryKey);
394:
395: return isIdentical;
396: }
397: if (unadvisedMethod.getName().equals("remove")) {
398: destroySession(id);
399:
400: return null;
401: }
402: throw new RuntimeException("NYI");
403: }
404:
405: /**
406: * Allow a container sub class to supplement an invocation. Per default nothing to supplement.
407: *
408: * @param invocation
409: * @return
410: */
411: protected EJBContainerInvocation populateInvocation(
412: EJBContainerInvocation invocation) {
413: return invocation;
414: }
415:
416: abstract protected void removeHandle(Handle handle)
417: throws Exception;
418: }
|