Skip to content

Commit e6ce828

Browse files
committed
Merge pull request #355 from UNC-Libraries/move-ignorance-is-bliss
Allowing moves to continue in cases where a moved item is in multiple…
2 parents 66c1e05 + 725a38b commit e6ce828

File tree

2 files changed

+100
-70
lines changed

2 files changed

+100
-70
lines changed

persistence/src/main/java/edu/unc/lib/dl/services/DigitalObjectManagerImpl.java

+15-3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import edu.unc.lib.dl.util.ContentModelHelper.CDRProperty;
8080
import edu.unc.lib.dl.util.ContentModelHelper.Datastream;
8181
import edu.unc.lib.dl.util.ContentModelHelper.Model;
82+
import edu.unc.lib.dl.util.ContentModelHelper.Relationship;
8283
import edu.unc.lib.dl.util.IllegalRepositoryStateException;
8384
import edu.unc.lib.dl.util.IndexingActionType;
8485
import edu.unc.lib.dl.util.PremisEventLogger;
@@ -947,17 +948,28 @@ private Map<PID, List<PID>> getChildrenContainerMap(Collection<PID> moving) {
947948
// Determine the set of parents for all of the PIDs to be moved
948949
Map<PID, List<PID>> childContainerMap = new HashMap<>();
949950
for (PID pid : moving) {
950-
PID source = this.tripleStoreQueryService.fetchContainer(pid);
951-
if (source == null) {
951+
// Get all containers which contain the moved object.
952+
String query = String.format(
953+
"select $pid from <%1$s> where $pid <%2$s> <%3$s>;",
954+
tripleStoreQueryService.getResourceIndexModelUri(),
955+
Relationship.contains, pid.getURI());
956+
List<List<String>> result = tripleStoreQueryService.queryResourceIndex(query);
957+
if (result == null) {
952958
log.warn("Attempting to move orphaned object {}", pid);
953-
} else {
959+
continue;
960+
}
961+
962+
for (List<String> sourceList : result) {
963+
PID source = new PID(sourceList.get(0));
964+
954965
List<PID> moveFromSource = childContainerMap.get(source);
955966
if (moveFromSource == null) {
956967
moveFromSource = new ArrayList<>();
957968
childContainerMap.put(source, moveFromSource);
958969
}
959970
moveFromSource.add(pid);
960971
}
972+
961973
}
962974
return childContainerMap;
963975
}

persistence/src/test/java/edu/unc/lib/dl/services/DigitalObjectManagerMoveTest.java

+85-67
Original file line numberDiff line numberDiff line change
@@ -194,45 +194,15 @@ public void oneSourceTest() throws Exception {
194194

195195
List<PID> moving = Arrays.asList(new PID("uuid:child1"), new PID("uuid:child5"));
196196

197-
when(tripleStoreQueryService.fetchContainer(any(PID.class))).thenReturn(source1PID);
197+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
198+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())));
198199

199200
digitalMan.move(moving, destPID, "user", "");
200201

201202
verify(managementClient, times(2)).getXMLDatastreamIfExists(eq(source1PID), eq(RELS_EXT.getName()));
202-
203-
ArgumentCaptor<Document> sourceRelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
204-
205-
verify(managementClient, times(2)).modifyDatastream(eq(source1PID), eq(RELS_EXT.getName()), anyString(),
206-
anyString(), sourceRelsExtUpdateCaptor.capture());
207-
208-
List<Document> sourceRelsAnswers = sourceRelsExtUpdateCaptor.getAllValues();
209-
// Check the state of the source after removal but before cleanup
210-
Document sourceRelsExt = sourceRelsAnswers.get(0);
211-
Set<PID> children = JDOMQueryUtil.getRelationSet(sourceRelsExt.getRootElement(), contains);
212-
assertEquals("Incorrect number of children in source container after move", 10, children.size());
213-
214-
Set<PID> removed = JDOMQueryUtil.getRelationSet(sourceRelsExt.getRootElement(), removedChild);
215-
assertEquals("Moved child gravestones not correctly set in source container", 2, removed.size());
216-
217-
// Check that tombstones were cleaned up by the end of the operation
218-
Document cleanRelsExt = sourceRelsAnswers.get(1);
219-
children = JDOMQueryUtil.getRelationSet(cleanRelsExt.getRootElement(), contains);
220-
assertEquals("Incorrect number of children in source container after cleanup", 10, children.size());
221-
222-
removed = JDOMQueryUtil.getRelationSet(cleanRelsExt.getRootElement(), removedChild);
223-
assertEquals("Child tombstones not cleaned up", 0, removed.size());
224-
225-
// Verify that the destination had the moved children added to it
226-
verify(managementClient).getXMLDatastreamIfExists(eq(destPID), eq(RELS_EXT.getName()));
227-
ArgumentCaptor<Document> destRelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
228-
verify(managementClient).modifyDatastream(eq(destPID), eq(RELS_EXT.getName()), anyString(),
229-
anyString(), destRelsExtUpdateCaptor.capture());
230-
assertFalse("Moved children were still present in source", children.containsAll(moving));
231-
232-
Document destRelsExt = destRelsExtUpdateCaptor.getValue();
233-
children = JDOMQueryUtil.getRelationSet(destRelsExt.getRootElement(), contains);
234-
assertEquals("Incorrect number of children in destination container after moved", 9, children.size());
235-
assertTrue("Moved children were not present in destination", children.containsAll(moving));
203+
204+
verifySourceMoved(source1PID, moving, 10);
205+
verifyDestinationMoved(moving, 9);
236206
}
237207

238208
private void makeDatastream(String contentPath, String datastream, PID pid) throws Exception {
@@ -294,7 +264,8 @@ public void oneSourceLockFailTest() throws Exception {
294264

295265
List<PID> moving = Arrays.asList(new PID("uuid:child1"), new PID("uuid:child5"));
296266

297-
when(tripleStoreQueryService.fetchContainer(any(PID.class))).thenReturn(source1PID);
267+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
268+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())));
298269

299270
digitalMan.move(moving, destPID, "user", "");
300271

@@ -344,40 +315,18 @@ public void multiSourceTest() throws Exception {
344315
makeMatcherPair("/fedora/containerRELSEXT1.xml", source1PID);
345316
makeMatcherPair("/fedora/containerRELSEXT3.xml", source2PID);
346317

347-
when(tripleStoreQueryService.fetchContainer(eq(new PID("uuid:child1")))).thenReturn(source1PID);
348-
when(tripleStoreQueryService.fetchContainer(eq(new PID("uuid:child32")))).thenReturn(source2PID);
318+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
319+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())))
320+
.thenReturn(Arrays.asList(Arrays.asList(source2PID.getPid())));
349321

350322
digitalMan.move(moving, destPID, "user", "");
351323

352324
verify(managementClient, times(2)).getXMLDatastreamIfExists(eq(source1PID), eq(RELS_EXT.getName()));
353325
verify(managementClient, times(2)).getXMLDatastreamIfExists(eq(source2PID), eq(RELS_EXT.getName()));
354-
355-
ArgumentCaptor<Document> source1RelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
356-
verify(managementClient, times(2)).modifyDatastream(eq(source1PID), eq(RELS_EXT.getName()), anyString(),
357-
anyString(), source1RelsExtUpdateCaptor.capture());
358-
359-
// Check that the first source was updated
360-
Document clean1RelsExt = source1RelsExtUpdateCaptor.getValue();
361-
Set<PID> children = JDOMQueryUtil.getRelationSet(clean1RelsExt.getRootElement(), contains);
362-
assertEquals("Incorrect number of children in source 1 after cleanup", 11, children.size());
363-
364-
// Check that the second source was updated
365-
ArgumentCaptor<Document> source2RelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
366-
verify(managementClient, times(2)).modifyDatastream(eq(source2PID), eq(RELS_EXT.getName()), anyString(),
367-
anyString(), source2RelsExtUpdateCaptor.capture());
368-
Document clean2RelsExt = source2RelsExtUpdateCaptor.getValue();
369-
children = JDOMQueryUtil.getRelationSet(clean2RelsExt.getRootElement(), contains);
370-
assertEquals("Incorrect number of children in source 2 after cleanup", 1, children.size());
371-
372-
// Check that items from both source 1 and 2 ended up in the destination.
373-
ArgumentCaptor<Document> destRelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
374-
verify(managementClient).modifyDatastream(eq(destPID), eq(RELS_EXT.getName()), anyString(),
375-
anyString(), destRelsExtUpdateCaptor.capture());
376-
377-
Document destRelsExt = destRelsExtUpdateCaptor.getValue();
378-
children = JDOMQueryUtil.getRelationSet(destRelsExt.getRootElement(), contains);
379-
assertEquals("Incorrect number of children in destination container after moved", 9, children.size());
380-
assertTrue("Moved children were not present in destination", children.containsAll(moving));
326+
327+
verifySourceMoved(source1PID, Arrays.asList(new PID("uuid:child1")), 11);
328+
verifySourceMoved(source2PID, Arrays.asList(new PID("uuid:child32")), 1);
329+
verifyDestinationMoved(moving, 9);
381330
}
382331

383332
@Test
@@ -409,6 +358,11 @@ public void rollbackTest() throws Exception {
409358
when(tripleStoreQueryService.fetchContainer(any(PID.class))).thenReturn(source1PID).thenReturn(source1PID)
410359
.thenReturn(null).thenReturn(null);
411360

361+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
362+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())))
363+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())))
364+
.thenReturn(null).thenReturn(null);
365+
412366
when(managementClient.getXMLDatastreamIfExists(eq(destPID), eq(RELS_EXT.getName()))).thenReturn(null);
413367

414368
try {
@@ -457,8 +411,11 @@ public void rollbackAfterDestinationUpdateTest() throws Exception {
457411

458412
List<PID> moving = Arrays.asList(new PID("uuid:child1"), new PID("uuid:child5"));
459413

460-
when(tripleStoreQueryService.fetchContainer(any(PID.class))).thenReturn(source1PID).thenReturn(source1PID)
461-
.thenReturn(destPID).thenReturn(destPID);
414+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
415+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())))
416+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid())))
417+
.thenReturn(Arrays.asList(Arrays.asList(destPID.getPid())))
418+
.thenReturn(Arrays.asList(Arrays.asList(destPID.getPid())));
462419

463420
// Second attempt to get the source RELS-EXT will return null to trigger rollback
464421
when (managementClient.getXMLDatastreamIfExists(eq(source1PID), eq(RELS_EXT.getName())))
@@ -509,4 +466,65 @@ public void rollbackAfterDestinationUpdateTest() throws Exception {
509466
children = JDOMQueryUtil.getRelationSet(destRBRelsExt.getRootElement(), contains);
510467
assertEquals("Incorrect number of children in destination container after rollback", 7, children.size());
511468
}
469+
470+
@Test
471+
public void multipleContainersTest() throws Exception {
472+
473+
makeMatcherPair("/fedora/containerRELSEXT1.xml", source1PID);
474+
makeMatcherPair("/fedora/containerRELSEXT1.xml", source2PID);
475+
476+
List<PID> moving = Arrays.asList(new PID("uuid:child1"));
477+
478+
when(tripleStoreQueryService.queryResourceIndex(anyString()))
479+
.thenReturn(Arrays.asList(Arrays.asList(source1PID.getPid()),
480+
Arrays.asList(source2PID.getPid())));
481+
482+
digitalMan.move(moving, destPID, "user", "");
483+
484+
verifySourceMoved(source1PID, moving, 11);
485+
verifySourceMoved(source2PID, moving, 11);
486+
verifyDestinationMoved(moving, 8);
487+
}
488+
489+
private void verifySourceMoved(PID sourcePID, List<PID> moving, int sourceContainsCount)
490+
throws Exception {
491+
ArgumentCaptor<Document> sourceRelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
492+
493+
verify(managementClient, times(2)).modifyDatastream(eq(sourcePID), eq(RELS_EXT.getName()), anyString(),
494+
anyString(), sourceRelsExtUpdateCaptor.capture());
495+
496+
List<Document> sourceRelsAnswers = sourceRelsExtUpdateCaptor.getAllValues();
497+
// Check the state of the source after removal but before cleanup
498+
Document sourceRelsExt = sourceRelsAnswers.get(0);
499+
Set<PID> children = JDOMQueryUtil.getRelationSet(sourceRelsExt.getRootElement(), contains);
500+
assertEquals("Incorrect number of children in source container after move", sourceContainsCount, children.size());
501+
502+
Set<PID> removed = JDOMQueryUtil.getRelationSet(sourceRelsExt.getRootElement(), removedChild);
503+
assertEquals("Moved child gravestones not correctly set in source container", moving.size(), removed.size());
504+
505+
// Check that tombstones were cleaned up by the end of the operation
506+
Document cleanRelsExt = sourceRelsAnswers.get(1);
507+
children = JDOMQueryUtil.getRelationSet(cleanRelsExt.getRootElement(), contains);
508+
assertEquals("Incorrect number of children in source container after cleanup", sourceContainsCount, children.size());
509+
510+
removed = JDOMQueryUtil.getRelationSet(cleanRelsExt.getRootElement(), removedChild);
511+
assertEquals("Child tombstones not cleaned up", 0, removed.size());
512+
513+
assertFalse("Moved children were still present in source", children.containsAll(moving));
514+
}
515+
516+
private void verifyDestinationMoved(List<PID> moving, int destinationCount)
517+
throws Exception {
518+
// Verify that the destination had the moved children added to it
519+
verify(managementClient).getXMLDatastreamIfExists(eq(destPID), eq(RELS_EXT.getName()));
520+
ArgumentCaptor<Document> destRelsExtUpdateCaptor = ArgumentCaptor.forClass(Document.class);
521+
verify(managementClient).modifyDatastream(eq(destPID), eq(RELS_EXT.getName()), anyString(),
522+
anyString(), destRelsExtUpdateCaptor.capture());
523+
524+
525+
Document destRelsExt = destRelsExtUpdateCaptor.getValue();
526+
Set<PID> children = JDOMQueryUtil.getRelationSet(destRelsExt.getRootElement(), contains);
527+
assertEquals("Incorrect number of children in destination container after moved", destinationCount, children.size());
528+
assertTrue("Moved children were not present in destination", children.containsAll(moving));
529+
}
512530
}

0 commit comments

Comments
 (0)