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