KickJava   Java API By Example, From Geeks To Geeks.

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


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.Fqn;
11 import org.jboss.cache.GlobalTransaction;
12 import org.jboss.cache.InvocationContext;
13 import org.jboss.cache.NodeSPI;
14 import org.jboss.cache.marshall.MethodCall;
15 import org.jboss.cache.marshall.MethodDeclarations;
16 import org.jboss.cache.optimistic.DataVersioningException;
17 import org.jboss.cache.optimistic.DefaultDataVersion;
18 import org.jboss.cache.optimistic.TransactionWorkspace;
19 import org.jboss.cache.optimistic.WorkspaceNode;
20
21 import javax.transaction.Transaction JavaDoc;
22 import java.util.Collection JavaDoc;
23 import java.util.Map JavaDoc;
24
25 /**
26  * Validates the data in the transaction workspace against data in the actual
27  * cache (versions only), and then performs commits if necessary. Does not
28  * pass on prepare/commit/rollbacks to the other interceptors.
29  * <p/>
30  * Currently uses simplistic integer based versioning and validation. Plans are
31  * to have this configurable as there will always be a performance/complexity
32  * tradeoff.
33  * <p/>
34  * On the commit it applies the changes in the workspace to the real nodes in
35  * the cache.
36  * <p/>
37  * On rollback clears the nodes in the workspace.
38  *
39  * @author Manik Surtani (<a HREF="mailto:manik@jboss.org">manik@jboss.org</a>)
40  * @author Steve Woodcock (<a HREF="mailto:stevew@jofti.com">stevew@jofti.com</a>)
41  */

42 public class OptimisticValidatorInterceptor extends OptimisticInterceptor
43 {
44    public Object JavaDoc invoke(MethodCall m) throws Throwable JavaDoc
45    {
46       // bypass for buddy group org metod calls.
47
if (MethodDeclarations.isBuddyGroupOrganisationMethod(m.getMethodId())) return super.invoke(m);
48
49       InvocationContext ctx = cache.getInvocationContext();
50       Transaction tx = ctx.getTransaction();
51       GlobalTransaction gtx = ctx.getGlobalTransaction();
52       Object JavaDoc retval = null;
53
54       if (tx == null)
55       {
56          throw new CacheException("Not in a transaction");
57       }
58
59       // Methods we are interested in are prepare/commit
60
// They do not go further than this interceptor
61
switch (m.getMethodId())
62       {
63          case MethodDeclarations.optimisticPrepareMethod_id:
64             // should pass in a different prepare here
65
validateNodes(gtx);
66             break;
67          case MethodDeclarations.commitMethod_id:
68             commit(gtx);
69             break;
70          case MethodDeclarations.rollbackMethod_id:
71             rollBack(gtx);
72             break;
73          default:
74             retval = super.invoke(m);
75             break;
76       }
77       return retval;
78    }
79
80
81    private void validateNodes(GlobalTransaction gtx) throws CacheException
82    {
83       TransactionWorkspace workspace;
84
85       try
86       {
87          workspace = getTransactionWorkspace(gtx);
88       }
89       catch (CacheException e)
90       {
91          throw new CacheException("unable to retrieve workspace", e);
92       }
93
94       // should be an ordered list - get the set of nodes
95
Collection JavaDoc<WorkspaceNode> nodes = workspace.getNodes().values();
96
97       //we have all locks here so lets try and validate
98
if (log.isDebugEnabled()) log.debug("validating nodes. Num nodes: " + nodes.size());
99       simpleValidate(nodes);
100       log.debug("validated nodes");
101    }
102
103    private void simpleValidate(Collection JavaDoc<WorkspaceNode> nodes) throws DataVersioningException
104    {
105       boolean trace = log.isTraceEnabled();
106       for (WorkspaceNode workspaceNode : nodes)
107       {
108          Fqn fqn = workspaceNode.getFqn();
109          if (trace) log.trace("Validating version for node " + fqn);
110
111          NodeSPI realNode;
112          realNode = cache.peek(fqn);
113
114          // if this is a newly created node then we expect the underlying node to be null.
115
// if not, we have a problem...
116
if (realNode == null && !workspaceNode.isCreated())
117          {
118             throw new DataVersioningException("Real node for " + fqn + " is null, and this wasn't newly created in this tx!");
119          }
120
121          if (realNode != null && workspaceNode.isCreated())
122          {
123             throw new DataVersioningException("Tx attempted to create " + fqn + " anew. It has already been created since this tx started by another (possibly remote) tx.");
124          }
125
126          if (!workspaceNode.isCreated() && (workspaceNode.isDeleted() || workspaceNode.isDirty()))
127          {
128             // test that the 2 DataVersion types match up
129
if (!realNode.getVersion().getClass().equals(workspaceNode.getVersion().getClass()) && checkNotInitialRootVersion(realNode))
130             {
131                throw new DataVersioningException("Attempting to apply data version of type " + workspaceNode.getVersion().getClass() + " to a node [fqn = " + realNode.getFqn() + "] that already contains version of type " + realNode.getVersion().getClass());
132             }
133             if (realNode.getVersion().newerThan(workspaceNode.getVersion()))
134             {
135                // we have an out of date node here
136
throw new DataVersioningException("DataNode [" + fqn + "] version " + workspaceNode.getNode().getVersion() + " is newer than workspace node " + workspaceNode.getVersion());
137             }
138          }
139       }
140    }
141
142    private boolean checkNotInitialRootVersion(NodeSPI n)
143    {
144       return !n.getFqn().isRoot() || !(n.getVersion() instanceof DefaultDataVersion) || n.getVersion() != DefaultDataVersion.ZERO;
145    }
146
147    private void commit(GlobalTransaction gtx)
148    {
149       TransactionWorkspace workspace;
150
151       try
152       {
153          workspace = getTransactionWorkspace(gtx);
154       }
155       catch (CacheException e)
156       {
157          log.trace("we can't rollback", e);
158          return;
159       }
160
161       log.debug("commiting validated changes ");
162       // should be an ordered list
163
Collection JavaDoc<WorkspaceNode> nodes = workspace.getNodes().values();
164
165       boolean trace = log.isTraceEnabled();
166       for (WorkspaceNode wrappedNode : nodes)
167       {
168          // short circuit if this node is deleted?
169
if (wrappedNode.isDeleted())
170          {
171             if (trace) log.trace("Workspace node " + wrappedNode.getFqn() + " deleted; removing");
172             NodeSPI dNode = wrappedNode.getNode();
173
174
175             if (dNode.getFqn().isRoot())
176             {
177                log.warn("Attempted to delete the root node");
178             }
179             else
180             {
181                NodeSPI parent = dNode.getParent();
182                if (parent == null)
183                {
184                   throw new IllegalStateException JavaDoc("dNode " + dNode + " has no parent");
185                }
186
187                parent.removeChildDirect(dNode.getFqn().getLastElement());
188             }
189          }
190          else
191          {
192             // "Will somebody please think of the children!!"
193
// if (wrappedNode.hasCreatedOrRemovedChildren() handleChildNodes(wrappedNode);
194
if (wrappedNode.isDirty())
195             {
196                NodeSPI current = wrappedNode.getNode();
197                Map JavaDoc mergedChildren = wrappedNode.getMergedChildren();
198
199                // this could be done better to account for more subtle merges
200
current.setChildrenMapDirect(mergedChildren);
201
202                // do we need to notify listeners of a modification?? If all we've done is added children then don't
203
// notify.
204
Map JavaDoc mergedData = wrappedNode.getMergedData();
205
206                current.clearDataDirect();
207                current.putDirect(mergedData);
208
209                if (wrappedNode.isVersioningImplicit())
210                {
211                   if (trace) log.trace("Versioning is implicit; incrementing.");
212                   current.setVersion(((DefaultDataVersion) wrappedNode.getVersion()).increment());
213                }
214                else
215                {
216                   if (trace) log.trace("Versioning is explicit; not attempting an increment.");
217                   current.setVersion(wrappedNode.getVersion());
218                }
219                if (trace)
220                {
221                   log.trace("Setting version of node " + current.getFqn() + " from " + wrappedNode.getVersion() + " to " + current.getVersion());
222                }
223             }
224             else
225             {
226                if (trace)
227                {
228                   log.trace("Merging node " + wrappedNode.getFqn() + " not necessary since the node is not dirty");
229                }
230             }
231          }
232       }
233
234    }
235
236    private void rollBack(GlobalTransaction gtx)
237    {
238       TransactionWorkspace workspace;
239       try
240       {
241          workspace = getTransactionWorkspace(gtx);
242          Map JavaDoc nodes = workspace.getNodes();
243          nodes.clear();
244       }
245       catch (CacheException e)
246       {
247          log.info("Unable to roll back", e);
248       }
249    }
250 }
251
Popular Tags