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: // $Id: SessionContainer.java 57209 2006-09-26 12:21:57Z dimitris@jboss.org $
025:
026: import java.lang.reflect.Method;
027: import java.rmi.RemoteException;
028: import java.util.HashMap;
029: import java.util.Hashtable;
030: import java.util.Iterator;
031: import java.util.Map;
032:
033: import javax.ejb.EJBHome;
034: import javax.ejb.EJBLocalHome;
035: import javax.ejb.EJBMetaData;
036: import javax.ejb.EJBObject;
037: import javax.ejb.Handle;
038: import javax.ejb.HomeHandle;
039: import javax.ejb.RemoveException;
040: import javax.ejb.TimedObject;
041: import javax.ejb.Timer;
042: import javax.management.ObjectName;
043:
044: import org.jboss.invocation.Invocation;
045: import org.jboss.invocation.MarshalledInvocation;
046: import org.jboss.metadata.SessionMetaData;
047:
048: /**
049: * <p>
050: * Container dedicated to session beans. Contains factored out
051: * redundancies between stateless and stateful treatments, because
052: * (extending the spec) we would like to also support stateful
053: * web services.
054: * </p>
055: * @author <a href="mailto:Christoph.Jung@infor.de">Christoph G. Jung</a>
056: * @version $Revision: 57209 $
057: * @since 30.10.2003
058: */
059: public abstract class SessionContainer extends Container {
060: /**
061: * These are the mappings between the home interface methods and the
062: * container methods.
063: */
064: protected Map homeMapping;
065:
066: /**
067: * These are the mappings between the remote interface methods and the
068: * bean methods.
069: */
070: protected Map beanMapping;
071:
072: /**
073: * This is the first interceptor in the chain. The last interceptor must
074: * be provided by the container itself
075: */
076: protected Interceptor interceptor;
077:
078: /** this is the service endpoint class */
079: protected Class serviceEndpoint;
080:
081: /** This is the instancepool that is to be used */
082: protected InstancePool instancePool;
083:
084: /** set the instance pool */
085: public void setInstancePool(InstancePool ip) {
086: if (ip == null)
087: throw new IllegalArgumentException("Null pool");
088:
089: this .instancePool = ip;
090: ip.setContainer(this );
091: }
092:
093: /** return instance pool */
094: public InstancePool getInstancePool() {
095: return instancePool;
096: }
097:
098: /** return local proxy factory */
099: public LocalProxyFactory getLocalProxyFactory() {
100: return localProxyFactory;
101: }
102:
103: /** add an additional interceptor to the chain */
104: public void addInterceptor(Interceptor in) {
105: if (interceptor == null) {
106: interceptor = in;
107: } else {
108: Interceptor current = interceptor;
109: while (current.getNext() != null) {
110: current = current.getNext();
111: }
112:
113: current.setNext(in);
114: }
115: }
116:
117: /** return first interceptor */
118: public Interceptor getInterceptor() {
119: return interceptor;
120: }
121:
122: /** return service endpoint */
123: public Class getServiceEndpoint() {
124: return serviceEndpoint;
125: }
126:
127: // Container stuff
128:
129: protected void createService() throws Exception {
130: // Associate thread with classloader
131: ClassLoader oldCl = SecurityActions.getContextClassLoader();
132: SecurityActions.setContextClassLoader(getClassLoader());
133:
134: try {
135: // Acquire classes from CL
136: if (metaData.getHome() != null)
137: homeInterface = classLoader.loadClass(metaData
138: .getHome());
139: if (metaData.getRemote() != null)
140: remoteInterface = classLoader.loadClass(metaData
141: .getRemote());
142: if (((SessionMetaData) metaData).getServiceEndpoint() != null) {
143: serviceEndpoint = classLoader
144: .loadClass(((SessionMetaData) metaData)
145: .getServiceEndpoint());
146: }
147:
148: // Call default init
149: super .createService();
150:
151: // Make some additional validity checks with regards to the container configuration
152: checkCoherency();
153:
154: // Map the bean methods
155: setupBeanMapping();
156:
157: // Map the home methods
158: setupHomeMapping();
159:
160: // Map the interfaces to Long
161: setupMarshalledInvocationMapping();
162:
163: createInvokers();
164:
165: createInstanceCache();
166:
167: createInstancePool();
168:
169: createPersistenceManager();
170:
171: createInterceptors();
172: } finally {
173: // Reset classloader
174: SecurityActions.setContextClassLoader(oldCl);
175: }
176: }
177:
178: /**
179: * how home methods are treated by container
180: */
181: protected abstract void setupHomeMapping() throws Exception;
182:
183: /** loop through methods and setup mapping */
184: protected void setUpBeanMappingImpl(Map map, Method[] methods,
185: String declaringClass) throws NoSuchMethodException {
186: for (int i = 0; i < methods.length; i++) {
187: Method m = methods[i];
188: if (m.getDeclaringClass().getName().equals(declaringClass) == false) {
189: // Implemented by bean
190: try {
191: Method beanMethod = beanClass.getMethod(
192: m.getName(), m.getParameterTypes());
193: map.put(m, beanMethod);
194: } catch (NoSuchMethodException ex) {
195: throw new NoSuchMethodException(
196: "Not found in bean class: " + m);
197: }
198:
199: if (log.isDebugEnabled())
200: log.debug("Mapped " + m.getName() + " HASH "
201: + m.hashCode() + "to " + map.get(m));
202: } else {
203: // Implemented by container
204: try {
205: Method containerMethod = getClass().getMethod(
206: m.getName(),
207: new Class[] { Invocation.class });
208: map.put(m, containerMethod);
209: } catch (NoSuchMethodException e) {
210: throw new NoSuchMethodException(
211: "Not found in container class: " + m);
212: }
213:
214: if (log.isDebugEnabled())
215: log.debug("Mapped Container method " + m.getName()
216: + " HASH " + m.hashCode());
217: }
218: }
219: }
220:
221: /** build bean mappings for application logic */
222: protected void setupBeanMapping() throws NoSuchMethodException {
223: Map map = new HashMap();
224:
225: if (remoteInterface != null) {
226: Method[] m = remoteInterface.getMethods();
227: setUpBeanMappingImpl(map, m, "javax.ejb.EJBObject");
228: }
229:
230: if (localInterface != null) {
231: Method[] m = localInterface.getMethods();
232: setUpBeanMappingImpl(map, m, "javax.ejb.EJBLocalObject");
233: }
234:
235: if (TimedObject.class.isAssignableFrom(beanClass)) {
236: Method[] m = new Method[] { TimedObject.class.getMethod(
237: "ejbTimeout", new Class[] { Timer.class }) };
238: setUpBeanMappingImpl(map, m, "javax.ejb.Timer");
239: }
240:
241: if (serviceEndpoint != null) {
242: Method[] m = serviceEndpoint.getMethods();
243: setUpBeanMappingImpl(map, m, "java.rmi.Remote");
244: }
245:
246: beanMapping = map;
247: }
248:
249: /**
250: * sets up marshalled invocation mappings
251: * @throws Exception
252: */
253:
254: protected void setupMarshalledInvocationMapping() throws Exception {
255: // Create method mappings for container invoker
256: if (homeInterface != null) {
257: Method[] m = homeInterface.getMethods();
258: for (int i = 0; i < m.length; i++) {
259: marshalledInvocationMapping
260: .put(new Long(MarshalledInvocation
261: .calculateHash(m[i])), m[i]);
262: }
263: }
264:
265: if (remoteInterface != null) {
266: Method[] m = remoteInterface.getMethods();
267: for (int j = 0; j < m.length; j++) {
268: marshalledInvocationMapping
269: .put(new Long(MarshalledInvocation
270: .calculateHash(m[j])), m[j]);
271: }
272: }
273: // Get the getEJBObjectMethod
274: Method getEJBObjectMethod = Class.forName("javax.ejb.Handle")
275: .getMethod("getEJBObject", new Class[0]);
276:
277: // Hash it
278: marshalledInvocationMapping
279: .put(new Long(MarshalledInvocation
280: .calculateHash(getEJBObjectMethod)),
281: getEJBObjectMethod);
282: }
283:
284: protected void checkCoherency() throws Exception {
285: // Check clustering cohrency wrt metadata
286: //
287: if (metaData.isClustered()) {
288: boolean clusteredProxyFactoryFound = false;
289: Iterator it = proxyFactories.keySet().iterator();
290: while (it.hasNext()) {
291: String invokerBinding = (String) it.next();
292: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
293: .get(invokerBinding);
294: if (ci instanceof org.jboss.proxy.ejb.ClusterProxyFactory)
295: clusteredProxyFactoryFound = true;
296: }
297:
298: if (!clusteredProxyFactoryFound) {
299: log
300: .warn("*** EJB '"
301: + this .metaData.getEjbName()
302: + "' deployed as CLUSTERED but not a single clustered-invoker is bound to container ***");
303: }
304: }
305: }
306:
307: /** creates a new instance pool */
308: protected void createInstancePool() throws Exception {
309:
310: // Try to register the instance pool as an MBean
311: try {
312: ObjectName containerName = super .getJmxName();
313: Hashtable props = containerName.getKeyPropertyList();
314: props.put("plugin", "pool");
315: ObjectName poolName = new ObjectName(containerName
316: .getDomain(), props);
317: server.registerMBean(instancePool, poolName);
318: } catch (Throwable t) {
319: log.debug("Failed to register pool as mbean", t);
320: }
321: // Initialize pool
322: instancePool.create();
323: }
324:
325: /**
326: * no instance cache per default
327: */
328: protected void createInstanceCache() throws Exception {
329: }
330:
331: /** creates the invokers */
332: protected void createInvokers() throws Exception {
333: // Init container invoker
334: for (Iterator it = proxyFactories.keySet().iterator(); it
335: .hasNext();) {
336: String invokerBinding = (String) it.next();
337: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
338: .get(invokerBinding);
339: ci.create();
340: }
341: }
342:
343: /** Initialize the interceptors by calling the chain */
344: protected void createInterceptors() throws Exception {
345: Interceptor in = interceptor;
346: while (in != null) {
347: in.setContainer(this );
348: in.create();
349: in = in.getNext();
350: }
351: }
352:
353: /**
354: * no persistence manager per default
355: */
356: protected void createPersistenceManager() throws Exception {
357: }
358:
359: protected void startService() throws Exception {
360: // Associate thread with classloader
361: ClassLoader oldCl = SecurityActions.getContextClassLoader();
362: SecurityActions.setContextClassLoader(getClassLoader());
363:
364: try {
365: // Call default start
366: super .startService();
367:
368: startInvokers();
369:
370: startInstanceCache();
371:
372: startInstancePool();
373:
374: startPersistenceManager();
375:
376: startInterceptors();
377:
378: // Restore persisted ejb timers
379: restoreTimers();
380: } finally {
381: // Reset classloader
382: SecurityActions.setContextClassLoader(oldCl);
383: }
384: }
385:
386: /**
387: * no persistence manager per default
388: */
389: protected void startPersistenceManager() throws Exception {
390: }
391:
392: /**
393: * no instance cache per default
394: */
395: protected void startInstanceCache() throws Exception {
396: }
397:
398: /** Start container invokers */
399: protected void startInvokers() throws Exception {
400: for (Iterator it = proxyFactories.keySet().iterator(); it
401: .hasNext();) {
402: String invokerBinding = (String) it.next();
403: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
404: .get(invokerBinding);
405: ci.start();
406: }
407: }
408:
409: /** Start pool */
410: protected void startInstancePool() throws Exception {
411: instancePool.start();
412: }
413:
414: /** Start all interceptors in the chain **/
415: protected void startInterceptors() throws Exception {
416: Interceptor in = interceptor;
417: while (in != null) {
418: in.start();
419: in = in.getNext();
420: }
421: }
422:
423: protected void stopService() throws Exception {
424: // Associate thread with classloader
425: ClassLoader oldCl = SecurityActions.getContextClassLoader();
426: SecurityActions.setContextClassLoader(getClassLoader());
427:
428: try {
429: // Call default stop
430: super .stopService();
431:
432: stopInvokers();
433:
434: stopInstanceCache();
435:
436: stopInstancePool();
437:
438: stopPersistenceManager();
439:
440: stopInterceptors();
441: } finally {
442: // Reset classloader
443: SecurityActions.setContextClassLoader(oldCl);
444: }
445: }
446:
447: /** Stop all interceptors in the chain */
448: protected void stopInterceptors() {
449: Interceptor in = interceptor;
450: while (in != null) {
451: in.stop();
452: in = in.getNext();
453: }
454: }
455:
456: /** no persistence */
457: protected void stopPersistenceManager() {
458: }
459:
460: /** Stop pool */
461: protected void stopInstancePool() {
462: instancePool.stop();
463: }
464:
465: /** no instance cache */
466: protected void stopInstanceCache() {
467: }
468:
469: /** Stop container invoker */
470: protected void stopInvokers() {
471: for (Iterator it = proxyFactories.keySet().iterator(); it
472: .hasNext();) {
473: String invokerBinding = (String) it.next();
474: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
475: .get(invokerBinding);
476: ci.stop();
477: }
478: }
479:
480: protected void destroyService() throws Exception {
481: // Associate thread with classloader
482: ClassLoader oldCl = SecurityActions.getContextClassLoader();
483: SecurityActions.setContextClassLoader(getClassLoader());
484:
485: try {
486: destroyInvokers();
487:
488: destroyInstanceCache();
489:
490: destroyInstancePool();
491:
492: destroyPersistenceManager();
493:
494: destroyInterceptors();
495:
496: destroyMarshalledInvocationMapping();
497:
498: homeInterface = null;
499: remoteInterface = null;
500: serviceEndpoint = null;
501: beanMapping.clear();
502:
503: // Call default destroy
504: super .destroyService();
505: } finally {
506: // Reset classloader
507: SecurityActions.setContextClassLoader(oldCl);
508: }
509: }
510:
511: protected void destroyMarshalledInvocationMapping() {
512: MarshalledInvocation.removeHashes(homeInterface);
513: MarshalledInvocation.removeHashes(remoteInterface);
514: }
515:
516: protected void destroyInterceptors() {
517: // Destroy all the interceptors in the chain
518: Interceptor in = interceptor;
519: while (in != null) {
520: in.destroy();
521: in.setContainer(null);
522: in = in.getNext();
523: }
524: }
525:
526: protected void destroyPersistenceManager() {
527: }
528:
529: protected void destroyInstancePool() {
530: // Destroy pool
531: instancePool.destroy();
532: instancePool.setContainer(null);
533: try {
534: ObjectName containerName = super .getJmxName();
535: Hashtable props = containerName.getKeyPropertyList();
536: props.put("plugin", "pool");
537: ObjectName poolName = new ObjectName(containerName
538: .getDomain(), props);
539: server.unregisterMBean(poolName);
540: } catch (Throwable ignore) {
541: }
542: }
543:
544: protected void destroyInstanceCache() {
545: }
546:
547: protected void destroyInvokers() {
548: // Destroy container invoker
549: for (Iterator it = proxyFactories.keySet().iterator(); it
550: .hasNext();) {
551: String invokerBinding = (String) it.next();
552: EJBProxyFactory ci = (EJBProxyFactory) proxyFactories
553: .get(invokerBinding);
554: ci.destroy();
555: ci.setContainer(null);
556: }
557: }
558:
559: public Object internalInvokeHome(Invocation mi) throws Exception {
560: Method method = mi.getMethod();
561: if (method != null && method.getName().equals("remove")) {
562: // Handle or primary key?
563: Object arg = mi.getArguments()[0];
564: if (arg instanceof Handle) {
565: if (arg == null)
566: throw new RemoteException("Null handle");
567: Handle handle = (Handle) arg;
568: EJBObject ejbObject = handle.getEJBObject();
569: ejbObject.remove();
570: return null;
571: } else
572: throw new RemoveException(
573: "EJBHome.remove(Object) not allowed for session beans");
574: }
575: return getInterceptor().invokeHome(mi);
576: }
577:
578: /**
579: * This method does invocation interpositioning of tx and security,
580: * retrieves the instance from an object table, and invokes the method
581: * on the particular instance
582: */
583: public Object internalInvoke(Invocation mi) throws Exception {
584: // Invoke through interceptors
585: return getInterceptor().invoke(mi);
586: }
587:
588: // EJBObject implementation --------------------------------------
589:
590: /**
591: * While the following methods are implemented in the client in the case
592: * of JRMP we would need to implement them to fully support other transport
593: * protocols
594: *
595: * @return Always null
596: */
597: public Handle getHandle(Invocation mi) throws RemoteException {
598:
599: // TODO
600: return null;
601: }
602:
603: public Object getPrimaryKey(Invocation mi) throws RemoteException {
604: return getPrimaryKey();
605: }
606:
607: public Object getPrimaryKey() throws RemoteException {
608: throw new RemoteException(
609: "Call to getPrimaryKey not allowed on session bean");
610: }
611:
612: public EJBHome getEJBHome(Invocation mi) throws RemoteException {
613: EJBProxyFactory ci = getProxyFactory();
614: if (ci == null) {
615: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
616: throw new IllegalStateException(msg);
617: }
618:
619: return (EJBHome) ci.getEJBHome();
620: }
621:
622: /**
623: * @return Always false
624: */
625: public boolean isIdentical(Invocation mi) throws RemoteException {
626: EJBProxyFactory ci = getProxyFactory();
627: if (ci == null) {
628: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
629: throw new IllegalStateException(msg);
630: }
631:
632: return ci.isIdentical(this , mi);
633: }
634:
635: public EJBMetaData getEJBMetaDataHome(Invocation mi)
636: throws RemoteException {
637: return getEJBMetaDataHome();
638: }
639:
640: public EJBMetaData getEJBMetaDataHome() throws RemoteException {
641: EJBProxyFactory ci = getProxyFactory();
642: if (ci == null) {
643: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
644: throw new IllegalStateException(msg);
645: }
646:
647: return ci.getEJBMetaData();
648: }
649:
650: public HomeHandle getHomeHandleHome(Invocation mi)
651: throws RemoteException {
652: return getHomeHandleHome();
653: }
654:
655: public HomeHandle getHomeHandleHome() throws RemoteException {
656: EJBProxyFactory ci = getProxyFactory();
657: if (ci == null) {
658: String msg = "No ProxyFactory, check for ProxyFactoryFinderInterceptor";
659: throw new IllegalStateException(msg);
660: }
661:
662: EJBHome home = (EJBHome) ci.getEJBHome();
663: return home.getHomeHandle();
664: }
665:
666: // Home interface implementation ---------------------------------
667:
668: // local object interface implementation
669:
670: public EJBLocalHome getEJBLocalHome(Invocation mi) {
671: return localProxyFactory.getEJBLocalHome();
672: }
673:
674: /**
675: * needed for sub-inner-class access (old jdk compiler bug)
676: * @return
677: */
678: protected Map getHomeMapping() {
679: return homeMapping;
680: }
681:
682: /**
683: * needed for sub-inner-class access (old jdk compiler bug)
684: * @return
685: */
686: protected Map getBeanMapping() {
687: return beanMapping;
688: }
689:
690: }
|