diff --git a/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLProcessor.java b/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLProcessor.java index 3c7c87a3ac..685468a3d3 100644 --- a/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLProcessor.java +++ b/services-camel-app/src/main/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLProcessor.java @@ -49,6 +49,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -150,7 +151,10 @@ private void performExport(ExportXMLRequest request, long startTime) throws IOEx xfop.write(("").getBytes(UTF_8)); } - sendEmail(zipit(mdExportFile, filename), request, filename, pageStart, pageEnd, totalPids); + File zipFile = zipit(mdExportFile, filename); + sendEmail(zipFile, request, filename, pageStart, pageEnd, totalPids); + cleanupTempFiles(zipFile, mdExportFile); + log.info("Completed exported objects {} through {} for user {} to {}", pageStart, pageEnd, username, filename); } @@ -346,6 +350,11 @@ private File zipit(File mdExportFile, String filename) throws IOException { return mdExportZip; } + private void cleanupTempFiles(File zipFile, File xmlFile) throws IOException { + Files.delete(zipFile.toPath()); + Files.delete(xmlFile.toPath()); + } + private void sendEmail(File mdExportFile, ExportXMLRequest request, String filename, int pageStart, int pageEnd, int totalPids) { String emailBody; diff --git a/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLRouteIT.java b/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLRouteIT.java index b4f40a8d7c..9d37d18e5e 100644 --- a/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLRouteIT.java +++ b/services-camel-app/src/test/java/edu/unc/lib/boxc/services/camel/exportxml/ExportXMLRouteIT.java @@ -5,11 +5,14 @@ import static edu.unc.lib.boxc.model.fcrepo.ids.DatastreamPids.getTechnicalMetadataPid; import static edu.unc.lib.boxc.model.fcrepo.ids.RepositoryPaths.getContentRootPid; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.apache.commons.io.FilenameUtils.wildcardMatch; import static org.apache.jena.rdf.model.ResourceFactory.createResource; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -23,7 +26,9 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.time.Instant; +import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; @@ -42,8 +47,10 @@ import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.jupiter.api.AfterEach; +import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -137,8 +144,9 @@ public class ExportXMLRouteIT { private ArgumentCaptor bodyCaptor; @Captor private ArgumentCaptor filenameCaptor; - @Captor - private ArgumentCaptor attachmentCaptor; + private List attachmentPaths; + @Rule + public final TemporaryFolder tmpFolder = new TemporaryFolder(); private ContentRootObject rootObj; private AdminUnit unitObj; @@ -158,6 +166,18 @@ public void setup() throws Exception { agent = new AgentPrincipalsImpl("user", new AccessGroupSetImpl("adminGroup")); generateBaseStructure(); exportXmlProcessor.setObjectsPerExport(500); + + attachmentPaths = new ArrayList<>(); + doAnswer(invocation -> { + var attachment = invocation.getArgument(4, File.class); + if (attachment == null) { + return null; + } + var copiedFile = new File(tmpFolder.newFolder(), attachment.getName()); + FileUtils.copyFile(attachment, copiedFile); + attachmentPaths.add(copiedFile.toPath()); + return null; + }).when(emailHandler).sendEmail(any(), any(), any(), any(), any()); } @AfterEach @@ -179,6 +199,7 @@ public void exportWorksExcludeChildrenTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -202,6 +223,7 @@ public void exportCollectionExcludeChildrenTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -224,6 +246,7 @@ public void exportCollectionIncludeChildrenTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -292,6 +315,7 @@ public void exportUnitIncludeChildrenPagedTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(3); + assertTempFilesDeleted(); Element rootEl1 = getExportedDocumentRootEl(1); assertHasObjectWithoutMods(rootEl1, ResourceType.AdminUnit, unitObj.getPid()); @@ -325,6 +349,7 @@ public void exportUnitIncludeChildrenPagedExcludeNoDatastreamsTest() throws Exce assertTrue("Processing message did not match expectations", result); assertEmailSent(2); + assertTempFilesDeleted(); Element rootEl1 = getExportedDocumentRootEl(1); assertHasObjectWithMods(rootEl1, ResourceType.Collection, collObj1.getPid()); @@ -349,6 +374,7 @@ public void exportCollectionExcludeChildrenExcludeNoDatastreamsTest() throws Exc assertTrue("Processing message did not match expectations", result); assertEmailSent(1); + assertTempFilesDeleted(); Element rootEl1 = getExportedDocumentRootEl(1); assertHasObjectWithMods(rootEl1, ResourceType.Collection, collObj1.getPid()); @@ -371,7 +397,7 @@ public void exportWorkNoModsExcludeNoDatastreamsTest() throws Exception { assertEmailSent(); assertNull(filenameCaptor.getValue()); - assertNull(attachmentCaptor.getValue()); + assertTrue(attachmentPaths.isEmpty()); assertEquals("DCR Metadata Export returned no results", subjectCaptor.getValue()); } @@ -389,6 +415,7 @@ public void exportWorkWithModsExcludeNoDatastreamsIncChildrenTest() throws Excep assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -411,6 +438,7 @@ public void exportWorkWithModsExcludeNoDatastreamsExcChildrenTest() throws Excep assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -441,6 +469,7 @@ public void exportWorkModsAndFitsTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -476,6 +505,7 @@ public void exportWorkModsAndPremisTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -510,6 +540,7 @@ public void exportWorkModsAndPremisNoModsTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -543,6 +574,7 @@ public void exportCollectionFitsExcludeNoDatastreamTest() throws Exception { assertTrue("Processing message did not match expectations", result); assertEmailSent(); + assertTempFilesDeleted(); Element rootEl = getExportedDocumentRootEl(); @@ -565,7 +597,7 @@ private void assertEmailSent() { private void assertEmailSent(int numberEmails) { verify(emailHandler, times(numberEmails)).sendEmail(toCaptor.capture(), subjectCaptor.capture(), - bodyCaptor.capture(), filenameCaptor.capture(), attachmentCaptor.capture()); + bodyCaptor.capture(), filenameCaptor.capture(), any()); } private ExportXMLRequest createRequest(boolean exportChildren, boolean excludeNoDs, PID... pids) { @@ -602,7 +634,7 @@ private Document getExportedDocument(int page) throws Exception { String exportFile = filenameCaptor.getAllValues().get(page - 1); assertTrue("Unexpected export filename: " + exportFile, exportFile.matches("xml\\_export\\_.*\\_0+" + page + "\\.zip")); - return getExportedDocument(attachmentCaptor.getAllValues().get(page - 1)); + return getExportedDocument(attachmentPaths.get(page - 1).toFile()); } private Document getExportedDocument(File reportZip) throws Exception { @@ -658,6 +690,19 @@ private void assertHasObjectWithDatastream(Element rootEl, ResourceType expected assertEquals(expectedContent.trim(), content); } + private void assertTempFilesDeleted() { + var tempDir = System.getProperty("java.io.tmpdir"); + String wildCardValue = "xml_export*"; + try (var stream = Files.list(Paths.get(tempDir))) { + var filters = stream.filter(file -> + wildcardMatch(file.getFileName().toString(), wildCardValue)); + var results = filters.collect(Collectors.toList()); + assertTrue(results.isEmpty()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + private Element getDatastreamElByType(Element objEl, DatastreamType dsType) { return objEl.getChildren("datastream").stream() .filter(e -> e.getAttributeValue("type").equals(dsType.getId()))