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.InvocationContext; |
7 |
| import org.jboss.cache.NodeSPI; |
8 |
| import org.jboss.cache.loader.CacheLoader; |
9 |
| import org.jboss.cache.lock.NodeLock; |
10 |
| import org.jboss.cache.marshall.MethodCall; |
11 |
| import org.jboss.cache.marshall.MethodCallFactory; |
12 |
| import org.jboss.cache.marshall.MethodDeclarations; |
13 |
| import org.jboss.cache.transaction.GlobalTransaction; |
14 |
| import org.jboss.cache.transaction.TransactionEntry; |
15 |
| import org.jboss.cache.transaction.TransactionTable; |
16 |
| |
17 |
| import java.util.Collections; |
18 |
| import java.util.HashMap; |
19 |
| import java.util.List; |
20 |
| import java.util.ListIterator; |
21 |
| import java.util.Map; |
22 |
| import java.util.Set; |
23 |
| |
24 |
| |
25 |
| |
26 |
| |
27 |
| |
28 |
| |
29 |
| |
30 |
| public class CacheLoaderInterceptor extends Interceptor implements CacheLoaderInterceptorMBean |
31 |
| { |
32 |
| private long m_cacheLoads = 0; |
33 |
| private long m_cacheMisses = 0; |
34 |
| private TransactionTable txTable = null; |
35 |
| protected boolean isActivation = false; |
36 |
| protected CacheLoader loader; |
37 |
| |
38 |
| |
39 |
| |
40 |
| |
41 |
| |
42 |
| |
43 |
| protected boolean useCacheStore = true; |
44 |
| |
45 |
2072
| public void setCache(CacheSPI cache)
|
46 |
| { |
47 |
2072
| super.setCache(cache);
|
48 |
2072
| txTable = cache.getTransactionTable();
|
49 |
2072
| this.loader = cache.getCacheLoaderManager().getCacheLoader();
|
50 |
| } |
51 |
| |
52 |
| |
53 |
| |
54 |
| |
55 |
| |
56 |
| |
57 |
| |
58 |
| |
59 |
794745
| public Object invoke(InvocationContext ctx) throws Throwable
|
60 |
| { |
61 |
794745
| MethodCall m = ctx.getMethodCall();
|
62 |
794745
| Fqn fqn = null, fqn2 = null;
|
63 |
| |
64 |
794745
| Object[] args = m.getArgs();
|
65 |
794745
| boolean acquireLock = false;
|
66 |
| |
67 |
794745
| boolean initNode = false;
|
68 |
794745
| Object key = null;
|
69 |
794745
| TransactionEntry entry = null;
|
70 |
794745
| GlobalTransaction gtx;
|
71 |
794745
| boolean recursive = false;
|
72 |
| |
73 |
| |
74 |
?
| if ((gtx = ctx.getGlobalTransaction()) != null)
|
75 |
| { |
76 |
83145
| entry = txTable.get(gtx);
|
77 |
| } |
78 |
| |
79 |
794745
| if (log.isTraceEnabled())
|
80 |
| { |
81 |
0
| log.trace("invoke " + m);
|
82 |
| } |
83 |
794745
| switch (m.getMethodId())
|
84 |
| { |
85 |
0
| case MethodDeclarations.putDataEraseMethodLocal_id:
|
86 |
960
| case MethodDeclarations.putDataMethodLocal_id:
|
87 |
960
| fqn = (Fqn) args[1];
|
88 |
960
| initNode = true;
|
89 |
960
| break;
|
90 |
0
| case MethodDeclarations.putForExternalReadMethodLocal_id:
|
91 |
157871
| case MethodDeclarations.putKeyValMethodLocal_id:
|
92 |
157871
| fqn = (Fqn) args[1];
|
93 |
157871
| if (useCacheStore)
|
94 |
| { |
95 |
122242
| initNode = true;
|
96 |
| } |
97 |
| else |
98 |
| { |
99 |
35629
| acquireLock = true;
|
100 |
| } |
101 |
157871
| break;
|
102 |
36
| case MethodDeclarations.moveMethodLocal_id:
|
103 |
36
| fqn = (Fqn) args[0];
|
104 |
36
| fqn2 = (Fqn) args[1];
|
105 |
36
| acquireLock = true;
|
106 |
| |
107 |
36
| recursive = true;
|
108 |
36
| break;
|
109 |
0
| case MethodDeclarations.addChildMethodLocal_id:
|
110 |
0
| fqn = (Fqn) args[1];
|
111 |
0
| break;
|
112 |
277390
| case MethodDeclarations.getKeyValueMethodLocal_id:
|
113 |
277390
| fqn = (Fqn) args[0];
|
114 |
277390
| key = args[1];
|
115 |
277390
| acquireLock = true;
|
116 |
277390
| break;
|
117 |
282464
| case MethodDeclarations.getNodeMethodLocal_id:
|
118 |
297
| case MethodDeclarations.getKeysMethodLocal_id:
|
119 |
338
| case MethodDeclarations.getChildrenNamesMethodLocal_id:
|
120 |
0
| case MethodDeclarations.releaseAllLocksMethodLocal_id:
|
121 |
14
| case MethodDeclarations.printMethodLocal_id:
|
122 |
283113
| fqn = (Fqn) args[0];
|
123 |
283113
| acquireLock = true;
|
124 |
283113
| break;
|
125 |
49
| case MethodDeclarations.rollbackMethod_id:
|
126 |
| |
127 |
49
| cleanupNodesCreated(entry);
|
128 |
49
| break;
|
129 |
75326
| default:
|
130 |
75326
| if (!useCacheStore)
|
131 |
| { |
132 |
2499
| if (m.getMethodId() == MethodDeclarations.removeKeyMethodLocal_id)
|
133 |
| { |
134 |
53
| fqn = (Fqn) args[1];
|
135 |
| } |
136 |
2446
| else if (m.getMethodId() == MethodDeclarations.removeDataMethodLocal_id)
|
137 |
| { |
138 |
39
| fqn = (Fqn) args[1];
|
139 |
39
| initNode = true;
|
140 |
| } |
141 |
| } |
142 |
75326
| break;
|
143 |
| } |
144 |
| |
145 |
| |
146 |
| |
147 |
| |
148 |
794745
| if (fqn != null)
|
149 |
| { |
150 |
719448
| if (fqn2 != null)
|
151 |
| { |
152 |
36
| loadIfNeeded(ctx, fqn2, key, initNode, acquireLock, m, entry, false, m.getMethodId() == MethodDeclarations.moveMethodLocal_id);
|
153 |
| } |
154 |
719448
| loadIfNeeded(ctx, fqn, key, initNode, acquireLock, m, entry, recursive, m.getMethodId() == MethodDeclarations.moveMethodLocal_id);
|
155 |
| } |
156 |
| |
157 |
794745
| return super.invoke(ctx);
|
158 |
| } |
159 |
| |
160 |
| |
161 |
719484
| private void loadIfNeeded(InvocationContext ctx, Fqn fqn, Object key, boolean initNode, boolean acquireLock, MethodCall m, TransactionEntry entry, boolean recursive, boolean isMove) throws Throwable
|
162 |
| { |
163 |
719484
| NodeSPI n = cache.peek(fqn, true);
|
164 |
| |
165 |
719484
| boolean mustLoad = mustLoad(n, key);
|
166 |
719484
| if (log.isTraceEnabled())
|
167 |
| { |
168 |
0
| log.trace("load element " + fqn + " mustLoad=" + mustLoad);
|
169 |
| } |
170 |
719484
| if (mustLoad)
|
171 |
| { |
172 |
126343
| if (initNode)
|
173 |
| { |
174 |
122997
| n = createTempNode(fqn, entry);
|
175 |
| } |
176 |
| |
177 |
| |
178 |
| |
179 |
| |
180 |
| |
181 |
| |
182 |
| |
183 |
| |
184 |
| |
185 |
| |
186 |
| |
187 |
| |
188 |
| |
189 |
126343
| if (acquireLock)
|
190 |
| { |
191 |
3331
| lock(fqn, NodeLock.LockType.WRITE, false);
|
192 |
| } |
193 |
| |
194 |
126343
| if (!initNode && !wasRemovedInTx(fqn, ctx.getGlobalTransaction()))
|
195 |
| { |
196 |
3346
| n = loadNode(ctx, fqn, n, entry);
|
197 |
| } |
198 |
| } |
199 |
| |
200 |
| |
201 |
719484
| if (recursive || m.getMethodId() == MethodDeclarations.getChildrenNamesMethodLocal_id)
|
202 |
| { |
203 |
360
| loadChildren(fqn, n, recursive, isMove);
|
204 |
| } |
205 |
| } |
206 |
| |
207 |
| |
208 |
| |
209 |
| |
210 |
| |
211 |
| |
212 |
386
| private void loadChildren(Fqn fqn, NodeSPI node, boolean recursive, boolean isMove) throws Throwable
|
213 |
| { |
214 |
| |
215 |
386
| if (node != null && node.getChildrenLoaded())
|
216 |
| { |
217 |
34
| return;
|
218 |
| } |
219 |
352
| Set children_names = loader.getChildrenNames(fqn);
|
220 |
| |
221 |
352
| if (log.isTraceEnabled())
|
222 |
| { |
223 |
0
| log.trace("load children " + fqn + " children=" + children_names);
|
224 |
| } |
225 |
| |
226 |
| |
227 |
352
| if (children_names == null)
|
228 |
| { |
229 |
116
| if (node != null)
|
230 |
| { |
231 |
102
| if (useCacheStore)
|
232 |
| { |
233 |
34
| node.removeChildrenDirect();
|
234 |
| } |
235 |
102
| node.setChildrenLoaded(true);
|
236 |
| } |
237 |
116
| return;
|
238 |
| } |
239 |
| |
240 |
| |
241 |
236
| if (node == null)
|
242 |
| { |
243 |
0
| node = createNodes(fqn, null);
|
244 |
| } |
245 |
| |
246 |
| |
247 |
236
| for (Object name : children_names)
|
248 |
| { |
249 |
598
| Fqn child_fqn = new Fqn(name);
|
250 |
| |
251 |
| |
252 |
598
| NodeSPI child = node.addChildDirect(child_fqn);
|
253 |
598
| if ((isMove || isActivation) && recursive)
|
254 |
| { |
255 |
| |
256 |
26
| child.putAllDirect(loader.get(child.getFqn()));
|
257 |
26
| child.setDataLoaded(true);
|
258 |
| } |
259 |
| else |
260 |
| { |
261 |
572
| child.setDataLoaded(false);
|
262 |
| } |
263 |
598
| if (recursive)
|
264 |
| { |
265 |
26
| loadChildren(child.getFqn(), child, true, isMove);
|
266 |
| } |
267 |
| } |
268 |
236
| lock(fqn, recursive ? NodeLock.LockType.WRITE : NodeLock.LockType.READ, true);
|
269 |
236
| node.setChildrenLoaded(true);
|
270 |
| } |
271 |
| |
272 |
719484
| private boolean mustLoad(NodeSPI n, Object key)
|
273 |
| { |
274 |
719484
| if (n == null)
|
275 |
| { |
276 |
2119
| log.trace("mustLoad, node null");
|
277 |
2119
| return true;
|
278 |
| } |
279 |
717365
| if (!n.getDataLoaded())
|
280 |
| { |
281 |
124224
| log.trace("must Load, uninitialized");
|
282 |
124224
| return true;
|
283 |
| } |
284 |
| |
285 |
| |
286 |
| |
287 |
| |
288 |
| |
289 |
| |
290 |
593141
| return false;
|
291 |
| } |
292 |
| |
293 |
22
| public long getCacheLoaderLoads()
|
294 |
| { |
295 |
22
| return m_cacheLoads;
|
296 |
| } |
297 |
| |
298 |
23
| public long getCacheLoaderMisses()
|
299 |
| { |
300 |
23
| return m_cacheMisses;
|
301 |
| } |
302 |
| |
303 |
2
| public void resetStatistics()
|
304 |
| { |
305 |
2
| m_cacheLoads = 0;
|
306 |
2
| m_cacheMisses = 0;
|
307 |
| } |
308 |
| |
309 |
0
| public Map<String, Object> dumpStatistics()
|
310 |
| { |
311 |
0
| Map<String, Object> retval = new HashMap<String, Object>();
|
312 |
0
| retval.put("CacheLoaderLoads", m_cacheLoads);
|
313 |
0
| retval.put("CacheLoaderMisses", m_cacheMisses);
|
314 |
0
| return retval;
|
315 |
| } |
316 |
| |
317 |
3567
| protected void lock(Fqn fqn, NodeLock.LockType lock_type, boolean recursive) throws Throwable
|
318 |
| { |
319 |
| |
320 |
112
| if (configuration.isNodeLockingOptimistic()) return;
|
321 |
| |
322 |
3455
| MethodCall m = MethodCallFactory.create(MethodDeclarations.lockMethodLocal,
|
323 |
| fqn, lock_type, recursive); |
324 |
| |
325 |
| |
326 |
3455
| cache.getInterceptorChain().get(0).invoke(InvocationContext.fromMethodCall(m));
|
327 |
| |
328 |
| } |
329 |
| |
330 |
| |
331 |
| |
332 |
| |
333 |
| |
334 |
| |
335 |
1559
| protected NodeSPI getNode(Fqn fqn)
|
336 |
| { |
337 |
1559
| return cache.peek(fqn, true);
|
338 |
| |
339 |
| |
340 |
| |
341 |
| |
342 |
| |
343 |
| |
344 |
| |
345 |
| |
346 |
| |
347 |
| |
348 |
| |
349 |
| |
350 |
| |
351 |
| |
352 |
| } |
353 |
| |
354 |
| |
355 |
| |
356 |
| |
357 |
| |
358 |
| |
359 |
3346
| private boolean wasRemovedInTx(Fqn fqn, GlobalTransaction t)
|
360 |
| { |
361 |
3346
| if (t == null)
|
362 |
| { |
363 |
2847
| return false;
|
364 |
| } |
365 |
499
| TransactionEntry entry = txTable.get(t);
|
366 |
499
| for (MethodCall m : entry.getCacheLoaderModifications())
|
367 |
| { |
368 |
3226
| if (m.getMethodId() == MethodDeclarations.removeNodeMethodLocal_id
|
369 |
| && fqn.isChildOrEquals((Fqn) m.getArgs()[1])) |
370 |
| { |
371 |
0
| return true;
|
372 |
| } |
373 |
| } |
374 |
499
| return false;
|
375 |
| } |
376 |
| |
377 |
| |
378 |
| |
379 |
| |
380 |
| |
381 |
| |
382 |
3346
| private NodeSPI loadNode(InvocationContext ctx, Fqn fqn, NodeSPI n, TransactionEntry entry) throws Exception
|
383 |
| { |
384 |
0
| if (log.isTraceEnabled()) log.trace("loadNode " + fqn);
|
385 |
3346
| Map nodeData = loadData(fqn);
|
386 |
3346
| if (nodeData != null)
|
387 |
| { |
388 |
2224
| log.trace("Node data is not null, loading");
|
389 |
| |
390 |
2224
| cache.getNotifier().notifyNodeLoaded(fqn, true, Collections.emptyMap(), ctx);
|
391 |
2224
| if (isActivation)
|
392 |
| { |
393 |
1473
| cache.getNotifier().notifyNodeActivated(fqn, true, Collections.emptyMap(), ctx);
|
394 |
| } |
395 |
| |
396 |
2224
| n = createNodes(fqn, entry);
|
397 |
| |
398 |
2224
| n.putAllDirect(nodeData);
|
399 |
| |
400 |
2224
| cache.getNotifier().notifyNodeLoaded(fqn, false, nodeData, ctx);
|
401 |
2224
| if (isActivation)
|
402 |
| { |
403 |
1473
| cache.getNotifier().notifyNodeActivated(fqn, false, nodeData, ctx);
|
404 |
| } |
405 |
| } |
406 |
| |
407 |
3346
| if (n != null && !n.getDataLoaded())
|
408 |
| { |
409 |
1272
| log.trace("Setting dataLoaded to true");
|
410 |
1272
| n.setDataLoaded(true);
|
411 |
| } |
412 |
3346
| return n;
|
413 |
| } |
414 |
| |
415 |
| |
416 |
| |
417 |
| |
418 |
122997
| private NodeSPI createTempNode(Fqn fqn, TransactionEntry entry) throws Exception
|
419 |
| { |
420 |
122997
| NodeSPI n = createNodes(fqn, entry);
|
421 |
122997
| n.setDataLoaded(false);
|
422 |
122997
| if (log.isTraceEnabled())
|
423 |
| { |
424 |
0
| log.trace("createTempNode n " + n);
|
425 |
| } |
426 |
122997
| return n;
|
427 |
| } |
428 |
| |
429 |
| |
430 |
125221
| private NodeSPI createNodes(Fqn fqn, TransactionEntry entry) throws Exception
|
431 |
| { |
432 |
125215
| Fqn tmp_fqn = Fqn.ROOT;
|
433 |
| |
434 |
125221
| int size = fqn.size();
|
435 |
| |
436 |
| |
437 |
125221
| NodeSPI n = cache.getRoot();
|
438 |
125221
| for (int i = 0; i < size; i++)
|
439 |
| { |
440 |
383779
| Object child_name = fqn.get(i);
|
441 |
383779
| tmp_fqn = new Fqn(tmp_fqn, child_name);
|
442 |
| |
443 |
383779
| NodeSPI child_node = findChild(n, child_name);
|
444 |
383779
| boolean last = (i == size - 1);
|
445 |
| |
446 |
383779
| if (child_node == null)
|
447 |
| { |
448 |
1804
| if (last)
|
449 |
| { |
450 |
1715
| child_node = n.addChildDirect(new Fqn(child_name));
|
451 |
1715
| child_node.setDataLoaded(true);
|
452 |
| } |
453 |
| else |
454 |
| { |
455 |
89
| child_node = n.addChildDirect(new Fqn(child_name));
|
456 |
89
| child_node.setDataLoaded(false);
|
457 |
| } |
458 |
| |
459 |
1804
| if (entry != null)
|
460 |
| { |
461 |
110
| entry.loadUninitialisedNode(tmp_fqn);
|
462 |
| } |
463 |
| } |
464 |
| |
465 |
383779
| n = child_node;
|
466 |
| } |
467 |
| |
468 |
125221
| return n;
|
469 |
| } |
470 |
| |
471 |
383779
| private NodeSPI findChild(NodeSPI node, Object child_name)
|
472 |
| { |
473 |
383779
| Map children = node.getChildrenMapDirect();
|
474 |
188
| if (children == null) return null;
|
475 |
383591
| return (NodeSPI) children.get(child_name);
|
476 |
| } |
477 |
| |
478 |
49
| private void cleanupNodesCreated(TransactionEntry entry)
|
479 |
| { |
480 |
49
| boolean traceEnabled = log.isTraceEnabled();
|
481 |
49
| log.trace("Removing temporarily created nodes from treecache");
|
482 |
| |
483 |
| |
484 |
49
| List list = entry.getDummyNodesCreatedByCacheLoader();
|
485 |
49
| if (list != null && list.size() > 0)
|
486 |
| { |
487 |
0
| ListIterator i = list.listIterator(list.size());
|
488 |
0
| while (i.hasPrevious())
|
489 |
| { |
490 |
0
| Fqn fqn = (Fqn) i.previous();
|
491 |
0
| try
|
492 |
| { |
493 |
0
| cache.evict(fqn, false);
|
494 |
| } |
495 |
| catch (CacheException e) |
496 |
| { |
497 |
0
| if (traceEnabled) log.trace("Unable to evict node " + fqn, e);
|
498 |
| } |
499 |
| } |
500 |
| } |
501 |
| } |
502 |
| |
503 |
| |
504 |
3346
| private Map loadData(Fqn fqn) throws Exception
|
505 |
| { |
506 |
| |
507 |
3346
| Map nodeData = loader.get(fqn);
|
508 |
3346
| boolean nodeExists = (nodeData != null);
|
509 |
0
| if (log.isTraceEnabled()) log.trace("nodeExists " + nodeExists);
|
510 |
| |
511 |
3346
| if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
|
512 |
| { |
513 |
3346
| if (nodeExists)
|
514 |
| { |
515 |
2224
| m_cacheLoads++;
|
516 |
| } |
517 |
| else |
518 |
| { |
519 |
1122
| m_cacheMisses++;
|
520 |
| } |
521 |
| } |
522 |
| |
523 |
| |
524 |
| |
525 |
| |
526 |
| |
527 |
| |
528 |
| |
529 |
| |
530 |
| |
531 |
| |
532 |
| |
533 |
| |
534 |
| |
535 |
| |
536 |
| |
537 |
| |
538 |
| |
539 |
| |
540 |
| |
541 |
| |
542 |
| |
543 |
| |
544 |
3346
| return nodeData;
|
545 |
| } |
546 |
| |
547 |
0
| private void warnCustom()
|
548 |
| { |
549 |
0
| log.warn("CacheLoader.get(Fqn) returned a null; assuming the node nodes not exist.");
|
550 |
0
| log.warn("The CacheLoader interface has changed since JBossCache 1.3.x");
|
551 |
0
| log.warn("Please see http://jira.jboss.com/jira/browse/JBCACHE-118");
|
552 |
0
| log.warn("CacheLoader.get() should return an empty Map if the node does exist but doesn't have any attributes.");
|
553 |
| } |
554 |
| |
555 |
| } |