1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.connector.store.jpa.model.basic;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.io.OutputStream;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.Iterator;
40 import java.util.LinkedList;
41 import java.util.List;
42 import java.util.ListIterator;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.UUID;
46 import java.util.zip.GZIPInputStream;
47 import java.util.zip.GZIPOutputStream;
48 import javax.persistence.EntityManager;
49 import javax.persistence.NoResultException;
50 import javax.persistence.Query;
51 import net.jcip.annotations.Immutable;
52 import net.jcip.annotations.NotThreadSafe;
53 import org.modeshape.common.util.IoUtil;
54 import org.modeshape.common.util.Logger;
55 import org.modeshape.common.util.StringUtil;
56 import org.modeshape.connector.store.jpa.JpaConnectorI18n;
57 import org.modeshape.connector.store.jpa.model.common.NamespaceEntity;
58 import org.modeshape.connector.store.jpa.model.common.WorkspaceEntity;
59 import org.modeshape.connector.store.jpa.util.Namespaces;
60 import org.modeshape.connector.store.jpa.util.RequestProcessorCache;
61 import org.modeshape.connector.store.jpa.util.Serializer;
62 import org.modeshape.connector.store.jpa.util.Workspaces;
63 import org.modeshape.connector.store.jpa.util.Serializer.LargeValues;
64 import org.modeshape.graph.ExecutionContext;
65 import org.modeshape.graph.JcrLexicon;
66 import org.modeshape.graph.Location;
67 import org.modeshape.graph.ModeShapeLexicon;
68 import org.modeshape.graph.NodeConflictBehavior;
69 import org.modeshape.graph.connector.UuidAlreadyExistsException;
70 import org.modeshape.graph.observe.Observer;
71 import org.modeshape.graph.property.Binary;
72 import org.modeshape.graph.property.Name;
73 import org.modeshape.graph.property.NameFactory;
74 import org.modeshape.graph.property.Path;
75 import org.modeshape.graph.property.PathFactory;
76 import org.modeshape.graph.property.PathNotFoundException;
77 import org.modeshape.graph.property.Property;
78 import org.modeshape.graph.property.PropertyFactory;
79 import org.modeshape.graph.property.PropertyType;
80 import org.modeshape.graph.property.Reference;
81 import org.modeshape.graph.property.ReferentialIntegrityException;
82 import org.modeshape.graph.property.UuidFactory;
83 import org.modeshape.graph.property.ValueFactories;
84 import org.modeshape.graph.property.ValueFactory;
85 import org.modeshape.graph.property.ValueFormatException;
86 import org.modeshape.graph.request.CloneBranchRequest;
87 import org.modeshape.graph.request.CloneWorkspaceRequest;
88 import org.modeshape.graph.request.CopyBranchRequest;
89 import org.modeshape.graph.request.CreateNodeRequest;
90 import org.modeshape.graph.request.CreateWorkspaceRequest;
91 import org.modeshape.graph.request.DeleteBranchRequest;
92 import org.modeshape.graph.request.DeleteChildrenRequest;
93 import org.modeshape.graph.request.DestroyWorkspaceRequest;
94 import org.modeshape.graph.request.GetWorkspacesRequest;
95 import org.modeshape.graph.request.InvalidRequestException;
96 import org.modeshape.graph.request.InvalidWorkspaceException;
97 import org.modeshape.graph.request.MoveBranchRequest;
98 import org.modeshape.graph.request.ReadAllChildrenRequest;
99 import org.modeshape.graph.request.ReadAllPropertiesRequest;
100 import org.modeshape.graph.request.ReadBlockOfChildrenRequest;
101 import org.modeshape.graph.request.ReadBranchRequest;
102 import org.modeshape.graph.request.ReadNextBlockOfChildrenRequest;
103 import org.modeshape.graph.request.ReadNodeRequest;
104 import org.modeshape.graph.request.ReadPropertyRequest;
105 import org.modeshape.graph.request.Request;
106 import org.modeshape.graph.request.UpdatePropertiesRequest;
107 import org.modeshape.graph.request.VerifyWorkspaceRequest;
108 import org.modeshape.graph.request.processor.RequestProcessor;
109
110
111
112
113 @NotThreadSafe
114 public class BasicRequestProcessor extends RequestProcessor {
115
116 protected final EntityManager entities;
117 protected final ValueFactory<String> stringFactory;
118 protected final PathFactory pathFactory;
119 protected final PropertyFactory propertyFactory;
120 protected final NameFactory nameFactory;
121 protected final UuidFactory uuidFactory;
122 protected final Namespaces namespaces;
123 protected final Workspaces workspaces;
124 protected final UUID rootNodeUuid;
125 protected final String rootNodeUuidString;
126 protected final String nameOfDefaultWorkspace;
127 protected final String[] predefinedWorkspaceNames;
128 protected final boolean creatingWorkspacesAllowed;
129 protected final Serializer serializer;
130 protected final long largeValueMinimumSizeInBytes;
131 protected final boolean compressData;
132 protected final Logger logger;
133 protected final RequestProcessorCache cache;
134 protected final boolean enforceReferentialIntegrity;
135 private final Set<Long> workspaceIdsWithChangedReferences = new HashSet<Long>();
136
137 private enum UuidConflictBehavior {
138 ALWAYS_CREATE_NEW_UUID,
139 REPLACE_EXISTING_NODE,
140 THROW_EXCEPTION
141 }
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156 public BasicRequestProcessor( String sourceName,
157 ExecutionContext context,
158 Observer observer,
159 EntityManager entityManager,
160 UUID rootNodeUuid,
161 String nameOfDefaultWorkspace,
162 String[] predefinedWorkspaceNames,
163 long largeValueMinimumSizeInBytes,
164 boolean creatingWorkspacesAllowed,
165 boolean compressData,
166 boolean enforceReferentialIntegrity ) {
167 super(sourceName, context, observer);
168 assert entityManager != null;
169 assert rootNodeUuid != null;
170 assert predefinedWorkspaceNames != null;
171 this.entities = entityManager;
172 ValueFactories valuesFactory = context.getValueFactories();
173 this.stringFactory = valuesFactory.getStringFactory();
174 this.pathFactory = valuesFactory.getPathFactory();
175 this.propertyFactory = context.getPropertyFactory();
176 this.nameFactory = valuesFactory.getNameFactory();
177 this.uuidFactory = valuesFactory.getUuidFactory();
178 this.namespaces = new Namespaces(entityManager);
179 this.workspaces = new Workspaces(entityManager);
180 this.rootNodeUuid = rootNodeUuid;
181 this.rootNodeUuidString = this.rootNodeUuid.toString();
182 this.nameOfDefaultWorkspace = nameOfDefaultWorkspace;
183 this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
184 this.largeValueMinimumSizeInBytes = largeValueMinimumSizeInBytes;
185 this.compressData = compressData;
186 this.enforceReferentialIntegrity = enforceReferentialIntegrity;
187 this.serializer = new Serializer(context, true);
188 this.logger = getExecutionContext().getLogger(getClass());
189 this.cache = new RequestProcessorCache(this.pathFactory);
190 this.predefinedWorkspaceNames = predefinedWorkspaceNames;
191
192
193 this.entities.getTransaction().begin();
194 }
195
196
197
198
199
200
201 @Override
202 public void process( CreateNodeRequest request ) {
203 logger.trace(request.toString());
204 Location actualLocation = null;
205 try {
206
207 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
208 if (workspace == null) return;
209 Long workspaceId = workspace.getId();
210 assert workspaceId != null;
211
212
213 Location parentLocation = request.under();
214 ActualLocation actual = getActualLocation(workspace, parentLocation);
215 String parentUuidString = actual.uuid;
216 assert parentUuidString != null;
217
218
219
220 String uuidString = null;
221 UUID uuid = null;
222 for (Property property : request.properties()) {
223 if (property.getName().equals(ModeShapeLexicon.UUID)) {
224 uuid = uuidFactory.create(property.getFirstValue());
225 uuidString = stringFactory.create(property.getFirstValue());
226 break;
227 }
228 }
229
230 if (uuid == null) {
231 for (Property property : request.properties()) {
232 if (property.getName().equals(JcrLexicon.UUID)) {
233 uuid = uuidFactory.create(property.getFirstValue());
234 uuidString = stringFactory.create(property.getFirstValue());
235 break;
236 }
237 }
238 }
239
240 switch (request.conflictBehavior()) {
241 case DO_NOT_REPLACE:
242 case UPDATE:
243 if (uuid != null) {
244 ActualLocation existing = getActualLocation(workspace, Location.create(uuid));
245
246 if (existing != null) {
247 if (NodeConflictBehavior.UPDATE.equals(request.conflictBehavior())) {
248 createProperties(workspace, uuidString, request.properties());
249 }
250
251 request.setActualLocationOfNode(existing.location);
252 return;
253 }
254 }
255
256 Name newName = request.named();
257 for (Location childLocation : getAllChildren(workspaceId, actual)) {
258 if (newName.equals(childLocation.getPath().getLastSegment().getName())) {
259 if (NodeConflictBehavior.UPDATE.equals(request.conflictBehavior())) {
260 createProperties(workspace, uuidString, request.properties());
261 }
262 request.setActualLocationOfNode(childLocation);
263 recordChange(request);
264 return;
265 }
266 }
267
268 break;
269
270 case REPLACE:
271 if (uuid != null) {
272 ActualLocation existing = getActualLocation(workspace, Location.create(uuid));
273
274 if (existing != null) {
275 delete(request, existing.location, workspace.getName(), true);
276 }
277 }
278 break;
279 case APPEND:
280 break;
281 }
282
283 if (uuidString == null) uuidString = UUID.randomUUID().toString();
284 assert uuidString != null;
285 createProperties(workspace, uuidString, request.properties());
286
287
288 Name childName = request.named();
289 String childNsUri = childName.getNamespaceUri();
290 NamespaceEntity ns = namespaces.get(childNsUri, true);
291 assert ns != null;
292 final Path parentPath = actual.location.getPath();
293 assert parentPath != null;
294
295
296 actualLocation = addNewChild(workspaceId, actual, uuidString, childName, true).location;
297
298
299 cache.setAllChildren(workspace.getId(), actualLocation.getPath(), new LinkedList<Location>());
300
301
302
303
304 } catch (Throwable e) {
305 request.setError(e);
306 logger.trace(e, "Problem " + request);
307 return;
308 }
309 request.setActualLocationOfNode(actualLocation);
310 recordChange(request);
311 }
312
313
314
315
316
317
318
319
320
321
322
323
324 protected ActualLocation addNewChild( Long workspaceId,
325 ActualLocation parent,
326 String childUuid,
327 Name childName,
328 boolean allowSameNameChildrenInNewNode ) {
329 int nextSnsIndex = 1;
330 int nextIndexInParent = 0;
331 String childNsUri = childName.getNamespaceUri();
332 NamespaceEntity ns = namespaces.get(childNsUri, true);
333 assert ns != null;
334
335
336 Path parentPath = null;
337 String parentUuid = null;
338 ChildEntity parentEntity = null;
339 if (parent == null) {
340 return new ActualLocation(Location.create(pathFactory.createRootPath(), UUID.fromString(childUuid)), childUuid, null);
341 }
342 parentPath = parent.location.getPath();
343 parentUuid = parent.uuid;
344 parentEntity = parent.childEntity;
345
346 assert workspaceId != null;
347
348 ChildId id = new ChildId(workspaceId, childUuid);
349 ChildEntity entity = null;
350
351
352 LinkedList<Location> childrenOfParent = cache.getAllChildren(workspaceId, parentPath);
353
354
355 if (parentEntity == null || parentEntity.getAllowsSameNameChildren()) {
356
357
358 if (childrenOfParent != null) {
359
360
361 nextIndexInParent = childrenOfParent.size();
362 if (nextIndexInParent > 0) {
363
364 ListIterator<Location> iter = childrenOfParent.listIterator(childrenOfParent.size());
365 while (iter.hasPrevious()) {
366 Location existing = iter.previous();
367 Path.Segment segment = existing.getPath().getLastSegment();
368 if (segment.getName().equals(childName)) {
369
370 nextSnsIndex = segment.getIndex() + 1;
371
372
373 break;
374 }
375 }
376 }
377 } else {
378
379
380
381
382 String childLocalName = childName.getLocalName();
383 Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
384 query.setParameter("workspaceId", workspaceId);
385 query.setParameter("parentUuid", parentUuid);
386 query.setParameter("ns", ns.getId());
387 query.setParameter("childName", childLocalName);
388 try {
389 Integer result = (Integer)query.getSingleResult();
390 nextSnsIndex = result != null ? result + 1 : 1;
391 } catch (NoResultException e) {
392 }
393
394
395 query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
396 query.setParameter("workspaceId", workspaceId);
397 query.setParameter("parentUuid", parentUuid);
398 try {
399 Integer result = (Integer)query.getSingleResult();
400 nextIndexInParent = result != null ? result + 1 : 0;
401 } catch (NoResultException e) {
402 }
403 }
404
405
406 entity = new ChildEntity(id, parentUuid, nextIndexInParent, ns, childName.getLocalName(), nextSnsIndex);
407 } else {
408
409
410 if (childrenOfParent != null) {
411
412
413 nextIndexInParent = childrenOfParent.size();
414 } else {
415
416 Query query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
417 query.setParameter("workspaceId", workspaceId);
418 query.setParameter("parentUuid", parentUuid);
419 try {
420 Integer result = (Integer)query.getSingleResult();
421 nextIndexInParent = result != null ? result + 1 : 0;
422 } catch (NoResultException e) {
423 }
424 }
425
426
427 entity = new ChildEntity(id, parentUuid, nextIndexInParent, ns, childName.getLocalName(), 1);
428 }
429
430
431 entity.setAllowsSameNameChildren(allowSameNameChildrenInNewNode);
432 entities.persist(entity);
433
434
435 Path path = pathFactory.create(parentPath, childName, nextSnsIndex);
436 Location actualLocation = Location.create(path, UUID.fromString(childUuid));
437
438
439 if (childrenOfParent != null) {
440
441 childrenOfParent.add(actualLocation);
442 } else {
443 cache.setAllChildren(workspaceId, parentPath, null);
444 }
445 return new ActualLocation(actualLocation, entity.getId().getChildUuidString(), entity);
446 }
447
448 protected class NextChildIndexes {
449 protected final int nextIndexInParent;
450 protected final int nextSnsIndex;
451
452 protected NextChildIndexes( int nextIndexInParent,
453 int nextSnsIndex ) {
454 this.nextIndexInParent = nextIndexInParent;
455 this.nextSnsIndex = nextSnsIndex;
456 }
457 }
458
459
460
461
462
463
464 @Override
465 public void process( ReadNodeRequest request ) {
466 logger.trace(request.toString());
467 Location actualLocation = null;
468 try {
469
470 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
471 if (workspace == null) return;
472 Long workspaceId = workspace.getId();
473 assert workspaceId != null;
474
475 Location location = request.at();
476 ActualLocation actual = getActualLocation(workspace, location);
477 String parentUuidString = actual.uuid;
478 actualLocation = actual.location;
479
480
481
482
483 request.addProperty(propertyFactory.create(ModeShapeLexicon.UUID, UUID.fromString(parentUuidString)));
484
485
486 Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
487 query.setParameter("workspaceId", workspaceId);
488 query.setParameter("uuid", parentUuidString);
489 try {
490 PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
491
492
493 boolean compressed = entity.isCompressed();
494 Collection<Property> properties = new LinkedList<Property>();
495 byte[] data = entity.getData();
496 if (data != null) {
497 LargeValueSerializer largeValues = new LargeValueSerializer(entity);
498 ByteArrayInputStream bais = new ByteArrayInputStream(data);
499 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
500 ObjectInputStream ois = new ObjectInputStream(is);
501 try {
502 serializer.deserializeAllProperties(ois, properties, largeValues);
503 for (Property property : properties) {
504 request.addProperty(property);
505 }
506 } finally {
507 ois.close();
508 }
509 }
510
511 } catch (NoResultException e) {
512
513 }
514
515
516 for (Location childLocation : getAllChildren(workspaceId, actual)) {
517 request.addChild(childLocation);
518 }
519 } catch (NoResultException e) {
520
521 } catch (Throwable e) {
522 request.setError(e);
523 return;
524 }
525 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
526 setCacheableInfo(request);
527 }
528
529
530
531
532
533
534 @Override
535 public void process( ReadAllChildrenRequest request ) {
536 logger.trace(request.toString());
537 Location actualLocation = null;
538 try {
539
540 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
541 if (workspace == null) return;
542 Long workspaceId = workspace.getId();
543 assert workspaceId != null;
544
545 Location location = request.of();
546 ActualLocation actual = getActualLocation(workspace, location);
547 actualLocation = actual.location;
548
549
550 for (Location childLocation : getAllChildren(workspaceId, actual)) {
551 request.addChild(childLocation);
552 }
553 } catch (NoResultException e) {
554
555 } catch (Throwable e) {
556 request.setError(e);
557 return;
558 }
559 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
560 setCacheableInfo(request);
561 }
562
563
564
565
566
567
568
569
570
571 protected LinkedList<Location> getAllChildren( Long workspaceId,
572 ActualLocation parent ) {
573 assert parent != null;
574 Path parentPath = parent.location.getPath();
575 assert parentPath != null;
576 LinkedList<Location> cachedChildren = cache.getAllChildren(workspaceId, parentPath);
577 if (cachedChildren != null) {
578
579 return cachedChildren;
580 }
581
582
583 Query query = entities.createNamedQuery("ChildEntity.findAllUnderParent");
584 query.setParameter("workspaceId", workspaceId);
585 query.setParameter("parentUuidString", parent.uuid);
586 LinkedList<Location> childLocations = new LinkedList<Location>();
587 @SuppressWarnings( "unchecked" )
588 List<ChildEntity> children = query.getResultList();
589 for (ChildEntity child : children) {
590 String namespaceUri = child.getChildNamespace().getUri();
591 String localName = child.getChildName();
592 Name childName = nameFactory.create(namespaceUri, localName);
593 int sns = child.getSameNameSiblingIndex();
594 Path childPath = pathFactory.create(parentPath, childName, sns);
595 String childUuidString = child.getId().getChildUuidString();
596 Location childLocation = Location.create(childPath, UUID.fromString(childUuidString));
597 childLocations.add(childLocation);
598 }
599
600 cache.setAllChildren(workspaceId, parentPath, childLocations);
601 return childLocations;
602 }
603
604
605
606
607
608
609 @Override
610 public void process( ReadBlockOfChildrenRequest request ) {
611 logger.trace(request.toString());
612 Location actualLocation = null;
613 final int startingIndex = request.startingAtIndex();
614 try {
615
616 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
617 if (workspace == null) return;
618 Long workspaceId = workspace.getId();
619 assert workspaceId != null;
620
621 Location parentLocation = request.of();
622 ActualLocation actualParent = getActualLocation(workspace, parentLocation);
623 actualLocation = actualParent.location;
624
625 Path parentPath = actualParent.location.getPath();
626 assert parentPath != null;
627 LinkedList<Location> cachedChildren = cache.getAllChildren(workspaceId, parentPath);
628 if (cachedChildren != null) {
629
630 if (startingIndex < cachedChildren.size()) {
631 ListIterator<Location> iter = cachedChildren.listIterator(startingIndex);
632 for (int i = 0; i != request.count() && iter.hasNext(); ++i) {
633 Location child = iter.next();
634 request.addChild(child);
635 }
636 }
637 } else {
638
639 Query query = entities.createNamedQuery("ChildEntity.findRangeUnderParent");
640 query.setParameter("workspaceId", workspaceId);
641 query.setParameter("parentUuidString", actualParent.uuid);
642 query.setParameter("firstIndex", startingIndex);
643 query.setParameter("afterIndex", startingIndex + request.count());
644 @SuppressWarnings( "unchecked" )
645 List<ChildEntity> children = query.getResultList();
646 for (ChildEntity child : children) {
647 String namespaceUri = child.getChildNamespace().getUri();
648 String localName = child.getChildName();
649 Name childName = nameFactory.create(namespaceUri, localName);
650 int sns = child.getSameNameSiblingIndex();
651 Path childPath = pathFactory.create(parentPath, childName, sns);
652 String childUuidString = child.getId().getChildUuidString();
653 Location childLocation = Location.create(childPath, UUID.fromString(childUuidString));
654 request.addChild(childLocation);
655 }
656
657 }
658
659 } catch (NoResultException e) {
660
661 } catch (Throwable e) {
662 request.setError(e);
663 return;
664 }
665 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
666 setCacheableInfo(request);
667 }
668
669
670
671
672
673
674 @Override
675 public void process( ReadNextBlockOfChildrenRequest request ) {
676 logger.trace(request.toString());
677 Location actualLocation = null;
678 final Location previousSibling = request.startingAfter();
679 final int count = request.count();
680 try {
681
682 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
683 if (workspace == null) return;
684 Long workspaceId = workspace.getId();
685 assert workspaceId != null;
686
687 ActualLocation actualSibling = getActualLocation(workspace, previousSibling);
688 actualLocation = actualSibling.location;
689 if (!actualLocation.getPath().isRoot()) {
690
691 Path parentPath = actualSibling.location.getPath().getParent();
692 assert parentPath != null;
693 LinkedList<Location> cachedChildren = cache.getAllChildren(workspaceId, parentPath);
694 if (cachedChildren != null) {
695
696
697 boolean accumulate = false;
698 int counter = 0;
699 for (Location child : cachedChildren) {
700 if (accumulate) {
701
702 request.addChild(child);
703 ++counter;
704 if (counter <= count) continue;
705 break;
706 }
707
708 if (child.isSame(previousSibling)) {
709 accumulate = true;
710 }
711 }
712 } else {
713
714
715
716 ChildEntity previousChild = actualSibling.childEntity;
717 if (previousChild == null) {
718 Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
719 query.setParameter("workspaceId", workspaceId);
720 query.setParameter("childUuidString", actualSibling.uuid);
721 previousChild = (ChildEntity)query.getSingleResult();
722 }
723 int startingIndex = previousChild.getIndexInParent() + 1;
724 String parentUuid = previousChild.getParentUuidString();
725
726
727 Query query = entities.createNamedQuery("ChildEntity.findRangeUnderParent");
728 query.setParameter("workspaceId", workspaceId);
729 query.setParameter("parentUuidString", parentUuid);
730 query.setParameter("firstIndex", startingIndex);
731 query.setParameter("afterIndex", startingIndex + request.count());
732 @SuppressWarnings( "unchecked" )
733 List<ChildEntity> children = query.getResultList();
734 LinkedList<Location> allChildren = null;
735 if (startingIndex == 1 && children.size() < request.count()) {
736
737
738
739 allChildren = new LinkedList<Location>();
740 allChildren.add(actualSibling.location);
741 }
742 for (ChildEntity child : children) {
743 String namespaceUri = child.getChildNamespace().getUri();
744 String localName = child.getChildName();
745 Name childName = nameFactory.create(namespaceUri, localName);
746 int sns = child.getSameNameSiblingIndex();
747 Path childPath = pathFactory.create(parentPath, childName, sns);
748 String childUuidString = child.getId().getChildUuidString();
749 Location childLocation = Location.create(childPath, UUID.fromString(childUuidString));
750 request.addChild(childLocation);
751 if (allChildren != null) {
752
753 allChildren.add(childLocation);
754 }
755 }
756
757 if (allChildren != null) {
758 cache.setAllChildren(workspaceId, parentPath, allChildren);
759 }
760 }
761 }
762
763 } catch (NoResultException e) {
764
765 } catch (Throwable e) {
766 request.setError(e);
767 return;
768 }
769 if (actualLocation != null) request.setActualLocationOfStartingAfterNode(actualLocation);
770 setCacheableInfo(request);
771 }
772
773
774
775
776
777
778 @Override
779 public void process( ReadAllPropertiesRequest request ) {
780 logger.trace(request.toString());
781 Location actualLocation = null;
782 try {
783
784 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
785 if (workspace == null) return;
786 Long workspaceId = workspace.getId();
787 assert workspaceId != null;
788
789 Location location = request.at();
790 ActualLocation actual = getActualLocation(workspace, location);
791 String uuidString = actual.uuid;
792 actualLocation = actual.location;
793
794
795 request.addProperty(actualLocation.getIdProperty(ModeShapeLexicon.UUID));
796
797
798 Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
799 query.setParameter("workspaceId", workspaceId);
800 query.setParameter("uuid", uuidString);
801 PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
802
803
804 boolean compressed = entity.isCompressed();
805 int propertyCount = entity.getPropertyCount();
806 Collection<Property> properties = new ArrayList<Property>(propertyCount);
807 byte[] data = entity.getData();
808 if (data != null) {
809 LargeValueSerializer largeValues = new LargeValueSerializer(entity);
810 ByteArrayInputStream bais = new ByteArrayInputStream(data);
811 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
812 ObjectInputStream ois = new ObjectInputStream(is);
813 try {
814 serializer.deserializeAllProperties(ois, properties, largeValues);
815 for (Property property : properties) {
816 request.addProperty(property);
817 }
818 } finally {
819 ois.close();
820 }
821 }
822 } catch (NoResultException e) {
823
824 } catch (Throwable e) {
825 request.setError(e);
826 return;
827 }
828 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
829 setCacheableInfo(request);
830 }
831
832
833
834
835
836
837 @Override
838 public void process( ReadPropertyRequest request ) {
839 logger.trace(request.toString());
840
841 Location actualLocation = null;
842 try {
843
844 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
845 if (workspace == null) return;
846 Long workspaceId = workspace.getId();
847 assert workspaceId != null;
848
849
850 final Name propertyName = request.named();
851 if (ModeShapeLexicon.UUID.equals(propertyName) || JcrLexicon.UUID.equals(propertyName)) {
852 try {
853
854 Location location = request.on();
855 ActualLocation actual = getActualLocation(workspace, location);
856 UUID uuid = actual.location.getUuid();
857 Property uuidProperty = getExecutionContext().getPropertyFactory().create(propertyName, uuid);
858 request.setProperty(uuidProperty);
859 request.setActualLocationOfNode(actual.location);
860 setCacheableInfo(request);
861 } catch (Throwable e) {
862 request.setError(e);
863 }
864 return;
865 }
866
867 Location location = request.on();
868 ActualLocation actual = getActualLocation(workspace, location);
869 String uuidString = actual.uuid;
870 actualLocation = actual.location;
871
872
873 Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
874 query.setParameter("workspaceId", workspaceId);
875 query.setParameter("uuid", uuidString);
876 PropertiesEntity entity = (PropertiesEntity)query.getSingleResult();
877
878
879 boolean compressed = entity.isCompressed();
880 int propertyCount = entity.getPropertyCount();
881 Collection<Property> properties = new ArrayList<Property>(propertyCount);
882 byte[] data = entity.getData();
883 if (data != null) {
884 LargeValueSerializer largeValues = new LargeValueSerializer(entity);
885 ByteArrayInputStream bais = new ByteArrayInputStream(data);
886 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
887 ObjectInputStream ois = new ObjectInputStream(is);
888 try {
889 Serializer.LargeValues skippedLargeValues = Serializer.NO_LARGE_VALUES;
890 serializer.deserializeSomeProperties(ois, properties, largeValues, skippedLargeValues, propertyName);
891 for (Property property : properties) {
892 request.setProperty(property);
893 }
894 } finally {
895 ois.close();
896 }
897 }
898 } catch (NoResultException e) {
899
900 } catch (Throwable e) {
901 request.setError(e);
902 return;
903 }
904 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
905 setCacheableInfo(request);
906 }
907
908
909
910
911
912
913 @Override
914 public void process( UpdatePropertiesRequest request ) {
915 logger.trace(request.toString());
916 Location actualLocation = null;
917 Set<Name> createdProperties = null;
918 try {
919
920 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
921 if (workspace == null) return;
922 Long workspaceId = workspace.getId();
923 assert workspaceId != null;
924
925 Location location = request.on();
926 ActualLocation actual = getActualLocation(workspace, location);
927 actualLocation = actual.location;
928
929
930 Query query = entities.createNamedQuery("PropertiesEntity.findByUuid");
931 query.setParameter("workspaceId", workspaceId);
932 query.setParameter("uuid", actual.uuid);
933 PropertiesEntity entity = null;
934 try {
935 entity = (PropertiesEntity)query.getSingleResult();
936
937
938
939 boolean compressed = entity.isCompressed();
940 byte[] originalData = entity.getData();
941 ByteArrayOutputStream baos = new ByteArrayOutputStream();
942 ObjectOutputStream oos;
943 GZIPOutputStream gos = null;
944 if (compressed) {
945 gos = new GZIPOutputStream(baos);
946 oos = new ObjectOutputStream(gos);
947 } else {
948 oos = new ObjectOutputStream(baos);
949 }
950
951
952 int numProps = 0;
953 LargeValueSerializer largeValues = null;
954 Map<Name, Property> props = request.properties();
955 References refs = enforceReferentialIntegrity ? new References() : null;
956 if (originalData == null) {
957 largeValues = new LargeValueSerializer(entity);
958 numProps = props.size();
959 createdProperties = props.keySet();
960 serializer.serializeProperties(oos, numProps, props.values(), largeValues, refs);
961 if (gos != null) gos.finish();
962 } else {
963 boolean hadLargeValues = !entity.getLargeValues().isEmpty();
964 Set<String> largeValueHashesWritten = hadLargeValues ? new HashSet<String>() : null;
965 largeValues = new LargeValueSerializer(entity, largeValueHashesWritten);
966 ByteArrayInputStream bais = new ByteArrayInputStream(originalData);
967 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
968 ObjectInputStream ois = new ObjectInputStream(is);
969 SkippedLargeValues removedValues = new SkippedLargeValues(largeValues);
970 createdProperties = new HashSet<Name>();
971 try {
972 Serializer.ReferenceValues refValues = refs != null ? refs : Serializer.NO_REFERENCES_VALUES;
973 numProps = serializer.reserializeProperties(ois,
974 oos,
975 props,
976 largeValues,
977 removedValues,
978 createdProperties,
979 refValues);
980 if (gos != null) gos.finish();
981 } finally {
982 try {
983 ois.close();
984 } finally {
985 oos.close();
986 }
987 }
988
989
990
991 if (hadLargeValues) {
992
993 removedValues.skippedKeys.removeAll(largeValueHashesWritten);
994 for (String oldHexKey : removedValues.skippedKeys) {
995 LargeValueId id = new LargeValueId(oldHexKey);
996 entity.getLargeValues().remove(id);
997 }
998 }
999
1000 if (refs != null) {
1001
1002 if (refs.hasRemoved()) {
1003 for (Reference reference : refs.getRemoved()) {
1004 String toUuid = resolveToUuid(workspace, reference);
1005 if (toUuid != null) {
1006 ReferenceId id = new ReferenceId(workspaceId, actual.uuid, toUuid);
1007 ReferenceEntity refEntity = entities.find(ReferenceEntity.class, id);
1008 if (refEntity != null) {
1009 entities.remove(refEntity);
1010 workspaceIdsWithChangedReferences.add(workspaceId);
1011 }
1012 }
1013 }
1014 }
1015 }
1016 }
1017 entity.setPropertyCount(numProps);
1018 entity.setData(baos.toByteArray());
1019 entity.setCompressed(compressData);
1020
1021 if (refs != null && refs.hasWritten()) {
1022
1023 Set<Reference> newReferences = refs.getWritten();
1024
1025 newReferences.removeAll(refs.getRead());
1026 if (newReferences.size() != 0) {
1027
1028 for (Reference reference : newReferences) {
1029 String toUuid = resolveToUuid(workspace, reference);
1030 if (toUuid != null) {
1031 ReferenceId id = new ReferenceId(workspaceId, actual.uuid, toUuid);
1032 ReferenceEntity refEntity = new ReferenceEntity(id);
1033 entities.persist(refEntity);
1034 workspaceIdsWithChangedReferences.add(workspaceId);
1035 }
1036 }
1037 }
1038 }
1039 } catch (NoResultException e) {
1040
1041 createProperties(workspace, actual.uuid, request.properties().values());
1042 createdProperties = request.properties().keySet();
1043 }
1044
1045 } catch (Throwable e) {
1046 request.setError(e);
1047 return;
1048 }
1049 if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
1050 request.setNewProperties(createdProperties);
1051 recordChange(request);
1052 }
1053
1054
1055
1056
1057
1058
1059 @Override
1060 public void process( ReadBranchRequest request ) {
1061 logger.trace(request.toString());
1062 Location actualLocation = null;
1063 try {
1064
1065 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
1066 if (workspace == null) return;
1067 Long workspaceId = workspace.getId();
1068 assert workspaceId != null;
1069
1070 Location location = request.at();
1071 ActualLocation actual = getActualLocation(workspace, location);
1072 actualLocation = actual.location;
1073 Path path = actualLocation.getPath();
1074
1075
1076 Map<String, Location> locationsByUuid = new HashMap<String, Location>();
1077 locationsByUuid.put(actual.uuid, location);
1078
1079
1080 int maxDepth = request.maximumDepth();
1081 SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
1082 entities,
1083 workspaceId,
1084 actualLocation.getUuid(),
1085 path,
1086 maxDepth);
1087
1088 try {
1089
1090 Path parent = path;
1091 String parentUuid = actual.uuid;
1092 Location parentLocation = actualLocation;
1093 List<Location> children = new LinkedList<Location>();
1094 Map<Location, List<Location>> childrenByParentLocation = new HashMap<Location, List<Location>>();
1095 childrenByParentLocation.put(parentLocation, children);
1096 boolean includeChildrenOfNodesAtMaxDepth = true;
1097 for (ChildEntity child : query.getNodes(false, includeChildrenOfNodesAtMaxDepth)) {
1098 String namespaceUri = child.getChildNamespace().getUri();
1099 String localName = child.getChildName();
1100 Name childName = nameFactory.create(namespaceUri, localName);
1101 int sns = child.getSameNameSiblingIndex();
1102
1103 String childParentUuid = child.getParentUuidString();
1104 if (!parentUuid.equals(childParentUuid)) {
1105
1106 parentLocation = locationsByUuid.get(childParentUuid);
1107 parent = parentLocation.getPath();
1108 parentUuid = childParentUuid;
1109
1110 children = childrenByParentLocation.get(parentLocation);
1111 if (children == null) {
1112 children = new LinkedList<Location>();
1113 childrenByParentLocation.put(parentLocation, children);
1114 }
1115 }
1116 assert children != null;
1117 Path childPath = pathFactory.create(parent, childName, sns);
1118 String childUuidString = child.getId().getChildUuidString();
1119 Location childLocation = Location.create(childPath, UUID.fromString(childUuidString));
1120 locationsByUuid.put(childUuidString, childLocation);
1121 children.add(childLocation);
1122 }
1123
1124 for (Map.Entry<Location, List<Location>> entry : childrenByParentLocation.entrySet()) {
1125
1126 if (!entry.getValue().isEmpty()) {
1127 request.setChildren(entry.getKey(), entry.getValue());
1128 }
1129 }
1130
1131
1132
1133
1134
1135 includeChildrenOfNodesAtMaxDepth = false;
1136
1137
1138 for (PropertiesEntity props : query.getProperties(true, includeChildrenOfNodesAtMaxDepth)) {
1139 boolean compressed = props.isCompressed();
1140 int propertyCount = props.getPropertyCount();
1141 Collection<Property> properties = new ArrayList<Property>(propertyCount);
1142 Location nodeLocation = locationsByUuid.get(props.getId().getUuidString());
1143 assert nodeLocation != null;
1144
1145 properties.add(actualLocation.getIdProperty(ModeShapeLexicon.UUID));
1146
1147 byte[] data = props.getData();
1148 if (data != null) {
1149 LargeValueSerializer largeValues = new LargeValueSerializer(props);
1150 ByteArrayInputStream bais = new ByteArrayInputStream(data);
1151 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
1152 ObjectInputStream ois = new ObjectInputStream(is);
1153 try {
1154 serializer.deserializeAllProperties(ois, properties, largeValues);
1155 request.setProperties(nodeLocation, properties);
1156 } finally {
1157 ois.close();
1158 }
1159 }
1160 }
1161 } finally {
1162
1163 query.close();
1164 }
1165
1166 } catch (Throwable e) {
1167 request.setError(e);
1168 return;
1169 }
1170 request.setActualLocationOfNode(actualLocation);
1171 setCacheableInfo(request);
1172 }
1173
1174 private ActualLocation copyNode( EntityManager entities,
1175 WorkspaceEntity fromWorkspace,
1176 WorkspaceEntity intoWorkspace,
1177 ChildEntity original,
1178 ActualLocation actualNewParent,
1179 UuidConflictBehavior uuidConflictBehavior,
1180 Name desiredName,
1181 Path.Segment desiredSegment,
1182 Map<String, String> oldUuidsToNewUuids,
1183 Map<Location, ChildEntity> addedLocations,
1184 Map<String, Location> deletedLocations ) {
1185 assert fromWorkspace != null;
1186 assert intoWorkspace != null;
1187 assert original != null;
1188 assert (desiredName != null ? desiredSegment == null : desiredSegment != null) : "Either desiredName or desiredSegment must not be null";
1189 assert oldUuidsToNewUuids != null;
1190
1191
1192 String newUuid = original.getId().getChildUuidString();
1193 ActualLocation newLocation = null;
1194 ActualLocation existingLocation = null;
1195
1196 UUID oldUuid = UUID.fromString(original.getId().getChildUuidString());
1197
1198 switch (uuidConflictBehavior) {
1199 case ALWAYS_CREATE_NEW_UUID:
1200 newUuid = UUID.randomUUID().toString();
1201
1202 break;
1203 case THROW_EXCEPTION:
1204 try {
1205 existingLocation = getActualLocation(intoWorkspace, Location.create(oldUuid));
1206 String pathAsString = existingLocation.toString();
1207 throw new UuidAlreadyExistsException(this.getSourceName(), oldUuid, pathAsString, intoWorkspace.getName());
1208 } catch (PathNotFoundException pnfe) {
1209
1210 }
1211 break;
1212 case REPLACE_EXISTING_NODE:
1213 try {
1214 if (desiredSegment != null) {
1215 Location nLocation = Location.create(pathFactory.create(actualNewParent.location.getPath(),
1216 desiredSegment));
1217 existingLocation = getActualLocation(intoWorkspace, nLocation);
1218 nLocation = Location.create(nLocation.getPath(), UUID.fromString(existingLocation.uuid));
1219 assert nLocation.getUuid() != null;
1220 deletedLocations.putAll(computeDeletedLocations(intoWorkspace, nLocation, true));
1221 newUuid = existingLocation.childEntity.getId().getChildUuidString();
1222
1223 } else {
1224 existingLocation = getActualLocation(intoWorkspace, Location.create(oldUuid));
1225 deletedLocations.putAll(computeDeletedLocations(intoWorkspace, existingLocation.location, true));
1226 }
1227 } catch (PathNotFoundException pnfe) {
1228 }
1229 break;
1230 default:
1231 throw new IllegalStateException("Unexpected UuidConflictBehavior value: " + uuidConflictBehavior);
1232 }
1233 oldUuidsToNewUuids.put(original.getId().getChildUuidString(), newUuid);
1234
1235 if (existingLocation != null && existingLocation.childEntity.getParentUuidString().equals(actualNewParent.uuid)) {
1236 if (desiredName == null) {
1237 assert desiredSegment != null;
1238 desiredName = desiredSegment.getName();
1239 }
1240 NamespaceEntity namespace = NamespaceEntity.findByUri(entities, desiredName.getNamespaceUri());
1241
1242 ChildEntity existingChild = existingLocation.childEntity;
1243 existingChild.setChildName(desiredName.getLocalName());
1244 existingChild.setChildNamespace(namespace);
1245 existingChild.setParentUuidString(actualNewParent.uuid);
1246 existingChild.setAllowsSameNameChildren(original.getAllowsSameNameChildren());
1247 existingChild.setIndexInParent(original.getIndexInParent());
1248 existingChild.setSameNameSiblingIndex(original.getSameNameSiblingIndex());
1249 Location parentLocation = actualNewParent.location;
1250 assert parentLocation.hasPath();
1251
1252 Name segmentName = nameFactory.create(existingChild.getChildNamespace().getUri(), existingChild.getChildName());
1253 Path.Segment newSegment = pathFactory.createSegment(segmentName, existingChild.getSameNameSiblingIndex());
1254 Path newPath = pathFactory.create(parentLocation.getPath(), newSegment);
1255 newLocation = new ActualLocation(Location.create(newPath, UUID.fromString(newUuid)), newUuid, existingChild);
1256
1257 } else {
1258
1259 boolean allowSnS = original.getAllowsSameNameChildren();
1260 if (desiredName == null) {
1261 assert desiredSegment != null;
1262 desiredName = desiredSegment.getName();
1263 }
1264 newLocation = addNewChild(intoWorkspace.getId(), actualNewParent, newUuid, desiredName, allowSnS);
1265 }
1266
1267 assert newLocation != null;
1268 addedLocations.put(newLocation.location, newLocation.childEntity);
1269
1270 return newLocation;
1271 }
1272
1273
1274
1275
1276
1277
1278 @Override
1279 public void process( CopyBranchRequest request ) {
1280 logger.trace(request.toString());
1281 Location actualFromLocation = null;
1282 Location actualToLocation = null;
1283 try {
1284
1285 WorkspaceEntity fromWorkspace = getExistingWorkspace(request.fromWorkspace(), request);
1286 if (fromWorkspace == null) return;
1287 WorkspaceEntity intoWorkspace = getExistingWorkspace(request.intoWorkspace(), request);
1288 if (intoWorkspace == null) return;
1289 Long fromWorkspaceId = fromWorkspace.getId();
1290 Long intoWorkspaceId = intoWorkspace.getId();
1291 assert fromWorkspaceId != null;
1292 assert intoWorkspaceId != null;
1293
1294 Location fromLocation = request.from();
1295 ActualLocation actualFrom = getActualLocation(fromWorkspace, fromLocation);
1296 actualFromLocation = actualFrom.location;
1297 Path fromPath = actualFromLocation.getPath();
1298
1299 Location newParentLocation = request.into();
1300 ActualLocation actualNewParent = getActualLocation(intoWorkspace, newParentLocation);
1301 assert actualNewParent != null;
1302
1303
1304 Map<String, String> originalToNewUuid = new HashMap<String, String>();
1305
1306
1307 SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
1308 entities,
1309 fromWorkspaceId,
1310 actualFromLocation.getUuid(),
1311 fromPath,
1312 0);
1313 try {
1314
1315 List<ChildEntity> originalNodes = query.getNodes(true, true);
1316 Iterator<ChildEntity> originalIter = originalNodes.iterator();
1317 Map<Location, ChildEntity> addedLocations = new HashMap<Location, ChildEntity>();
1318 Map<String, Location> deletedLocations = new HashMap<String, Location>();
1319
1320
1321 if (originalIter.hasNext()) {
1322 ChildEntity original = originalIter.next();
1323
1324 Name desiredName = request.desiredName();
1325 if (desiredName == null) desiredName = fromPath.getLastSegment().getName();
1326 actualToLocation = this.copyNode(entities,
1327 fromWorkspace,
1328 intoWorkspace,
1329 original,
1330 actualNewParent,
1331 UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
1332 desiredName,
1333 null,
1334 originalToNewUuid,
1335 addedLocations,
1336 deletedLocations).location;
1337 }
1338
1339
1340 while (originalIter.hasNext()) {
1341 ChildEntity original = originalIter.next();
1342 String newParentUuidOfCopy = originalToNewUuid.get(original.getParentUuidString());
1343 assert newParentUuidOfCopy != null;
1344
1345 actualNewParent = getActualLocation(intoWorkspace, Location.create(UUID.fromString(newParentUuidOfCopy)));
1346
1347 Name desiredName = nameFactory.create(original.getChildNamespace().getUri(), original.getChildName());
1348 this.copyNode(entities,
1349 fromWorkspace,
1350 intoWorkspace,
1351 original,
1352 actualNewParent,
1353 UuidConflictBehavior.ALWAYS_CREATE_NEW_UUID,
1354 desiredName,
1355 null,
1356 originalToNewUuid,
1357 addedLocations,
1358 deletedLocations);
1359 }
1360 entities.flush();
1361
1362 Set<String> newNodesWithReferenceProperties = new HashSet<String>();
1363
1364 for (ReferenceEntity reference : query.getInternalReferences()) {
1365 String newFromUuid = originalToNewUuid.get(reference.getId().getFromUuidString());
1366 assert newFromUuid != null;
1367 String newToUuid = originalToNewUuid.get(reference.getId().getToUuidString());
1368 assert newToUuid != null;
1369 ReferenceEntity copy = new ReferenceEntity(new ReferenceId(intoWorkspaceId, newFromUuid, newToUuid));
1370 entities.persist(copy);
1371 newNodesWithReferenceProperties.add(newFromUuid);
1372 }
1373
1374
1375
1376 for (ReferenceEntity reference : query.getOutwardReferences()) {
1377 String oldToUuid = reference.getId().getToUuidString();
1378 String newFromUuid = originalToNewUuid.get(reference.getId().getFromUuidString());
1379 assert newFromUuid != null;
1380
1381 ActualLocation refTargetLocation = getActualLocation(intoWorkspace,
1382 Location.create(UUID.fromString(oldToUuid)));
1383 if (refTargetLocation == null) {
1384
1385
1386 ValueFactory<Reference> refFactory = getExecutionContext().getValueFactories().getReferenceFactory();
1387 Map<Location, List<Reference>> invalidRefs = new HashMap<Location, List<Reference>>();
1388 UUID fromUuid = UUID.fromString(reference.getId().getFromUuidString());
1389 ActualLocation actualRefFromLocation = getActualLocation(intoWorkspace, Location.create(fromUuid));
1390 Location refFromLocation = actualRefFromLocation.location;
1391 List<Reference> refs = invalidRefs.get(fromLocation);
1392 if (refs == null) {
1393 refs = new ArrayList<Reference>();
1394 invalidRefs.put(refFromLocation, refs);
1395 }
1396 UUID toUuid = UUID.fromString(oldToUuid);
1397 refs.add(refFactory.create(toUuid));
1398
1399 String msg = JpaConnectorI18n.invalidReferences.text(reference.getId().getFromUuidString());
1400 throw new ReferentialIntegrityException(invalidRefs, msg);
1401 }
1402
1403 ReferenceEntity copy = new ReferenceEntity(new ReferenceId(intoWorkspaceId, newFromUuid, oldToUuid));
1404 entities.persist(copy);
1405 newNodesWithReferenceProperties.add(newFromUuid);
1406 }
1407
1408 Set<PropertiesEntity> addedProps = new HashSet<PropertiesEntity>();
1409
1410 for (PropertiesEntity original : query.getProperties(true, true)) {
1411
1412 String copyUuid = originalToNewUuid.get(original.getId().getUuidString());
1413 assert copyUuid != null;
1414
1415
1416 boolean compressed = original.isCompressed();
1417 byte[] originalData = original.getData();
1418 NodeId propertiesId = new NodeId(intoWorkspaceId, copyUuid);
1419 PropertiesEntity copy = entities.find(PropertiesEntity.class, propertiesId);
1420
1421 if (copy == null) {
1422 copy = new PropertiesEntity(propertiesId);
1423 }
1424 copy.setCompressed(compressed);
1425 if (newNodesWithReferenceProperties.contains(copyUuid)) {
1426
1427
1428 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1429 OutputStream os = compressed ? new GZIPOutputStream(baos) : baos;
1430 ObjectOutputStream oos = new ObjectOutputStream(os);
1431 ByteArrayInputStream bais = new ByteArrayInputStream(originalData);
1432 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
1433 ObjectInputStream ois = new ObjectInputStream(is);
1434 try {
1435 serializer.adjustReferenceProperties(ois, oos, originalToNewUuid);
1436 } finally {
1437 try {
1438 ois.close();
1439 } finally {
1440 oos.close();
1441 }
1442 }
1443 copy.setData(baos.toByteArray());
1444 } else {
1445
1446 copy.setData(originalData);
1447 }
1448 copy.setPropertyCount(original.getPropertyCount());
1449 copy.setReferentialIntegrityEnforced(original.isReferentialIntegrityEnforced());
1450 addedProps.add(copy);
1451 entities.persist(copy);
1452 }
1453 entities.flush();
1454 } finally {
1455
1456 query.close();
1457 }
1458
1459 } catch (Throwable e) {
1460 request.setError(e);
1461 return;
1462 }
1463 request.setActualLocations(actualFromLocation, actualToLocation);
1464 recordChange(request);
1465 }
1466
1467
1468
1469
1470
1471
1472 @Override
1473 public void process( CloneBranchRequest request ) {
1474 logger.trace(request.toString());
1475 Location actualFromLocation = null;
1476 Location actualToLocation = null;
1477 Set<Location> removedLocations = null;
1478 try {
1479
1480 WorkspaceEntity fromWorkspace = getExistingWorkspace(request.fromWorkspace(), request);
1481 if (fromWorkspace == null) return;
1482 WorkspaceEntity intoWorkspace = getExistingWorkspace(request.intoWorkspace(), request);
1483 if (intoWorkspace == null) return;
1484 Long fromWorkspaceId = fromWorkspace.getId();
1485 Long intoWorkspaceId = intoWorkspace.getId();
1486 assert fromWorkspaceId != null;
1487 assert intoWorkspaceId != null;
1488
1489 Location fromLocation = request.from();
1490 ActualLocation actualFrom = getActualLocation(fromWorkspace, fromLocation);
1491 actualFromLocation = actualFrom.location;
1492 Path fromPath = actualFromLocation.getPath();
1493
1494 Location newParentLocation = request.into();
1495 ActualLocation actualNewParent = getActualLocation(intoWorkspace, newParentLocation);
1496 assert actualNewParent != null;
1497
1498
1499 Map<String, String> originalToNewUuid = new HashMap<String, String>();
1500
1501
1502 SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
1503 entities,
1504 fromWorkspaceId,
1505 actualFromLocation.getUuid(),
1506 fromPath,
1507 0);
1508
1509 UuidConflictBehavior conflictBehavior = request.removeExisting() ? UuidConflictBehavior.REPLACE_EXISTING_NODE : UuidConflictBehavior.THROW_EXCEPTION;
1510 try {
1511
1512 List<ChildEntity> originalNodes = query.getNodes(true, true);
1513 Iterator<ChildEntity> originalIter = originalNodes.iterator();
1514 Map<Location, ChildEntity> addedLocations = new HashMap<Location, ChildEntity>();
1515 Map<String, Location> deletedLocations = new HashMap<String, Location>();
1516
1517
1518 if (originalIter.hasNext()) {
1519 ChildEntity original = originalIter.next();
1520
1521 Name desiredName = request.desiredName();
1522 actualToLocation = this.copyNode(entities,
1523 fromWorkspace,
1524 intoWorkspace,
1525 original,
1526 actualNewParent,
1527 conflictBehavior,
1528 desiredName,
1529 desiredName != null ? null : fromPath.getLastSegment(),
1530 originalToNewUuid,
1531 addedLocations,
1532 deletedLocations).location;
1533 }
1534
1535
1536 while (originalIter.hasNext()) {
1537 ChildEntity original = originalIter.next();
1538 String newParentUuidOfCopy = originalToNewUuid.get(original.getParentUuidString());
1539 assert newParentUuidOfCopy != null;
1540
1541 actualNewParent = getActualLocation(intoWorkspace, Location.create(UUID.fromString(newParentUuidOfCopy)));
1542
1543 Name desiredName = nameFactory.create(original.getChildNamespace().getUri(), original.getChildName());
1544 this.copyNode(entities,
1545 fromWorkspace,
1546 intoWorkspace,
1547 original,
1548 actualNewParent,
1549 conflictBehavior,
1550 desiredName,
1551 null,
1552 originalToNewUuid,
1553 addedLocations,
1554 deletedLocations);
1555 }
1556 entities.flush();
1557
1558 Set<String> newNodesWithReferenceProperties = new HashSet<String>();
1559
1560 for (ReferenceEntity reference : query.getInternalReferences()) {
1561 String newFromUuid = originalToNewUuid.get(reference.getId().getFromUuidString());
1562 assert newFromUuid != null;
1563 String newToUuid = originalToNewUuid.get(reference.getId().getToUuidString());
1564 assert newToUuid != null;
1565 ReferenceEntity copy = new ReferenceEntity(new ReferenceId(intoWorkspaceId, newFromUuid, newToUuid));
1566 entities.persist(copy);
1567 newNodesWithReferenceProperties.add(newFromUuid);
1568 }
1569
1570
1571
1572 for (ReferenceEntity reference : query.getOutwardReferences()) {
1573 String oldToUuid = reference.getId().getToUuidString();
1574 String newFromUuid = originalToNewUuid.get(reference.getId().getFromUuidString());
1575 assert newFromUuid != null;
1576
1577 ActualLocation refTargetLocation = getActualLocation(intoWorkspace,
1578 Location.create(UUID.fromString(oldToUuid)));
1579 if (refTargetLocation == null) {
1580
1581
1582 ValueFactory<Reference> refFactory = getExecutionContext().getValueFactories().getReferenceFactory();
1583 Map<Location, List<Reference>> invalidRefs = new HashMap<Location, List<Reference>>();
1584 UUID fromUuid = UUID.fromString(reference.getId().getFromUuidString());
1585 ActualLocation actualRefFromLocation = getActualLocation(intoWorkspace, Location.create(fromUuid));
1586 Location refFromLocation = actualRefFromLocation.location;
1587 List<Reference> refs = invalidRefs.get(fromLocation);
1588 if (refs == null) {
1589 refs = new ArrayList<Reference>();
1590 invalidRefs.put(refFromLocation, refs);
1591 }
1592 UUID toUuid = UUID.fromString(oldToUuid);
1593 refs.add(refFactory.create(toUuid));
1594
1595 String msg = JpaConnectorI18n.invalidReferences.text(reference.getId().getFromUuidString());
1596 throw new ReferentialIntegrityException(invalidRefs, msg);
1597 }
1598
1599 ReferenceEntity copy = new ReferenceEntity(new ReferenceId(intoWorkspaceId, newFromUuid, oldToUuid));
1600 entities.persist(copy);
1601 newNodesWithReferenceProperties.add(newFromUuid);
1602 }
1603
1604 Set<PropertiesEntity> addedProps = new HashSet<PropertiesEntity>();
1605
1606 for (PropertiesEntity original : query.getProperties(true, true)) {
1607
1608 String copyUuid = originalToNewUuid.get(original.getId().getUuidString());
1609 assert copyUuid != null;
1610
1611
1612 boolean compressed = original.isCompressed();
1613 byte[] originalData = original.getData();
1614 NodeId propertiesId = new NodeId(intoWorkspaceId, copyUuid);
1615 PropertiesEntity copy = entities.find(PropertiesEntity.class, propertiesId);
1616
1617 if (copy == null) {
1618 copy = new PropertiesEntity(propertiesId);
1619 }
1620 copy.setCompressed(compressed);
1621 if (newNodesWithReferenceProperties.contains(copyUuid)) {
1622
1623
1624 ByteArrayOutputStream baos = new ByteArrayOutputStream();
1625 OutputStream os = compressed ? new GZIPOutputStream(baos) : baos;
1626 ObjectOutputStream oos = new ObjectOutputStream(os);
1627 ByteArrayInputStream bais = new ByteArrayInputStream(originalData);
1628 InputStream is = compressed ? new GZIPInputStream(bais) : bais;
1629 ObjectInputStream ois = new ObjectInputStream(is);
1630 try {
1631 serializer.adjustReferenceProperties(ois, oos, originalToNewUuid);
1632 } finally {
1633 try {
1634 ois.close();
1635 } finally {
1636 oos.close();
1637 }
1638 }
1639 copy.setData(baos.toByteArray());
1640 } else {
1641
1642 copy.setData(originalData);
1643 }
1644 copy.setPropertyCount(original.getPropertyCount());
1645 copy.setReferentialIntegrityEnforced(original.isReferentialIntegrityEnforced());
1646 addedProps.add(copy);
1647 entities.persist(copy);
1648 }
1649 entities.flush();
1650
1651 if (request.removeExisting()) {
1652
1653
1654
1655
1656
1657 Map<String, Location> netDeletedLocations = new HashMap<String, Location>(deletedLocations);
1658 netDeletedLocations.values().removeAll(addedLocations.keySet());
1659
1660 if (netDeletedLocations.size() > 0) {
1661
1662 List<ReferenceEntity> invalidReferences = ReferenceEntity.getReferencesToUuids(intoWorkspace.getId(),
1663 netDeletedLocations.keySet(),
1664 entities);
1665
1666 for (Iterator<ReferenceEntity> iter = invalidReferences.iterator(); iter.hasNext();) {
1667 ReferenceEntity invalidRef = iter.next();
1668 if (netDeletedLocations.keySet().contains(invalidRef.getId().getFromUuidString())) {
1669 iter.remove();
1670 }
1671 }
1672
1673 if (invalidReferences.size() > 0) {
1674
1675
1676 ValueFactory<Reference> refFactory = getExecutionContext().getValueFactories().getReferenceFactory();
1677 Map<Location, List<Reference>> invalidRefs = new HashMap<Location, List<Reference>>();
1678 for (ReferenceEntity entity : invalidReferences) {
1679 UUID fromUuid = UUID.fromString(entity.getId().getFromUuidString());
1680 ActualLocation actualRefFromLocation = getActualLocation(intoWorkspace, Location.create(fromUuid));
1681 Location refFromLocation = actualRefFromLocation.location;
1682 List<Reference> refs = invalidRefs.get(fromLocation);
1683 if (refs == null) {
1684 refs = new ArrayList<Reference>();
1685 invalidRefs.put(refFromLocation, refs);
1686 }
1687 UUID toUuid = UUID.fromString(entity.getId().getToUuidString());
1688 refs.add(refFactory.create(toUuid));
1689 }
1690 String msg = JpaConnectorI18n.unableToDeleteBecauseOfReferences.text();
1691 throw new ReferentialIntegrityException(invalidRefs, msg);
1692 }
1693
1694
1695
1696
1697 for (Location location : netDeletedLocations.values()) {
1698 ActualLocation node = getActualLocation(intoWorkspace, location);
1699 ChildEntity entity = node.childEntity;
1700
1701 if (entity != null) {
1702 entities.remove(node.childEntity);
1703 }
1704 PropertiesEntity.deletePropertiesFor(intoWorkspace.getId(), node.uuid, entities);
1705
1706 }
1707
1708 cache.removeBranch(intoWorkspaceId, netDeletedLocations.values());
1709 }
1710
1711 if (!deletedLocations.isEmpty()) {
1712 removedLocations = Collections.unmodifiableSet(new HashSet<Location>(deletedLocations.values()));
1713 }
1714 LargeValueEntity.deleteUnused(entities);
1715 }
1716
1717
1718
1719 } finally {
1720
1721 query.close();
1722 }
1723
1724 } catch (Throwable e) {
1725 request.setError(e);
1726 return;
1727 }
1728 request.setActualLocations(actualFromLocation, actualToLocation);
1729 request.setRemovedNodes(removedLocations);
1730 recordChange(request);
1731 }
1732
1733
1734
1735
1736
1737
1738 @Override
1739 public void process( DeleteBranchRequest request ) {
1740 logger.trace(request.toString());
1741 Location location = delete(request, request.at(), request.inWorkspace(), true);
1742 if (location != null) {
1743 request.setActualLocationOfNode(location);
1744 recordChange(request);
1745 }
1746 }
1747
1748
1749
1750
1751
1752
1753 @Override
1754 public void process( DeleteChildrenRequest request ) {
1755 logger.trace(request.toString());
1756 Location location = delete(request, request.at(), request.inWorkspace(), false);
1757 if (location != null) {
1758 request.setActualLocationOfNode(location);
1759 recordChange(request);
1760 }
1761 }
1762
1763 protected Location delete( Request request,
1764 Location location,
1765 String workspaceName,
1766 boolean deleteTopOfBranch ) {
1767 Location actualLocation = null;
1768 try {
1769
1770 WorkspaceEntity workspace = getExistingWorkspace(workspaceName, request);
1771 if (workspace == null) return null;
1772 Long workspaceId = workspace.getId();
1773 assert workspaceId != null;
1774
1775 ActualLocation actual = getActualLocation(workspace, location);
1776 actualLocation = actual.location;
1777 Path path = actualLocation.getPath();
1778
1779
1780 SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
1781 entities,
1782 workspaceId,
1783 actualLocation.getUuid(),
1784 path,
1785 0);
1786 try {
1787 ChildEntity deleted = query.getNode();
1788 String parentUuidString = deleted.getParentUuidString();
1789 String childName = deleted.getChildName();
1790 long nsId = deleted.getChildNamespace().getId();
1791 int indexInParent = deleted.getIndexInParent();
1792
1793
1794 List<Location> deletedLocations = query.getNodeLocations(true, true);
1795
1796
1797 query.deleteSubgraph(deleteTopOfBranch);
1798
1799
1800 List<ReferenceEntity> invalidReferences = query.getInwardReferences();
1801 if (invalidReferences.size() > 0) {
1802
1803
1804 ValueFactory<Reference> refFactory = getExecutionContext().getValueFactories().getReferenceFactory();
1805 Map<Location, List<Reference>> invalidRefs = new HashMap<Location, List<Reference>>();
1806 for (ReferenceEntity entity : invalidReferences) {
1807 UUID fromUuid = UUID.fromString(entity.getId().getFromUuidString());
1808 ActualLocation actualFromLocation = getActualLocation(workspace, Location.create(fromUuid));
1809 Location fromLocation = actualFromLocation.location;
1810 List<Reference> refs = invalidRefs.get(fromLocation);
1811 if (refs == null) {
1812 refs = new ArrayList<Reference>();
1813 invalidRefs.put(fromLocation, refs);
1814 }
1815 UUID toUuid = UUID.fromString(entity.getId().getToUuidString());
1816 refs.add(refFactory.create(toUuid));
1817 }
1818 String msg = JpaConnectorI18n.unableToDeleteBecauseOfReferences.text();
1819 throw new ReferentialIntegrityException(invalidRefs, msg);
1820 }
1821
1822 if (deleteTopOfBranch) {
1823
1824 ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities,
1825 workspaceId,
1826 parentUuidString,
1827 childName,
1828 nsId,
1829 indexInParent);
1830 entities.flush();
1831 }
1832
1833
1834 cache.removeBranch(workspaceId, deletedLocations);
1835 } finally {
1836
1837 query.close();
1838 }
1839
1840 } catch (Throwable e) {
1841 request.setError(e);
1842 return null;
1843 }
1844 return actualLocation;
1845 }
1846
1847 protected Map<String, Location> computeDeletedLocations( WorkspaceEntity workspace,
1848 Location topNodeLocation,
1849 boolean deleteTopOfBranch ) {
1850 Location actualLocation = null;
1851
1852
1853 if (workspace == null) return null;
1854 Long workspaceId = workspace.getId();
1855 assert workspaceId != null;
1856
1857 ActualLocation actual = getActualLocation(workspace, topNodeLocation);
1858 actualLocation = actual.location;
1859 Path path = actualLocation.getPath();
1860
1861
1862 SubgraphQuery query = SubgraphQuery.create(getExecutionContext(),
1863 entities,
1864 workspaceId,
1865 actualLocation.getUuid(),
1866 path,
1867 0);
1868 try {
1869
1870 List<Location> deletedLocations = query.getNodeLocations(true, true);
1871
1872 Map<String, Location> results = new HashMap<String, Location>(deletedLocations.size());
1873 for (Location location : deletedLocations) {
1874 results.put(location.getUuid().toString(), location);
1875 }
1876
1877 return results;
1878 } finally {
1879
1880 query.close();
1881 }
1882 }
1883
1884 private Location moveNodeToLastChild( WorkspaceEntity workspace,
1885 ActualLocation actualFromLocation,
1886 ActualLocation actualIntoLocation,
1887 Name desiredName ) {
1888 long workspaceId = workspace.getId();
1889 Path oldPath = actualFromLocation.location.getPath();
1890
1891 ChildEntity fromEntity = actualFromLocation.childEntity;
1892
1893
1894 final int oldIndex = fromEntity.getIndexInParent();
1895 final String oldParentUuid = fromEntity.getParentUuidString();
1896
1897 String toUuidString = actualIntoLocation.uuid;
1898
1899
1900 String childOldLocalName = fromEntity.getChildName();
1901 String childLocalName = null;
1902 NamespaceEntity ns = null;
1903 Name childName = desiredName;
1904 if (childName != null) {
1905 childLocalName = desiredName.getLocalName();
1906 String childNsUri = childName.getNamespaceUri();
1907 ns = namespaces.get(childNsUri, true);
1908 } else {
1909 childName = oldPath.getLastSegment().getName();
1910 childLocalName = fromEntity.getChildName();
1911 ns = fromEntity.getChildNamespace();
1912 }
1913
1914 int nextSnsIndex = 1;
1915 int nextIndexInParent = 1;
1916
1917
1918 Query query = entities.createNamedQuery("ChildEntity.findMaximumSnsIndex");
1919 query.setParameter("workspaceId", workspaceId);
1920 query.setParameter("parentUuid", toUuidString);
1921 query.setParameter("ns", ns.getId());
1922 query.setParameter("childName", childLocalName);
1923 try {
1924 Integer index = (Integer)query.getSingleResult();
1925 if (index != null) nextSnsIndex = index.intValue() + 1;
1926 } catch (NoResultException e) {
1927 }
1928
1929
1930 query = entities.createNamedQuery("ChildEntity.findMaximumChildIndex");
1931 query.setParameter("workspaceId", workspaceId);
1932 query.setParameter("parentUuid", toUuidString);
1933 try {
1934 Integer index = (Integer)query.getSingleResult();
1935 if (index != null) nextIndexInParent = index + 1;
1936 } catch (NoResultException e) {
1937 }
1938
1939 fromEntity.setParentUuidString(toUuidString);
1940 fromEntity.setChildName(childLocalName);
1941 fromEntity.setChildNamespace(ns);
1942 fromEntity.setIndexInParent(nextIndexInParent);
1943 fromEntity.setSameNameSiblingIndex(nextSnsIndex);
1944
1945
1946 entities.flush();
1947
1948
1949 Path newParentPath = actualIntoLocation.location.getPath();
1950 Path newPath = pathFactory.create(newParentPath, childName, nextSnsIndex);
1951 Location newLocation = actualFromLocation.location.with(newPath);
1952
1953
1954 ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities,
1955 workspaceId,
1956 oldParentUuid,
1957 childOldLocalName,
1958 ns.getId(),
1959 oldIndex);
1960
1961
1962 cache.moveNode(workspaceId, actualFromLocation.location, oldIndex, newLocation);
1963
1964 return newLocation;
1965 }
1966
1967 @SuppressWarnings( "unchecked" )
1968 private Location moveNodeBefore( WorkspaceEntity workspace,
1969 ActualLocation actualFromLocation,
1970 ActualLocation actualIntoLocation,
1971 ActualLocation actualBeforeLocation ) {
1972
1973 long workspaceId = workspace.getId();
1974
1975 ChildEntity fromEntity = actualFromLocation.childEntity;
1976 assert fromEntity != null;
1977
1978 Path oldPath = actualFromLocation.location.getPath();
1979 String oldParentUuid = fromEntity.getParentUuidString();
1980
1981 String toUuidString = actualIntoLocation.uuid;
1982 Location beforeLocation = actualBeforeLocation.location;
1983 ChildEntity beforeEntity = actualBeforeLocation.childEntity;
1984
1985
1986
1987
1988
1989 if (beforeEntity == null) {
1990 beforeEntity = findNode(workspaceId, actualBeforeLocation.uuid);
1991 }
1992
1993 assert beforeEntity != null;
1994
1995 Name childName = oldPath.getLastSegment().getName();
1996 String childLocalName = fromEntity.getChildName();
1997 NamespaceEntity ns = fromEntity.getChildNamespace();
1998
1999 boolean sameParent = oldParentUuid.equals(toUuidString);
2000
2001 if (sameParent) {
2002 int oldIndex;
2003 if (fromEntity.getIndexInParent() < beforeEntity.getIndexInParent()) {
2004 oldIndex = beforeEntity.getIndexInParent();
2005
2006 int snsCount = ChildEntity.adjustSnsIndexesAndIndexes(entities,
2007 workspaceId,
2008 fromEntity.getParentUuidString(),
2009 fromEntity.getIndexInParent(),
2010 beforeEntity.getIndexInParent(),
2011 fromEntity.getChildNamespace().getId(),
2012 fromEntity.getChildName(),
2013 -1);
2014
2015 fromEntity.setIndexInParent(oldIndex);
2016 fromEntity.setSameNameSiblingIndex(fromEntity.getSameNameSiblingIndex() + snsCount);
2017 } else {
2018 oldIndex = beforeEntity.getIndexInParent();
2019
2020 int snsCount = ChildEntity.adjustSnsIndexesAndIndexes(entities,
2021 workspaceId,
2022 fromEntity.getParentUuidString(),
2023 beforeEntity.getIndexInParent() - 1,
2024 fromEntity.getIndexInParent() - 1,
2025 fromEntity.getChildNamespace().getId(),
2026 fromEntity.getChildName(),
2027 +1);
2028
2029 fromEntity.setIndexInParent(oldIndex);
2030 fromEntity.setSameNameSiblingIndex(fromEntity.getSameNameSiblingIndex() - snsCount);
2031
2032 }
2033
2034 Path newPath = pathFactory.create(oldPath.getParent(),
2035 pathFactory.createSegment(fromEntity.getChildName(),
2036 fromEntity.getSameNameSiblingIndex()));
2037 return actualFromLocation.location.with(newPath);
2038 }
2039
2040 int oldIndex = fromEntity.getIndexInParent();
2041
2042
2043
2044
2045
2046
2047
2048 Query query = entities.createNamedQuery("ChildEntity.findAllUnderParent");
2049 query.setParameter("workspaceId", workspaceId);
2050 query.setParameter("parentUuidString", toUuidString);
2051
2052 int nextIndexInParent = 0;
2053 int nextSnsIndex = 1;
2054 try {
2055 List<ChildEntity> children = query.getResultList();
2056 Path beforePath = beforeLocation.getPath();
2057 Path.Segment beforeSegment = beforePath.getLastSegment();
2058
2059 boolean foundBefore = false;
2060 for (ChildEntity child : children) {
2061 NamespaceEntity namespace = child.getChildNamespace();
2062 if (namespace.getUri().equals(ns.getUri()) && child.getChildName().equals(childLocalName)
2063 && child.getSameNameSiblingIndex() == beforeSegment.getIndex()) {
2064 foundBefore = true;
2065 nextIndexInParent = child.getIndexInParent();
2066 nextSnsIndex = beforeSegment.getIndex();
2067 }
2068
2069 if (foundBefore) {
2070 child.setIndexInParent(child.getIndexInParent() + 1);
2071 if (child.getChildName().equals(childLocalName) && namespace.getUri().equals(ns.getUri())) {
2072 child.setSameNameSiblingIndex(child.getSameNameSiblingIndex() + 1);
2073 }
2074 entities.persist(child);
2075 }
2076 }
2077
2078 } catch (NoResultException e) {
2079 }
2080
2081 fromEntity.setParentUuidString(toUuidString);
2082 fromEntity.setChildName(childLocalName);
2083 fromEntity.setChildNamespace(ns);
2084 fromEntity.setIndexInParent(nextIndexInParent);
2085 fromEntity.setSameNameSiblingIndex(nextSnsIndex);
2086
2087
2088 entities.flush();
2089
2090
2091 Path newParentPath = actualIntoLocation.location.getPath();
2092 Path newPath = pathFactory.create(newParentPath, childName, nextSnsIndex);
2093 Location oldLocation = actualFromLocation.location;
2094 Location newLocation = oldLocation.with(newPath);
2095
2096
2097 ChildEntity.adjustSnsIndexesAndIndexesAfterRemoving(entities, workspaceId, oldParentUuid, childLocalName, ns.getId(), -1);
2098
2099
2100 cache.moveNode(workspaceId, oldLocation, oldIndex, newLocation);
2101
2102 return newLocation;
2103 }
2104
2105
2106
2107
2108
2109
2110 @Override
2111 public void process( MoveBranchRequest request ) {
2112 logger.trace(request.toString());
2113 Location actualOldLocation = null;
2114 Location actualNewLocation = null;
2115 try {
2116
2117 WorkspaceEntity workspace = getExistingWorkspace(request.inWorkspace(), request);
2118 if (workspace == null) return;
2119 Long workspaceId = workspace.getId();
2120 assert workspaceId != null;
2121
2122 Location fromLocation = request.from();
2123 ActualLocation actualLocation = getActualLocation(workspace, fromLocation);
2124 if (actualLocation.childEntity == null) {
2125 actualLocation = new ActualLocation(actualLocation.location, actualLocation.uuid, findNode(workspaceId,
2126 actualLocation.uuid));
2127 }
2128
2129 actualOldLocation = actualLocation.location;
2130 Path oldPath = actualOldLocation.getPath();
2131
2132
2133 if (oldPath.isRoot()) {
2134 String msg = JpaConnectorI18n.unableToMoveRootNode.text(getSourceName());
2135 throw new InvalidRequestException(msg);
2136 }
2137
2138 if (request.hasNoEffect()) {
2139 actualNewLocation = actualOldLocation;
2140 } else {
2141
2142
2143 Location beforeLocation = request.before();
2144 if (beforeLocation == null) {
2145 ActualLocation actualIntoLocation = getActualLocation(workspace, request.into());
2146
2147 actualNewLocation = moveNodeToLastChild(workspace, actualLocation, actualIntoLocation, request.desiredName());
2148 } else {
2149
2150 ActualLocation actualBeforeLocation = getActualLocation(workspace, beforeLocation);
2151 ActualLocation actualIntoLocation = getActualLocation(workspace, Location.create(beforeLocation.getPath()
2152 .getParent()));
2153
2154 actualNewLocation = moveNodeBefore(workspace, actualLocation, actualIntoLocation, actualBeforeLocation);
2155 }
2156 }
2157 } catch (Throwable e) {
2158
2159
2160
2161
2162 request.setError(e);
2163 return;
2164 }
2165 request.setActualLocations(actualOldLocation, actualNewLocation);
2166 recordChange(request);
2167 }
2168
2169
2170
2171
2172
2173
2174 @Override
2175 public void process( VerifyWorkspaceRequest request ) {
2176
2177 String workspaceName = request.workspaceName();
2178 if (workspaceName == null) workspaceName = nameOfDefaultWorkspace;
2179 WorkspaceEntity workspace = getExistingWorkspace(workspaceName, request);
2180 if (workspace != null) {
2181 Long workspaceId = workspace.getId();
2182 assert workspaceId != null;
2183 ActualLocation actual = getActualLocation(workspace, Location.create(pathFactory.createRootPath()));
2184 request.setActualRootLocation(actual.location);
2185 request.setActualWorkspaceName(workspace.getName());
2186 }
2187 }
2188
2189
2190
2191
2192
2193
2194 @Override
2195 public void process( GetWorkspacesRequest request ) {
2196
2197 Set<String> names = workspaces.getWorkspaceNames();
2198
2199 for (String name : this.predefinedWorkspaceNames) {
2200 names.add(name);
2201 }
2202 request.setAvailableWorkspaceNames(Collections.unmodifiableSet(names));
2203 setCacheableInfo(request);
2204 }
2205
2206
2207
2208
2209
2210
2211 @Override
2212 public void process( CreateWorkspaceRequest request ) {
2213 String name = request.desiredNameOfNewWorkspace();
2214 if (!creatingWorkspacesAllowed) {
2215 String msg = JpaConnectorI18n.unableToCreateWorkspaces.text(getSourceName());
2216 request.setError(new InvalidRequestException(msg));
2217 return;
2218 }
2219 Set<String> existingNames = workspaces.getWorkspaceNames();
2220 int counter = 0;
2221 while (existingNames.contains(name)) {
2222 switch (request.conflictBehavior()) {
2223 case CREATE_WITH_ADJUSTED_NAME:
2224 name = request.desiredNameOfNewWorkspace() + ++counter;
2225 break;
2226 case DO_NOT_CREATE:
2227 default:
2228 String msg = JpaConnectorI18n.workspaceAlreadyExists.text(getSourceName(), name);
2229 request.setError(new InvalidWorkspaceException(msg));
2230 return;
2231 }
2232 }
2233
2234 WorkspaceEntity entity = workspaces.create(name);
2235 request.setActualWorkspaceName(entity.getName());
2236
2237 Location root = Location.create(pathFactory.createRootPath());
2238 request.setActualRootLocation(getActualLocation(entity, root).location);
2239 recordChange(request);
2240 }
2241
2242
2243
2244
2245
2246
2247 @SuppressWarnings( "unchecked" )
2248 @Override
2249 public void process( CloneWorkspaceRequest request ) {
2250 if (!creatingWorkspacesAllowed) {
2251 String msg = JpaConnectorI18n.unableToCreateWorkspaces.text(getSourceName());
2252 request.setError(new InvalidRequestException(msg));
2253 return;
2254 }
2255 Set<String> existingNames = workspaces.getWorkspaceNames();
2256 String name = request.desiredNameOfTargetWorkspace();
2257 int counter = 0;
2258 while (existingNames.contains(name)) {
2259 switch (request.targetConflictBehavior()) {
2260 case CREATE_WITH_ADJUSTED_NAME:
2261 name = request.desiredNameOfTargetWorkspace() + ++counter;
2262 break;
2263 case DO_NOT_CREATE:
2264 default:
2265 String msg = JpaConnectorI18n.workspaceAlreadyExists.text(getSourceName(), name);
2266 request.setError(new InvalidWorkspaceException(msg));
2267 return;
2268 }
2269 }
2270 String fromWorkspaceName = request.nameOfWorkspaceToBeCloned();
2271 WorkspaceEntity fromWorkspace = workspaces.get(fromWorkspaceName, false);
2272 if (fromWorkspace == null) {
2273 switch (request.cloneConflictBehavior()) {
2274 case SKIP_CLONE:
2275 break;
2276 case DO_NOT_CLONE:
2277 default:
2278 String msg = JpaConnectorI18n.workspaceDoesNotExist.text(getSourceName(), fromWorkspaceName);
2279 request.setError(new InvalidRequestException(msg));
2280 return;
2281 }
2282 }
2283
2284
2285 WorkspaceEntity intoWorkspace = workspaces.create(name);
2286 String newWorkspaceName = intoWorkspace.getName();
2287 request.setActualWorkspaceName(newWorkspaceName);
2288
2289 if (fromWorkspace != null) {
2290
2291 Long fromWorkspaceId = fromWorkspace.getId();
2292 Long intoWorkspaceId = intoWorkspace.getId();
2293 Query query = entities.createNamedQuery("ChildEntity.findInWorkspace");
2294 query.setParameter("workspaceId", fromWorkspaceId);
2295 List<ChildEntity> childEntities = query.getResultList();
2296 for (ChildEntity child : childEntities) {
2297 ChildId origId = child.getId();
2298 ChildId copyId = new ChildId(intoWorkspaceId, origId.getChildUuidString());
2299 ChildEntity copy = new ChildEntity(copyId, child.getParentUuidString(), child.getIndexInParent(),
2300 child.getChildNamespace(), child.getChildName());
2301 copy.setAllowsSameNameChildren(child.getAllowsSameNameChildren());
2302 copy.setSameNameSiblingIndex(child.getSameNameSiblingIndex());
2303 entities.persist(copy);
2304 }
2305 entities.flush();
2306
2307 query = entities.createNamedQuery("PropertiesEntity.findInWorkspace");
2308 query.setParameter("workspaceId", fromWorkspaceId);
2309 List<PropertiesEntity> properties = query.getResultList();
2310 for (PropertiesEntity property : properties) {
2311 NodeId copyId = new NodeId(intoWorkspaceId, property.getId().getUuidString());
2312 PropertiesEntity copy = new PropertiesEntity(copyId);
2313 copy.setCompressed(property.isCompressed());
2314 copy.setData(property.getData());
2315 copy.setPropertyCount(property.getPropertyCount());
2316 copy.setReferentialIntegrityEnforced(property.isReferentialIntegrityEnforced());
2317 Collection<LargeValueId> ids = property.getLargeValues();
2318 if (ids.size() != 0) {
2319 copy.getLargeValues().addAll(ids);
2320 }
2321 entities.persist(copy);
2322 }
2323 entities.flush();
2324
2325 query = entities.createNamedQuery("ReferenceEntity.findInWorkspace");
2326 query.setParameter("workspaceId", fromWorkspaceId);
2327 List<ReferenceEntity> references = query.getResultList();
2328 for (ReferenceEntity reference : references) {
2329 ReferenceId from = reference.getId();
2330 ReferenceId copy = new ReferenceId(fromWorkspaceId, from.getFromUuidString(), from.getToUuidString());
2331 entities.persist(new ReferenceEntity(copy));
2332 }
2333 entities.flush();
2334 }
2335
2336
2337 Location root = Location.create(pathFactory.createRootPath(), rootNodeUuid);
2338 request.setActualRootLocation(getActualLocation(intoWorkspace, root).location);
2339 recordChange(request);
2340 }
2341
2342
2343
2344
2345
2346
2347 @Override
2348 public void process( DestroyWorkspaceRequest request ) {
2349
2350 WorkspaceEntity workspace = getExistingWorkspace(request.workspaceName(), request);
2351 if (workspace == null) return;
2352 Long workspaceId = workspace.getId();
2353 assert workspaceId != null;
2354
2355
2356 ActualLocation actual = getActualLocation(workspace, Location.create(pathFactory.createRootPath()));
2357
2358
2359 workspaces.destroy(workspace.getName());
2360
2361
2362 Query delete = entities.createQuery("delete PropertiesEntity entity where entity.id.workspaceId = :workspaceId");
2363 delete.setParameter("workspaceId", workspaceId);
2364 delete.executeUpdate();
2365
2366 delete = entities.createQuery("delete ChildEntity entity where entity.id.workspaceId = :workspaceId");
2367 delete.setParameter("workspaceId", workspaceId);
2368 delete.executeUpdate();
2369
2370 delete = entities.createQuery("delete ReferenceEntity entity where entity.id.workspaceId = :workspaceId");
2371 delete.setParameter("workspaceId", workspaceId);
2372 delete.executeUpdate();
2373
2374
2375 LargeValueEntity.deleteUnused(entities);
2376
2377
2378 request.setActualRootLocation(actual.location);
2379 recordChange(request);
2380 }
2381
2382
2383
2384
2385
2386
2387
2388 @Override
2389 public void close() {
2390
2391 verifyReferences();
2392
2393
2394 super.close();
2395 }
2396
2397 protected WorkspaceEntity getExistingWorkspace( String workspaceName,
2398 Request request ) {
2399 WorkspaceEntity workspace = workspaces.get(workspaceName, false);
2400 if (workspace == null) {
2401
2402 for (String name : predefinedWorkspaceNames) {
2403 if (workspaceName.equals(name)) {
2404
2405 return workspaces.create(workspaceName);
2406 }
2407 }
2408 String msg = JpaConnectorI18n.workspaceDoesNotExist.text(getSourceName(), workspaceName);
2409 request.setError(new InvalidWorkspaceException(msg));
2410 }
2411 return workspace;
2412 }
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424 protected void verifyReferences() throws ReferentialIntegrityException {
2425 if (!enforceReferentialIntegrity) return;
2426 if (!workspaceIdsWithChangedReferences.isEmpty()) {
2427
2428 Map<Location, List<Reference>> invalidRefs = new HashMap<Location, List<Reference>>();
2429 for (Long workspaceId : workspaceIdsWithChangedReferences) {
2430
2431
2432 ReferenceEntity.deleteUnenforcedReferences(workspaceId, entities);
2433
2434
2435 int numUnresolved = ReferenceEntity.countAllReferencesResolved(workspaceId, entities);
2436 if (numUnresolved != 0) {
2437 List<ReferenceEntity> references = ReferenceEntity.verifyAllReferencesResolved(workspaceId, entities);
2438 ValueFactory<Reference> refFactory = getExecutionContext().getValueFactories().getReferenceFactory();
2439 for (ReferenceEntity entity : references) {
2440 ReferenceId id = entity.getId();
2441 UUID fromUuid = UUID.fromString(id.getFromUuidString());
2442 Location location = Location.create(fromUuid);
2443 WorkspaceEntity dummyWorkspaceReference = new WorkspaceEntity();
2444 dummyWorkspaceReference.setId(id.getWorkspaceId());
2445 dummyWorkspaceReference.setName("<unknown>");
2446 location = getActualLocation(dummyWorkspaceReference, location).location;
2447 List<Reference> refs = invalidRefs.get(location);
2448 if (refs == null) {
2449 refs = new ArrayList<Reference>();
2450 invalidRefs.put(location, refs);
2451 }
2452 UUID toUuid = UUID.fromString(id.getToUuidString());
2453 refs.add(refFactory.create(toUuid));
2454 }
2455 }
2456 }
2457
2458 workspaceIdsWithChangedReferences.clear();
2459
2460 if (!invalidRefs.isEmpty()) {
2461 String msg = JpaConnectorI18n.invalidReferences.text(getSourceName());
2462 throw new ReferentialIntegrityException(invalidRefs, msg);
2463 }
2464 }
2465 }
2466
2467 protected String createProperties( WorkspaceEntity workspace,
2468 String uuidString,
2469 Collection<Property> properties ) throws IOException {
2470 assert uuidString != null;
2471
2472
2473 NodeId nodeId = new NodeId(workspace.getId(), uuidString);
2474 PropertiesEntity props = new PropertiesEntity(nodeId);
2475
2476
2477 boolean processProperties = true;
2478 if (properties.isEmpty()) processProperties = false;
2479 else if (properties.size() == 1 && properties.iterator().next().getName().equals(JcrLexicon.NAME)) processProperties = false;
2480
2481 if (processProperties) {
2482 References refs = enforceReferentialIntegrity ? new References() : null;
2483 LargeValueSerializer largeValues = new LargeValueSerializer(props);
2484 ByteArrayOutputStream baos = new ByteArrayOutputStream();
2485 OutputStream os = compressData ? new GZIPOutputStream(baos) : baos;
2486 ObjectOutputStream oos = new ObjectOutputStream(os);
2487 int numProperties = properties.size();
2488 try {
2489 Serializer.ReferenceValues refValues = refs != null ? refs : Serializer.NO_REFERENCES_VALUES;
2490 serializer.serializeProperties(oos, numProperties, properties, largeValues, refValues);
2491 } finally {
2492 oos.close();
2493 }
2494
2495 props.setData(baos.toByteArray());
2496 props.setPropertyCount(numProperties);
2497
2498
2499 if (refs != null && refs.hasWritten()) {
2500 for (Reference reference : refs.getWritten()) {
2501 String toUuid = resolveToUuid(workspace, reference);
2502 if (toUuid != null) {
2503 ReferenceId id = new ReferenceId(workspace.getId(), uuidString, toUuid);
2504 ReferenceEntity refEntity = new ReferenceEntity(id);
2505 entities.persist(refEntity);
2506 workspaceIdsWithChangedReferences.add(workspace.getId());
2507 }
2508 }
2509 }
2510 } else {
2511 props.setData(null);
2512 props.setPropertyCount(0);
2513 }
2514 props.setCompressed(compressData);
2515 props.setReferentialIntegrityEnforced(true);
2516
2517 entities.persist(props);
2518
2519
2520 return uuidString;
2521 }
2522
2523
2524
2525
2526
2527
2528
2529
2530 protected String resolveToUuid( WorkspaceEntity workspace,
2531 Reference reference ) {
2532
2533 try {
2534 UUID uuid = uuidFactory.create(reference);
2535 ActualLocation actualLocation = getActualLocation(workspace, Location.create(uuid));
2536 return actualLocation.uuid;
2537 } catch (ValueFormatException e) {
2538
2539 } catch (PathNotFoundException e) {
2540
2541 }
2542
2543 return null;
2544 }
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573 protected ActualLocation getActualLocation( WorkspaceEntity workspace,
2574 Location original ) throws PathNotFoundException {
2575 assert original != null;
2576
2577 long workspaceId = workspace.getId();
2578
2579
2580 Property uuidProperty = original.getIdProperty(ModeShapeLexicon.UUID);
2581 String uuidString = uuidProperty != null && !uuidProperty.isEmpty() ? stringFactory.create(uuidProperty.getFirstValue()) : null;
2582
2583 Path path = original.getPath();
2584 if (path != null) {
2585
2586 Location cached = cache.getLocationFor(workspaceId, path);
2587 if (cached != null) {
2588 return new ActualLocation(original, cached.getUuid().toString(), null);
2589 }
2590 }
2591
2592
2593 if (uuidString != null) {
2594
2595
2596 String nodeUuidString = uuidString;
2597 LinkedList<Path.Segment> segments = new LinkedList<Path.Segment>();
2598 ChildEntity entity = null;
2599 ChildEntity originalEntity = null;
2600 while (uuidString != null && !uuidString.equals(this.rootNodeUuidString)) {
2601 Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
2602 query.setParameter("workspaceId", workspaceId);
2603 query.setParameter("childUuidString", uuidString);
2604 try {
2605
2606 entity = (ChildEntity)query.getSingleResult();
2607 if (originalEntity == null) originalEntity = entity;
2608 String localName = entity.getChildName();
2609 String uri = entity.getChildNamespace().getUri();
2610 int sns = entity.getSameNameSiblingIndex();
2611 Name name = nameFactory.create(uri, localName);
2612 segments.addFirst(pathFactory.createSegment(name, sns));
2613 uuidString = entity.getParentUuidString();
2614 } catch (NoResultException e) {
2615 if (!uuidString.equals(this.rootNodeUuidString)) {
2616 String workspaceName = workspace.getName();
2617 String msg = JpaConnectorI18n.invalidUuidForWorkspace.text(uuidString, workspaceName);
2618 throw new PathNotFoundException(original, pathFactory.createRootPath(), msg);
2619 }
2620 }
2621 }
2622 Path fullPath = pathFactory.createAbsolutePath(segments);
2623 Location newLocation = original.with(fullPath);
2624 cache.addNewNode(workspaceId, newLocation);
2625 return new ActualLocation(newLocation, nodeUuidString, originalEntity);
2626 }
2627
2628
2629 if (path == null) {
2630 String propName = ModeShapeLexicon.UUID.getString(getExecutionContext().getNamespaceRegistry());
2631 String msg = JpaConnectorI18n.locationShouldHavePathAndOrProperty.text(getSourceName(), propName);
2632 throw new PathNotFoundException(original, pathFactory.createRootPath(), msg);
2633 }
2634
2635
2636 if (path.isRoot()) {
2637 Location newLocation = original.with(rootNodeUuid);
2638 cache.addNewNode(workspaceId, newLocation);
2639 return new ActualLocation(newLocation, rootNodeUuidString, null);
2640 }
2641
2642 Location cachedParent = cache.getLocationFor(workspaceId, path.getParent());
2643 if (cachedParent != null) {
2644
2645 ChildEntity child = findByPathSegment(workspaceId, cachedParent.getUuid().toString(), path.getLastSegment());
2646
2647
2648 if (child == null) {
2649
2650 throw new PathNotFoundException(original, cachedParent.getPath(), JpaConnectorI18n.nodeDoesNotExist.text(path));
2651 }
2652
2653 uuidString = child.getId().getChildUuidString();
2654 Location newLocation = original.with(UUID.fromString(uuidString));
2655 cache.addNewNode(workspaceId, newLocation);
2656 return new ActualLocation(original, uuidString, child);
2657 }
2658
2659
2660 String parentUuid = this.rootNodeUuidString;
2661 ChildEntity child = null;
2662 for (Path.Segment segment : path) {
2663 child = findByPathSegment(workspaceId, parentUuid, segment);
2664 if (child == null) {
2665
2666 Path lowest = path;
2667 while (lowest.getLastSegment() != segment) {
2668 lowest = lowest.getParent();
2669 }
2670 lowest = lowest.getParent();
2671 throw new PathNotFoundException(original, lowest);
2672 }
2673 parentUuid = child.getId().getChildUuidString();
2674 }
2675 assert child != null;
2676 uuidString = child.getId().getChildUuidString();
2677 Location newLocation = original.with(UUID.fromString(uuidString));
2678 cache.addNewNode(workspaceId, newLocation);
2679 return new ActualLocation(newLocation, uuidString, child);
2680 }
2681
2682 protected ChildEntity findNode( long workspaceId,
2683 String uuidString ) {
2684 Query query = entities.createNamedQuery("ChildEntity.findByChildUuid");
2685 query.setParameter("workspaceId", workspaceId);
2686 query.setParameter("childUuidString", uuidString);
2687 try {
2688
2689 return (ChildEntity)query.getSingleResult();
2690 } catch (NoResultException e) {
2691 return null;
2692 }
2693 }
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704 protected ChildEntity findByPathSegment( Long workspaceId,
2705 String parentUuid,
2706 Path.Segment pathSegment ) {
2707 assert namespaces != null;
2708 assert parentUuid != null;
2709 assert pathSegment != null;
2710 assert workspaceId != null;
2711 Name name = pathSegment.getName();
2712 String localName = name.getLocalName();
2713 String nsUri = name.getNamespaceUri();
2714 NamespaceEntity ns = namespaces.get(nsUri, false);
2715 if (ns == null) {
2716
2717 return null;
2718 }
2719 int snsIndex = pathSegment.hasIndex() ? pathSegment.getIndex() : 1;
2720 Query query = entities.createNamedQuery("ChildEntity.findByPathSegment");
2721 query.setParameter("workspaceId", workspaceId);
2722 query.setParameter("parentUuidString", parentUuid);
2723 query.setParameter("ns", ns.getId());
2724 query.setParameter("childName", localName);
2725 query.setParameter("sns", snsIndex);
2726 try {
2727 return (ChildEntity)query.getSingleResult();
2728 } catch (NoResultException e) {
2729 return null;
2730 }
2731 }
2732
2733 protected String createHexValuesString( Collection<String> hexValues ) {
2734 if (hexValues == null || hexValues.isEmpty()) return null;
2735 StringBuilder sb = new StringBuilder();
2736 boolean first = true;
2737 for (String hexValue : hexValues) {
2738 if (first) {
2739 first = false;
2740 } else {
2741 sb.append(',');
2742 }
2743 sb.append(hexValue);
2744 }
2745 return sb.toString();
2746 }
2747
2748 protected Collection<String> createHexValues( String hexValuesString ) {
2749 return Arrays.asList(hexValuesString.split(","));
2750 }
2751
2752 protected class LargeValueSerializer implements LargeValues {
2753 private final PropertiesEntity properties;
2754 private final Set<String> written;
2755
2756 public LargeValueSerializer( PropertiesEntity entity ) {
2757 this.properties = entity;
2758 this.written = null;
2759 }
2760
2761 public LargeValueSerializer( PropertiesEntity entity,
2762 Set<String> written ) {
2763 this.properties = entity;
2764 this.written = written;
2765 }
2766
2767
2768
2769
2770
2771
2772 public long getMinimumSize() {
2773 return largeValueMinimumSizeInBytes;
2774 }
2775
2776
2777
2778
2779
2780
2781
2782 public Object read( ValueFactories valueFactories,
2783 byte[] hash,
2784 long length ) throws IOException {
2785 String hashStr = StringUtil.getHexString(hash);
2786
2787 LargeValueId largeValueId = new LargeValueId(hashStr);
2788 LargeValueEntity entity = entities.find(LargeValueEntity.class, largeValueId);
2789 if (entity != null) {
2790
2791 byte[] data = entity.getData();
2792 if (entity.isCompressed()) {
2793 InputStream stream = new GZIPInputStream(new ByteArrayInputStream(data));
2794 try {
2795 data = IoUtil.readBytes(stream);
2796 } finally {
2797 stream.close();
2798 }
2799 }
2800 return valueFactories.getValueFactory(entity.getType()).create(data);
2801 }
2802 throw new IOException(JpaConnectorI18n.unableToReadLargeValue.text(getSourceName(), hashStr));
2803 }
2804
2805
2806
2807
2808
2809
2810
2811 public void write( byte[] hash,
2812 long length,
2813 PropertyType type,
2814 Object value ) throws IOException {
2815 if (value == null) return;
2816 String hashStr = StringUtil.getHexString(hash);
2817 if (written != null) written.add(hashStr);
2818
2819
2820 final LargeValueId id = new LargeValueId(hashStr);
2821 for (LargeValueId existing : properties.getLargeValues()) {
2822 if (existing.equals(id)) {
2823
2824 return;
2825 }
2826 }
2827 LargeValueEntity entity = entities.find(LargeValueEntity.class, id);
2828 if (entity == null) {
2829
2830 entity = new LargeValueEntity();
2831 entity.setCompressed(true);
2832 entity.setId(id);
2833 entity.setLength(length);
2834 entity.setType(type);
2835 ValueFactories factories = getExecutionContext().getValueFactories();
2836 byte[] bytes = null;
2837 switch (type) {
2838 case BINARY:
2839 Binary binary = factories.getBinaryFactory().create(value);
2840 InputStream stream = null;
2841 try {
2842 binary.acquire();
2843 stream = binary.getStream();
2844 if (compressData) stream = new GZIPInputStream(stream);
2845 bytes = IoUtil.readBytes(stream);
2846 } finally {
2847 try {
2848 if (stream != null) stream.close();
2849 } finally {
2850 binary.release();
2851 }
2852 }
2853 break;
2854 case URI:
2855
2856 default:
2857 String str = factories.getStringFactory().create(value);
2858 if (compressData) {
2859 ByteArrayOutputStream bs = new ByteArrayOutputStream();
2860 OutputStream strStream = new GZIPOutputStream(bs);
2861 try {
2862 IoUtil.write(str, strStream);
2863 } finally {
2864 strStream.close();
2865 }
2866 bytes = bs.toByteArray();
2867 } else {
2868 bytes = str.getBytes();
2869 }
2870 break;
2871 }
2872 entity.setData(bytes);
2873 entities.persist(entity);
2874 }
2875
2876 assert id.getHash() != null;
2877 properties.getLargeValues().add(id);
2878 }
2879
2880 }
2881
2882 protected class RecordingLargeValues implements LargeValues {
2883 protected final Collection<String> readKeys = new HashSet<String>();
2884 protected final Collection<String> writtenKeys = new HashSet<String>();
2885 protected final LargeValues delegate;
2886
2887 RecordingLargeValues( LargeValues delegate ) {
2888 assert delegate != null;
2889 this.delegate = delegate;
2890 }
2891
2892
2893
2894
2895
2896
2897 public long getMinimumSize() {
2898 return delegate.getMinimumSize();
2899 }
2900
2901
2902
2903
2904
2905
2906
2907 public Object read( ValueFactories valueFactories,
2908 byte[] hash,
2909 long length ) throws IOException {
2910 String key = StringUtil.getHexString(hash);
2911 readKeys.add(key);
2912 return delegate.read(valueFactories, hash, length);
2913 }
2914
2915 public void write( byte[] hash,
2916 long length,
2917 PropertyType type,
2918 Object value ) throws IOException {
2919 String key = StringUtil.getHexString(hash);
2920 writtenKeys.add(key);
2921 delegate.write(hash, length, type, value);
2922 }
2923 }
2924
2925 protected class SkippedLargeValues implements LargeValues {
2926 protected Collection<String> skippedKeys = new HashSet<String>();
2927 protected final LargeValues delegate;
2928
2929 SkippedLargeValues( LargeValues delegate ) {
2930 assert delegate != null;
2931 this.delegate = delegate;
2932 }
2933
2934
2935
2936
2937
2938
2939 public long getMinimumSize() {
2940 return delegate.getMinimumSize();
2941 }
2942
2943
2944
2945
2946
2947
2948
2949 public Object read( ValueFactories valueFactories,
2950 byte[] hash,
2951 long length ) throws IOException {
2952 String key = StringUtil.getHexString(hash);
2953 skippedKeys.add(key);
2954 return null;
2955 }
2956
2957 public void write( byte[] hash,
2958 long length,
2959 PropertyType type,
2960 Object value ) {
2961 throw new UnsupportedOperationException();
2962 }
2963 }
2964
2965 @Immutable
2966 protected static class ActualLocation {
2967
2968 protected final Location location;
2969
2970 protected final String uuid;
2971
2972 protected final ChildEntity childEntity;
2973
2974 protected ActualLocation( Location location,
2975 String uuid,
2976 ChildEntity childEntity ) {
2977 assert location != null;
2978 assert uuid != null;
2979 this.location = location;
2980 this.uuid = uuid;
2981 this.childEntity = childEntity;
2982 }
2983
2984
2985
2986
2987
2988
2989 @Override
2990 public String toString() {
2991 return this.location.toString() + " (uuid=" + uuid + ") " + childEntity;
2992 }
2993 }
2994
2995 protected class References implements Serializer.ReferenceValues {
2996 private Set<Reference> read;
2997 private Set<Reference> removed;
2998 private Set<Reference> written;
2999
3000 protected References() {
3001 }
3002
3003
3004
3005
3006
3007
3008 public void read( Reference reference ) {
3009 if (read == null) read = new HashSet<Reference>();
3010 read.add(reference);
3011 }
3012
3013
3014
3015
3016
3017
3018 public void remove( Reference reference ) {
3019 if (removed == null) removed = new HashSet<Reference>();
3020 removed.add(reference);
3021 }
3022
3023
3024
3025
3026
3027
3028 public void write( Reference reference ) {
3029 if (written == null) written = new HashSet<Reference>();
3030 written.add(reference);
3031 }
3032
3033 public boolean hasRead() {
3034 return read != null;
3035 }
3036
3037 public boolean hasRemoved() {
3038 return removed != null;
3039 }
3040
3041 public boolean hasWritten() {
3042 return written != null;
3043 }
3044
3045
3046
3047
3048 public Set<Reference> getRead() {
3049 if (read != null) return read;
3050 return Collections.emptySet();
3051 }
3052
3053
3054
3055
3056 public Set<Reference> getRemoved() {
3057 if (removed != null) return removed;
3058 return Collections.emptySet();
3059 }
3060
3061
3062
3063
3064 public Set<Reference> getWritten() {
3065 if (written != null) return written;
3066 return Collections.emptySet();
3067 }
3068 }
3069 }