1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
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.InvocationContext; |
12 |
| import org.jboss.cache.NodeSPI; |
13 |
| import org.jboss.cache.marshall.MethodCall; |
14 |
| import org.jboss.cache.marshall.MethodDeclarations; |
15 |
| import org.jboss.cache.optimistic.DataVersioningException; |
16 |
| import org.jboss.cache.optimistic.DefaultDataVersion; |
17 |
| import org.jboss.cache.optimistic.TransactionWorkspace; |
18 |
| import org.jboss.cache.optimistic.WorkspaceNode; |
19 |
| import org.jboss.cache.transaction.GlobalTransaction; |
20 |
| |
21 |
| import java.util.Collection; |
22 |
| import java.util.List; |
23 |
| import java.util.Map; |
24 |
| import java.util.Set; |
25 |
| |
26 |
| |
27 |
| |
28 |
| |
29 |
| |
30 |
| |
31 |
| |
32 |
| |
33 |
| |
34 |
| |
35 |
| |
36 |
| |
37 |
| |
38 |
| |
39 |
| |
40 |
| |
41 |
| |
42 |
| |
43 |
| |
44 |
| |
45 |
| public class OptimisticValidatorInterceptor extends OptimisticInterceptor |
46 |
| { |
47 |
2107999
| public Object invoke(InvocationContext ctx) throws Throwable
|
48 |
| { |
49 |
2107999
| MethodCall m = ctx.getMethodCall();
|
50 |
2107999
| Object retval = null;
|
51 |
| |
52 |
| |
53 |
| |
54 |
2107999
| switch (m.getMethodId())
|
55 |
| { |
56 |
1129
| case MethodDeclarations.optimisticPrepareMethod_id:
|
57 |
| |
58 |
1129
| validateNodes(getGlobalTransaction(ctx));
|
59 |
1100
| break;
|
60 |
1052381
| case MethodDeclarations.commitMethod_id:
|
61 |
1052381
| commit(getGlobalTransaction(ctx));
|
62 |
1052381
| break;
|
63 |
51
| case MethodDeclarations.rollbackMethod_id:
|
64 |
51
| rollBack(getGlobalTransaction(ctx));
|
65 |
51
| break;
|
66 |
1054438
| default:
|
67 |
1054438
| retval = super.invoke(ctx);
|
68 |
1054438
| break;
|
69 |
| } |
70 |
2107970
| return retval;
|
71 |
| } |
72 |
| |
73 |
1129
| private void validateNodes(GlobalTransaction gtx) throws CacheException
|
74 |
| { |
75 |
1129
| TransactionWorkspace workspace = getTransactionWorkspace(gtx);
|
76 |
| |
77 |
| |
78 |
1129
| Collection<WorkspaceNode> nodes = workspace.getNodes().values();
|
79 |
| |
80 |
| |
81 |
0
| if (log.isDebugEnabled()) log.debug("Validating " + nodes.size() + " nodes.");
|
82 |
1129
| simpleValidate(nodes);
|
83 |
1100
| log.debug("Successfully validated nodes");
|
84 |
| } |
85 |
| |
86 |
1129
| private void simpleValidate(Collection<WorkspaceNode> nodes) throws DataVersioningException
|
87 |
| { |
88 |
1129
| for (WorkspaceNode workspaceNode : nodes)
|
89 |
| { |
90 |
2853
| Fqn fqn = workspaceNode.getFqn();
|
91 |
0
| if (trace) log.trace("Validating version for node " + fqn);
|
92 |
| |
93 |
2853
| NodeSPI underlyingNode;
|
94 |
2853
| underlyingNode = cache.peek(fqn, true);
|
95 |
| |
96 |
| |
97 |
| |
98 |
2853
| if (underlyingNode == null && !workspaceNode.isCreated())
|
99 |
| { |
100 |
2
| throw new DataVersioningException("Underlying node for " + fqn + " is null, and this node wasn't newly created in this transaction! We have a concurrent deletion event.");
|
101 |
| } |
102 |
| |
103 |
2851
| if (underlyingNode != null && workspaceNode.isCreated() && workspaceNode.isDirty())
|
104 |
| { |
105 |
6
| throw new DataVersioningException("Transaction attempted to create " + fqn + " anew. It has already been created since this transaction started, by another (possibly remote) transaction. We have a concurrent creation event.");
|
106 |
| } |
107 |
| |
108 |
2845
| if (!workspaceNode.isCreated() && (workspaceNode.isDeleted() || workspaceNode.isDirty()))
|
109 |
| { |
110 |
| |
111 |
552
| if (underlyingNode.getVersion().newerThan(workspaceNode.getVersion()))
|
112 |
| { |
113 |
| |
114 |
18
| throw new DataVersioningException("Version mismatch for node " + fqn + ": underlying node with version " + workspaceNode.getNode().getVersion() + " is newer than workspace node, with version " + workspaceNode.getVersion());
|
115 |
| } |
116 |
| } |
117 |
| } |
118 |
| } |
119 |
| |
120 |
| |
121 |
1052381
| private void commit(GlobalTransaction gtx)
|
122 |
| { |
123 |
1052381
| TransactionWorkspace workspace;
|
124 |
| |
125 |
1052381
| try
|
126 |
| { |
127 |
1052381
| workspace = getTransactionWorkspace(gtx);
|
128 |
| } |
129 |
| catch (CacheException e) |
130 |
| { |
131 |
0
| log.warn("we can't rollback", e);
|
132 |
0
| return;
|
133 |
| } |
134 |
| |
135 |
0
| if (log.isDebugEnabled()) log.debug("Commiting successfully validated changes for GlobalTransaction " + gtx);
|
136 |
| |
137 |
| |
138 |
1052381
| Collection<WorkspaceNode> workspaceNodes = workspace.getNodes().values();
|
139 |
| |
140 |
1052381
| for (WorkspaceNode workspaceNode : workspaceNodes)
|
141 |
| { |
142 |
1053331
| NodeSPI underlyingNode = workspaceNode.getNode();
|
143 |
| |
144 |
| |
145 |
1053331
| if (workspaceNode.isDeleted())
|
146 |
| { |
147 |
0
| if (trace) log.trace("Workspace node " + workspaceNode.getFqn() + " deleted; removing");
|
148 |
| |
149 |
172
| if (underlyingNode.getFqn().isRoot())
|
150 |
| { |
151 |
0
| throw new CacheException("An illegal attempt to delete the root node!");
|
152 |
| } |
153 |
| else |
154 |
| { |
155 |
172
| NodeSPI parent = underlyingNode.getParent();
|
156 |
172
| if (parent == null)
|
157 |
| { |
158 |
0
| throw new CacheException("Underlying node " + underlyingNode + " has no parent");
|
159 |
| } |
160 |
| |
161 |
172
| parent.removeChildDirect(underlyingNode.getFqn().getLastElement());
|
162 |
| } |
163 |
| } |
164 |
| else |
165 |
| { |
166 |
1053159
| boolean updateVersion = false;
|
167 |
1053159
| if (workspaceNode.isChildrenModified())
|
168 |
| { |
169 |
1088
| log.trace("Updating children since node has modified children");
|
170 |
| |
171 |
1088
| List<Set<Fqn>> deltas = workspaceNode.getMergedChildren();
|
172 |
| |
173 |
0
| if (trace) log.trace("Applying children deltas to parent node " + underlyingNode.getFqn());
|
174 |
1088
| for (Fqn child : deltas.get(0))
|
175 |
| { |
176 |
1018
| underlyingNode.addChildDirect(workspaceNode.getChild(child.getLastElement()));
|
177 |
| } |
178 |
| |
179 |
1088
| for (Fqn child : deltas.get(1))
|
180 |
| { |
181 |
153
| underlyingNode.removeChildDirect(child.getLastElement());
|
182 |
| } |
183 |
| |
184 |
1088
| updateVersion = cache.getConfiguration().isLockParentForChildInsertRemove();
|
185 |
| |
186 |
| |
187 |
| } |
188 |
| |
189 |
1053159
| if (workspaceNode.isDirty())
|
190 |
| { |
191 |
1204
| log.trace("Merging data since node is dirty");
|
192 |
1204
| Map mergedData = workspaceNode.getMergedData();
|
193 |
1204
| underlyingNode.clearDataDirect();
|
194 |
1204
| underlyingNode.putAllDirect(mergedData);
|
195 |
1204
| updateVersion = true;
|
196 |
| } |
197 |
| |
198 |
1053159
| if (updateVersion)
|
199 |
| { |
200 |
1225
| if (workspaceNode.isVersioningImplicit())
|
201 |
| { |
202 |
0
| if (trace) log.trace("Versioning is implicit; incrementing.");
|
203 |
1059
| underlyingNode.setVersion(((DefaultDataVersion) workspaceNode.getVersion()).increment());
|
204 |
| } |
205 |
| else |
206 |
| { |
207 |
0
| if (trace) log.trace("Versioning is explicit; not attempting an increment.");
|
208 |
166
| underlyingNode.setVersion(workspaceNode.getVersion());
|
209 |
| } |
210 |
| |
211 |
1225
| if (trace)
|
212 |
0
| log.trace("Setting version of node " + underlyingNode.getFqn() + " from " + workspaceNode.getVersion() + " to " + underlyingNode.getVersion());
|
213 |
| } |
214 |
| else |
215 |
| { |
216 |
1051934
| if (trace)
|
217 |
0
| log.trace("Version update on " + workspaceNode.getFqn() + " not necessary since the node is not dirty or LockParentForChildInsertRemove is set to false");
|
218 |
| } |
219 |
| } |
220 |
| } |
221 |
| |
222 |
| } |
223 |
| |
224 |
51
| private void rollBack(GlobalTransaction gtx)
|
225 |
| { |
226 |
51
| TransactionWorkspace workspace;
|
227 |
51
| workspace = getTransactionWorkspace(gtx);
|
228 |
51
| workspace.clearNodes();
|
229 |
| } |
230 |
| } |