KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > StatefulSessionInstanceInterceptor


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;
23
24 import org.jboss.ejb.BeanLock;
25 import org.jboss.ejb.Container;
26 import org.jboss.ejb.EnterpriseContext;
27 import org.jboss.ejb.InstanceCache;
28 import org.jboss.ejb.InstancePool;
29 import org.jboss.ejb.StatefulSessionContainer;
30 import org.jboss.ejb.AllowedOperationsAssociation;
31 import org.jboss.invocation.Invocation;
32 import org.jboss.invocation.InvocationType;
33 import org.jboss.logging.Logger;
34 import org.jboss.metadata.SessionMetaData;
35
36 import javax.ejb.EJBException JavaDoc;
37 import javax.ejb.EJBObject JavaDoc;
38 import javax.ejb.Handle JavaDoc;
39 import javax.ejb.NoSuchObjectLocalException JavaDoc;
40 import javax.ejb.TimedObject JavaDoc;
41 import javax.ejb.Timer JavaDoc;
42 import javax.transaction.RollbackException JavaDoc;
43 import javax.transaction.Status JavaDoc;
44 import javax.transaction.Synchronization JavaDoc;
45 import javax.transaction.Transaction JavaDoc;
46 import java.lang.reflect.Method JavaDoc;
47 import java.rmi.NoSuchObjectException JavaDoc;
48 import java.rmi.RemoteException JavaDoc;
49
50 /**
51  * This container acquires the given instance.
52  *
53  * @author <a HREF="mailto:rickard.oberg@telkel.com">Rickard Oberg</a>
54  * @author <a HREF="mailto:marc.fleury@jboss.org">Marc Fleury</a>
55  * @author <a HREF="mailto:bill@burkecentral.com">Bill Burke</a>
56  * @author <a HREF="mailto:scott.stark@jboss.org">Scott Stark</a>
57  * @version $Revision: 58441 $
58  *
59  */

60 public class StatefulSessionInstanceInterceptor
61    extends AbstractInterceptor
62 {
63    // Constants ----------------------------------------------------
64

65    // Attributes ---------------------------------------------------
66

67    /** Instance logger. */
68    protected Logger log = Logger.getLogger(this.getClass());
69    
70    protected StatefulSessionContainer container;
71    
72    // Static -------------------------------------------------------
73

74    private static final Method JavaDoc getEJBHome;
75    private static final Method JavaDoc getHandle;
76    private static final Method JavaDoc getPrimaryKey;
77    private static final Method JavaDoc isIdentical;
78    private static final Method JavaDoc remove;
79    private static final Method JavaDoc getEJBObject;
80    private static final Method JavaDoc ejbTimeout;
81
82    static
83    {
84       try
85       {
86          Class JavaDoc[] noArg = new Class JavaDoc[0];
87          getEJBHome = EJBObject JavaDoc.class.getMethod("getEJBHome", noArg);
88          getHandle = EJBObject JavaDoc.class.getMethod("getHandle", noArg);
89          getPrimaryKey = EJBObject JavaDoc.class.getMethod("getPrimaryKey", noArg);
90          isIdentical = EJBObject JavaDoc.class.getMethod("isIdentical", new Class JavaDoc[]{EJBObject JavaDoc.class});
91          remove = EJBObject JavaDoc.class.getMethod("remove", noArg);
92          getEJBObject = Handle JavaDoc.class.getMethod("getEJBObject", noArg);
93          ejbTimeout = TimedObject JavaDoc.class.getMethod("ejbTimeout", new Class JavaDoc[]{Timer JavaDoc.class});
94       }
95       catch (Exception JavaDoc e)
96       {
97          e.printStackTrace();
98          throw new ExceptionInInitializerError JavaDoc(e);
99       }
100    }
101    
102    // Constructors -------------------------------------------------
103

104    // Public -------------------------------------------------------
105

106    public void setContainer(Container container)
107    {
108       this.container = (StatefulSessionContainer)container;
109    }
110    
111    public Container getContainer()
112    {
113       return container;
114    }
115    
116    // Interceptor implementation -----------------------------------
117

118    public Object JavaDoc invokeHome(Invocation mi)
119       throws Exception JavaDoc
120    {
121       // Invocation on the handle, we don't need a bean instance
122
if (getEJBObject.equals(mi.getMethod()))
123          return getNext().invokeHome(mi);
124
125       // get a new context from the pool (this is a home method call)
126
InstancePool pool = container.getInstancePool();
127       EnterpriseContext ctx = pool.get();
128
129       // set the context on the Invocation
130
mi.setEnterpriseContext(ctx);
131       
132       // It is a new context for sure so we can lock it
133
ctx.lock();
134       
135       // Set the current security information
136
ctx.setPrincipal(mi.getPrincipal());
137  
138       AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_HOME);
139
140       try
141       {
142          // Invoke through interceptors
143
return getNext().invokeHome(mi);
144       }
145       finally
146       {
147          synchronized (ctx)
148          {
149             AllowedOperationsAssociation.popInMethodFlag();
150
151             // Release the lock
152
ctx.unlock();
153             
154             // Still free? Not free if create() was called successfully
155
if (ctx.getId() == null)
156             {
157                pool.free(ctx);
158             }
159          }
160       }
161    }
162    
163    protected void register(EnterpriseContext ctx, Transaction JavaDoc tx, BeanLock lock)
164    {
165       // Create a new synchronization
166
InstanceSynchronization synch = new InstanceSynchronization(ctx, lock);
167       
168       try
169       {
170          // OSH: An extra check to avoid warning.
171
// Can go when we are sure that we no longer get
172
// the JTA violation warning.
173
if (tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
174          {
175             
176             return;
177          }
178          
179          // We want to be notified when the transaction commits
180
try
181          {
182             tx.registerSynchronization(synch);
183          }
184          catch (Exception JavaDoc ex)
185          {
186             // synch adds a reference to the lock, so we must release the ref
187
// because afterCompletion will never get called.
188
getContainer().getLockManager().removeLockRef(lock.getId());
189             throw ex;
190          }
191          
192          // EJB 1.1, 6.5.3
193
synch.afterBegin();
194          
195       } catch (RollbackException JavaDoc e)
196       {
197          
198       } catch (Exception JavaDoc e)
199       {
200          
201          throw new EJBException JavaDoc(e);
202          
203       }
204    }
205    
206    public Object JavaDoc invoke(Invocation mi)
207    throws Exception JavaDoc
208    {
209       InstanceCache cache = container.getInstanceCache();
210       InstancePool pool = container.getInstancePool();
211       Object JavaDoc methodID = mi.getId();
212       EnterpriseContext ctx = null;
213       
214       BeanLock lock = container.getLockManager().getLock(methodID);
215       try
216       {
217          /* The security context must be established before the cache
218          lookup because the activation of a session should have the caller's
219          security context as ejbActivate is allowed to call other secured
220          resources. Since the pm makes the ejbActivate call, we need to
221          set the caller's security context. The only reason this shows up for
222          stateful session is that we moved the SecurityInterceptor to after
223          the instance interceptor to allow security exceptions to result in
224          invalidation of the session. This may be too literal an interpretation
225          of the ejb spec requirement that runtime exceptions should invalidate
226          the session.
227           */

228          if(SecurityActions.peekRunAsIdentity() == null)
229             SecurityActions.pushSubjectContext(mi.getPrincipal(), mi.getCredential(), null);
230
231          lock.sync();
232          try
233          {
234             // Get context
235
try
236             {
237                ctx = cache.get(methodID);
238             }
239             catch (NoSuchObjectException JavaDoc e)
240             {
241                if (mi.isLocal())
242                   throw new NoSuchObjectLocalException JavaDoc(e.getMessage());
243                else
244                   throw e;
245             }
246             catch (EJBException JavaDoc e)
247             {
248                throw e;
249             }
250             catch (RemoteException JavaDoc e)
251             {
252                throw e;
253             }
254             catch (Exception JavaDoc e)
255             {
256                InvocationType type = mi.getType();
257                boolean isLocal = (type == InvocationType.LOCAL || type == InvocationType.LOCALHOME);
258                if (isLocal)
259                   throw new EJBException JavaDoc("Unable to get an instance from the pool/cache", e);
260                else
261                   throw new RemoteException JavaDoc("Unable to get an intance from the pool/cache", e);
262             }
263             
264             // Associate it with the method invocation
265
mi.setEnterpriseContext(ctx);
266             // Set the JACC EnterpriseBean PolicyContextHandler data
267
EnterpriseBeanPolicyContextHandler.setEnterpriseBean(ctx.getInstance());
268
269             // BMT beans will lock and replace tx no matter what, CMT do work on transaction
270
boolean isBMT = ((SessionMetaData)container.getBeanMetaData()).isBeanManagedTx();
271             if (isBMT == false)
272             {
273                
274                // Do we have a running transaction with the context
275
if (ctx.getTransaction() != null &&
276                // And are we trying to enter with another transaction
277
!ctx.getTransaction().equals(mi.getTransaction()))
278                {
279                   // Calls must be in the same transaction
280
StringBuffer JavaDoc msg = new StringBuffer JavaDoc("Application Error: " +
281                      "tried to enter Stateful bean with different tx context");
282                   msg.append(", contextTx: " + ctx.getTransaction());
283                   msg.append(", methodTx: " + mi.getTransaction());
284                   throw new EJBException JavaDoc(msg.toString());
285                }
286
287                //If the instance will participate in a new transaction we register a sync for it
288
if (ctx.getTransaction() == null && mi.getTransaction() != null)
289                {
290                   register(ctx, mi.getTransaction(), lock);
291                }
292             }
293             
294             if (!ctx.isLocked())
295             {
296                
297                //take it!
298
ctx.lock();
299             }
300             else
301             {
302                if (!isCallAllowed(mi))
303                {
304                   // Concurent calls are not allowed
305
throw new EJBException JavaDoc("Application Error: no concurrent " +
306                         "calls on stateful beans");
307                }
308                else
309                {
310                   ctx.lock();
311                }
312             }
313          }
314          finally
315          {
316             lock.releaseSync();
317          }
318
319          // Set the current security information
320
ctx.setPrincipal(mi.getPrincipal());
321
322          if (ejbTimeout.equals(mi.getMethod()))
323             AllowedOperationsAssociation.pushInMethodFlag(IN_EJB_TIMEOUT);
324          else
325             AllowedOperationsAssociation.pushInMethodFlag(IN_BUSINESS_METHOD);
326
327          boolean validContext = true;
328          try
329          {
330             // Invoke through interceptors
331
Object JavaDoc ret = getNext().invoke(mi);
332             return ret;
333          }
334          catch (RemoteException JavaDoc e)
335          {
336             // Discard instance
337
cache.remove(methodID);
338             pool.discard(ctx);
339             validContext = false;
340
341             throw e;
342          }
343          catch (RuntimeException JavaDoc e)
344          {
345             // Discard instance
346
cache.remove(methodID);
347             pool.discard(ctx);
348             validContext = false;
349
350             throw e;
351          }
352          catch (Error JavaDoc e)
353          {
354             // Discard instance
355
cache.remove(methodID);
356             pool.discard(ctx);
357             validContext = false;
358
359             throw e;
360          }
361          finally
362          {
363             AllowedOperationsAssociation.popInMethodFlag();
364
365             if (validContext)
366             {
367                // Still a valid instance
368
lock.sync();
369                try
370                {
371
372                   // release it
373
ctx.unlock();
374                   
375                   // if removed, remove from cache
376
if (ctx.getId() == null)
377                   {
378                      // Remove from cache
379
cache.remove(methodID);
380                      pool.free(ctx);
381                   }
382                }
383                finally
384                {
385                   lock.releaseSync();
386                }
387             }
388          }
389       }
390       finally
391       {
392          container.getLockManager().removeLockRef(lock.getId());
393          SecurityActions.popSubjectContext();
394       }
395    }
396    
397    protected boolean isCallAllowed(Invocation mi)
398    {
399       Method JavaDoc m = mi.getMethod();
400       if (m.equals(getEJBHome) ||
401       m.equals(getHandle) ||
402       m.equals(getPrimaryKey) ||
403       m.equals(isIdentical) ||
404       m.equals(remove))
405       {
406          return true;
407       }
408       return false;
409    }
410    
411    // Inner classes -------------------------------------------------
412

413    private class InstanceSynchronization
414    implements Synchronization JavaDoc
415    {
416       /**
417        * The context we manage.
418        */

419       private EnterpriseContext ctx;
420       
421       // a utility boolean for session sync
422
private boolean notifySession = false;
423       
424       // Utility methods for the notifications
425
private Method JavaDoc afterBegin;
426       private Method JavaDoc beforeCompletion;
427       private Method JavaDoc afterCompletion;
428       private BeanLock lock;
429       private boolean beforeCompletionInvoked = false;
430       
431       /**
432        * Create a new instance synchronization instance.
433        */

434       InstanceSynchronization(EnterpriseContext ctx, BeanLock lock)
435       {
436          this.ctx = ctx;
437          this.lock = lock;
438          this.lock.addRef();
439          
440          // Let's compute it now, to speed things up we could
441
notifySession = (ctx.getInstance() instanceof javax.ejb.SessionSynchronization JavaDoc);
442          
443          if (notifySession)
444          {
445             try
446             {
447                // Get the class we are working on
448
Class JavaDoc sync = Class.forName("javax.ejb.SessionSynchronization");
449                
450                // Lookup the methods on it
451
afterBegin = sync.getMethod("afterBegin", new Class JavaDoc[0]);
452                beforeCompletion = sync.getMethod("beforeCompletion", new Class JavaDoc[0]);
453                afterCompletion = sync.getMethod("afterCompletion", new Class JavaDoc[]
454                {boolean.class});
455             }
456             catch (Exception JavaDoc e)
457             {
458                log.error("failed to setup InstanceSynchronization", e);
459             }
460          }
461       }
462       
463       // Synchronization implementation -----------------------------
464

465       public void afterBegin()
466       {
467          if (notifySession)
468          {
469             try
470             {
471                AllowedOperationsAssociation.pushInMethodFlag(IN_AFTER_BEGIN);
472                afterBegin.invoke(ctx.getInstance(), new Object JavaDoc[0]);
473             }
474             catch (Exception JavaDoc e)
475             {
476                log.error("failed to invoke afterBegin", e);
477             }
478             finally{
479                AllowedOperationsAssociation.popInMethodFlag();
480             }
481          }
482       }
483       
484       public void beforeCompletion()
485       {
486          if( log.isTraceEnabled() )
487             log.trace("beforeCompletion called");
488
489          // lock the context the transaction is being commited (no need for sync)
490
ctx.lock();
491          beforeCompletionInvoked = true;
492          
493          if (notifySession)
494          {
495             try
496             {
497                AllowedOperationsAssociation.pushInMethodFlag(IN_BEFORE_COMPLETION);
498                beforeCompletion.invoke(ctx.getInstance(), new Object JavaDoc[0]);
499             }
500             catch (Exception JavaDoc e)
501             {
502                log.error("failed to invoke beforeCompletion", e);
503             }
504             finally{
505                AllowedOperationsAssociation.popInMethodFlag();
506             }
507          }
508       }
509       
510       public void afterCompletion(int status)
511       {
512          if( log.isTraceEnabled() )
513             log.trace("afterCompletion called");
514          
515          lock.sync();
516          try
517          {
518             // finish the transaction association
519
ctx.setTransaction(null);
520             
521             if (beforeCompletionInvoked)
522                ctx.unlock();
523             
524             if (notifySession)
525             {
526                
527                try
528                {
529                   AllowedOperationsAssociation.pushInMethodFlag(IN_AFTER_COMPLETION);
530                   if (status == Status.STATUS_COMMITTED)
531                   {
532                      afterCompletion.invoke(ctx.getInstance(), new Object JavaDoc[]{Boolean.TRUE});
533                   }
534                   else
535                   {
536                      afterCompletion.invoke(ctx.getInstance(), new Object JavaDoc[]{Boolean.FALSE});
537                   }
538                }
539                catch (Exception JavaDoc e)
540                {
541                   log.error("failed to invoke afterCompletion", e);
542                }
543                finally{
544                   AllowedOperationsAssociation.popInMethodFlag();
545                }
546             }
547          }
548          finally
549          {
550             lock.releaseSync();
551             container.getLockManager().removeLockRef(lock.getId());
552          }
553       }
554    }
555 }
556
557
Popular Tags