KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > ejb > containers > ReadOnlyBeanContainer


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.ejb.containers;
24
25 import java.rmi.RemoteException JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.util.*;
28 import java.lang.reflect.Method JavaDoc;
29
30 import javax.ejb.*;
31 import javax.transaction.*;
32
33 import com.sun.ejb.*;
34 import com.sun.ejb.spi.container.BeanStateSynchronization;
35 import com.sun.enterprise.*;
36 import com.sun.enterprise.deployment.*;
37 import com.sun.enterprise.log.Log;
38
39 import java.util.logging.*;
40 import com.sun.ejb.containers.util.cache.EJBObjectCache;
41 import com.sun.ejb.containers.util.cache.FIFOEJBObjectCache;
42 import com.sun.ejb.containers.util.cache.UnboundedEJBObjectCache;
43 import com.sun.appserv.util.cache.Cache;
44 import com.sun.logging.*;
45
46 import com.sun.ejb.spi.distributed.DistributedEJBServiceFactory;
47 import com.sun.ejb.spi.distributed.DistributedReadOnlyBeanService;
48 import com.sun.ejb.spi.distributed.ReadOnlyBeanRefreshEventHandler;
49 import java.util.Timer JavaDoc;
50 import java.util.TimerTask JavaDoc;
51
52
53 /**
54  * The Container that manages instances of ReadOnly Beans. This container
55  * blocks all calls to ejbStore() and selectively performs ejbLoad()
56  *
57  * @author Mahesh Kannan
58  * @author Pramod Gopinath
59  */

60
61 public class ReadOnlyBeanContainer
62     extends EntityContainer
63     implements ReadOnlyBeanRefreshEventHandler
64 {
65     private static Logger _logger;
66     static {
67         _logger=LogDomains.getLogger(LogDomains.EJB_LOGGER);
68     }
69     
70     private long refreshPeriodInMillis = 0;
71
72     // Sequence number incremented each time a bean-level refresh is requested.
73
// PK-level data structure has a corresponding sequence number that is used
74
// to determine when it needs updating due to bean-level refresh.
75
private int beanLevelSequenceNum = 1;
76
77     // Last time a bean-level timeout refresh event occurred.
78
private long beanLevelLastRefreshRequestedAt = 0;
79
80
81     // timer task for refreshing or null if no refresh.
82
private TimerTask JavaDoc refreshTask = null;
83
84     private EJBObjectCache robCache;
85     
86     private DistributedReadOnlyBeanService distributedReadOnlyBeanService;
87     
88     protected ReadOnlyBeanContainer(EjbDescriptor desc, ClassLoader JavaDoc loader)
89         throws Exception JavaDoc
90     {
91         super(desc, loader);
92
93         containerFactory = (ContainerFactoryImpl)
94             theSwitch.getContainerFactory();
95         EjbEntityDescriptor ed = (EjbEntityDescriptor)desc;
96         refreshPeriodInMillis =
97             ed.getIASEjbExtraDescriptors().getRefreshPeriodInSeconds() * 1000;
98
99         if( refreshPeriodInMillis > 0 ) {
100             Timer JavaDoc timer =
101                 ContainerFactoryImpl.getContainerService().getTimer();
102             refreshTask = new RefreshTask();
103             timer.scheduleAtFixedRate(refreshTask, refreshPeriodInMillis,
104                                       refreshPeriodInMillis);
105         } else {
106             refreshPeriodInMillis = 0;
107         }
108
109         // Create read-only bean cache
110
long idleTimeoutInMillis = (cacheProp.cacheIdleTimeoutInSeconds <= 0) ?
111             -1 : (cacheProp.cacheIdleTimeoutInSeconds * 1000);
112
113         if( (cacheProp.maxCacheSize <= 0) && (idleTimeoutInMillis <= 0) ) {
114             robCache = new UnboundedEJBObjectCache(ejbDescriptor.getName());
115             robCache.init(DEFAULT_CACHE_SIZE, cacheProp.numberOfVictimsToSelect,
116                           0L, 1.0F, null);
117         } else {
118             int cacheSize = (cacheProp.maxCacheSize <= 0) ?
119                 DEFAULT_CACHE_SIZE : cacheProp.maxCacheSize;
120             robCache = new FIFOEJBObjectCache(ejbDescriptor.getName());
121         
122             robCache.init(cacheSize,
123                           cacheProp.numberOfVictimsToSelect,
124                           idleTimeoutInMillis, 1.0F, null);
125             // .setEJBObjectCacheListener(
126
// new EJBObjectCacheVictimHandler());
127
}
128         
129         this.distributedReadOnlyBeanService =
130             DistributedEJBServiceFactory.getDistributedEJBService()
131                 .getDistributedReadOnlyBeanService();
132         this.distributedReadOnlyBeanService.addReadOnlyBeanRefreshEventHandler(
133                 getContainerId(), getClassLoader(), this);
134     }
135     
136     private void updateBeanLevelRefresh() {
137                
138         beanLevelSequenceNum++;
139         beanLevelLastRefreshRequestedAt = System.currentTimeMillis();
140         _logger.log(Level.FINE, "updating bean-level refresh for " +
141                     " read-only bean " + ejbDescriptor.getName() +
142                     " at " + new Date(beanLevelLastRefreshRequestedAt) +
143                     " beanLevelSequenceNum = " + beanLevelSequenceNum);
144     }
145
146     protected void callEJBStore(EntityBean ejb, EntityContextImpl context) {
147         // this method in the ReadOnlyBean case should be a no-op
148
// and should not throw any exception.
149
}
150     
151     protected void callEJBLoad(EntityBean ejb, EntityContextImpl entityCtx)
152         throws Exception JavaDoc
153     {
154         ReadOnlyContextImpl context = (ReadOnlyContextImpl) entityCtx;
155                
156         ReadOnlyBeanInfo robInfo = context.getReadOnlyBeanInfo();
157
158         // Grab the pk-specific lock before doing the refresh comparisons.
159
// In the common-case, the lock will only be held for a very short
160
// amount of time. In the case where a pk-level refresh is needed,
161
// we want to ensure that no concurrent refreshes for the same
162
// pk can occur.
163

164         int pkLevelSequenceNum = 0;
165
166         synchronized(robInfo) {
167             
168             int currentBeanLevelSequenceNum = beanLevelSequenceNum;
169
170             if( robInfo.beanLevelSequenceNum != currentBeanLevelSequenceNum) {
171
172                 if( _logger.isLoggable(Level.FINE) ) {
173                     _logger.log(Level.FINE, "REFRESH DUE TO BEAN-LEVEL UPDATE:"
174                                 + " Bean-level sequence num = " +
175                                 beanLevelSequenceNum +
176                                 robInfo + " current time is " + new Date());
177                 }
178                 
179                 robInfo.refreshNeeded = true;
180             }
181             
182             // Refresh could be true EITHER because time-based refresh
183
// occurred or programmatic refresh of this PK.
184
if (robInfo.refreshNeeded) {
185                 
186                 if( _logger.isLoggable(Level.FINE) ) {
187                     _logger.log(Level.FINE, " PK-LEVEL REFRESH : "
188                                 + robInfo + " current time is " + new Date());
189                 }
190                 
191                 try {
192
193                     if( isContainerManagedPers ) {
194                         BeanStateSynchronization beanStateSynch =
195                             (BeanStateSynchronization) ejb;
196
197                         beanStateSynch.ejb__refresh(entityCtx.getPrimaryKey());
198
199                         if( _logger.isLoggable(Level.FINE) ) {
200                             _logger.log(Level.FINE, " PK-LEVEL REFRESH DONE :"
201                                 + robInfo + " current time is " + new Date());
202                         }
203
204                     } else {
205
206                         if( ejb instanceof BeanStateSynchronization ) {
207                             // For debugging purposes, call into ejb__refresh
208
// if it's present on a BMP bean class
209
BeanStateSynchronization beanStateSynch =
210                                 (BeanStateSynchronization) ejb;
211                             
212                             beanStateSynch.ejb__refresh
213                                 (entityCtx.getPrimaryKey());
214                         }
215
216                     }
217                     
218                 } finally {
219                     // Always set refreshNeeded to false
220
robInfo.refreshNeeded = false;
221                 }
222
223                 // Rob info only updated if no errors so far.
224

225                 updateAfterRefresh(robInfo);
226             }
227                         
228             pkLevelSequenceNum = robInfo.pkLevelSequenceNum;
229             
230         } // releases lock for pk's read-only bean info
231

232         // @@@ NOTE : optimize this. calling txMgr.getStatus() accesses a
233
// thread-local, which can be expensive. there should be an
234
// easy way to skip this check if we know there isn't a tx
235
// (e.g. if the bean is bmp, or because the caller knows it
236
// already)
237
if (transactionManager.getStatus() != Status.STATUS_NO_TRANSACTION)
238         {
239             // Always call ejbLoad if this call is being made within a tx
240
// context. In this case, the context will have been taken
241
// from the entity pool. Also, callEJBLoad will only be called
242
// this one time for this tx, so the instance will see
243
// repeatable reads, even if the pk-level state is refreshed
244
// before the tx commits.
245
callLoad(ejb, context, pkLevelSequenceNum,
246                      System.currentTimeMillis());
247            
248         } else if( context.getPKLevelSequenceNum() != pkLevelSequenceNum ) {
249
250             // Now do instance-level refresh check to see if
251
// ejbLoad is warranted.
252
callLoad(ejb, context, pkLevelSequenceNum,
253                      System.currentTimeMillis());
254             return;
255         }
256     }
257         
258     private void callLoad(EntityBean ejb, EntityContextImpl entityCtx,
259                           int pkLevelSequenceNum,
260                           long currentTime) throws Exception JavaDoc {
261
262         ReadOnlyContextImpl context = (ReadOnlyContextImpl) entityCtx;
263         
264         if( _logger.isLoggable(Level.FINE) ) {
265             _logger.log(Level.FINE,
266                         "Calling ejbLoad for read-only bean " +
267                         ejbDescriptor.getName() + " primary key " +
268                         entityCtx.getPrimaryKey() + " at " +
269                         new Date(currentTime));
270         }
271
272         try {
273             context.setInEjbLoad(true);
274             ejb.ejbLoad();
275
276             if( pkLevelSequenceNum > 0 ) {
277                 // Synch up pk-level sequence num after successful load
278
context.setPKLevelSequenceNum(pkLevelSequenceNum);
279             }
280
281             // Set last refresh time after successful load
282
context.setLastRefreshedAt(currentTime);
283         } finally {
284             context.setInEjbLoad(false);
285         }
286
287     }
288     
289     protected void callEJBRemove(EntityBean ejb, EntityContextImpl context)
290         throws Exception JavaDoc
291     {
292
293         // This will only be called for BMP read-only beans since AS 7
294
// allowed the client to make this call. Calls to remove
295
// CMP read-only beans result in a runtime exception.
296

297         Object JavaDoc pk = context.getPrimaryKey();
298         robCache.removeAll(pk);
299         
300     }
301     
302     public void undeploy() {
303         this.distributedReadOnlyBeanService.removeReadOnlyBeanRefreshEventHandler(
304                 getContainerId());
305         super.undeploy();
306         
307         if( refreshTask != null ) {
308             refreshTask.cancel();
309         }
310
311         robCache.clear();
312     }
313     
314     // Called from BaseContainer just before invoking a business method
315
// whose tx attribute is TX_NEVER / TX_NOT_SUPPORTED / TX_SUPPORTS
316
// without a client tx.
317
void preInvokeNoTx(Invocation inv) {
318         EntityContextImpl context = (EntityContextImpl)inv.context;
319         
320         if ( context.getState() == DESTROYED )
321             return;
322         
323         if ( !inv.invocationInfo.isCreateHomeFinder ) {
324             // follow EJB2.0 section 12.1.6.1
325
EntityBean e = (EntityBean)context.getEJB();
326             try {
327                 callEJBLoad(e, context);
328             } catch ( NoSuchEntityException ex ) {
329                 _logger.log(Level.FINE, "Exception in preInvokeNoTx()", ex);
330                 // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
331
forceDestroyBean(context);
332                 
333                 throw new NoSuchObjectLocalException(
334                     "NoSuchEntityException thrown by ejbLoad, " +
335                     "EJB instance discarded");
336             } catch ( Exception JavaDoc ex ) {
337                 // Error during ejbLoad, so discard bean: EJB2.0 18.3.3
338
forceDestroyBean(context);
339                 
340                 throw new EJBException(ex);
341             }
342             context.setNewlyActivated(false);
343         }
344     }
345     
346     protected void afterNewlyActivated(EntityContextImpl context) {
347         // In the case of ReadOnlyBean store the Context into the list
348
ReadOnlyBeanInfo robInfo = addToCache(context.getPrimaryKey(), true);
349
350         // Set the read-only bean info on the context so we can access it
351
// without doing a cache lookup.
352
ReadOnlyContextImpl readOnlyContext = (ReadOnlyContextImpl) context;
353         readOnlyContext.setReadOnlyBeanInfo(robInfo);
354     }
355     
356     protected void addPooledEJB(EntityContextImpl ctx) {
357         try {
358             ReadOnlyContextImpl readOnlyCtx = (ReadOnlyContextImpl)ctx;
359             if( readOnlyCtx.getReadOnlyBeanInfo() != null ) {
360
361                 readOnlyCtx.setReadOnlyBeanInfo(null);
362                 
363                 robCache.remove(ctx.getPrimaryKey(), true);
364             }
365         } catch (Exception JavaDoc ex) {
366
367             _logger.log(Level.SEVERE, "ejb.addPooledEJB", ex);
368             EJBException ejbEx = new EJBException();
369             ejbEx.initCause(ex);
370             throw ejbEx;
371
372         } finally {
373             super.addPooledEJB(ctx);
374         }
375     }
376     
377     protected void forceDestroyBean(EJBContextImpl context) {
378         
379         try {
380             ReadOnlyContextImpl readOnlyCtx = (ReadOnlyContextImpl) context;
381             if( readOnlyCtx.getReadOnlyBeanInfo() != null ) {
382
383                 readOnlyCtx.setReadOnlyBeanInfo(null);
384
385                 robCache.remove(readOnlyCtx.getPrimaryKey(), true);
386             }
387             
388         } catch (Exception JavaDoc ex) {
389
390             _logger.log(Level.SEVERE, "ejb.forceDestroyBean", ex);
391             EJBException ejbEx = new EJBException();
392             ejbEx.initCause(ex);
393             throw ejbEx;
394     
395         } finally {
396             super.forceDestroyBean(context);
397         }
398     }
399
400     public void preInvoke(Invocation inv) {
401
402         // Overriding preInvoke is the best way to interpose on the
403
// create early enough to throw an exception or eat the
404
// request before too much setup work is done by the container.
405
// It's better to keep this logic in the Read-Only Bean container
406
// than to put it in the InvocationHandlers. Note that
407
// interposition for the remove operation is handled below
408
// by overriding the removeBean method.
409
if( (inv.invocationInfo != null) &&
410             inv.invocationInfo.startsWithCreate ) {
411
412             String JavaDoc msg = "Error for ejb " + ejbDescriptor.getName() +
413                 ". create is not allowed for read-only entity beans";
414
415             if( isContainerManagedPers ) {
416                 // EJB team decided that throwing a runtime exception was more
417
// appropriate in this case since creation is not a
418
// supported operation for read-only beans. If the application
419
// is coded this way, it's best to throw a system exception
420
// to signal that the application is broken. NOTE that this
421
// only applies to the CMP 1.x and 2.x read-only bean
422
// functionality added starting with AS 8.1.
423

424                 throw new EJBException(msg);
425                                        
426             } else {
427                 // Preserve AS 7 BMP ROB create behavior
428
CreateException ce = new CreateException(msg);
429                 throw new PreInvokeException(ce);
430             }
431
432         } else {
433             super.preInvoke(inv);
434         }
435     }
436
437     protected void removeBean(EJBLocalRemoteObject ejbo, Method JavaDoc removeMethod,
438                               boolean local)
439         throws RemoveException, EJBException, RemoteException JavaDoc
440     {
441
442         String JavaDoc msg = "Error for ejb " + ejbDescriptor.getName() +
443             ". remove is not allowed for read-only entity beans";
444
445         if( isContainerManagedPers ) {
446             
447             // EJB team decided that throwing a runtime exception was more
448
// appropriate in this case since removal is not a
449
// supported operation for read-only beans. If the application
450
// is coded this way, it's best to throw a system exception
451
// to signal that the application is broken. NOTE that this
452
// only applies to the CMP 1.x and 2.x read-only bean
453
// functionality added starting with AS 8.1.
454

455             // There's no post-invoke logic to convert local exceptions
456
// to remote, so take care of that here.
457
if (local) {
458                 throw new EJBException(msg);
459             } else {
460                 throw new RemoteException JavaDoc(msg);
461             }
462
463         } else {
464             // Preserve AS 7 BMP ROB removal behavior.
465
// Calls to ejbRemove on BMP read-only beans in AS 7
466
// were silently "eaten" by the ejb container. The
467
// client didn't receive any exception, but ejbRemove
468
// was not called on the container.
469
}
470     }
471     
472     protected void initializeHome()
473         throws Exception JavaDoc
474     {
475         super.initializeHome();
476
477         if (isRemote) {
478             ((ReadOnlyEJBHomeImpl) this.ejbHomeImpl).
479                 setReadOnlyBeanContainer(this);
480         }
481         
482         if (isLocal) {
483             ReadOnlyEJBLocalHomeImpl readOnlyLocalHomeImpl =
484                 (ReadOnlyEJBLocalHomeImpl) ejbLocalHomeImpl;
485             readOnlyLocalHomeImpl.setReadOnlyBeanContainer(this);
486         }
487     }
488     
489     public void setRefreshFlag(Object JavaDoc primaryKey) {
490         
491         try {
492             handleRefreshRequest(primaryKey);
493         } finally {
494             distributedReadOnlyBeanService.notifyRefresh(
495                     getContainerId(), primaryKey);
496         }
497     }
498         
499     public void handleRefreshRequest(Object JavaDoc primaryKey) {
500         // Lookup the read-only bean info for this pk.
501
// If there is no entry for this pk, do nothing.
502
// If there is a cache hit we *don't* want to increment the
503
// ref count.
504
ReadOnlyBeanInfo robInfo = (ReadOnlyBeanInfo)
505             robCache.get(primaryKey, false);
506         if( robInfo != null ) {
507            
508             synchronized(robInfo) {
509                 
510                 robInfo.refreshNeeded = true;
511                 robInfo.lastRefreshRequestedAt = System.currentTimeMillis();
512                 
513                 if( _logger.isLoggable(Level.FINE) ) {
514                     _logger.log(Level.FINE,
515                         "Updating refresh time for read-only bean " +
516                         ejbDescriptor.getName() + " primary key " + primaryKey
517                         + " at " + new Date(robInfo.lastRefreshRequestedAt) +
518                         " pkLevelSequenceNum = " + robInfo.pkLevelSequenceNum);
519                 }
520             }
521         } else {
522             _logger.log(Level.FINE,
523                         "Refresh event for unknown read-only bean PK = " +
524                         primaryKey + " at " + new Date());
525         }
526     }
527     
528     /**
529      * invoked when application calls refreshAll()
530      */

531     void refreshAll() {
532         try {
533             handleRefreshAllRequest();
534         } finally {
535             distributedReadOnlyBeanService.notifyRefreshAll(getContainerId());
536         }
537     }
538     
539     public void handleRefreshAllRequest() {
540         updateBeanLevelRefresh();
541     }
542
543     protected EntityContextImpl createEntityContextInstance(EntityBean ejb,
544         EntityContainer entityContainer)
545     {
546         return new ReadOnlyContextImpl(ejb, entityContainer);
547     }
548
549     private ReadOnlyBeanInfo addToCache(Object JavaDoc primaryKey, boolean incrementRefCount) {
550         
551         // Optimize for the cache where the cache item already
552
// exists and we have a 2nd, 3rd, 4th, etc. context for
553
// the same primary key. If the item exists, the ref count
554
// will be incremented.
555
ReadOnlyBeanInfo robInfo = (ReadOnlyBeanInfo)
556             robCache.get(primaryKey, incrementRefCount);
557
558         if( robInfo == null ) {
559
560             // If the item doesn't exist, create a new one. The cache
561
// ensures that the ref count is correct in the face of concurrent
562
// puts.
563

564             ReadOnlyBeanInfo newRobInfo = new ReadOnlyBeanInfo();
565
566             newRobInfo.primaryKey = primaryKey;
567
568             // Initialize bean level sequence num so that the first time an
569
// instance of this PK goes through callEJBLoad, it will force
570
// a refresh.
571
newRobInfo.beanLevelSequenceNum = -1;
572             newRobInfo.refreshNeeded = true;
573
574             newRobInfo.pkLevelSequenceNum = 1;
575
576             newRobInfo.lastRefreshRequestedAt = 0;
577             newRobInfo.lastRefreshedAt = 0;
578
579             // Cache ejbObject/ejbLocalObject within ROB info.
580
// This value is used by
581
// findByPrimaryKey to avoid a DB access. Caching here
582
// ensures that there will be one DB access for the PK
583
// regardless of the order in which findByPrimaryKey is called
584
// with respect to the business method call. This also covers
585
// the case where a business method is invoked through the
586
// local view and findByPrimaryKey is invoked through the
587
// Remote view (or vice versa).
588
if( ejbDescriptor.isLocalInterfacesSupported() ) {
589                 newRobInfo.cachedEjbLocalObject =
590                     getEJBLocalObjectForPrimaryKey(primaryKey);
591             }
592             if( ejbDescriptor.isRemoteInterfacesSupported() ) {
593                 newRobInfo.cachedEjbObject =
594                     getEJBObjectStub(primaryKey, null);
595             }
596             
597             ReadOnlyBeanInfo otherRobInfo = (ReadOnlyBeanInfo)
598                 robCache.put(primaryKey, newRobInfo, incrementRefCount);
599
600             // If someone else inserted robInfo for this pk before *our* put(),
601
// use that as the pk's robInfo. Otherwise, the new robInfo we
602
// created is the "truth" for this pk.
603
robInfo = (otherRobInfo == null) ? newRobInfo : otherRobInfo;
604         }
605
606         return robInfo;
607     }
608     
609     //Called from InvocationHandler for findByPrimaryKey
610
//The super class (EntityContainer) also defines this method whcih is where
611
// the real work (of finding it from the database) is done.
612
protected Object JavaDoc invokeFindByPrimaryKey(Method JavaDoc method, Invocation inv,
613         Object JavaDoc[] args)
614     throws Throwable JavaDoc
615     {
616     Object JavaDoc returnValue = null;
617     ReadOnlyBeanInfo robInfo = addToCache(args[0], false);
618     synchronized (robInfo) {
619         returnValue = inv.isLocal
620         ? robInfo.cachedEjbLocalObject : robInfo.cachedEjbObject;
621
622         if ( robInfo.refreshNeeded ) {
623         _logger.log(Level.FINE, "ReadOnlyBeanContainer calling ejb.ejbFindByPK... for pk=" + args[0]);
624         returnValue = super.invokeFindByPrimaryKey(method, inv, args);
625         robInfo.refreshNeeded = false;
626
627         //set the seq numbers so that the subsequent business method calls
628
// (if within expiration time) do not have to call ejb__refresh!!
629
updateAfterRefresh(robInfo);
630
631         }
632     }
633
634     return returnValue;
635     }
636
637     public Object JavaDoc postFind(Invocation inv, Object JavaDoc primaryKeys,
638                            Object JavaDoc[] findParams)
639         throws FinderException
640     {
641
642         // Always call parent to convert pks to ejbobjects/ejblocalobjects.
643
Object JavaDoc returnValue = super.postFind(inv, primaryKeys, findParams);
644
645         // Only proceed if this is not a findByPK method. FindByPK
646
// processing is special since it's possible to actually
647
// skip the db access for the query itself. The caching requirements
648
// to actually skip nonFindByPK queries are extremely complex, but
649
// the next best thing to skipping the query is to populate the
650
// RobInfo cache with an entry for each pk in the result set. If
651
// a PK is part of the result set for a nonFindByPK query before
652
// it is accessed through some other means, no new refresh will be
653
// required. This will have the largest benefits for large result
654
// sets since it's possible for a query to return N beans from one
655
// db access, which would otherwise require N db accesses if the
656
// refresh were done upon business method invocation or findByPK.
657
// If a PK has been accessed before appearing in the result set of
658
// a nonFindByPK finder, there is no performance gain.
659
if( !inv.method.getName().equals("findByPrimaryKey") ) {
660             if ( primaryKeys instanceof Enumeration ) {
661                  Enumeration e = (Enumeration) primaryKeys;
662                  while ( e.hasMoreElements() ) {
663                      Object JavaDoc primaryKey = e.nextElement();
664                      if( primaryKey != null ) {
665                          updateRobInfoAfterFinder(primaryKey);
666                      }
667                  }
668             } else if ( primaryKeys instanceof Collection ) {
669                 Collection c = (Collection)primaryKeys;
670                 Iterator it = c.iterator();
671                 while ( it.hasNext() ) {
672                     Object JavaDoc primaryKey = it.next();
673                     if( primaryKey != null ) {
674                         updateRobInfoAfterFinder(primaryKey);
675                     }
676                 }
677             } else {
678                 if( primaryKeys != null ) {
679                     updateRobInfoAfterFinder(primaryKeys);
680                 }
681             }
682         }
683
684         return returnValue;
685     }
686
687     private void updateRobInfoAfterFinder(Object JavaDoc primaryKey) {
688         ReadOnlyBeanInfo robInfo = addToCache(primaryKey, false);
689         synchronized (robInfo) {
690             if( robInfo.refreshNeeded ) {
691                 robInfo.refreshNeeded = false;
692                 updateAfterRefresh(robInfo);
693             }
694         }
695     }
696
697     //Called after a sucessful ejb_refresh and
698
//it is assumed that the caller has a lock on the robInfo
699
private void updateAfterRefresh(ReadOnlyBeanInfo robInfo) {
700     robInfo.beanLevelSequenceNum = beanLevelSequenceNum;
701     robInfo.pkLevelSequenceNum++;
702     robInfo.lastRefreshedAt = System.currentTimeMillis();
703     }
704
705     private class RefreshTask extends TimerTask JavaDoc {
706
707         public void run() {
708             updateBeanLevelRefresh();
709         }
710     }
711     
712 }
713
Popular Tags