KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > interceptors > CacheLoaderInterceptor


1 package org.jboss.cache.interceptors;
2
3 import org.jboss.cache.CacheException;
4 import org.jboss.cache.CacheSPI;
5 import org.jboss.cache.Fqn;
6 import org.jboss.cache.GlobalTransaction;
7 import org.jboss.cache.InvocationContext;
8 import org.jboss.cache.NodeSPI;
9 import org.jboss.cache.TransactionEntry;
10 import org.jboss.cache.TransactionTable;
11 import org.jboss.cache.loader.AsyncCacheLoader;
12 import org.jboss.cache.loader.CacheLoader;
13 import org.jboss.cache.loader.ChainingCacheLoader;
14 import org.jboss.cache.lock.NodeLock;
15 import org.jboss.cache.marshall.MethodCall;
16 import org.jboss.cache.marshall.MethodCallFactory;
17 import org.jboss.cache.marshall.MethodDeclarations;
18
19 import java.util.Collections JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Iterator JavaDoc;
22 import java.util.List JavaDoc;
23 import java.util.ListIterator JavaDoc;
24 import java.util.Map JavaDoc;
25 import java.util.Set JavaDoc;
26
27 /**
28  * Loads nodes that don't exist at the time of the call into memory from the CacheLoader
29  *
30  * @author Bela Ban
31  * @version $Id: CacheLoaderInterceptor.java,v 1.72 2007/01/10 03:55:41 msurtani Exp $
32  */

33 public class CacheLoaderInterceptor extends BaseCacheLoaderInterceptor implements CacheLoaderInterceptorMBean
34 {
35    private boolean isCustomCacheLoader;
36    private long m_cacheLoads = 0;
37    private long m_cacheMisses = 0;
38    private TransactionTable txTable = null;
39    protected boolean isActivation = false;
40
41    /**
42     * True if CacheStoreInterceptor is in place.
43     * This allows us to skip loading keys for remove(Fqn, key) and put(Fqn, key).
44     * It also affects removal of node data and listing children.
45     */

46    protected boolean useCacheStore = true;
47
48    public void setCache(CacheSPI cache)
49    {
50       super.setCache(cache);
51       isCustomCacheLoader = isCustomCacheLoaderConfigured(loader);
52       txTable = cache.getTransactionTable();
53    }
54
55    private boolean isCustomCacheLoaderConfigured(CacheLoader cl)
56    {
57       if (cl instanceof ChainingCacheLoader)
58       {
59          // test all loaders in the chain.
60
ChainingCacheLoader ccl = (ChainingCacheLoader) cl;
61          Iterator JavaDoc it = ccl.getCacheLoaders().iterator();
62          boolean isCustom = false;
63          while (it.hasNext())
64          {
65             CacheLoader nextCacheLoader = (CacheLoader) it.next();
66             isCustom = isCustom || isCustomCacheLoaderConfigured(nextCacheLoader);
67          }
68          return isCustom;
69       }
70       else if (cl instanceof AsyncCacheLoader)
71       {
72          // test the underlying cache loader
73
CacheLoader underlying = ((AsyncCacheLoader) cl).getCacheLoader();
74          return isCustomCacheLoaderConfigured(underlying);
75       }
76       else
77       {
78          // tests for org.jboss.cache.loader.*
79
Package JavaDoc pkg = cl.getClass().getPackage();// may be null if this is an inner class? In which case it is certainly a custom cache loader instance.
80
return pkg == null || !pkg.getName().startsWith("org.jboss.cache");
81       }
82    }
83
84    /**
85     * Makes sure a node is loaded into memory before a call executes (no-op if node is already loaded). If attributes
86     * of a node are to be accessed by the method, the attributes are also loaded.
87     *
88     * @return
89     * @throws Throwable
90     */

91    public Object JavaDoc invoke(MethodCall m) throws Throwable JavaDoc
92    {
93       Fqn fqn = null, fqn2 = null;// if set, load the data. fqn2 for 2nd fqn in move().
94

95       Object JavaDoc[] args = m.getArgs();
96       boolean acquireLock = false;// do we need to acquire a lock if we load this node from cloader?
97

98       boolean initNode = false;// keep uninitialized
99
Object JavaDoc key = null;
100       InvocationContext ctx = cache.getInvocationContext();
101       TransactionEntry entry = null;
102       GlobalTransaction gtx;
103       boolean recursive = false;// do we also load children?
104

105
106       if ((gtx = ctx.getGlobalTransaction()) != null)
107       {
108          entry = txTable.get(gtx);
109       }
110
111       if (log.isTraceEnabled())
112       {
113          log.trace("invoke " + m);
114       }
115       switch (m.getMethodId())
116       {
117          case MethodDeclarations.putDataEraseMethodLocal_id:
118          case MethodDeclarations.putDataMethodLocal_id:
119             fqn = (Fqn) args[1];
120             initNode = true;
121             break;
122          case MethodDeclarations.putKeyValMethodLocal_id:
123             fqn = (Fqn) args[1];
124             if (useCacheStore)
125             {
126                initNode = true;
127             }
128             else
129             {
130                acquireLock = true;
131             }
132             break;
133          case MethodDeclarations.moveMethodLocal_id:
134             fqn = (Fqn) args[0];
135             fqn2 = (Fqn) args[1];
136             acquireLock = true;
137             //initNode = true;
138
recursive = true;
139             break;
140          case MethodDeclarations.addChildMethodLocal_id:
141             fqn = (Fqn) args[1];
142             break;
143          case MethodDeclarations.getKeyValueMethodLocal_id:
144             fqn = (Fqn) args[0];
145             key = args[1];
146             acquireLock = true;
147             break;
148          case MethodDeclarations.getNodeMethodLocal_id:
149          case MethodDeclarations.getKeysMethodLocal_id:
150          case MethodDeclarations.getChildrenNamesMethodLocal_id:
151          case MethodDeclarations.releaseAllLocksMethodLocal_id:
152          case MethodDeclarations.printMethodLocal_id:
153             fqn = (Fqn) args[0];
154             acquireLock = true;
155             break;
156          case MethodDeclarations.rollbackMethod_id:
157             // clean up nodesCreated map
158
cleanupNodesCreated(entry);
159             break;
160          default:
161             if (!useCacheStore)
162             {
163                if (m.getMethodId() == MethodDeclarations.removeKeyMethodLocal_id)
164                {
165                   fqn = (Fqn) args[1];
166                }
167                else if (m.getMethodId() == MethodDeclarations.removeDataMethodLocal_id)
168                {
169                   fqn = (Fqn) args[1];
170                   initNode = true;
171                }
172             }
173             break;
174       }
175
176       /* On the way in: load elements into cache from the CacheLoader if not yet in the cache. We need to synchronize
177       this so only 1 thread attempts to load a given element */

178
179       if (fqn != null)
180       {
181          if (fqn2 != null)
182          {
183             loadIfNeeded(fqn2, key, initNode, acquireLock, m, entry, false, m.getMethodId() == MethodDeclarations.moveMethodLocal_id);
184          }
185          loadIfNeeded(fqn, key, initNode, acquireLock, m, entry, recursive, m.getMethodId() == MethodDeclarations.moveMethodLocal_id);
186       }
187
188       return super.invoke(m);
189    }
190
191
192    private void loadIfNeeded(Fqn fqn, Object JavaDoc key, boolean initNode, boolean acquireLock, MethodCall m, TransactionEntry entry, boolean recursive, boolean isMove) throws Throwable JavaDoc
193    {
194       obtainLoaderLock(fqn);
195
196       try
197       {
198
199          NodeSPI n = cache.peek(fqn);
200
201          boolean mustLoad = mustLoad(n, key);
202          if (log.isTraceEnabled())
203          {
204             log.trace("load element " + fqn + " mustLoad=" + mustLoad);
205          }
206          if (mustLoad)
207          {
208             if (initNode)
209             {
210                n = createTempNode(fqn, entry);
211             }
212             else if (!wasRemovedInTx(fqn))
213             {
214                n = loadNode(fqn, n, entry);
215             }
216             // Only attempt to acquire this lock if we need to - i.e., if
217
// the lock hasn't already been acquired by the Lock
218
// interceptor. CRUD methods (put, remove) would have acquired
219
// this lock - even if the node is not in memory and needs to be
220
// loaded. Non-CRUD methods (put) would NOT have acquired this
221
// lock so if we are to load the node from cache loader, we need
222
// to acquire a write lock here. as a 'catch-all', DO NOT
223
// attempt to acquire a lock here *anyway*, even for CRUD
224
// methods - this leads to a deadlock when you have threads
225
// simultaneously trying to create a node. See
226
// org.jboss.cache.loader.deadlock.ConcurrentCreationDeadlockTest
227
// - Manik Surtani (21 March 2006)
228
if (acquireLock)
229             {
230                lock(fqn, NodeLock.LockType.WRITE, false);// non-recursive for now
231
}
232          }
233
234          // The complete list of children aren't known without loading them
235
if (recursive || m.getMethodId() == MethodDeclarations.getChildrenNamesMethodLocal_id)
236          {
237             loadChildren(fqn, n, recursive, isMove);
238          }
239
240       }
241       finally
242       {
243          releaseLoaderLock(fqn);
244       }
245
246    }
247
248    /**
249     * Load the children.
250     *
251     * @param node may be null if the node was not found.
252     */

253    private void loadChildren(Fqn fqn, NodeSPI node, boolean recursive, boolean isMove) throws Throwable JavaDoc
254    {
255
256       if (node != null && node.getChildrenLoaded())
257       {
258          return;
259       }
260       Set JavaDoc children_names = loader.getChildrenNames(fqn);
261
262       if (log.isTraceEnabled())
263       {
264          log.trace("load children " + fqn + " children=" + children_names);
265       }
266
267       // For getChildrenNames null means no children
268
if (children_names == null)
269       {
270          if (node != null)
271          {
272             if (useCacheStore)
273             {
274                node.removeChildrenDirect();//getChildrenMapDirect().clear();
275
}
276             node.setChildrenLoaded(true);
277          }
278          return;
279       }
280
281       // Create if node had not been created already
282
if (node == null)
283       {
284          node = createNodes(fqn, null);// dont care about local transactions
285
}
286
287       // Create one DataNode per child, mark as UNINITIALIZED
288
for (Object JavaDoc name : children_names)
289       {
290          Fqn child_fqn = new Fqn(name);// this is a RELATIVE Fqn!!
291

292          // create child if it didn't exist
293
NodeSPI child = node.addChildDirect(child_fqn);
294          if ((isMove || isActivation) && recursive)
295          {
296             // load data for children as well!
297
child.putDirect(loader.get(child.getFqn()));
298             child.setDataLoaded(true);
299          }
300          else
301          {
302             child.setDataLoaded(false);
303          }
304          if (recursive)
305          {
306             loadChildren(child.getFqn(), child, true, isMove);
307          }
308       }
309       lock(fqn, recursive ? NodeLock.LockType.WRITE : NodeLock.LockType.READ, true);// recursive=true: lock entire subtree
310
node.setChildrenLoaded(true);
311    }
312
313    private boolean mustLoad(NodeSPI n, Object JavaDoc key)
314    {
315       if (n == null)
316       {
317          log.trace("mustLoad, node null");
318          return true;
319       }
320       if (!n.getDataLoaded())
321       {
322          log.trace("must Load, uninitialized");
323          return true;
324       }
325       /*
326       if (!n.getKeys().contains(key)) {
327          log.trace("must Load, no key");
328          return true;
329       }
330       */

331       return false;
332    }
333
334    public long getCacheLoaderLoads()
335    {
336       return m_cacheLoads;
337    }
338
339    public long getCacheLoaderMisses()
340    {
341       return m_cacheMisses;
342    }
343
344    public void resetStatistics()
345    {
346       m_cacheLoads = 0;
347       m_cacheMisses = 0;
348    }
349
350    public Map JavaDoc<String JavaDoc, Object JavaDoc> dumpStatistics()
351    {
352       Map JavaDoc<String JavaDoc, Object JavaDoc> retval = new HashMap JavaDoc<String JavaDoc, Object JavaDoc>();
353       retval.put("CacheLoaderLoads", m_cacheLoads);
354       retval.put("CacheLoaderMisses", m_cacheMisses);
355       return retval;
356    }
357
358    protected void lock(Fqn fqn, NodeLock.LockType lock_type, boolean recursive) throws Throwable JavaDoc
359    {
360
361       if (configuration.isNodeLockingOptimistic()) return;
362
363       MethodCall m = MethodCallFactory.create(MethodDeclarations.lockMethodLocal,
364               fqn, lock_type, recursive);
365       super.invoke(m);
366    }
367
368    /**
369     * Retrieves a node from memory; doesn't access the cache loader
370     *
371     * @param fqn
372     */

373    protected NodeSPI getNode(Fqn fqn)
374    {
375       return cache.peek(fqn);
376       // int treeNodeSize = fqn.size();
377
//
378
// // root node
379
// Node n = cache.getRoot();
380
// Node child_node;
381
// Object child_name;
382
// for (int i = 0; i < treeNodeSize && n != null; i++)
383
// {
384
// child_name = fqn.get(i);
385
// cache.getInvocationContext().getOptionOverrides().setBypassInterceptorChain(true);
386
// child_node = n.getChild(new Fqn(child_name));
387
// n = child_node;
388
// }
389
// return n;
390
}
391
392    /**
393     * Returns true if the FQN or parent was removed during the current
394     * transaction.
395     * This is O(N) WRT to the number of modifications so far.
396     */

397    private boolean wasRemovedInTx(Fqn fqn)
398    {
399       GlobalTransaction t = cache.getInvocationContext().getGlobalTransaction();
400       if (t == null)
401       {
402          return false;
403       }
404       TransactionEntry entry = txTable.get(t);
405       Iterator JavaDoc i = entry.getCacheLoaderModifications().iterator();
406       while (i.hasNext())
407       {
408          MethodCall m = (MethodCall) i.next();
409          if (m.getMethodId() == MethodDeclarations.removeNodeMethodLocal_id
410                  && fqn.isChildOrEquals((Fqn) m.getArgs()[1]))
411          {
412             return true;
413          }
414       }
415       return false;
416    }
417
418    /**
419     * Loads a node from disk; if it exists creates parent TreeNodes.
420     * If it doesn't exist on disk but in memory, clears the
421     * uninitialized flag, otherwise returns null.
422     */

423    private NodeSPI loadNode(Fqn fqn, NodeSPI n, TransactionEntry entry) throws Exception JavaDoc
424    {
425       if (log.isTraceEnabled()) log.trace("loadNode " + fqn);
426       Map JavaDoc nodeData = loadData(fqn);
427       if (nodeData != null)
428       {
429          log.trace("Node data is not null, loading");
430          n = createNodes(fqn, entry);
431 // n.clearDataDirect();
432
n.putDirect(nodeData);
433       }
434
435       if (n != null && !n.getDataLoaded())
436       {
437          log.trace("Setting dataLoaded to true");
438          n.setDataLoaded(true);
439       }
440       return n;
441    }
442
443    /**
444     * Creates a new memory node in preparation for storage.
445     */

446    private NodeSPI createTempNode(Fqn fqn, TransactionEntry entry) throws Exception JavaDoc
447    {
448       NodeSPI n = createNodes(fqn, entry);
449       n.setDataLoaded(false);
450       if (log.isTraceEnabled())
451       {
452          log.trace("createTempNode n " + n);
453       }
454       return n;
455    }
456
457
458    private NodeSPI createNodes(Fqn fqn, TransactionEntry entry) throws Exception JavaDoc
459    {
460       Fqn tmp_fqn = Fqn.ROOT;
461
462       int size = fqn.size();
463
464       // root node
465
NodeSPI n = cache.getRoot();
466       for (int i = 0; i < size; i++)
467       {
468          Object JavaDoc child_name = fqn.get(i);
469          tmp_fqn = new Fqn(tmp_fqn, child_name);
470
471          NodeSPI child_node = findChild(n, child_name);
472          boolean last = (i == size - 1);
473
474          if (child_node == null)
475          {
476             if (last)
477             {
478                child_node = n.addChildDirect(new Fqn(child_name));
479                child_node.setDataLoaded(true);
480             }
481             else
482             {
483                child_node = n.addChildDirect(new Fqn(child_name));
484                child_node.setDataLoaded(false);
485             }
486
487             if (entry != null)
488             {
489                entry.loadUninitialisedNode(tmp_fqn);
490             }
491          }
492
493          n = child_node;
494       }
495
496       return n;
497    }
498
499    private NodeSPI findChild(NodeSPI child, Object JavaDoc child_name)
500    {
501       return (NodeSPI) child.getChildrenMapDirect().get(child_name);
502    }
503
504    private void cleanupNodesCreated(TransactionEntry entry)
505    {
506       boolean traceEnabled = log.isTraceEnabled();
507       log.trace("Removing temporarily created nodes from treecache");
508
509       // this needs to be done in reverse order.
510
List JavaDoc list = entry.getDummyNodesCreatedByCacheLoader();
511       if (list != null && list.size() > 0)
512       {
513          ListIterator JavaDoc i = list.listIterator(list.size());
514          while (i.hasPrevious())
515          {
516             Fqn fqn = (Fqn) i.previous();
517             try
518             {
519                cache.evict(fqn, false);
520             }
521             catch (CacheException e)
522             {
523                if (traceEnabled) log.trace("Unable to evict node " + fqn, e);
524             }
525          }
526       }
527    }
528
529
530    private Map JavaDoc loadData(Fqn fqn) throws Exception JavaDoc
531    {
532
533       Map JavaDoc nodeData = loader.get(fqn);
534       boolean nodeExists = (nodeData != null);
535       if (log.isTraceEnabled()) log.trace("nodeExists " + nodeExists);
536
537       if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
538       {
539          if (nodeExists)
540          {
541             m_cacheLoads++;
542          }
543          else
544          {
545             m_cacheMisses++;
546          }
547       }
548
549       // BES Jan-4-2007 Stop doing this; it's annoying and people
550
// should have converted by now
551
// if (!nodeExists && isCustomCacheLoader)
552
// {
553
// warnCustom();
554
// }
555

556       if (nodeExists)
557       {
558          cache.getNotifier().notifyNodeLoaded(fqn, true, Collections.emptyMap(), true);
559          cache.getNotifier().notifyNodeLoaded(fqn, false, nodeData, true);
560
561          if (isActivation)
562          {
563             cache.getNotifier().notifyNodeActivated(fqn, true, true);
564             cache.getNotifier().notifyNodeActivated(fqn, false, true);
565          }
566       }
567
568       return nodeData;
569    }
570
571    private void warnCustom()
572    {
573       log.warn("CacheLoader.get(Fqn) returned a null; assuming the node nodes not exist.");
574       log.warn("The CacheLoader interface has changed since JBossCache 1.3.x");
575       log.warn("Please see http://jira.jboss.com/jira/browse/JBCACHE-118");
576       log.warn("CacheLoader.get() should return an empty Map if the node does exist but doesn't have any attributes.");
577    }
578
579 }
Popular Tags