1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| package org.jboss.cache.interceptors; |
8 |
| |
9 |
| import org.jboss.cache.CacheException; |
10 |
| import org.jboss.cache.CacheSPI; |
11 |
| import org.jboss.cache.Fqn; |
12 |
| import org.jboss.cache.InvocationContext; |
13 |
| import org.jboss.cache.NodeFactory; |
14 |
| import org.jboss.cache.NodeNotExistsException; |
15 |
| import org.jboss.cache.NodeSPI; |
16 |
| import org.jboss.cache.config.Option; |
17 |
| import org.jboss.cache.marshall.MethodCall; |
18 |
| import org.jboss.cache.marshall.MethodDeclarations; |
19 |
| import org.jboss.cache.notifications.Notifier; |
20 |
| import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType.*; |
21 |
| import org.jboss.cache.optimistic.DataVersion; |
22 |
| import org.jboss.cache.optimistic.DefaultDataVersion; |
23 |
| import org.jboss.cache.optimistic.TransactionWorkspace; |
24 |
| import org.jboss.cache.optimistic.WorkspaceNode; |
25 |
| import org.jboss.cache.transaction.GlobalTransaction; |
26 |
| |
27 |
| import java.util.Collections; |
28 |
| import java.util.HashMap; |
29 |
| import java.util.Map; |
30 |
| import java.util.SortedMap; |
31 |
| |
32 |
| |
33 |
| |
34 |
| |
35 |
| |
36 |
| |
37 |
| |
38 |
| |
39 |
| public class OptimisticNodeInterceptor extends OptimisticInterceptor |
40 |
| { |
41 |
| |
42 |
| |
43 |
| |
44 |
| private NodeFactory nodeFactory; |
45 |
| private Notifier notifier; |
46 |
| |
47 |
907
| public void setCache(CacheSPI c)
|
48 |
| { |
49 |
907
| super.setCache(c);
|
50 |
907
| nodeFactory = c.getConfiguration().getRuntimeConfig().getNodeFactory();
|
51 |
907
| notifier = cache.getNotifier();
|
52 |
| } |
53 |
| |
54 |
1054615
| public Object invoke(InvocationContext ctx) throws Throwable
|
55 |
| { |
56 |
1054615
| MethodCall m = ctx.getMethodCall();
|
57 |
1054615
| Object[] args = m.getArgs();
|
58 |
| |
59 |
1054615
| Object result = null;
|
60 |
| |
61 |
1054615
| if (MethodDeclarations.isCrudMethod(m.getMethodId()))
|
62 |
| { |
63 |
1904
| GlobalTransaction gtx = getGlobalTransaction(ctx);
|
64 |
1904
| TransactionWorkspace workspace = getTransactionWorkspace(gtx);
|
65 |
1904
| Fqn fqn = getFqn(args, m.getMethodId());
|
66 |
1904
| WorkspaceNode workspaceNode = fetchWorkspaceNode(fqn, workspace, true);
|
67 |
| |
68 |
| |
69 |
1904
| if (workspaceNode == null && m.getMethodId() == MethodDeclarations.dataGravitationCleanupMethod_id)
|
70 |
| { |
71 |
4
| workspaceNode = fetchWorkspaceNode(getBackupFqn(args), workspace, true);
|
72 |
| } |
73 |
| |
74 |
1904
| if (workspaceNode != null)
|
75 |
| { |
76 |
| |
77 |
1881
| if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null)
|
78 |
| { |
79 |
| |
80 |
222
| if (ctx.isOriginLocal() && m.getMethodId() == MethodDeclarations.moveMethodLocal_id)
|
81 |
0
| throw new CacheException("Setting a data version while performing a move() is not supported!!");
|
82 |
| |
83 |
222
| workspace.setVersioningImplicit(false);
|
84 |
222
| DataVersion version = ctx.getOptionOverrides().getDataVersion();
|
85 |
| |
86 |
222
| workspaceNode.setVersion(version);
|
87 |
0
| if (trace) log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to explicit");
|
88 |
| |
89 |
222
| workspaceNode.setVersioningImplicit(false);
|
90 |
| } |
91 |
| else |
92 |
| { |
93 |
0
| if (trace) log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to implicit");
|
94 |
1659
| workspaceNode.setVersioningImplicit(true);
|
95 |
| } |
96 |
| } |
97 |
| else |
98 |
| { |
99 |
| |
100 |
23
| if ((ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isFailSilently()) && MethodDeclarations.isPutMethod(m.getMethodId()))
|
101 |
| { |
102 |
0
| throw new CacheException("Unable to set node version for " + fqn + ", node is null.");
|
103 |
| } |
104 |
| } |
105 |
| |
106 |
1904
| switch (m.getMethodId())
|
107 |
| { |
108 |
28
| case MethodDeclarations.moveMethodLocal_id:
|
109 |
28
| Fqn parentFqn = (Fqn) args[1];
|
110 |
28
| moveNodeAndNotify(parentFqn, workspaceNode, workspace, ctx);
|
111 |
28
| break;
|
112 |
235
| case MethodDeclarations.putDataMethodLocal_id:
|
113 |
235
| putDataMapAndNotify((Map<Object, Object>) args[2], false, workspace, workspaceNode, ctx);
|
114 |
235
| break;
|
115 |
2
| case MethodDeclarations.putDataEraseMethodLocal_id:
|
116 |
2
| putDataMapAndNotify((Map<Object, Object>) args[2], (Boolean) args[args.length - 1], workspace, workspaceNode, ctx);
|
117 |
2
| break;
|
118 |
1415
| case MethodDeclarations.putKeyValMethodLocal_id:
|
119 |
16
| case MethodDeclarations.putForExternalReadMethodLocal_id:
|
120 |
1431
| Object key = args[2];
|
121 |
1431
| Object value = args[3];
|
122 |
1431
| result = putDataKeyValueAndNotify(key, value, workspace, workspaceNode, ctx);
|
123 |
1431
| break;
|
124 |
170
| case MethodDeclarations.removeNodeMethodLocal_id:
|
125 |
170
| result = removeNode(workspace, workspaceNode, true, ctx);
|
126 |
170
| break;
|
127 |
21
| case MethodDeclarations.removeKeyMethodLocal_id:
|
128 |
21
| Object removeKey = args[2];
|
129 |
21
| result = removeKeyAndNotify(removeKey, workspace, workspaceNode, ctx);
|
130 |
21
| break;
|
131 |
10
| case MethodDeclarations.removeDataMethodLocal_id:
|
132 |
10
| removeDataAndNotify(workspace, workspaceNode, ctx);
|
133 |
10
| break;
|
134 |
7
| case MethodDeclarations.dataGravitationCleanupMethod_id:
|
135 |
7
| result = super.invoke(ctx);
|
136 |
0
| default:
|
137 |
7
| if (log.isWarnEnabled()) log.warn("Cannot handle CRUD method " + m);
|
138 |
7
| break;
|
139 |
| } |
140 |
| |
141 |
1904
| addToModificationList(gtx, m, ctx);
|
142 |
| } |
143 |
| else |
144 |
| { |
145 |
1052711
| switch (m.getMethodId())
|
146 |
| { |
147 |
1050374
| case MethodDeclarations.getKeyValueMethodLocal_id:
|
148 |
1050374
| result = getValueForKeyAndNotify(args, getTransactionWorkspace(getGlobalTransaction(ctx)), ctx);
|
149 |
1050373
| break;
|
150 |
34
| case MethodDeclarations.getKeysMethodLocal_id:
|
151 |
34
| result = getKeysAndNotify(args, getTransactionWorkspace(getGlobalTransaction(ctx)), ctx);
|
152 |
34
| break;
|
153 |
282
| case MethodDeclarations.getChildrenNamesMethodLocal_id:
|
154 |
282
| result = getChildNamesAndNotify(args, getTransactionWorkspace(getGlobalTransaction(ctx)), ctx);
|
155 |
282
| break;
|
156 |
1247
| case MethodDeclarations.getNodeMethodLocal_id:
|
157 |
1247
| result = getNodeAndNotify(args, getTransactionWorkspace(getGlobalTransaction(ctx)), ctx);
|
158 |
1247
| break;
|
159 |
774
| default:
|
160 |
0
| if (trace) log.trace("read Method " + m + " called - Not handling, passing on.");
|
161 |
774
| result = super.invoke(ctx);
|
162 |
772
| break;
|
163 |
| } |
164 |
| } |
165 |
1054612
| return result;
|
166 |
| } |
167 |
| |
168 |
| |
169 |
| |
170 |
| |
171 |
| |
172 |
| |
173 |
| |
174 |
1904
| private Fqn getFqn(Object[] args, int methodId)
|
175 |
| { |
176 |
1904
| return (Fqn) args[methodId == MethodDeclarations.moveMethodLocal_id ? 0 : 1];
|
177 |
| } |
178 |
| |
179 |
| |
180 |
| |
181 |
| |
182 |
| |
183 |
| |
184 |
| |
185 |
4
| private Fqn getBackupFqn(Object[] args)
|
186 |
| { |
187 |
4
| return (Fqn) args[2];
|
188 |
| } |
189 |
| |
190 |
| |
191 |
| |
192 |
| |
193 |
| |
194 |
| |
195 |
| |
196 |
1904
| private void addToModificationList(GlobalTransaction gtx, MethodCall m, InvocationContext ctx)
|
197 |
| { |
198 |
1904
| Option opt = ctx.getOptionOverrides();
|
199 |
1904
| if (opt == null || !opt.isCacheModeLocal())
|
200 |
| { |
201 |
1842
| txTable.addModification(gtx, m);
|
202 |
0
| if (log.isDebugEnabled()) log.debug("Adding Method " + m + " to modification list");
|
203 |
| } |
204 |
107
| if (cache.getCacheLoaderManager() != null) txTable.addCacheLoaderModification(gtx, m);
|
205 |
| |
206 |
| } |
207 |
| |
208 |
| |
209 |
| |
210 |
| |
211 |
| |
212 |
| |
213 |
| |
214 |
| |
215 |
| |
216 |
| |
217 |
| |
218 |
| |
219 |
| |
220 |
| |
221 |
| |
222 |
| |
223 |
28
| private void moveNodeAndNotify(Fqn parentFqn, WorkspaceNode node, TransactionWorkspace ws, InvocationContext ctx)
|
224 |
| { |
225 |
28
| Fqn nodeFqn = node.getFqn();
|
226 |
28
| if (nodeFqn.isRoot())
|
227 |
| { |
228 |
0
| log.warn("Attempting to move the root node. Not taking any action, treating this as a no-op.");
|
229 |
0
| return;
|
230 |
| } |
231 |
| |
232 |
28
| WorkspaceNode parent = fetchWorkspaceNode(parentFqn, ws, false);
|
233 |
0
| if (parent == null) throw new NodeNotExistsException("Node " + parentFqn + " does not exist!");
|
234 |
| |
235 |
28
| WorkspaceNode oldParent = fetchWorkspaceNode(nodeFqn.getParent(), ws, false);
|
236 |
0
| if (oldParent == null) throw new NodeNotExistsException("Node " + nodeFqn.getParent() + " does not exist!");
|
237 |
| |
238 |
28
| Object nodeName = nodeFqn.getLastElement();
|
239 |
| |
240 |
| |
241 |
| |
242 |
28
| oldParent.removeChild(new Fqn(nodeName));
|
243 |
| |
244 |
| |
245 |
| |
246 |
28
| Fqn nodeNewFqn = new Fqn(parent.getFqn(), nodeFqn.getLastElement());
|
247 |
| |
248 |
| |
249 |
28
| notifier.notifyNodeMoved(nodeFqn, nodeNewFqn, true, ctx);
|
250 |
28
| recursiveMoveNode(node, parent.getFqn(), ws);
|
251 |
| |
252 |
| |
253 |
28
| removeNode(ws, node, false, ctx);
|
254 |
| |
255 |
| |
256 |
28
| notifier.notifyNodeMoved(nodeFqn, nodeNewFqn, false, ctx);
|
257 |
| } |
258 |
| |
259 |
| |
260 |
| |
261 |
| |
262 |
| |
263 |
| |
264 |
| |
265 |
| |
266 |
38
| private void recursiveMoveNode(WorkspaceNode node, Fqn newBase, TransactionWorkspace ws)
|
267 |
| { |
268 |
38
| Fqn newFqn = new Fqn(newBase, node.getFqn().getLastElement());
|
269 |
38
| WorkspaceNode movedNode = fetchWorkspaceNode(newFqn, ws, true);
|
270 |
38
| movedNode.putAll(node.getData());
|
271 |
| |
272 |
| |
273 |
38
| for (Object n : node.getChildrenNames())
|
274 |
| { |
275 |
10
| WorkspaceNode child = fetchWorkspaceNode(new Fqn(node.getFqn(), n), ws, false);
|
276 |
10
| if (child != null) recursiveMoveNode(child, newFqn, ws);
|
277 |
| } |
278 |
| } |
279 |
| |
280 |
237
| private void putDataMapAndNotify(Map<Object, Object> data, boolean eraseExisitng, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx)
|
281 |
| { |
282 |
237
| if (workspaceNode == null)
|
283 |
0
| throw new NodeNotExistsException("optimisticCreateIfNotExistsInterceptor should have created this node!");
|
284 |
| |
285 |
237
| notifier.notifyNodeModified(workspaceNode.getFqn(), true, PUT_MAP, workspaceNode.getData(), ctx);
|
286 |
2
| if (eraseExisitng) workspaceNode.clearData();
|
287 |
237
| workspaceNode.putAll(data);
|
288 |
237
| workspace.addNode(workspaceNode);
|
289 |
| |
290 |
237
| notifier.notifyNodeModified(workspaceNode.getFqn(), false, PUT_MAP, workspaceNode.getData(), ctx);
|
291 |
| } |
292 |
| |
293 |
1431
| private Object putDataKeyValueAndNotify(Object key, Object value, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx)
|
294 |
| { |
295 |
1431
| if (workspaceNode == null)
|
296 |
0
| throw new NodeNotExistsException("optimisticCreateIfNotExistsInterceptor should have created this node!");
|
297 |
| |
298 |
1431
| Map addedData = Collections.singletonMap(key, value);
|
299 |
| |
300 |
1431
| notifier.notifyNodeModified(workspaceNode.getFqn(), true, PUT_DATA, workspaceNode.getData(), ctx);
|
301 |
| |
302 |
1431
| Object old = workspaceNode.put(key, value);
|
303 |
1431
| workspace.addNode(workspaceNode);
|
304 |
| |
305 |
| |
306 |
1431
| notifier.notifyNodeModified(workspaceNode.getFqn(), false, PUT_DATA, addedData, ctx);
|
307 |
| |
308 |
1431
| return old;
|
309 |
| } |
310 |
| |
311 |
198
| private boolean removeNode(TransactionWorkspace workspace, WorkspaceNode workspaceNode, boolean notify, InvocationContext ctx) throws CacheException
|
312 |
| { |
313 |
| |
314 |
20
| if (workspaceNode == null) return false;
|
315 |
| |
316 |
178
| Fqn parentFqn = workspaceNode.getFqn().getParent();
|
317 |
178
| WorkspaceNode parentNode = fetchWorkspaceNode(parentFqn, workspace, true);
|
318 |
0
| if (parentNode == null) throw new NodeNotExistsException("Unable to find parent node with fqn " + parentFqn);
|
319 |
| |
320 |
| |
321 |
150
| if (notify) notifier.notifyNodeRemoved(workspaceNode.getFqn(), true, workspaceNode.getData(), ctx);
|
322 |
| |
323 |
178
| parentNode.removeChild(workspaceNode.getFqn().getLastElement());
|
324 |
| |
325 |
178
| Fqn nodeFqn = workspaceNode.getFqn();
|
326 |
| |
327 |
178
| SortedMap<Fqn, WorkspaceNode> tailMap = workspace.getNodesAfter(workspaceNode.getFqn());
|
328 |
| |
329 |
178
| for (WorkspaceNode toDelete : tailMap.values())
|
330 |
| { |
331 |
262
| if (toDelete.getFqn().isChildOrEquals(nodeFqn))
|
332 |
| { |
333 |
0
| if (trace) log.trace("marking node " + toDelete.getFqn() + " as deleted");
|
334 |
199
| toDelete.markAsDeleted(true);
|
335 |
| } |
336 |
| else |
337 |
| { |
338 |
63
| break;
|
339 |
| } |
340 |
| } |
341 |
| |
342 |
| |
343 |
150
| if (notify) notifier.notifyNodeRemoved(workspaceNode.getFqn(), false, null, ctx);
|
344 |
178
| return true;
|
345 |
| } |
346 |
| |
347 |
21
| private Object removeKeyAndNotify(Object removeKey, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx)
|
348 |
| { |
349 |
1
| if (workspaceNode == null) return null;
|
350 |
| |
351 |
| |
352 |
20
| notifier.notifyNodeModified(workspaceNode.getFqn(), true, REMOVE_DATA, workspaceNode.getData(), ctx);
|
353 |
| |
354 |
20
| Object old = workspaceNode.remove(removeKey);
|
355 |
20
| workspace.addNode(workspaceNode);
|
356 |
| |
357 |
20
| Map removedData = Collections.singletonMap(removeKey, old);
|
358 |
| |
359 |
20
| notifier.notifyNodeModified(workspaceNode.getFqn(), false, REMOVE_DATA, removedData, ctx);
|
360 |
| |
361 |
20
| return old;
|
362 |
| } |
363 |
| |
364 |
10
| private void removeDataAndNotify(TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx)
|
365 |
| { |
366 |
1
| if (workspaceNode == null) return;
|
367 |
| |
368 |
9
| Map data = new HashMap(workspaceNode.getData());
|
369 |
| |
370 |
| |
371 |
9
| notifier.notifyNodeModified(workspaceNode.getFqn(), true, REMOVE_DATA, data, ctx);
|
372 |
| |
373 |
9
| workspaceNode.clearData();
|
374 |
9
| workspace.addNode(workspaceNode);
|
375 |
| |
376 |
| |
377 |
9
| notifier.notifyNodeModified(workspaceNode.getFqn(), false, REMOVE_DATA, data, ctx);
|
378 |
| } |
379 |
| |
380 |
1050373
| private Object getValueForKeyAndNotify(Object[] args, TransactionWorkspace workspace, InvocationContext ctx)
|
381 |
| { |
382 |
1050373
| Fqn fqn = (Fqn) args[0];
|
383 |
1050373
| Object key = args[1];
|
384 |
1050373
| WorkspaceNode workspaceNode = fetchWorkspaceNode(fqn, workspace, false);
|
385 |
| |
386 |
1050373
| if (workspaceNode == null)
|
387 |
| { |
388 |
0
| if (trace) log.debug("Unable to find node " + fqn + " in workspace.");
|
389 |
504
| return null;
|
390 |
| } |
391 |
| else |
392 |
| { |
393 |
| |
394 |
1049869
| notifier.notifyNodeVisited(fqn, true, ctx);
|
395 |
1049869
| Object val = workspaceNode.get(key);
|
396 |
1049869
| workspace.addNode(workspaceNode);
|
397 |
1049869
| notifier.notifyNodeVisited(fqn, false, ctx);
|
398 |
1049869
| return val;
|
399 |
| } |
400 |
| } |
401 |
| |
402 |
1247
| private Object getNodeAndNotify(Object[] args, TransactionWorkspace workspace, InvocationContext ctx)
|
403 |
| { |
404 |
1247
| Fqn fqn = (Fqn) args[0];
|
405 |
| |
406 |
1247
| WorkspaceNode workspaceNode = fetchWorkspaceNode(fqn, workspace, false);
|
407 |
| |
408 |
1247
| if (workspaceNode == null)
|
409 |
| { |
410 |
0
| if (trace) log.trace("Unable to find node " + fqn + " in workspace.");
|
411 |
93
| return null;
|
412 |
| } |
413 |
| else |
414 |
| { |
415 |
1154
| notifier.notifyNodeVisited(fqn, true, ctx);
|
416 |
1154
| workspace.addNode(workspaceNode);
|
417 |
1154
| notifier.notifyNodeVisited(fqn, false, ctx);
|
418 |
1154
| return workspaceNode.getNode();
|
419 |
| } |
420 |
| } |
421 |
| |
422 |
34
| private Object getKeysAndNotify(Object[] args, TransactionWorkspace workspace, InvocationContext ctx)
|
423 |
| { |
424 |
34
| Fqn fqn = (Fqn) args[0];
|
425 |
| |
426 |
34
| WorkspaceNode workspaceNode = fetchWorkspaceNode(fqn, workspace, false);
|
427 |
| |
428 |
34
| if (workspaceNode == null)
|
429 |
| { |
430 |
0
| if (trace) log.trace("unable to find node " + fqn + " in workspace.");
|
431 |
0
| return null;
|
432 |
| } |
433 |
| else |
434 |
| { |
435 |
34
| notifier.notifyNodeVisited(fqn, true, ctx);
|
436 |
34
| Object keySet = workspaceNode.getKeys();
|
437 |
34
| workspace.addNode(workspaceNode);
|
438 |
34
| notifier.notifyNodeVisited(fqn, false, ctx);
|
439 |
34
| return keySet;
|
440 |
| } |
441 |
| } |
442 |
| |
443 |
282
| private Object getChildNamesAndNotify(Object[] args, TransactionWorkspace workspace, InvocationContext ctx)
|
444 |
| { |
445 |
282
| Fqn fqn = (Fqn) args[0];
|
446 |
| |
447 |
282
| WorkspaceNode workspaceNode = fetchWorkspaceNode(fqn, workspace, false);
|
448 |
| |
449 |
282
| if (workspaceNode == null)
|
450 |
| { |
451 |
0
| if (trace) log.trace("Unable to find node " + fqn + " in workspace.");
|
452 |
0
| return null;
|
453 |
| } |
454 |
| else |
455 |
| { |
456 |
282
| notifier.notifyNodeVisited(fqn, true, ctx);
|
457 |
282
| Object nameSet = workspaceNode.getChildrenNames();
|
458 |
282
| workspace.addNode(workspaceNode);
|
459 |
282
| notifier.notifyNodeVisited(fqn, false, ctx);
|
460 |
282
| return nameSet;
|
461 |
| } |
462 |
| } |
463 |
| |
464 |
| |
465 |
| |
466 |
| |
467 |
| |
468 |
| |
469 |
| |
470 |
| |
471 |
| |
472 |
| |
473 |
| |
474 |
| |
475 |
| |
476 |
| |
477 |
| |
478 |
| |
479 |
| |
480 |
| |
481 |
| |
482 |
| |
483 |
1054138
| private WorkspaceNode fetchWorkspaceNode(Fqn fqn, TransactionWorkspace workspace, boolean undeleteIfNecessary)
|
484 |
| { |
485 |
1054138
| WorkspaceNode workspaceNode = workspace.getNode(fqn);
|
486 |
| |
487 |
1054138
| if (workspaceNode == null)
|
488 |
| { |
489 |
1051764
| NodeSPI node = cache.peek(fqn, true);
|
490 |
615
| if (node == null) return null;
|
491 |
| |
492 |
| |
493 |
1051149
| workspaceNode = nodeFactory.createWorkspaceNode(node, workspace);
|
494 |
| |
495 |
| |
496 |
1051149
| workspace.addNode(workspaceNode);
|
497 |
| } |
498 |
| |
499 |
| |
500 |
1053523
| if (workspaceNode.isDeleted())
|
501 |
| { |
502 |
0
| if (trace) log.trace("Node " + fqn + " has been deleted in the workspace.");
|
503 |
21
| if (undeleteIfNecessary)
|
504 |
| { |
505 |
12
| workspaceNode.markAsDeleted(false);
|
506 |
| |
507 |
12
| WorkspaceNode parent = fetchWorkspaceNode(fqn.getParent(), workspace, true);
|
508 |
12
| parent.addChild(workspaceNode);
|
509 |
| } |
510 |
| else |
511 |
| { |
512 |
| |
513 |
9
| workspaceNode = null;
|
514 |
| } |
515 |
| } |
516 |
| |
517 |
| |
518 |
1053523
| if (workspaceNode != null && !(workspaceNode.getVersion() instanceof DefaultDataVersion))
|
519 |
| { |
520 |
39
| workspaceNode.setVersioningImplicit(false);
|
521 |
| } |
522 |
| |
523 |
1053523
| return workspaceNode;
|
524 |
| } |
525 |
| } |