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.plugins.local;
23
24 import java.lang.reflect.InvocationHandler;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Proxy;
27 import java.lang.reflect.Constructor;
28 import java.rmi.AccessException;
29 import java.rmi.NoSuchObjectException;
30 import java.security.Principal;
31 import java.security.PrivilegedAction;
32 import java.security.AccessController;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.Map;
39 import javax.ejb.AccessLocalException;
40 import javax.ejb.EJBLocalHome;
41 import javax.ejb.EJBLocalObject;
42 import javax.ejb.NoSuchObjectLocalException;
43 import javax.ejb.TransactionRequiredLocalException;
44 import javax.ejb.TransactionRolledbackLocalException;
45 import javax.naming.Context;
46 import javax.naming.InitialContext;
47 import javax.transaction.Transaction;
48 import javax.transaction.TransactionManager;
49 import javax.transaction.TransactionRequiredException;
50 import javax.transaction.TransactionRolledbackException;
51
52 import org.jboss.ejb.Container;
53 import org.jboss.ejb.EJBProxyFactoryContainer;
54 import org.jboss.ejb.LocalProxyFactory;
55 import org.jboss.invocation.InvocationType;
56 import org.jboss.invocation.MarshalledInvocation;
57 import org.jboss.invocation.LocalEJBInvocation;
58 import org.jboss.logging.Logger;
59 import org.jboss.metadata.BeanMetaData;
60 import org.jboss.naming.Util;
61 import org.jboss.security.SecurityContext;
62 import org.jboss.security.SecurityContextAssociation;
63 import org.jboss.util.NestedRuntimeException;
64 import org.jboss.tm.TransactionLocal;
65
66 /**
67 * The LocalProxyFactory implementation that handles local ejb interface
68 * proxies.
69 *
70 * @author <a href="mailto:docodan@mvcsoft.com">Daniel OConnor</a>
71 * @author <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
72 * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
73 * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
74 * @author Anil.Saldhana@redhat.com
75 * $Revision: 72053 $
76 */
77 public class BaseLocalProxyFactory implements LocalProxyFactory
78 {
79 // Attributes ----------------------------------------------------
80 protected static Logger log = Logger.getLogger(BaseLocalProxyFactory.class);
81
82 /**
83 * A map of the BaseLocalProxyFactory instances keyed by localJndiName
84 */
85 protected static Map invokerMap = Collections.synchronizedMap(new HashMap());
86
87 protected Container container;
88
89 /**
90 * The JNDI name of the local home interface binding
91 */
92 protected String localJndiName;
93
94 protected TransactionManager transactionManager;
95
96 // The home can be one.
97 protected EJBLocalHome home;
98
99 // The Stateless Object can be one.
100 protected EJBLocalObject statelessObject;
101
102 protected Map beanMethodInvokerMap;
103 protected Map homeMethodInvokerMap;
104 protected Class localHomeClass;
105 protected Class localClass;
106
107 protected Constructor proxyClassConstructor;
108
109 private final TransactionLocal cache = new TransactionLocal()
110 {
111 protected Object initialValue()
112 {
113 return new HashMap();
114 }
115 };
116
117 // Static --------------------------------------------------------
118
119 // Constructors --------------------------------------------------
120
121 // Public --------------------------------------------------------
122
123 // ContainerService implementation -------------------------------
124
125 public void setContainer(Container con)
126 {
127 this.container = con;
128 }
129
130 public void create() throws Exception
131 {
132 BeanMetaData metaData = container.getBeanMetaData();
133 localJndiName = metaData.getLocalJndiName();
134 }
135
136 public void start()
137 throws Exception
138 {
139 BeanMetaData metaData = container.getBeanMetaData();
140 EJBProxyFactoryContainer invokerContainer =
141 (EJBProxyFactoryContainer) container;
142 localHomeClass = invokerContainer.getLocalHomeClass();
143 localClass = invokerContainer.getLocalClass();
144 if(localHomeClass == null || localClass == null)
145 {
146 log.debug(metaData.getEjbName()
147 +
148 " cannot be Bound, doesn't " +
149 "have local and local home interfaces");
150 return;
151 }
152
153 // this is faster than newProxyInstance
154 Class[] intfs = {localClass};
155 Class proxyClass = Proxy.getProxyClass(ClassLoaderAction.UTIL.get(localClass), intfs);
156 final Class[] constructorParams =
157 {InvocationHandler.class};
158
159 proxyClassConstructor = proxyClass.getConstructor(constructorParams);
160
161 Context iniCtx = new InitialContext();
162 String beanName = metaData.getEjbName();
163
164 // Set the transaction manager and transaction propagation
165 // context factory of the GenericProxy class
166 transactionManager =
167 (TransactionManager) iniCtx.lookup("java:/TransactionManager");
168
169 // Create method mappings for container invoker
170 Method[] methods = localClass.getMethods();
171 beanMethodInvokerMap = new HashMap();
172 for(int i = 0; i < methods.length; i++)
173 {
174 long hash = MarshalledInvocation.calculateHash(methods[i]);
175 beanMethodInvokerMap.put(new Long(hash), methods[i]);
176 }
177
178 methods = localHomeClass.getMethods();
179 homeMethodInvokerMap = new HashMap();
180 for(int i = 0; i < methods.length; i++)
181 {
182 long hash = MarshalledInvocation.calculateHash(methods[i]);
183 homeMethodInvokerMap.put(new Long(hash), methods[i]);
184 }
185
186 // bind that referance to my name
187 Util.rebind(iniCtx, localJndiName, getEJBLocalHome());
188 invokerMap.put(localJndiName, this);
189 log.info("Bound EJB LocalHome '" + beanName + "' to jndi '" + localJndiName + "'");
190 }
191
192 public void stop()
193 {
194 // Clean up the home proxy binding
195 try
196 {
197 if(invokerMap.remove(localJndiName) == this)
198 {
199 log.info("Unbind EJB LocalHome '" + container.getBeanMetaData().getEjbName() + "' from jndi '" + localJndiName + "'");
200
201 InitialContext ctx = new InitialContext();
202 ctx.unbind(localJndiName);
203 }
204 }
205 catch(Exception ignore)
206 {
207 }
208 }
209
210 public void destroy()
211 {
212 if(beanMethodInvokerMap != null)
213 {
214 beanMethodInvokerMap.clear();
215 }
216 if(homeMethodInvokerMap != null)
217 {
218 homeMethodInvokerMap.clear();
219 }
220 MarshalledInvocation.removeHashes(localHomeClass);
221 MarshalledInvocation.removeHashes(localClass);
222
223 container = null;
224 }
225
226 public Constructor getProxyClassConstructor()
227 {
228 if(proxyClassConstructor == null)
229 {
230 }
231 return proxyClassConstructor;
232 }
233
234 // EJBProxyFactory implementation -------------------------------
235 public EJBLocalHome getEJBLocalHome()
236 {
237 if(home == null)
238 {
239 EJBProxyFactoryContainer cic = (EJBProxyFactoryContainer) container;
240 InvocationHandler handler = new LocalHomeProxy(localJndiName, this);
241 ClassLoader loader = ClassLoaderAction.UTIL.get(cic.getLocalHomeClass());
242 Class[] interfaces = {cic.getLocalHomeClass()};
243
244 home = (EJBLocalHome) Proxy.newProxyInstance(loader,
245 interfaces,
246 handler);
247 }
248 return home;
249 }
250
251 public EJBLocalObject getStatelessSessionEJBLocalObject()
252 {
253 if(statelessObject == null)
254 {
255 EJBProxyFactoryContainer cic = (EJBProxyFactoryContainer) container;
256 InvocationHandler handler =
257 new StatelessSessionProxy(localJndiName, this);
258 ClassLoader loader = ClassLoaderAction.UTIL.get(cic.getLocalClass());
259 Class[] interfaces = {cic.getLocalClass()};
260
261 statelessObject = (EJBLocalObject) Proxy.newProxyInstance(loader,
262 interfaces,
263 handler);
264 }
265 return statelessObject;
266 }
267
268 public EJBLocalObject getStatefulSessionEJBLocalObject(Object id)
269 {
270 InvocationHandler handler =
271 new StatefulSessionProxy(localJndiName, id, this);
272 try
273 {
274 return (EJBLocalObject) proxyClassConstructor.newInstance(new Object[]{handler});
275 }
276 catch(Exception ex)
277 {
278 throw new NestedRuntimeException(ex);
279 }
280 }
281
282 public Object getEntityEJBObject(Object id)
283 {
284 return getEntityEJBLocalObject(id);
285 }
286
287 public EJBLocalObject getEntityEJBLocalObject(Object id, boolean create)
288 {
289 EJBLocalObject result = null;
290 if(id != null)
291 {
292 final Transaction tx = cache.getTransaction();
293 if(tx == null)
294 {
295 result = createEJBLocalObject(id);
296 }
297 else
298 {
299 Map map = (Map) cache.get(tx);
300 if(create)
301 {
302 result = createEJBLocalObject(id);
303 map.put(id, result);
304 }
305 else
306 {
307 result = (EJBLocalObject) map.get(id);
308 if(result == null)
309 {
310 result = createEJBLocalObject(id);
311 map.put(id, result);
312 }
313 }
314 }
315 }
316 return result;
317 }
318
319 public EJBLocalObject getEntityEJBLocalObject(Object id)
320 {
321 return getEntityEJBLocalObject(id, false);
322 }
323
324 public Collection getEntityLocalCollection(Collection ids)
325 {
326 ArrayList list = new ArrayList(ids.size());
327 Iterator iter = ids.iterator();
328 while(iter.hasNext())
329 {
330 final Object nextId = iter.next();
331 list.add(getEntityEJBLocalObject(nextId));
332 }
333 return list;
334 }
335
336 /**
337 * Invoke a Home interface method.
338 */
339 public Object invokeHome(Method m, Object[] args) throws Exception
340 {
341 // Set the right context classloader
342 ClassLoader oldCl = TCLAction.UTIL.getContextClassLoader();
343 boolean setCl = !oldCl.equals(container.getClassLoader());
344 if(setCl)
345 {
346 TCLAction.UTIL.setContextClassLoader(container.getClassLoader());
347 }
348 container.pushENC();
349
350 SecurityActions sa = SecurityActions.UTIL.getSecurityActions();
351
352 try
353 {
354 LocalEJBInvocation invocation = new LocalEJBInvocation(null,
355 m,
356 args,
357 getTransaction(),
358 sa.getPrincipal(),
359 sa.getCredential());
360 invocation.setType(InvocationType.LOCALHOME);
361
362 return container.invoke(invocation);
363 }
364 catch(AccessException ae)
365 {
366 throw new AccessLocalException(ae.getMessage(), ae);
367 }
368 catch(NoSuchObjectException nsoe)
369 {
370 throw new NoSuchObjectLocalException(nsoe.getMessage(), nsoe);
371 }
372 catch(TransactionRequiredException tre)
373 {
374 throw new TransactionRequiredLocalException(tre.getMessage());
375 }
376 catch(TransactionRolledbackException trbe)
377 {
378 throw new TransactionRolledbackLocalException(trbe.getMessage(), trbe);
379 }
380 finally
381 {
382 container.popENC();
383 if(setCl)
384 {
385 TCLAction.UTIL.setContextClassLoader(oldCl);
386 }
387 }
388 }
389
390 public String getJndiName()
391 {
392 return localJndiName;
393 }
394
395 /**
396 * Return the transaction associated with the current thread.
397 * Returns <code>null</code> if the transaction manager was never
398 * set, or if no transaction is associated with the current thread.
399 */
400 Transaction getTransaction() throws javax.transaction.SystemException
401 {
402 if(transactionManager == null)
403 {
404 return null;
405 }
406 return transactionManager.getTransaction();
407 }
408
409 /**
410 * Invoke a local interface method.
411 */
412 public Object invoke(Object id, Method m, Object[] args)
413 throws Exception
414 {
415 // Set the right context classloader
416 ClassLoader oldCl = TCLAction.UTIL.getContextClassLoader();
417 boolean setCl = !oldCl.equals(container.getClassLoader());
418 if(setCl)
419 {
420 TCLAction.UTIL.setContextClassLoader(container.getClassLoader());
421 }
422 container.pushENC();
423
424 SecurityActions sa = SecurityActions.UTIL.getSecurityActions();
425 try
426 {
427 LocalEJBInvocation invocation = new LocalEJBInvocation(id,
428 m,
429 args,
430 getTransaction(),
431 sa.getPrincipal(),
432 sa.getCredential());
433 invocation.setType(InvocationType.LOCAL);
434
435 return container.invoke(invocation);
436 }
437 catch(AccessException ae)
438 {
439 throw new AccessLocalException(ae.getMessage(), ae);
440 }
441 catch(NoSuchObjectException nsoe)
442 {
443 throw new NoSuchObjectLocalException(nsoe.getMessage(), nsoe);
444 }
445 catch(TransactionRequiredException tre)
446 {
447 throw new TransactionRequiredLocalException(tre.getMessage());
448 }
449 catch(TransactionRolledbackException trbe)
450 {
451 throw new TransactionRolledbackLocalException(trbe.getMessage(), trbe);
452 }
453 finally
454 {
455 container.popENC();
456 if(setCl)
457 {
458 TCLAction.UTIL.setContextClassLoader(oldCl);
459 }
460 }
461 }
462
463 private EJBLocalObject createEJBLocalObject(Object id)
464 {
465 InvocationHandler handler = new EntityProxy(localJndiName, id, this);
466 try
467 {
468 return (EJBLocalObject) proxyClassConstructor.newInstance(new Object[]{handler});
469 }
470 catch(Exception ex)
471 {
472 throw new NestedRuntimeException(ex);
473 }
474 }
475
476
477 interface ClassLoaderAction
478 {
479 class UTIL
480 {
481 static ClassLoaderAction getClassLoaderAction()
482 {
483 return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
484 }
485
486 static ClassLoader get(Class clazz)
487 {
488 return getClassLoaderAction().get(clazz);
489 }
490 }
491
492 ClassLoaderAction PRIVILEGED = new ClassLoaderAction()
493 {
494 public ClassLoader get(final Class clazz)
495 {
496 return (ClassLoader)AccessController.doPrivileged(
497 new PrivilegedAction()
498 {
499 public Object run()
500 {
501 return clazz.getClassLoader();
502 }
503 }
504 );
505 }
506 };
507
508 ClassLoaderAction NON_PRIVILEGED = new ClassLoaderAction()
509 {
510 public ClassLoader get(Class clazz)
511 {
512 return clazz.getClassLoader();
513 }
514 };
515
516 ClassLoader get(Class clazz);
517 }
518
519 interface SecurityActions
520 {
521 class UTIL
522 {
523 static SecurityActions getSecurityActions()
524 {
525 return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
526 }
527 }
528
529 SecurityActions NON_PRIVILEGED = new SecurityActions()
530 {
531 public Principal getPrincipal()
532 {
533 SecurityContext sc = getSecurityContext();
534 if(sc == null)
535 return null;
536 return sc.getUtil().getUserPrincipal();
537 }
538
539 public Object getCredential()
540 {
541 SecurityContext sc = getSecurityContext();
542 if(sc == null)
543 return null;
544 return sc.getUtil().getCredential();
545 }
546
547 public SecurityContext getSecurityContext()
548 {
549 return SecurityContextAssociation.getSecurityContext();
550 }
551
552 };
553
554 SecurityActions PRIVILEGED = new SecurityActions()
555 {
556 private final PrivilegedAction getPrincipalAction = new PrivilegedAction()
557 {
558 public Object run()
559 {
560 SecurityContext sc = getSecurityContext();
561 if(sc == null)
562 return null;
563 return sc.getUtil().getUserPrincipal();
564 }
565 };
566
567 private final PrivilegedAction getCredentialAction = new PrivilegedAction()
568 {
569 public Object run()
570 {
571 SecurityContext sc = getSecurityContext();
572 if(sc == null)
573 return null;
574 return sc.getUtil().getCredential();
575 }
576 };
577
578 public Principal getPrincipal()
579 {
580 return (Principal)AccessController.doPrivileged(getPrincipalAction);
581 }
582
583 public Object getCredential()
584 {
585 return AccessController.doPrivileged(getCredentialAction);
586 }
587
588 public SecurityContext getSecurityContext()
589 {
590 return (SecurityContext)AccessController.doPrivileged(
591 new PrivilegedAction(){
592
593 public Object run()
594 {
595 return SecurityContextAssociation.getSecurityContext();
596 }});
597 }
598 };
599
600 Principal getPrincipal();
601
602 Object getCredential();
603 SecurityContext getSecurityContext();
604 }
605
606 interface TCLAction
607 {
608 class UTIL
609 {
610 static TCLAction getTCLAction()
611 {
612 return System.getSecurityManager() == null ? NON_PRIVILEGED : PRIVILEGED;
613 }
614
615 static ClassLoader getContextClassLoader()
616 {
617 return getTCLAction().getContextClassLoader();
618 }
619
620 static ClassLoader getContextClassLoader(Thread thread)
621 {
622 return getTCLAction().getContextClassLoader(thread);
623 }
624
625 static void setContextClassLoader(ClassLoader cl)
626 {
627 getTCLAction().setContextClassLoader(cl);
628 }
629
630 static void setContextClassLoader(Thread thread, ClassLoader cl)
631 {
632 getTCLAction().setContextClassLoader(thread, cl);
633 }
634 }
635
636 TCLAction NON_PRIVILEGED = new TCLAction()
637 {
638 public ClassLoader getContextClassLoader()
639 {
640 return Thread.currentThread().getContextClassLoader();
641 }
642
643 public ClassLoader getContextClassLoader(Thread thread)
644 {
645 return thread.getContextClassLoader();
646 }
647
648 public void setContextClassLoader(ClassLoader cl)
649 {
650 Thread.currentThread().setContextClassLoader(cl);
651 }
652
653 public void setContextClassLoader(Thread thread, ClassLoader cl)
654 {
655 thread.setContextClassLoader(cl);
656 }
657 };
658
659 TCLAction PRIVILEGED = new TCLAction()
660 {
661 private final PrivilegedAction getTCLPrivilegedAction = new PrivilegedAction()
662 {
663 public Object run()
664 {
665 return Thread.currentThread().getContextClassLoader();
666 }
667 };
668
669 public ClassLoader getContextClassLoader()
670 {
671 return (ClassLoader)AccessController.doPrivileged(getTCLPrivilegedAction);
672 }
673
674 public ClassLoader getContextClassLoader(final Thread thread)
675 {
676 return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction()
677 {
678 public Object run()
679 {
680 return thread.getContextClassLoader();
681 }
682 });
683 }
684
685 public void setContextClassLoader(final ClassLoader cl)
686 {
687 AccessController.doPrivileged(
688 new PrivilegedAction()
689 {
690 public Object run()
691 {
692 Thread.currentThread().setContextClassLoader(cl);
693 return null;
694 }
695 }
696 );
697 }
698
699 public void setContextClassLoader(final Thread thread, final ClassLoader cl)
700 {
701 AccessController.doPrivileged(
702 new PrivilegedAction()
703 {
704 public Object run()
705 {
706 thread.setContextClassLoader(cl);
707 return null;
708 }
709 }
710 );
711 }
712 };
713
714 ClassLoader getContextClassLoader();
715
716 ClassLoader getContextClassLoader(Thread thread);
717
718 void setContextClassLoader(ClassLoader cl);
719
720 void setContextClassLoader(Thread thread, ClassLoader cl);
721 }
722 }