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.ejb;
023:
024: import java.lang.reflect.Method;
025: import java.lang.reflect.InvocationTargetException;
026: import java.util.Map;
027: import java.util.HashMap;
028: import java.util.Hashtable;
029: import java.util.Set;
030: import java.rmi.RemoteException;
031:
032: import javax.ejb.EJBObject;
033: import javax.ejb.EJBLocalObject;
034: import javax.ejb.RemoveException;
035: import javax.ejb.EJBException;
036: import javax.ejb.Handle;
037: import javax.management.ObjectName;
038:
039: import org.jboss.invocation.Invocation;
040: import org.jboss.invocation.InvocationType;
041: import org.jboss.util.UnreachableStatementException;
042:
043: /**
044: * The container for <em>stateful</em> session beans.
045: * @author <a href="mailto:rickard.oberg@telkel.com">Rickard �berg</a>
046: * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a>
047: * @author <a href="mailto:marc.fleury@jboss.org">Marc Fleury</a>
048: * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
049: * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
050: * @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a>
051: * @version <tt>$Revision: 57209 $</tt>
052: * @jmx:mbean extends="org.jboss.ejb.ContainerMBean"
053: */
054:
055: public class StatefulSessionContainer extends SessionContainer
056: implements EJBProxyFactoryContainer, InstancePoolContainer {
057: /**
058: * This is the persistence manager for this container
059: */
060: protected StatefulSessionPersistenceManager persistenceManager;
061:
062: /**
063: * The instance cache.
064: */
065: protected InstanceCache instanceCache;
066: protected Method getEJBObject;
067:
068: public void setInstanceCache(InstanceCache ic) {
069: this .instanceCache = ic;
070: ic.setContainer(this );
071: }
072:
073: public InstanceCache getInstanceCache() {
074: return instanceCache;
075: }
076:
077: public StatefulSessionPersistenceManager getPersistenceManager() {
078: return persistenceManager;
079: }
080:
081: public void setPersistenceManager(
082: StatefulSessionPersistenceManager pm) {
083: persistenceManager = pm;
084: pm.setContainer(this );
085: }
086:
087: /**
088: * Override getMethodPermissions to work around the fact that stateful
089: * session handles obtain their ejb objects by doing an invocation on the
090: * container as a home method invocation using the Handle.getEJBObject
091: * method.
092: * @param m
093: * @param iface
094: * @return
095: */
096: public Set getMethodPermissions(Method m, InvocationType iface) {
097: if (m.equals(getEJBObject) == false)
098: return super .getMethodPermissions(m, iface);
099:
100: Class[] sig = {};
101: Set permissions = getBeanMetaData().getMethodPermissions(
102: "create", sig, iface);
103: return permissions;
104: }
105:
106: // Container implementation --------------------------------------
107: protected void createService() throws Exception {
108: super .createService();
109: // Get the Handle.getEJBObject method for permission checks
110: try {
111: getEJBObject = Handle.class.getMethod("getEJBObject",
112: new Class[0]);
113: } catch (Exception e) {
114: log
115: .warn("Failed to grant access to the Handle.getEJBObject method");
116: }
117: }
118:
119: /**
120: * creates and registers the instance cache
121: */
122: protected void createInstanceCache() throws Exception {
123: // Try to register the instance cache as an MBean
124: try {
125: ObjectName containerName = super .getJmxName();
126: Hashtable props = containerName.getKeyPropertyList();
127: props.put("plugin", "cache");
128: ObjectName cacheName = new ObjectName(containerName
129: .getDomain(), props);
130: server.registerMBean(instanceCache, cacheName);
131: } catch (Throwable t) {
132: log.debug("Failed to register cache as mbean", t);
133: }
134: // Init instance cache
135: instanceCache.create();
136: }
137:
138: /**
139: * create persistence manager
140: */
141: protected void createPersistenceManager() throws Exception {
142: persistenceManager.create();
143: }
144:
145: /**
146: * Start persistence
147: */
148: protected void startPersistenceManager() throws Exception {
149: persistenceManager.start();
150: }
151:
152: /**
153: * Start instance cache
154: */
155: protected void startInstanceCache() throws Exception {
156: instanceCache.start();
157: }
158:
159: /**
160: * Stop persistence
161: */
162: protected void stopPersistenceManager() {
163: persistenceManager.stop();
164: }
165:
166: /**
167: * Stop instance cache
168: */
169: protected void stopInstanceCache() {
170: instanceCache.stop();
171: }
172:
173: protected void destroyPersistenceManager() {
174: // Destroy persistence
175: persistenceManager.destroy();
176: persistenceManager.setContainer(null);
177: }
178:
179: protected void destroyInstanceCache() {
180: // Destroy instance cache
181: instanceCache.destroy();
182: instanceCache.setContainer(null);
183: try {
184: ObjectName containerName = super .getJmxName();
185: Hashtable props = containerName.getKeyPropertyList();
186: props.put("plugin", "cache");
187: ObjectName cacheName = new ObjectName(containerName
188: .getDomain(), props);
189: server.unregisterMBean(cacheName);
190: } catch (Throwable ignore) {
191: }
192: }
193:
194: // EJBObject implementation --------------------------------------
195:
196: public void remove(Invocation mi) throws RemoteException,
197: RemoveException {
198: // 7.6 EJB2.0, it is illegal to remove a bean while in a transaction
199: // if (((EnterpriseContext) mi.getEnterpriseContext()).getTransaction() != null)
200: // throw new RemoveException("StatefulSession bean in transaction, cannot remove (EJB2.0 7.6)");
201:
202: // if the session is removed already then let the user know they have a problem
203: StatefulSessionEnterpriseContext ctx = (StatefulSessionEnterpriseContext) mi
204: .getEnterpriseContext();
205: if (ctx.getId() == null) {
206: throw new RemoveException("SFSB has been removed already");
207: }
208:
209: // Remove from storage
210: try {
211: AllowedOperationsAssociation
212: .pushInMethodFlag(IN_EJB_REMOVE);
213: getPersistenceManager().removeSession(ctx);
214: } finally {
215: AllowedOperationsAssociation.popInMethodFlag();
216: }
217:
218: // We signify "removed" with a null id
219: ctx.setId(null);
220: removeCount++;
221: }
222:
223: // Home interface implementation ---------------------------------
224:
225: private void createSession(final Method m, final Object[] args,
226: final StatefulSessionEnterpriseContext ctx)
227: throws Exception {
228: boolean debug = log.isDebugEnabled();
229:
230: // Create a new ID and set it
231: Object id = getPersistenceManager().createId(ctx);
232: if (debug) {
233: log.debug("Created new session ID: " + id);
234: }
235: ctx.setId(id);
236:
237: // Invoke ejbCreate<METHOD>()
238: try {
239: AllowedOperationsAssociation
240: .pushInMethodFlag(IN_EJB_CREATE);
241:
242: // Build the ejbCreate<METHOD> from the home create<METHOD> sig
243: String createName = m.getName();
244: Object instance = ctx.getInstance();
245: String ejbCreateName = "ejbC" + createName.substring(1);
246: Method createMethod = instance.getClass().getMethod(
247: ejbCreateName, m.getParameterTypes());
248: if (debug) {
249: log.debug("Using create method for session: "
250: + createMethod);
251: }
252: createMethod.invoke(instance, args);
253: createCount++;
254: } catch (IllegalAccessException e) {
255: ctx.setId(null);
256:
257: throw new EJBException(e);
258: } catch (InvocationTargetException e) {
259: ctx.setId(null);
260:
261: Throwable t = e.getTargetException();
262: if (t instanceof RuntimeException) {
263: if (t instanceof EJBException)
264: throw (EJBException) t;
265: // Wrap runtime exceptions
266: throw new EJBException((Exception) t);
267: } else if (t instanceof Exception) {
268: // Remote, Create, or custom app. exception
269: throw (Exception) t;
270: } else if (t instanceof Error) {
271: throw (Error) t;
272: } else {
273: throw new org.jboss.util.UnexpectedThrowable(t);
274: }
275: } finally {
276: AllowedOperationsAssociation.popInMethodFlag();
277: }
278:
279: // call back to the PM to let it know that ejbCreate has been called with success
280: getPersistenceManager().createdSession(ctx);
281:
282: // Insert in cache
283: getInstanceCache().insert(ctx);
284:
285: // Create EJBObject
286: if (getProxyFactory() != null)
287: ctx.setEJBObject((EJBObject) getProxyFactory()
288: .getStatefulSessionEJBObject(id));
289:
290: // Create EJBLocalObject
291: if (getLocalHomeClass() != null)
292: ctx.setEJBLocalObject(getLocalProxyFactory()
293: .getStatefulSessionEJBLocalObject(id));
294: }
295:
296: public EJBObject createHome(Invocation mi) throws Exception {
297: StatefulSessionEnterpriseContext ctx = (StatefulSessionEnterpriseContext) mi
298: .getEnterpriseContext();
299: createSession(mi.getMethod(), mi.getArguments(), ctx);
300: return ctx.getEJBObject();
301: }
302:
303: // local home interface implementation
304:
305: /**
306: * @throws Error Not yet implemented
307: */
308: public void removeLocalHome(Invocation mi) throws RemoteException,
309: RemoveException {
310: throw new UnreachableStatementException();
311: }
312:
313: public EJBLocalObject createLocalHome(Invocation mi)
314: throws Exception {
315: StatefulSessionEnterpriseContext ctx = (StatefulSessionEnterpriseContext) mi
316: .getEnterpriseContext();
317: createSession(mi.getMethod(), mi.getArguments(), ctx);
318: return ctx.getEJBLocalObject();
319: }
320:
321: /**
322: * A method for the getEJBObject from the handle
323: */
324: public EJBObject getEJBObject(Invocation mi) throws RemoteException {
325: // All we need is an EJBObject for this Id, the first argument is the Id
326: EJBProxyFactory ci = getProxyFactory();
327: if (ci == null) {
328: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
329: throw new IllegalStateException(msg);
330: }
331:
332: Object id = mi.getArguments()[0];
333: if (id == null)
334: throw new IllegalStateException(
335: "Cannot get a session interface with a null id");
336:
337: // Does the session still exist?
338: InstanceCache cache = getInstanceCache();
339: BeanLock lock = getLockManager().getLock(id);
340: lock.sync();
341: try {
342: if (cache.get(id) == null)
343: throw new RemoteException("Session no longer exists: "
344: + id);
345: } finally {
346: lock.releaseSync();
347: getLockManager().removeLockRef(id);
348: }
349:
350: // Ok lets create the proxy
351: return (EJBObject) ci.getStatefulSessionEJBObject(id);
352: }
353:
354: // EJBHome implementation ----------------------------------------
355:
356: //
357: // These are implemented in the local proxy
358: //
359:
360: /**
361: * @throws Error Not yet implemented
362: */
363: public void removeHome(Invocation mi) throws RemoteException,
364: RemoveException {
365: throw new Error("Not Yet Implemented");
366: }
367:
368: // Private -------------------------------------------------------
369:
370: protected void setupHomeMapping() throws Exception {
371: // Adrian Brock: This should go away when we don't support EJB1x
372: boolean isEJB1x = metaData.getApplicationMetaData().isEJB1x();
373:
374: Map map = new HashMap();
375:
376: if (homeInterface != null) {
377:
378: Method[] m = homeInterface.getMethods();
379: for (int i = 0; i < m.length; i++) {
380: try {
381: // Implemented by container
382: if (isEJB1x == false
383: && m[i].getName().startsWith("create")) {
384: map.put(m[i], getClass().getMethod(
385: "createHome",
386: new Class[] { Invocation.class }));
387: } else {
388: map.put(m[i], getClass().getMethod(
389: m[i].getName() + "Home",
390: new Class[] { Invocation.class }));
391: }
392: } catch (NoSuchMethodException e) {
393: log.info(m[i].getName()
394: + " in bean has not been mapped");
395: }
396: }
397: }
398:
399: if (localHomeInterface != null) {
400: Method[] m = localHomeInterface.getMethods();
401: for (int i = 0; i < m.length; i++) {
402: try {
403: // Implemented by container
404: if (isEJB1x == false
405: && m[i].getName().startsWith("create")) {
406: map.put(m[i], getClass().getMethod(
407: "createLocalHome",
408: new Class[] { Invocation.class }));
409: } else {
410: map.put(m[i], getClass().getMethod(
411: m[i].getName() + "LocalHome",
412: new Class[] { Invocation.class }));
413: }
414: } catch (NoSuchMethodException e) {
415: log.info(m[i].getName()
416: + " in bean has not been mapped");
417: }
418: }
419: }
420:
421: try {
422: // Get getEJBObject from on Handle, first get the class
423: Class handleClass = Class.forName("javax.ejb.Handle");
424:
425: //Get only the one called handle.getEJBObject
426: Method getEJBObjectMethod = handleClass.getMethod(
427: "getEJBObject", new Class[0]);
428:
429: //Map it in the home stuff
430: map.put(getEJBObjectMethod, getClass().getMethod(
431: "getEJBObject", new Class[] { Invocation.class }));
432: } catch (NoSuchMethodException e) {
433: log.debug("Couldn't find getEJBObject method on container");
434: }
435:
436: homeMapping = map;
437: }
438:
439: protected Interceptor createContainerInterceptor() {
440: return new ContainerInterceptor();
441: }
442:
443: /**
444: * This is the last step before invocation - all interceptors are done
445: */
446: class ContainerInterceptor extends AbstractContainerInterceptor {
447: public Object invokeHome(Invocation mi) throws Exception {
448: boolean trace = log.isTraceEnabled();
449:
450: if (trace) {
451: log.trace("HOMEMETHOD coming in ");
452: log.trace("" + mi.getMethod());
453: log.trace("HOMEMETHOD coming in hashcode"
454: + mi.getMethod().hashCode());
455: log.trace("HOMEMETHOD coming in classloader"
456: + mi.getMethod().getDeclaringClass()
457: .getClassLoader().hashCode());
458: log.trace("CONTAINS "
459: + getHomeMapping().containsKey(mi.getMethod()));
460: }
461:
462: Method miMethod = mi.getMethod();
463: Method m = (Method) getHomeMapping().get(miMethod);
464: if (m == null) {
465: String msg = "Invalid invocation, check your deployment packaging"
466: + ", method=" + miMethod;
467: throw new EJBException(msg);
468: }
469:
470: // Invoke and handle exceptions
471: if (trace) {
472: log.trace("HOMEMETHOD m " + m);
473: java.util.Iterator iterator = getHomeMapping().keySet()
474: .iterator();
475: while (iterator.hasNext()) {
476: Method me = (Method) iterator.next();
477:
478: if (me.getName().endsWith("create")) {
479: log.trace(me.toString());
480: log.trace("" + me.hashCode());
481: log.trace(""
482: + me.getDeclaringClass()
483: .getClassLoader().hashCode());
484: log.trace("equals " + me.equals(mi.getMethod())
485: + " " + mi.getMethod().equals(me));
486: }
487: }
488: }
489:
490: try {
491: return mi.performCall(StatefulSessionContainer.this , m,
492: new Object[] { mi });
493: } catch (Exception e) {
494: rethrow(e);
495: }
496:
497: // We will never get this far, but the compiler does not know that
498: throw new org.jboss.util.UnreachableStatementException();
499: }
500:
501: public Object invoke(Invocation mi) throws Exception {
502: // wire the transaction on the context, this is how the instance remember the tx
503: // Unlike Entity beans we can't do that in the previous interceptors (ordering)
504: EnterpriseContext ctx = (EnterpriseContext) mi
505: .getEnterpriseContext();
506: if (ctx.getTransaction() == null)
507: ctx.setTransaction(mi.getTransaction());
508:
509: // Get method
510: Method miMethod = mi.getMethod();
511: Method m = (Method) getBeanMapping().get(miMethod);
512: if (m == null) {
513: String msg = "Invalid invocation, check your deployment packaging"
514: + ", method=" + miMethod;
515: throw new EJBException(msg);
516: }
517:
518: // Select instance to invoke (container or bean)
519: if (m.getDeclaringClass().equals(
520: StatefulSessionContainer.class)
521: || m.getDeclaringClass().equals(
522: SessionContainer.class)) {
523: // Invoke and handle exceptions
524: try {
525: return mi.performCall(
526: StatefulSessionContainer.this , m,
527: new Object[] { mi });
528: } catch (Exception e) {
529: rethrow(e);
530: }
531: } else {
532: // Invoke and handle exceptions
533: try {
534: Object bean = ctx.getInstance();
535: return mi.performCall(bean, m, mi.getArguments());
536: } catch (Exception e) {
537: rethrow(e);
538: }
539: }
540:
541: // We will never get this far, but the compiler does not know that
542: throw new org.jboss.util.UnreachableStatementException();
543: }
544: }
545: }
|