KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > EnterpriseContext


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 import java.rmi.RemoteException JavaDoc;
25 import java.security.Identity JavaDoc;
26 import java.security.Principal JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.Properties JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.Stack JavaDoc;
33 import javax.ejb.EJBContext JavaDoc;
34 import javax.ejb.EJBException JavaDoc;
35 import javax.ejb.EJBHome JavaDoc;
36 import javax.ejb.EJBLocalHome JavaDoc;
37 import javax.ejb.TimerService JavaDoc;
38 import javax.security.jacc.PolicyContextException JavaDoc;
39 import javax.transaction.HeuristicMixedException JavaDoc;
40 import javax.transaction.HeuristicRollbackException JavaDoc;
41 import javax.transaction.NotSupportedException JavaDoc;
42 import javax.transaction.RollbackException JavaDoc;
43 import javax.transaction.Status JavaDoc;
44 import javax.transaction.Synchronization JavaDoc;
45 import javax.transaction.SystemException JavaDoc;
46 import javax.transaction.Transaction JavaDoc;
47 import javax.transaction.TransactionManager JavaDoc;
48 import javax.transaction.UserTransaction JavaDoc;
49 import javax.naming.InitialContext JavaDoc;
50 import javax.naming.NamingException JavaDoc;
51  
52 import org.jboss.logging.Logger;
53 import org.jboss.metadata.ApplicationMetaData;
54 import org.jboss.metadata.BeanMetaData;
55 import org.jboss.metadata.SecurityRoleRefMetaData;
56 import org.jboss.security.AuthorizationManager;
57 import org.jboss.security.RealmMapping;
58 import org.jboss.security.RunAsIdentity;
59 import org.jboss.security.SecurityRoleRef;
60 import org.jboss.security.SimplePrincipal;
61 import org.jboss.security.authorization.AuthorizationContext;
62 import org.jboss.security.authorization.EJBResource;
63 import org.jboss.security.authorization.ResourceKeys;
64 import org.jboss.tm.TransactionTimeoutConfiguration;
65 import org.jboss.tm.usertx.client.ServerVMClientUserTransaction;
66
67 //$Id: EnterpriseContext.java 46497 2006-07-25 17:17:39Z asaldhana $
68

69 /**
70  * The EnterpriseContext is used to associate EJB instances with
71  * metadata about it.
72  *
73  * @see StatefulSessionEnterpriseContext
74  * @see StatelessSessionEnterpriseContext
75  * @see EntityEnterpriseContext
76  *
77  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard Öberg</a>
78  * @author <a HREF="mailto:marc.fleury@telkel.com">Marc Fleury</a>
79  * @author <a HREF="mailto:sebastien.alborini@m4x.org">Sebastien Alborini</a>
80  * @author <a HREF="mailto:juha@jboss.org">Juha Lindfors</a>
81  * @author <a HREF="mailto:osh@sparre.dk">Ole Husgaard</a>
82  * @author <a HREF="mailto:thomas.diesler@jboss.org">Thomas Diesler</a>
83  * @author <a HREF="mailto:anil.saldhana@jboss.org">Anil Saldhana</a>
84  * @version $Revision: 46497 $
85  */

86 public abstract class EnterpriseContext
87 implements AllowedOperationsFlags
88 {
89    // Constants -----------------------------------------------------
90

91    // Attributes ----------------------------------------------------
92

93    /** Instance logger. */
94    protected static Logger log = Logger.getLogger(EnterpriseContext.class);
95
96    /** The EJB instance */
97    Object JavaDoc instance;
98
99    /** The container using this context */
100    Container con;
101
102    /**
103     * Set to the synchronization currently associated with this context.
104     * May be null
105     */

106    Synchronization JavaDoc synch;
107
108    /** The transaction associated with the instance */
109    Transaction JavaDoc transaction;
110
111    /** The principal associated with the call */
112    private Principal JavaDoc principal;
113
114    /** The principal for the bean associated with the call */
115    private Principal JavaDoc beanPrincipal;
116
117    /** Only StatelessSession beans have no Id, stateful and entity do */
118    Object JavaDoc id;
119
120    /** The instance is being used. This locks it's state */
121    int locked = 0;
122
123    /** The instance is used in a transaction, synchronized methods on the tx */
124    Object JavaDoc txLock = new Object JavaDoc();
125
126    /**
127     * Holds one of the IN_METHOD constants, to indicate that we are in an ejb method
128     * According to the EJB2.1 spec not all context methods can be accessed at all times
129     * For example ctx.getPrimaryKey() should throw an IllegalStateException when called from within ejbCreate()
130     */

131    private Stack JavaDoc inMethodStack = new Stack JavaDoc();
132
133    // Static --------------------------------------------------------
134
//Registration for CachedConnectionManager so our UserTx can notify
135
//on tx started.
136
private static ServerVMClientUserTransaction.UserTransactionStartedListener tsl;
137
138    /**
139     * The <code>setUserTransactionStartedListener</code> method is called by
140     * CachedConnectionManager on start and stop. The tsl is notified on
141     * UserTransaction.begin so it (the CachedConnectionManager) can enroll
142     * connections that are already checked out.
143     *
144     * @param newTsl a <code>ServerVMClientUserTransaction.UserTransactionStartedListener</code> value
145     */

146    public static void setUserTransactionStartedListener(ServerVMClientUserTransaction.UserTransactionStartedListener newTsl)
147    {
148       tsl = newTsl;
149    }
150
151    // Constructors --------------------------------------------------
152

153    public EnterpriseContext(Object JavaDoc instance, Container con)
154    {
155       this.instance = instance;
156       this.con = con;
157    }
158
159    // Public --------------------------------------------------------
160

161    public Object JavaDoc getInstance()
162    {
163       return instance;
164    }
165
166    /**
167     * Gets the container that manages the wrapped bean.
168     */

169    public Container getContainer()
170    {
171       return con;
172    }
173
174    public abstract void discard()
175    throws RemoteException JavaDoc;
176
177    /**
178     * Get the EJBContext object
179     */

180    public abstract EJBContext JavaDoc getEJBContext();
181
182    public void setId(Object JavaDoc id)
183    {
184       this.id = id;
185    }
186
187    public Object JavaDoc getId()
188    {
189       return id;
190    }
191
192    public Object JavaDoc getTxLock()
193    {
194       return txLock;
195    }
196
197    public void setTransaction(Transaction JavaDoc transaction)
198    {
199       // DEBUG log.debug("EnterpriseContext.setTransaction "+((transaction == null) ? "null" : Integer.toString(transaction.hashCode())));
200
this.transaction = transaction;
201    }
202
203    public Transaction JavaDoc getTransaction()
204    {
205       return transaction;
206    }
207
208    public void setPrincipal(Principal JavaDoc principal)
209    {
210       this.principal = principal;
211       /* Clear the bean principal used for getCallerPrincipal and synch with the
212       new call principal
213       */

214       this.beanPrincipal = null;
215       if( con.getSecurityManager() != null )
216          this.beanPrincipal = getCallerPrincipal();
217    }
218
219    public void lock()
220    {
221       locked++;
222       //new Exception().printStackTrace();
223
//DEBUG log.debug("EnterpriseContext.lock() "+hashCode()+" "+locked);
224
}
225
226    public void unlock()
227    {
228
229       // release a lock
230
locked--;
231
232       //new Exception().printStackTrace();
233
if (locked < 0)
234       {
235          // new Exception().printStackTrace();
236
log.error("locked < 0", new Throwable JavaDoc());
237       }
238
239       //DEBUG log.debug("EnterpriseContext.unlock() "+hashCode()+" "+locked);
240
}
241
242    public boolean isLocked()
243    {
244
245       //DEBUG log.debug("EnterpriseContext.isLocked() "+hashCode()+" at "+locked);
246
return locked != 0;
247    }
248
249    public Principal JavaDoc getCallerPrincipal()
250    {
251       EJBContextImpl ctxImpl = (EJBContextImpl) getEJBContext();
252       return ctxImpl.getCallerPrincipalInternal();
253    }
254
255    /**
256     * before reusing this context we clear it of previous state called
257     * by pool.free()
258     */

259    public void clear()
260    {
261       this.id = null;
262       this.locked = 0;
263       this.principal = null;
264       this.beanPrincipal = null;
265       this.synch = null;
266       this.transaction = null;
267       this.inMethodStack.clear();
268    }
269
270    // Package protected ---------------------------------------------
271

272    // Protected -----------------------------------------------------
273

274    protected boolean isContainerManagedTx()
275    {
276       BeanMetaData md = con.getBeanMetaData();
277       return md.isContainerManagedTx();
278    }
279
280    protected boolean isUserManagedTx()
281    {
282       BeanMetaData md = con.getBeanMetaData();
283       return md.isContainerManagedTx() == false;
284    }
285
286    // Private -------------------------------------------------------
287

288    // Inner classes -------------------------------------------------
289

290    protected class EJBContextImpl
291    implements EJBContext JavaDoc
292    {
293       /**
294        * A per-bean instance UserTransaction instance cached after the
295        * first call to <code>getUserTransaction()</code>.
296        */

297       private UserTransactionImpl userTransaction = null;
298
299       private InitialContext JavaDoc ctx;
300
301
302       private InitialContext JavaDoc getContext()
303       {
304           if (ctx==null)
305           {
306               try
307               {
308                  ctx = new InitialContext JavaDoc();
309               }
310               catch (NamingException JavaDoc e)
311               {
312                  throw new RuntimeException JavaDoc(e);
313               }
314           }
315
316           return ctx;
317       }
318
319       protected EJBContextImpl()
320       {
321       }
322
323       public Object JavaDoc lookup(String JavaDoc name)
324       {
325          try
326          {
327             return getContext().lookup(name);
328          }
329          catch (NamingException JavaDoc ignored)
330          {
331          }
332          return null;
333       }
334
335       /**
336        * @deprecated
337        */

338       public Identity JavaDoc getCallerIdentity()
339       {
340          throw new EJBException JavaDoc("Deprecated");
341       }
342
343       public TimerService JavaDoc getTimerService() throws IllegalStateException JavaDoc
344       {
345          return getContainer().getTimerService(null);
346       }
347
348       /**
349        * Get the Principal for the current caller. This method
350        * cannot return null according to the ejb-spec.
351        */

352       public Principal JavaDoc getCallerPrincipal()
353       {
354          return getCallerPrincipalInternal();
355       }
356
357       /**
358        * The implementation of getCallerPrincipal()
359        * @return the caller principal
360        */

361       Principal JavaDoc getCallerPrincipalInternal()
362       {
363          if( beanPrincipal == null )
364          {
365             /* Get the run-as user or authenticated user. The run-as user is
366             returned before any authenticated user.
367             */

368             Principal JavaDoc caller = SecurityActions.getCallerPrincipal();
369             RealmMapping rm = con.getRealmMapping();
370             /* Apply any domain caller mapping. This should really only be
371             done for non-run-as callers.
372             */

373             if (rm != null)
374                caller = rm.getPrincipal(caller);
375             if( caller == null )
376             {
377                /* Try the incoming request principal. This is needed if a client
378                clears the current caller association and and an interceptor calls
379                getCallerPrincipal as the call stack unwinds.
380                */

381                if( principal != null )
382                {
383                  if( rm != null )
384                     caller = rm.getPrincipal(principal);
385                  else
386                     caller = principal;
387                }
388                // Check for an unauthenticated principal value
389
else
390                {
391                   ApplicationMetaData appMetaData = con.getBeanMetaData().getApplicationMetaData();
392                   String JavaDoc name = appMetaData.getUnauthenticatedPrincipal();
393                   if (name != null)
394                      caller = new SimplePrincipal(name);
395                }
396             }
397    
398             if( caller == null )
399             {
400                throw new IllegalStateException JavaDoc("No valid security context for the caller identity");
401             }
402             /* Save caller as the beanPrincipal for reuse if getCallerPrincipal is called as the
403                stack unwinds. An example of where this would occur is the cmp2 audit layer.
404             */

405             beanPrincipal = caller;
406          }
407          return beanPrincipal;
408       }
409
410       public EJBHome JavaDoc getEJBHome()
411       {
412          EJBProxyFactory proxyFactory = con.getProxyFactory();
413          if (proxyFactory == null)
414             throw new IllegalStateException JavaDoc("No remote home defined.");
415
416          return (EJBHome JavaDoc) proxyFactory.getEJBHome();
417       }
418
419       public EJBLocalHome JavaDoc getEJBLocalHome()
420       {
421          if (con.getLocalHomeClass() == null)
422             throw new IllegalStateException JavaDoc("No local home defined.");
423
424          if (con instanceof EntityContainer)
425             return ((EntityContainer) con).getLocalProxyFactory().getEJBLocalHome();
426          else if (con instanceof StatelessSessionContainer)
427             return ((StatelessSessionContainer) con).getLocalProxyFactory().getEJBLocalHome();
428          else if (con instanceof StatefulSessionContainer)
429             return ((StatefulSessionContainer) con).getLocalProxyFactory().getEJBLocalHome();
430
431          // Should never get here
432
throw new EJBException JavaDoc("No EJBLocalHome available (BUG!)");
433       }
434
435       /**
436        * @deprecated
437        */

438       public Properties JavaDoc getEnvironment()
439       {
440          throw new EJBException JavaDoc("Deprecated");
441       }
442
443       public boolean getRollbackOnly()
444       {
445          // EJB1.1 11.6.1: Must throw IllegalStateException if BMT
446
if (con.getBeanMetaData().isBeanManagedTx())
447             throw new IllegalStateException JavaDoc("getRollbackOnly() not allowed for BMT beans.");
448
449          try
450          {
451             TransactionManager JavaDoc tm = con.getTransactionManager();
452
453             // The getRollbackOnly and setRollBackOnly method of the SessionContext interface should be used
454
// only in the session bean methods that execute in the context of a transaction.
455
if (tm.getTransaction() == null)
456                throw new IllegalStateException JavaDoc("getRollbackOnly() not allowed without a transaction.");
457
458             return tm.getStatus() == Status.STATUS_MARKED_ROLLBACK;
459          }
460          catch (SystemException JavaDoc e)
461          {
462             log.warn("failed to get tx manager status; ignoring", e);
463             return true;
464          }
465       }
466
467       public void setRollbackOnly()
468       {
469          // EJB1.1 11.6.1: Must throw IllegalStateException if BMT
470
if (con.getBeanMetaData().isBeanManagedTx())
471             throw new IllegalStateException JavaDoc("setRollbackOnly() not allowed for BMT beans.");
472
473          try
474          {
475             TransactionManager JavaDoc tm = con.getTransactionManager();
476
477             // The getRollbackOnly and setRollBackOnly method of the SessionContext interface should be used
478
// only in the session bean methods that execute in the context of a transaction.
479
if (tm.getTransaction() == null)
480                throw new IllegalStateException JavaDoc("setRollbackOnly() not allowed without a transaction.");
481
482             tm.setRollbackOnly();
483          }
484          catch (SystemException JavaDoc e)
485          {
486             log.warn("failed to set rollback only; ignoring", e);
487          }
488       }
489
490       /**
491        * @deprecated
492        */

493       public boolean isCallerInRole(Identity JavaDoc id)
494       {
495          throw new EJBException JavaDoc("Deprecated");
496       }
497
498       /**
499        * Checks if the current caller has a given role.
500        * The current caller is either the principal associated with the method invocation
501        * or the current run-as principal.
502        */

503       public boolean isCallerInRole(String JavaDoc roleName)
504       {
505          //Obtain the Authorization Manager
506
AuthorizationManager authzManager = getContainer().getAuthorizationManager();
507          if( authzManager == null)
508          {
509             String JavaDoc msg = "isCallerInRole() called with no security context. "
510                + "Check that a security-domain has been set for the application.";
511                throw new IllegalStateException JavaDoc(msg);
512          }
513          //Check the caller of this beans run-as identity
514
RunAsIdentity runAsIdentity = SecurityActions.peekRunAsIdentity(1);
515          if (principal == null && runAsIdentity == null)
516             return false;
517          
518          //Generate the SecurityRoleRef set
519
Iterator JavaDoc it = getContainer().getBeanMetaData().getSecurityRoleReferences();
520          Set JavaDoc securityRoleRefs = new HashSet JavaDoc();
521          while(it.hasNext())
522          {
523             SecurityRoleRefMetaData meta = (SecurityRoleRefMetaData) it.next();
524             securityRoleRefs.add(new SecurityRoleRef(meta.getName(), meta.getLink(),meta.getDescription()));
525          }
526          final HashMap JavaDoc map = new HashMap JavaDoc();
527          map.put(ResourceKeys.EJB_NAME ,getContainer().getBeanMetaData().getEjbName());
528          map.put(ResourceKeys.EJB_PRINCIPAL, principal);
529          map.put(ResourceKeys.AUTHORIZATION_MANAGER,authzManager);
530          map.put(ResourceKeys.RUNASIDENTITY, SecurityActions.peekRunAsIdentity(1));
531          map.put(ResourceKeys.SECURITY_ROLE_REFERENCES, securityRoleRefs);
532          map.put(ResourceKeys.ROLENAME, roleName);
533          map.put(ResourceKeys.ROLEREF_PERM_CHECK, Boolean.TRUE);
534          try
535          {
536             map.put(ResourceKeys.CALLER_SUBJECT, SecurityActions.getContextSubject());
537          }
538          catch (PolicyContextException JavaDoc pe)
539          {
540            if(log.isTraceEnabled())
541               log.trace("PolicyContextException in getting caller subject:",pe);
542          }
543          EJBResource ejbResource = new EJBResource(map);
544          boolean isAuthorized = false;
545          try
546          {
547             int check = authzManager.authorize(ejbResource);
548             isAuthorized = (check == AuthorizationContext.PERMIT);
549          }
550          catch (Exception JavaDoc e)
551          {
552             isAuthorized = false;
553             if(log.isTraceEnabled())
554                log.trace("Error in authorization:",e);
555             else
556                log.error("isCallerInRole check failed:"+e.getLocalizedMessage());
557          }
558          return isAuthorized;
559       }
560
561       public UserTransaction JavaDoc getUserTransaction()
562       {
563          if (userTransaction == null)
564          {
565             if (isContainerManagedTx())
566             {
567                throw new IllegalStateException JavaDoc
568                ("CMT beans are not allowed to get a UserTransaction");
569             }
570
571             userTransaction = new UserTransactionImpl();
572          }
573
574          return userTransaction;
575       }
576    }
577
578    // Inner classes -------------------------------------------------
579

580    protected class UserTransactionImpl
581    implements UserTransaction JavaDoc
582    {
583       /** Timeout value in seconds for new transactions started by this bean instance. */
584       private int timeout = 0;
585
586       /** Whether trace is enabled */
587       boolean trace;
588
589       public UserTransactionImpl()
590       {
591          trace = log.isTraceEnabled();
592          if (trace)
593             log.trace("new UserTx: " + this);
594       }
595
596       public void begin()
597       throws NotSupportedException JavaDoc, SystemException JavaDoc
598       {
599          TransactionManager JavaDoc tm = con.getTransactionManager();
600
601          int oldTimeout = -1;
602          if (tm instanceof TransactionTimeoutConfiguration)
603             oldTimeout = ((TransactionTimeoutConfiguration) tm).getTransactionTimeout();
604
605          // Set the timeout value
606
tm.setTransactionTimeout(timeout);
607
608          try
609          {
610             // Start the transaction
611
tm.begin();
612
613             //notify checked out connections
614
if (tsl != null)
615                tsl.userTransactionStarted();
616
617             Transaction JavaDoc tx = tm.getTransaction();
618             if (trace)
619                log.trace("UserTx begin: " + tx);
620
621             // keep track of the transaction in enterprise context for BMT
622
setTransaction(tx);
623          }
624          finally
625          {
626             // Reset the transaction timeout (if we know what it was)
627
if (oldTimeout != -1)
628                tm.setTransactionTimeout(oldTimeout);
629          }
630       }
631
632       public void commit()
633       throws RollbackException JavaDoc, HeuristicMixedException JavaDoc, HeuristicRollbackException JavaDoc,
634       SecurityException JavaDoc, IllegalStateException JavaDoc, SystemException JavaDoc
635       {
636          TransactionManager JavaDoc tm = con.getTransactionManager();
637          try
638          {
639             Transaction JavaDoc tx = tm.getTransaction();
640             if (trace)
641                log.trace("UserTx commit: " + tx);
642
643             int status = tm.getStatus();
644             tm.commit();
645          }
646          finally
647          {
648             // According to the spec, after commit and rollback was called on
649
// UserTransaction, the thread is associated with no transaction.
650
// Since the BMT Tx interceptor will associate and resume the tx
651
// from the context with the thread that comes in
652
// on a subsequent invocation, we must set the context transaction to null
653
setTransaction(null);
654          }
655       }
656
657       public void rollback()
658       throws IllegalStateException JavaDoc, SecurityException JavaDoc, SystemException JavaDoc
659       {
660          TransactionManager JavaDoc tm = con.getTransactionManager();
661          try
662          {
663             Transaction JavaDoc tx = tm.getTransaction();
664             if (trace)
665                log.trace("UserTx rollback: " + tx);
666             tm.rollback();
667          }
668          finally
669          {
670             // According to the spec, after commit and rollback was called on
671
// UserTransaction, the thread is associated with no transaction.
672
// Since the BMT Tx interceptor will associate and resume the tx
673
// from the context with the thread that comes in
674
// on a subsequent invocation, we must set the context transaction to null
675
setTransaction(null);
676          }
677       }
678
679       public void setRollbackOnly()
680       throws IllegalStateException JavaDoc, SystemException JavaDoc
681       {
682          TransactionManager JavaDoc tm = con.getTransactionManager();
683          Transaction JavaDoc tx = tm.getTransaction();
684          if (trace)
685             log.trace("UserTx setRollbackOnly: " + tx);
686
687          tm.setRollbackOnly();
688       }
689
690       public int getStatus()
691       throws SystemException JavaDoc
692       {
693          TransactionManager JavaDoc tm = con.getTransactionManager();
694          return tm.getStatus();
695       }
696
697       /**
698        * Set the transaction timeout value for new transactions
699        * started by this instance.
700        */

701       public void setTransactionTimeout(int seconds)
702       throws SystemException JavaDoc
703       {
704          timeout = seconds;
705       }
706    }
707 }
708
Popular Tags