KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.cache.interceptors;
8
9 import org.jboss.cache.CacheException;
10 import org.jboss.cache.CacheListener;
11 import org.jboss.cache.CacheSPI;
12 import org.jboss.cache.Fqn;
13 import org.jboss.cache.GlobalTransaction;
14 import org.jboss.cache.InvocationContext;
15 import org.jboss.cache.NodeNotExistsException;
16 import org.jboss.cache.NodeSPI;
17 import org.jboss.cache.config.Option;
18 import org.jboss.cache.factories.NodeFactory;
19 import org.jboss.cache.marshall.MethodCall;
20 import org.jboss.cache.marshall.MethodDeclarations;
21 import org.jboss.cache.optimistic.DataVersion;
22 import org.jboss.cache.optimistic.TransactionWorkspace;
23 import org.jboss.cache.optimistic.WorkspaceNode;
24
25 import java.util.HashMap JavaDoc;
26 import java.util.Iterator JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.SortedMap JavaDoc;
29
30 /**
31  * Operations on nodes are done on the copies that exist in the workspace rather than passed down to the {@see CallInterceptor}
32  *
33  * @author <a HREF="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
34  * @author <a HREF="mailto:stevew@jofti.com">Steve Woodcock (stevew@jofti.com)</a>
35  */

36 public class OptimisticNodeInterceptor extends OptimisticInterceptor
37 {
38    private NodeFactory nodeFactory;
39
40    public void setCache(CacheSPI c)
41    {
42       super.setCache(c);
43       nodeFactory = c.getConfiguration().getRuntimeConfig().getNodeFactory();
44    }
45
46    public Object JavaDoc invoke(MethodCall m) throws Throwable JavaDoc
47    {
48       // bypass for buddy group org method calls.
49
if (MethodDeclarations.isBuddyGroupOrganisationMethod(m.getMethodId())) return super.invoke(m);
50
51       if (log.isTraceEnabled()) log.trace("Processing method call " + m);
52
53       InvocationContext ctx = cache.getInvocationContext();
54       Object JavaDoc[] args = m.getArgs();
55
56       Object JavaDoc result = null;
57
58       GlobalTransaction gtx = ctx.getGlobalTransaction();
59
60       TransactionWorkspace workspace = getTransactionWorkspace(gtx);
61
62       if (MethodDeclarations.moveMethodLocal_id == m.getMethodId())
63       {
64          if (ctx.getOptionOverrides().getDataVersion() != null && ctx.isOriginLocal())
65          {
66             throw new CacheException("Setting a data version while performing a move() is not supported!!");
67          }
68
69          Fqn parentFqn = (Fqn) args[1], nodeFqn = (Fqn) args[0];
70
71          if (log.isTraceEnabled())
72          {
73             log.trace("Adding nodes " + parentFqn + " and " + nodeFqn + " to the workspace.");
74          }
75
76          WorkspaceNode parent = getOrCreateWorkspaceNode(parentFqn, workspace, false);
77          if (parent == null) throw new NodeNotExistsException("Node " + parentFqn + " does not exist!");
78          WorkspaceNode node = getOrCreateWorkspaceNode(nodeFqn, workspace, true);
79
80          if (log.isTraceEnabled()) log.trace("Parent: " + parent);
81          if (log.isTraceEnabled()) log.trace("Node: " + node);
82
83          if (log.isTraceEnabled()) log.trace("Workspace snapshot: " + workspace);
84
85          // be greedy about it - get children as well.
86
greedyGetNodes(node, workspace);
87
88          // now that we have all we need in the workspace, perform the move.
89

90          doMove(parent, node, workspace);
91
92          addToModificationList(gtx, m);
93       }
94       else if (MethodDeclarations.isCrudMethod(m.getMethodId()))
95       {
96          // assign a global transaction here if we need to - should do all
97
// this in the transaction interceptor
98
Fqn fqn = getFqn(args);
99          WorkspaceNode workspaceNode = getOrCreateWorkspaceNode(fqn, workspace, true);
100
101
102          if (workspaceNode != null)
103          {
104             // use explicit versioning
105
if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null)
106             {
107                workspace.setVersioningImplicit(false);
108                DataVersion version = ctx.getOptionOverrides().getDataVersion();
109
110                workspaceNode.setVersion(version);
111                if (log.isTraceEnabled())
112                {
113                   log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to explicit");
114                }
115                workspaceNode.setVersioningImplicit(false);
116             }
117             else
118             {
119                if (log.isTraceEnabled())
120                {
121                   log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to implicit");
122                }
123                workspaceNode.setVersioningImplicit(true);
124             }
125          }
126          else
127          {
128             // "fail-more-silently" patch thanks to Owen Taylor - JBCACHE-767
129
if ((ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isFailSilently()) && MethodDeclarations.isPutMethod(m.getMethodId()))
130             {
131                throw new CacheException("Unable to set node version for " + getFqn(args) + ", node is null.");
132             }
133          }
134
135          switch (m.getMethodId())
136          {
137             case MethodDeclarations.putDataMethodLocal_id:
138                Boolean JavaDoc erase = (Boolean JavaDoc) args[3];
139                cache.getNotifier().notifyNodeModified(fqn, true, CacheListener.ModificationType.PUT_MAP, workspaceNode == null ? null : workspaceNode.getData(), false);
140                putDataMap((Map JavaDoc<Object JavaDoc, Object JavaDoc>) args[2], erase, workspace, workspaceNode);
141                cache.getNotifier().notifyNodeModified(fqn, false, CacheListener.ModificationType.PUT_MAP, workspaceNode.getData(), false);
142                break;
143             case MethodDeclarations.putDataEraseMethodLocal_id:
144                cache.getNotifier().notifyNodeModified(fqn, true, CacheListener.ModificationType.PUT_MAP, workspaceNode == null ? null : workspaceNode.getData(), false);
145                putDataMap((Map JavaDoc<Object JavaDoc, Object JavaDoc>) args[2], true, workspace, workspaceNode);
146                cache.getNotifier().notifyNodeModified(fqn, false, CacheListener.ModificationType.PUT_MAP, workspaceNode.getData(), false);
147                break;
148             case MethodDeclarations.putKeyValMethodLocal_id:
149                Object JavaDoc key = args[2];
150                Object JavaDoc value = args[3];
151                Map JavaDoc addedData = new HashMap JavaDoc();
152                addedData.put(key, value);
153
154                cache.getNotifier().notifyNodeModified(fqn, true, CacheListener.ModificationType.PUT_DATA, workspaceNode == null ? null : workspaceNode.getData(), false);
155                result = putDataKeyValue(key, value, workspace, workspaceNode);
156                cache.getNotifier().notifyNodeModified(fqn, false, CacheListener.ModificationType.PUT_DATA, addedData, false);
157                break;
158             case MethodDeclarations.removeNodeMethodLocal_id:
159                cache.getNotifier().notifyNodeRemoved(fqn, true, workspaceNode == null ? null : workspaceNode.getData(), false);
160                removeNode(workspace, workspaceNode);
161                cache.getNotifier().notifyNodeRemoved(fqn, false, null, false);
162                break;
163             case MethodDeclarations.removeKeyMethodLocal_id:
164                cache.getNotifier().notifyNodeModified(fqn, true, CacheListener.ModificationType.REMOVE_DATA, workspaceNode == null ? null : workspaceNode.getData(), false);
165                Object JavaDoc removeKey = args[2];
166                result = removeKey(removeKey, workspace, workspaceNode);
167                Map JavaDoc removedData = new HashMap JavaDoc();
168                removedData.put(removeKey, result);
169                cache.getNotifier().notifyNodeModified(fqn, false, CacheListener.ModificationType.REMOVE_DATA, removedData, false);
170                break;
171             case MethodDeclarations.removeDataMethodLocal_id:
172                Map JavaDoc data = workspaceNode == null ? null : new HashMap JavaDoc(workspaceNode.getData());
173                cache.getNotifier().notifyNodeModified(fqn, true, CacheListener.ModificationType.REMOVE_DATA, data, false);
174                removeData(workspace, workspaceNode);
175                cache.getNotifier().notifyNodeModified(fqn, false, CacheListener.ModificationType.REMOVE_DATA, data, false);
176                break;
177             case MethodDeclarations.dataGravitationCleanupMethod_id:
178                result = super.invoke(m);
179             default:
180                if (log.isInfoEnabled()) log.info("Cannot Handle Method " + m);
181                break;
182          }
183
184          addToModificationList(gtx, m);
185       }
186       else
187       {
188          switch (m.getMethodId())
189          {
190             case MethodDeclarations.getKeyValueMethodLocal_id:
191                result = getValueForKey(args, workspace);
192                break;
193             case MethodDeclarations.getKeysMethodLocal_id:
194                result = getKeys(args, workspace);
195                break;
196             case MethodDeclarations.getChildrenNamesMethodLocal_id:
197                result = getChildNames(args, workspace);
198                break;
199             case MethodDeclarations.getNodeMethodLocal_id:
200                result = getNode(args, workspace);
201                break;
202             default:
203                if (log.isInfoEnabled())
204                {
205                   log.info("read Method " + m + " called - don't know how to handle, passing on!");
206                }
207                result = super.invoke(m);
208                break;
209          }
210       }
211       return result;
212    }
213
214    private void addToModificationList(GlobalTransaction gtx, MethodCall m)
215    {
216       Option opt = cache.getInvocationContext().getOptionOverrides();
217       if (opt == null || !opt.isCacheModeLocal())
218       {
219          txTable.addModification(gtx, m);
220          if (log.isDebugEnabled()) log.debug("Adding Method " + m + " to modification list");
221       }
222       if (cache.getCacheLoaderManager() != null) txTable.addCacheLoaderModification(gtx, m);
223
224    }
225
226    public void doMove(WorkspaceNode parent, WorkspaceNode node, TransactionWorkspace ws)
227    {
228       Fqn nodeFqn = node.getFqn();
229       if (nodeFqn.isRoot())
230       {
231          log.warn("Attempting to move the root node. Not taking any action, treating this as a no-op.");
232          return;
233       }
234       WorkspaceNode oldParent = getOrCreateWorkspaceNode(nodeFqn.getParent(), ws, false);
235       if (oldParent == null) throw new NodeNotExistsException("Node " + nodeFqn.getParent() + " does not exist!");
236       Object JavaDoc nodeName = nodeFqn.getLastElement();
237
238       // now that we have the parent and target nodes:
239
// first correct the pointers at the pruning point
240
oldParent.removeChild(new Fqn(nodeName));
241       // parent.addChild(nodeName, node);
242
// parent.createChild(nodeName, nodeFqn, parent.getNode(), ((TreeCacheProxyImpl)cache).cache, null);
243

244       // parent pointer is calculated on the fly using Fqns.
245

246       // now adjust Fqns of node and all children.
247
// notify
248
Fqn nodeNewFqn = new Fqn(parent.getFqn(), nodeFqn.getLastElement());
249       cache.getNotifier().notifyNodeMoved(nodeFqn, nodeNewFqn, true, false);
250       moveFqns(node, parent.getFqn(), ws);
251
252       // remove old nodes. this may mark some nodes which have already been moved as deleted
253
removeNode(ws, node);
254
255       // notify
256
cache.getNotifier().notifyNodeMoved(nodeFqn, nodeNewFqn, false, false);
257    }
258
259    /**
260     * Moves a node to a new base.
261     */

262    private void moveFqns(WorkspaceNode node, Fqn newBase, TransactionWorkspace ws)
263    {
264       Fqn newFqn = new Fqn(newBase, node.getFqn().getLastElement());
265       WorkspaceNode movedNode = getOrCreateWorkspaceNode(newFqn, ws, true);
266       movedNode.put(node.getData());
267
268       // process children
269
for (Object JavaDoc n : node.getChildrenNames())
270       {
271          WorkspaceNode child = getOrCreateWorkspaceNode(new Fqn(node.getFqn(), n), ws, false);
272          if (child != null) moveFqns(child, newFqn, ws);
273       }
274    }
275
276    /**
277     * performs a getOrCreateNode on all n's children, recursively.
278     *
279     * @param n
280     */

281    protected void greedyGetNodes(WorkspaceNode n, TransactionWorkspace ws)
282    {
283       Fqn myFqn = n.getFqn();
284
285       for (Object JavaDoc child : n.getChildrenNames())
286       {
287          Fqn childFqn = new Fqn(myFqn, child);
288          WorkspaceNode cn = getOrCreateWorkspaceNode(childFqn, ws, false);
289          if (!ws.hasNode(childFqn)) ws.addNode(cn);
290          greedyGetNodes(cn, ws);
291       }
292    }
293
294    private Fqn getFqn(Object JavaDoc[] args)
295    {
296       return (Fqn) args[1];
297    }
298
299    private void putDataMap(Map JavaDoc<Object JavaDoc, Object JavaDoc> data, boolean eraseExisitng,
300                            TransactionWorkspace workspace, WorkspaceNode workspaceNode)
301    {
302       if (workspaceNode == null)
303       {
304          return;
305       }
306       if (eraseExisitng) workspaceNode.clearData();
307       workspaceNode.put(data);
308       workspace.addNode(workspaceNode);
309    }
310
311    private Object JavaDoc putDataKeyValue(Object JavaDoc key, Object JavaDoc value, TransactionWorkspace workspace, WorkspaceNode workspaceNode)
312    {
313       if (workspaceNode == null)
314       {
315          return null;// this should be an exception
316
}
317
318       Object JavaDoc old = workspaceNode.put(key, value);
319       workspace.addNode(workspaceNode);
320       return old;
321    }
322
323    private void removeNode(TransactionWorkspace workspace, WorkspaceNode workspaceNode) throws CacheException
324    {
325       if (log.isTraceEnabled())
326       {
327          log.trace("removeNode " + workspace + " node=" + workspaceNode);
328       }
329
330       // it is already removed - we can ignore it
331
if (workspaceNode == null)
332       {
333          return;
334       }
335
336       boolean debug = log.isDebugEnabled();
337
338       Fqn parentFqn = workspaceNode.getFqn().getParent();
339       WorkspaceNode parentNode = getOrCreateWorkspaceNode(parentFqn, workspace, false);
340       if (parentNode == null)
341       {
342          throw new NodeNotExistsException("Unable to find parent node with Fqn " + parentFqn);
343       }
344
345       parentNode.removeChild(new Fqn(workspaceNode.getFqn().getLastElement()));
346       workspace.addNode(parentNode);
347       if (debug) log.debug("added parent node " + parentNode.getFqn() + " to workspace");
348       Fqn nodeFqn = workspaceNode.getFqn();
349
350       // Mark this node and all children as deleted
351
workspace.addNode(workspaceNode);// deleted below
352
SortedMap JavaDoc tailMap = workspace.getNodesAfter(workspaceNode.getFqn());
353
354       for (Iterator JavaDoc it = tailMap.entrySet().iterator(); it.hasNext();)
355       {
356          WorkspaceNode toDelete = (WorkspaceNode) ((Map.Entry JavaDoc) it.next()).getValue();
357          if (toDelete.getFqn().isChildOrEquals(nodeFqn))
358          {
359             if (debug) log.debug("marking node " + toDelete.getFqn() + " as deleted");
360             toDelete.markAsDeleted(true);
361          }
362          else
363          {
364             break;// no more children, we came to the end
365
}
366       }
367    }
368
369    private Object JavaDoc removeKey(Object JavaDoc removeKey, TransactionWorkspace workspace, WorkspaceNode workspaceNode)
370    {
371       if (workspaceNode == null)
372       {
373          return null;
374       }
375
376       Object JavaDoc old = workspaceNode.remove(removeKey);
377       workspace.addNode(workspaceNode);
378       return old;
379    }
380
381    private void removeData(TransactionWorkspace workspace, WorkspaceNode workspaceNode)
382    {
383       if (workspaceNode == null)
384       {
385          return;
386       }
387       workspaceNode.clearData();
388       workspace.addNode(workspaceNode);
389    }
390
391    private Object JavaDoc getValueForKey(Object JavaDoc[] args, TransactionWorkspace workspace)
392    {
393       Fqn fqn = (Fqn) args[0];
394       Object JavaDoc key = args[1];
395       WorkspaceNode workspaceNode = getOrCreateWorkspaceNode(fqn, workspace, false);
396
397       if (workspaceNode == null)
398       {
399          if (log.isDebugEnabled()) log.debug("unable to find node " + fqn + " in workspace.");
400          return null;
401       }
402       else
403       {
404          //add this node into the wrokspace
405
cache.getNotifier().notifyNodeVisited(fqn, true, false);
406          Object JavaDoc val = workspaceNode.get(key);
407          workspace.addNode(workspaceNode);
408          cache.getNotifier().notifyNodeVisited(fqn, false, false);
409          return val;
410       }
411    }
412
413    private Object JavaDoc getNode(Object JavaDoc[] args, TransactionWorkspace workspace)
414    {
415       Fqn fqn = (Fqn) args[0];
416
417       WorkspaceNode workspaceNode = getOrCreateWorkspaceNode(fqn, workspace, false);
418
419       if (workspaceNode == null)
420       {
421          if (log.isDebugEnabled()) log.debug("unable to find node " + fqn + " in workspace.");
422          return null;
423       }
424       else
425       {
426          cache.getNotifier().notifyNodeVisited(fqn, true, false);
427          workspace.addNode(workspaceNode);
428          cache.getNotifier().notifyNodeVisited(fqn, false, false);
429          return workspaceNode.getNode();
430       }
431    }
432
433    private Object JavaDoc getKeys(Object JavaDoc[] args, TransactionWorkspace workspace)
434    {
435       Fqn fqn = (Fqn) args[0];
436
437       WorkspaceNode workspaceNode = getOrCreateWorkspaceNode(fqn, workspace, false);
438
439       if (workspaceNode == null)
440       {
441          if (log.isDebugEnabled()) log.debug("unable to find node " + fqn + " in workspace.");
442          return null;
443       }
444       else
445       {
446          cache.getNotifier().notifyNodeVisited(fqn, true, false);
447          Object JavaDoc keySet = workspaceNode.getKeys();
448          workspace.addNode(workspaceNode);
449          cache.getNotifier().notifyNodeVisited(fqn, false, false);
450          return keySet;
451       }
452    }
453
454    private Object JavaDoc getChildNames(Object JavaDoc[] args, TransactionWorkspace workspace)
455    {
456       Fqn fqn = (Fqn) args[0];
457
458       WorkspaceNode workspaceNode = getOrCreateWorkspaceNode(fqn, workspace, false);
459
460       if (workspaceNode == null)
461       {
462          if (log.isDebugEnabled()) log.debug("unable to find node " + fqn + " in workspace.");
463          return null;
464       }
465       else
466       {
467          cache.getNotifier().notifyNodeVisited(fqn, true, false);
468          Object JavaDoc nameSet = workspaceNode.getChildrenNames();
469          workspace.addNode(workspaceNode);
470          cache.getNotifier().notifyNodeVisited(fqn, false, false);
471          return nameSet;
472       }
473    }
474
475    private WorkspaceNode getOrCreateWorkspaceNode(Fqn fqn, TransactionWorkspace workspace, boolean undeleteIfNecessary)
476    {
477       WorkspaceNode workspaceNode = workspace.getNode(fqn);
478       // if we do not have the node then we need to add it to the workspace
479
if (workspaceNode == null)
480       {
481          NodeSPI node = cache.peek(fqn);
482          if (node == null)
483          {
484             return null;// seems to happen quite a bit
485
}
486          workspaceNode = nodeFactory.createWorkspaceNode(node, workspace);
487          workspace.addNode(workspaceNode);
488       }
489       // the node has been deleted dude!
490
if (workspaceNode.isDeleted())
491       {
492          if (log.isDebugEnabled()) log.debug("Node " + fqn + " has been deleted in the workspace.");
493          if (undeleteIfNecessary)
494          {
495             workspaceNode.markAsDeleted(false);
496          }
497          else
498          {
499             workspaceNode = null;
500          }
501       }
502       return workspaceNode;
503    }
504 }
505
Popular Tags