diff --git a/pom.xml b/pom.xml
index bb37321..c81ba1f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,45 @@
nl.knaw.dans
dans-java-utils
+
+ junit
+ junit
+ RELEASE
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.8.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ io.dropwizard
+ dropwizard-testing
+ 3.0.5
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.4.0
+ test
+
diff --git a/src/main/assembly/dist/bin/dd-manage-deposit b/src/main/assembly/dist/bin/dd-manage-deposit
index fe00670..0625e07 100644
--- a/src/main/assembly/dist/bin/dd-manage-deposit
+++ b/src/main/assembly/dist/bin/dd-manage-deposit
@@ -5,4 +5,4 @@ BINPATH=$(command readlink -f $0 2> /dev/null || command grealpath $0 2> /dev/nu
APPHOME=$(dirname $(dirname $BINPATH))
CONFIG_PATH=/etc/opt/dans.knaw.nl/$SCRIPTNAME/config.yml
-java -Ddans.default.config=$CONFIG_PATH -jar $APPHOME/bin/$SCRIPTNAME.jar "$@"
+java $DANS_JAVA_OPTS -Ddans.default.config=$CONFIG_PATH -jar $APPHOME/bin/$SCRIPTNAME.jar "$@"
diff --git a/src/main/assembly/dist/cfg/config.yml b/src/main/assembly/dist/cfg/config.yml
index e941205..cec3712 100644
--- a/src/main/assembly/dist/cfg/config.yml
+++ b/src/main/assembly/dist/cfg/config.yml
@@ -18,7 +18,7 @@ depositBoxes:
- /var/opt/dans.knaw.nl/tmp/auto-ingest/outbox/failed
- /var/opt/dans.knaw.nl/tmp/sword2-uploads
-pollingInterval: 5000
+pollingInterval: 500
depositPropertiesDatabase:
driverClass: org.postgresql.Driver
diff --git a/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositApplication.java b/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositApplication.java
index e5428f2..9326387 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositApplication.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositApplication.java
@@ -17,11 +17,11 @@
package nl.knaw.dans.managedeposit;
import io.dropwizard.core.Application;
+import io.dropwizard.core.setup.Bootstrap;
+import io.dropwizard.core.setup.Environment;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.hibernate.UnitOfWorkAwareProxyFactory;
-import io.dropwizard.core.setup.Bootstrap;
-import io.dropwizard.core.setup.Environment;
import nl.knaw.dans.managedeposit.core.CsvMessageBodyWriter;
import nl.knaw.dans.managedeposit.core.DepositProperties;
import nl.knaw.dans.managedeposit.core.service.DepositStatusUpdater;
@@ -34,10 +34,6 @@
public class DdManageDepositApplication extends Application {
- public static void main(final String[] args) throws Exception {
- new DdManageDepositApplication().run(args);
- }
-
private final HibernateBundle depositPropertiesHibernate =
new HibernateBundle<>(DepositProperties.class) {
@@ -47,6 +43,10 @@ public DataSourceFactory getDataSourceFactory(DdManageDepositConfiguration confi
}
};
+ public static void main(final String[] args) throws Exception {
+ new DdManageDepositApplication().run(args);
+ }
+
@Override
public String getName() {
return "Dd Manage Deposit";
@@ -70,13 +70,10 @@ public void run(final DdManageDepositConfiguration configuration, final Environm
final UnitOfWorkAwareProxyFactory proxyFactory = new UnitOfWorkAwareProxyFactory(depositPropertiesHibernate);
DepositStatusUpdater depositStatusUpdater = proxyFactory.create(
- DepositStatusUpdater.class,
- new Class[] { DepositPropertiesDAO.class },
- new Object[] { depositPropertiesDAO });
+ DepositStatusUpdater.class, DepositPropertiesDAO.class, depositPropertiesDAO);
final IngestPathMonitor ingestPathMonitor = new IngestPathMonitor(configuration.getDepositBoxes(), depositStatusUpdater, configuration.getPollingInterval());
environment.lifecycle().manage(ingestPathMonitor);
-
}
}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositConfiguration.java b/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositConfiguration.java
index 70fc225..e492e40 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositConfiguration.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/DdManageDepositConfiguration.java
@@ -18,7 +18,6 @@
import io.dropwizard.core.Configuration;
import io.dropwizard.db.DataSourceFactory;
-import nl.knaw.dans.managedeposit.core.service.TextTruncation;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
@@ -26,7 +25,9 @@
import java.util.ArrayList;
import java.util.List;
+@SuppressWarnings("unused")
public class DdManageDepositConfiguration extends Configuration {
+ private static final long DEFAULT_POLLING_INTERVAL = 500;
@Valid
@NotNull
private DataSourceFactory database = new DataSourceFactory();
@@ -52,7 +53,7 @@ public void setDepositPropertiesDatabase(DataSourceFactory database) {
}
public long getPollingInterval() {
- return pollingInterval > 0 ? pollingInterval : TextTruncation.pollingInterval;
+ return pollingInterval > 0 ? pollingInterval : DEFAULT_POLLING_INTERVAL;
}
public void setPollingInterval(long pollingInterval) {
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/CsvMessageBodyWriter.java b/src/main/java/nl/knaw/dans/managedeposit/core/CsvMessageBodyWriter.java
index 3236949..43ae7bb 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/CsvMessageBodyWriter.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/CsvMessageBodyWriter.java
@@ -45,14 +45,16 @@ public boolean isWriteable(Class type, Type genericType, Annotation[] annotation
@Override
public void writeTo(List data, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap multivaluedMap, OutputStream outputStream) throws
IOException, WebApplicationException {
- if (data != null && data.size() > 0) {
+ if (data != null && !data.isEmpty()) {
// TODO: pass the mapper in at configuration time
CsvMapper mapper = new CsvMapper();
Object o = data.get(0);
CsvSchema schema = mapper.schemaFor(o.getClass())
.withHeader()
.sortedBy("depositor", "depositId", "bagName", "depositState", "depositCreationTimestamp", "depositUpdateTimestamp", "description", "location", "storageInBytes", "deleted")
- .rebuild().build();
+ .rebuild()
+ .setNullValue("undefined")
+ .build();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.registerModule(new JavaTimeModule());
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/DepositProperties.java b/src/main/java/nl/knaw/dans/managedeposit/core/DepositProperties.java
index 177416a..941e074 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/DepositProperties.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/DepositProperties.java
@@ -30,6 +30,8 @@
name = "showAll",
query = "SELECT dp FROM DepositProperties dp"
)
+
+@SuppressWarnings("unused")
public class DepositProperties {
@Column(name = "depositor", nullable = false) // depositor.userId
@@ -49,10 +51,10 @@ public class DepositProperties {
@Column(name = "deposit_update_timestamp") // modified timestamp of deposit.properties
private OffsetDateTime depositUpdateTimestamp;
- @Column(name = "description", length = TextTruncation.maxDescriptionLength) // state.description
+ @Column(name = "description", length = TextTruncation.MAX_DESCRIPTION_LENGTH) // state.description
private String description;
- @Column(name = "location", length = TextTruncation.maxDirectoryLength) // full parent-path on disk
+ @Column(name = "location", length = TextTruncation.MAX_DIRECTORY_LENGTH) // full parent-path on disk
private String location;
@Column(name = "storage_in_bytes") // Total storage of deposit directory
@@ -60,15 +62,12 @@ public class DepositProperties {
@Column(name = "deleted") // deposit is deleted from inbox - archived
private boolean deleted;
- public String getDepositId() {
- return depositId;
- }
public DepositProperties() {
}
public DepositProperties(String depositId, String depositor, String bagName, String depositState,
- String description, OffsetDateTime depositCreationTimestamp, String location, long storageInBytes) {
+ String description, OffsetDateTime depositCreationTimestamp, String location, long storageInBytes, OffsetDateTime depositUpdateTimestamp) {
this.depositId = depositId;
this.depositor = depositor;
this.bagName = bagName;
@@ -77,6 +76,11 @@ public DepositProperties(String depositId, String depositor, String bagName, Str
this.depositCreationTimestamp = depositCreationTimestamp;
this.location = location;
this.storageInBytes = storageInBytes;
+ this.depositUpdateTimestamp = depositUpdateTimestamp;
+ }
+
+ public String getDepositId() {
+ return depositId;
}
public String getDepositor() {
@@ -169,4 +173,20 @@ public boolean equals(Object o) {
public int hashCode() {
return 31 * depositId.hashCode() + depositor.hashCode();
}
-}
\ No newline at end of file
+
+ @Override
+ public String toString() {
+ return "DepositProperties{" +
+ "depositor='" + depositor + "'" +
+ ", depositId='" + depositId + '\'' +
+ ", bagName='" + bagName + '\'' +
+ ", depositState='" + depositState + '\'' +
+ ", depositCreationTimestamp=" + depositCreationTimestamp +
+ ", depositUpdateTimestamp=" + depositUpdateTimestamp +
+ ", description='" + description + '\'' +
+ ", location='" + location + '\'' +
+ ", storageInBytes=" + storageInBytes +
+ ", deleted=" + deleted +
+ '}';
+ }
+}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesAssembler.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesAssembler.java
index 1a18fb7..13e4fbc 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesAssembler.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesAssembler.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.DateTimeException;
import java.time.OffsetDateTime;
import java.util.Optional;
@@ -34,49 +35,46 @@ class DepositPropertiesAssembler {
DepositPropertiesAssembler() {
}
- Optional assembleObject(File depositPropertiesFile, boolean updateModificationDateTime) {
-
+ Optional assembleObject(File depositPropertiesFile, long CalculatedFolderSize) {
Path depositPath = depositPropertiesFile.getParentFile().toPath();
log.debug("assembleObject(depositPropertiesPath:Path): '{}'", depositPropertiesFile.getAbsolutePath());
- DepositProperties dp; // = null
+ DepositProperties dp = null;
Configuration configuration;
try {
configuration = DepositPropertiesFileReader.readDepositProperties(depositPropertiesFile);
+ String creationTime = configuration.getString("creation.timestamp");
dp = new DepositProperties(depositPath.getFileName().toString(),
configuration.getString("depositor.userId", ""),
configuration.getString("bag-store.bag-name", ""),
configuration.getString("state.label", ""),
- TextTruncation.stripEnd(configuration.getString("state.description", ""), TextTruncation.maxDescriptionLength),
- OffsetDateTime.parse(configuration.getString("creation.timestamp", OffsetDateTime.now().toString())),
- TextTruncation.stripBegin(depositPropertiesFile.getParentFile().getParentFile().getAbsolutePath(), TextTruncation.maxDirectoryLength),
- calculateFolderSize(depositPath));
-
- if (updateModificationDateTime) {
- dp.setDepositUpdateTimestamp(OffsetDateTime.now());
- }
- else {
- dp.setDepositUpdateTimestamp(dp.getDepositCreationTimestamp());
- }
-
+ TextTruncation.stripEnd(configuration.getString("state.description", ""), TextTruncation.MAX_DESCRIPTION_LENGTH),
+ (creationTime == null || creationTime.isEmpty()) ? null : OffsetDateTime.parse(creationTime),
+ TextTruncation.stripBegin(depositPropertiesFile.getParentFile().getParentFile().getAbsolutePath(), TextTruncation.MAX_DIRECTORY_LENGTH),
+ CalculatedFolderSize == 0 ? calculateFolderSize(depositPath) : CalculatedFolderSize,
+ OffsetDateTime.now());
}
- catch (ConfigurationException e) {
- log.error(e.getMessage(), e);
- throw new RuntimeException(e);
+ catch (ConfigurationException | DateTimeException e) {
+ log.error("Error reading deposit.properties file: {}", e.getMessage());
}
- return Optional.of(dp);
+ catch (RuntimeException e) {
+ log.error("Error accessing deposit files : {}", e.getMessage());
+ }
+ return Optional.ofNullable(dp);
}
private long calculateFolderSize(Path path) {
- long size;
- try (var pathStream = Files.walk(path)) {
- size = pathStream
- .filter(p -> p.toFile().isFile())
- .mapToLong(p -> p.toFile().length())
- .sum();
- }
- catch (IOException e) {
- throw new RuntimeException(e);
+ long size = 0;
+ if (Files.exists(path)) {
+ try (var pathStream = Files.walk(path)) {
+ size = pathStream
+ .filter(p -> p.toFile().isFile())
+ .mapToLong(p -> p.toFile().length())
+ .sum();
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
}
return size;
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesFileReader.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesFileReader.java
index cfe8402..14a89ee 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesFileReader.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositPropertiesFileReader.java
@@ -26,7 +26,7 @@
import java.io.File;
class DepositPropertiesFileReader {
- private static final Logger log = LoggerFactory.getLogger(DepositPropertiesAssembler.class);
+ private static final Logger log = LoggerFactory.getLogger(DepositPropertiesFileReader.class);
static public Configuration readDepositProperties(File propertiesFile) throws ConfigurationException {
log.debug("readDepositProperties: '{}'", propertiesFile.toString());
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdater.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdater.java
index 936fa79..aeb5807 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdater.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdater.java
@@ -28,7 +28,7 @@
import java.util.Optional;
public class DepositStatusUpdater {
- private static final Logger log = LoggerFactory.getLogger(DepositPropertiesAssembler.class);
+ private static final Logger log = LoggerFactory.getLogger(DepositStatusUpdater.class);
private final DepositPropertiesDAO depositPropertiesDAO;
private final DepositPropertiesAssembler depositPropertiesAssembler;
@@ -38,47 +38,69 @@ public DepositStatusUpdater(DepositPropertiesDAO depositPropertiesDAO) {
}
@UnitOfWork
- public void onCreateDeposit(File depositPropertiesFile) {
- Optional dp = depositPropertiesDAO.findById(depositPropertiesFile.getParentFile().getName());
+ public void onDepositCreate(File depositPropertiesFile) {
+ // Note: the 'move deposit' action is processed by the system in two steps: 1. `create` deposit in the new location; 2. `delete` it from the old location.
+ Optional record = depositPropertiesDAO.findById(depositPropertiesFile.getParentFile().getName());
- if (dp.isPresent()) {
- // The 'move deposit' action is processed in two steps: 1. `create` deposit in the new location; 2. `delete` it from the old location.
- // Update the location column.
- Path depositLocationFolder = Path.of(depositPropertiesFile.getParentFile().getParentFile().getAbsolutePath());
- Optional updatedNumber = depositPropertiesDAO.updateDepositLocation(dp.get().getDepositId(), depositLocationFolder);
- if (updatedNumber.isPresent())
- log.debug("onCreateDeposit - `location` of deposit '{}' has been updated to '{}' ", dp.get().getDepositId(), depositLocationFolder);
+ if (record.isPresent()) {
+ // Update the location column and other fields
+ Optional dpObject = depositPropertiesAssembler.assembleObject(depositPropertiesFile, record.get().getStorageInBytes());
+ if (dpObject.isPresent()) {
+ depositPropertiesDAO.merge(dpObject.get());
+ log.debug("onDepositCreate - The deposit '{}' location and/or state has been updated to '{}' ", record.get().getDepositId(), depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
+ else {
+ writeErrorMsg(depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
}
else {
- Optional dpObject = depositPropertiesAssembler.assembleObject(depositPropertiesFile, false);
- dpObject.ifPresent(depositPropertiesDAO::save);
- log.debug("onCreateDeposit: A new deposit has been registered `{}`", depositPropertiesFile.getParentFile().getAbsolutePath());
+ Optional dpObject = depositPropertiesAssembler.assembleObject(depositPropertiesFile, 0);
+ if (dpObject.isPresent()) {
+ depositPropertiesDAO.save(dpObject.get());
+ log.debug("onDepositCreate: A new deposit has been registered '{}'", depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
+ else {
+ writeErrorMsg(depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
}
}
@UnitOfWork
- public void onChangeDeposit(File depositPropertiesFile) {
- Optional dpObject = depositPropertiesAssembler.assembleObject(depositPropertiesFile, true);
- dpObject.ifPresent(depositPropertiesDAO::save);
- log.debug("onChangeDeposit: deposit.properties has been changed `{}`", depositPropertiesFile.getParentFile().getAbsolutePath());
+ public void onDepositChange(File depositPropertiesFile) {
+ Optional record = depositPropertiesDAO.findById(depositPropertiesFile.getParentFile().getName());
+ long folder_size = record.map(DepositProperties::getStorageInBytes).orElse(0L);
+ Optional dpObject = depositPropertiesAssembler.assembleObject(depositPropertiesFile, folder_size);
+ if (dpObject.isPresent()) {
+ depositPropertiesDAO.merge(dpObject.get());
+ log.debug("onDepositChange: deposit.properties has been changed '{}'", depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
+ else {
+ writeErrorMsg(depositPropertiesFile.getParentFile().getAbsolutePath());
+ }
}
@UnitOfWork
- public void onDeleteDeposit(File depositPropertiesFile) {
- // At this stage, the deposit.properties file's handle is present but the content is null (impossible to read data of the file)
- Optional dp = depositPropertiesDAO.findById(depositPropertiesFile.getParentFile().getName());
+ public void onDepositDelete(File depositPropertiesFile) {
+ // Note: if delete notify is part of a folder moving, then at this stage, the deposit.properties file's handle is present but the content is null (impossible to read data of the file)
+ Optional record = depositPropertiesDAO.findById(depositPropertiesFile.getParentFile().getName());
- try {
- // The 'move deposit' action is processed in two steps: 1. `create` deposit in the new location; 2. `delete` it from the old location. Ignore delete step
- if (dp.isPresent() && Files.isSameFile(Path.of(dp.get().getLocation()), Path.of(depositPropertiesFile.getParentFile().getParentFile().getAbsolutePath()))) {
- String depositId = depositPropertiesFile.getParentFile().getName();
- Optional deletedNumber = depositPropertiesDAO.updateDeleteFlag(depositId, true);
- log.debug("onDeleteDeposit - 'deleted' mark has been set to '{}' for deposit.properties from '{}' ", deletedNumber.isPresent(), depositId);
+ // The 'move deposit' action is processed in two steps: 1. `create` deposit in the new location; 2. `delete` it from the old location. Ignore delete step
+ if (record.isPresent()) {
+ try {
+ if (Files.isSameFile(Path.of(record.get().getLocation()), Path.of(depositPropertiesFile.getParentFile().getParentFile().getAbsolutePath())) && !depositPropertiesFile.exists()) {
+ String depositId = depositPropertiesFile.getParentFile().getName();
+ Optional deletedNumber = depositPropertiesDAO.updateDeleteFlag(depositId, true);
+ log.debug("onDepositDelete - 'deleted' mark has been set to '{}' for deposit.properties from '{}' ", deletedNumber.isPresent() && deletedNumber.get() > 0, depositId);
+ }
+ }
+ catch (IOException e) {
+ writeErrorMsg(e.getMessage());
}
}
- catch (IOException e) {
- log.debug("The 'move deposit' action is processed in two steps: 1. `create` deposit in the new location; 2. `delete` it from the old location. Ignore delete step for {}", depositPropertiesFile.getParentFile().getAbsolutePath());
- }
+ }
+
+ private void writeErrorMsg(String additionalInfo) {
+ log.error("Error creating / Updating deposit record: '{}'", additionalInfo);
}
}
\ No newline at end of file
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/DepthFileFilter.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepthFileFilter.java
new file mode 100644
index 0000000..b200053
--- /dev/null
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/DepthFileFilter.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nl.knaw.dans.managedeposit.core.service;
+
+import org.apache.commons.io.filefilter.AbstractFileFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.nio.file.Path;
+
+public class DepthFileFilter extends AbstractFileFilter {
+ private static final Logger log = LoggerFactory.getLogger(DepthFileFilter.class);
+ private final Path absoluteBaseFolder;
+ private final int requiredNameCount;
+
+ public DepthFileFilter(Path baseFolder, int depthLimit) {
+ this.absoluteBaseFolder = baseFolder.toAbsolutePath();
+ this.requiredNameCount = this.absoluteBaseFolder.getNameCount() + depthLimit;
+ }
+
+ private boolean confirmParent(File file) {
+ var path = file.getAbsoluteFile().toPath();
+ if (!path.startsWith(absoluteBaseFolder)) {
+ log.warn(String.format("[%s] must be a child of [%s]", path, absoluteBaseFolder));
+ return false;
+ }
+ return path.getNameCount() == requiredNameCount;
+ }
+
+ @Override
+ public boolean accept(File file) {
+ return confirmParent(file);
+ }
+
+ @Override
+ public boolean accept(File dir, String name) {
+ return confirmParent(dir);
+ }
+}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitor.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitor.java
index 179b49e..b0cfbd2 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitor.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitor.java
@@ -18,7 +18,6 @@
import io.dropwizard.lifecycle.Managed;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.FileFilterUtils;
-import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
@@ -47,23 +46,31 @@ public IngestPathMonitor(List depositBoxesPaths, DepositStatusUpdater depo
}
private void startMonitors() throws Exception {
- IOFileFilter directories = FileFilterUtils.and(FileFilterUtils.directoryFileFilter(), HiddenFileFilter.VISIBLE);
- IOFileFilter files = FileFilterUtils.and(FileFilterUtils.fileFileFilter(), FileFilterUtils.nameFileFilter("deposit.properties", IOCase.INSENSITIVE));
- IOFileFilter filter = FileFilterUtils.or(directories, files);
-
- log.info("Starting 'IngestPathMonitor', file filter: deposit.properties");
+ log.info("Starting 'IngestPathMonitor', with file filter: deposit.properties");
+ var observers = new ArrayList<>();
for (Path folder : toMonitorPaths) {
- FileAlterationObserver observer = new FileAlterationObserver(folder.toFile(), filter);
+ IOFileFilter directories = FileFilterUtils.and(
+ FileFilterUtils.directoryFileFilter(),
+ new DepthFileFilter(folder, 1)
+ );
+ IOFileFilter files = FileFilterUtils.and(
+ FileFilterUtils.fileFileFilter(),
+ FileFilterUtils.nameFileFilter("deposit.properties", IOCase.INSENSITIVE),
+ new DepthFileFilter(folder, 2)
+ );
+ IOFileFilter filter = FileFilterUtils.or(directories, files);
+ FileAlterationObserver observer = new FileAlterationObserver(folder.toFile(), filter);
observer.addListener(this);
+ observers.add(observer);
- FileAlterationMonitor monitor = new FileAlterationMonitor(this.pollingInterval, observer);
- fileAlterationMonitors.add(monitor);
-
- monitor.start();
- log.debug("'IngestPathMonitor' is going to monitor the folder '{}'", folder);
}
+
+ FileAlterationMonitor monitor = new FileAlterationMonitor(this.pollingInterval, observers.toArray(new FileAlterationObserver[0]));
+ fileAlterationMonitors.add(monitor);
+ log.debug("'IngestPathMonitor' is going to monitor the folders\n '{}'", toMonitorPaths);
+ monitor.start();
}
@Override
@@ -94,19 +101,19 @@ public void stop() throws Exception {
@Override
public void onFileCreate(File file) {
log.debug("onFileCreate: '{}'", file.getAbsolutePath());
- depositStatusUpdater.onCreateDeposit(file);
+ depositStatusUpdater.onDepositCreate(file);
}
@Override
public void onFileDelete(File file) {
log.debug("onFileDelete: '{}'", file.getAbsolutePath());
- depositStatusUpdater.onDeleteDeposit(file);
+ depositStatusUpdater.onDepositDelete(file);
}
@Override
public void onFileChange(File file) {
log.debug("onFileChange: '{}'", file.getAbsolutePath());
- depositStatusUpdater.onChangeDeposit(file);
+ depositStatusUpdater.onDepositChange(file);
}
}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/core/service/TextTruncation.java b/src/main/java/nl/knaw/dans/managedeposit/core/service/TextTruncation.java
index 4192693..f367890 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/core/service/TextTruncation.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/core/service/TextTruncation.java
@@ -16,9 +16,8 @@
package nl.knaw.dans.managedeposit.core.service;
public class TextTruncation {
- public static final long pollingInterval = 5 * 1000;
- public static final int maxDescriptionLength = 1024;
- public static final int maxDirectoryLength = 512;
+ public static final int MAX_DESCRIPTION_LENGTH = 1024;
+ public static final int MAX_DIRECTORY_LENGTH = 512;
public static String stripEnd(String text, int maxLength) {
return text.length() > maxLength ? text.substring(0, maxLength) : text;
@@ -29,5 +28,4 @@ public static String stripBegin(String text, int maxLength) {
return textLength > maxLength ? text.substring(textLength - maxLength) : text;
}
-
}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/db/DepositPropertiesDAO.java b/src/main/java/nl/knaw/dans/managedeposit/db/DepositPropertiesDAO.java
index 1db4751..60db574 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/db/DepositPropertiesDAO.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/db/DepositPropertiesDAO.java
@@ -19,6 +19,8 @@
import nl.knaw.dans.managedeposit.core.DepositProperties;
import org.hibernate.SessionFactory;
import org.hibernate.query.Query;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
@@ -26,7 +28,7 @@
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
-import java.nio.file.Path;
+import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
@@ -38,6 +40,7 @@
@SuppressWarnings("resource")
public class DepositPropertiesDAO extends AbstractDAO {
+ private static final Logger log = LoggerFactory.getLogger(DepositPropertiesDAO.class);
public DepositPropertiesDAO(SessionFactory sessionFactory) {
super(sessionFactory);
@@ -70,7 +73,7 @@ public List findAll() {
public List findSelection(Map> queryParameters) {
CriteriaBuilder criteriaBuilder = currentSession().getCriteriaBuilder();
- if (queryParameters.size() == 0)
+ if (queryParameters.isEmpty())
return findAll();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(DepositProperties.class);
@@ -83,7 +86,7 @@ public List findSelection(Map> queryPara
public Optional deleteSelection(Map> queryParameters) {
var criteriaBuilder = currentSession().getCriteriaBuilder();
- if (queryParameters.size() == 0) // Note: all records will be deleted (accidentally) without any specified query parameter
+ if (queryParameters.isEmpty()) // Note: all records will be deleted (accidentally) without any specified query parameter
return Optional.of(0);
CriteriaDelete deleteQuery = criteriaBuilder.createCriteriaDelete(DepositProperties.class);
@@ -129,20 +132,30 @@ private Predicate buildQueryCriteria(Map> queryParameters,
case "startdate":
case "enddate":
- DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- LocalDate date = LocalDate.parse(value, formatter);
- var asked_date = OffsetDateTime.of(date.atStartOfDay(), ZoneOffset.UTC);
-
- if (parameter.equals("startdate"))
- orPredicateItem = criteriaBuilder.greaterThan(root.get("depositCreationTimestamp"), asked_date);
- else
- orPredicateItem = criteriaBuilder.lessThan(root.get("depositCreationTimestamp"), asked_date);
+ if (value.isEmpty()) {
+ orPredicateItem = criteriaBuilder.isNull(root.get("depositCreationTimestamp"));
+ }
+ else {
+ try {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ LocalDate date = LocalDate.parse(value, formatter);
+ var asked_date = OffsetDateTime.of(date.atStartOfDay(), ZoneOffset.UTC);
+
+ if (parameter.equals("startdate"))
+ orPredicateItem = criteriaBuilder.greaterThan(root.get("depositCreationTimestamp"), asked_date);
+ else
+ orPredicateItem = criteriaBuilder.lessThan(root.get("depositCreationTimestamp"), asked_date);
+ }
+ catch (DateTimeException e) {
+ log.warn("Error parsing the date: {}", e.getMessage());
+ continue;
+ }
+ }
break;
default:
orPredicateItem = criteriaBuilder.equal(root.get(key), value);
}
-
orPredicatesList.add(orPredicateItem);
}
@@ -169,18 +182,4 @@ public Optional updateDeleteFlag(String depositId, boolean deleted) {
return Optional.of(query.executeUpdate());
}
- public Optional updateDepositLocation(String depositId, Path currentParentPath) {
- CriteriaBuilder criteriaBuilder = currentSession().getCriteriaBuilder();
- CriteriaUpdate criteriaUpdate = criteriaBuilder.createCriteriaUpdate(DepositProperties.class);
- Root root = criteriaUpdate.from(DepositProperties.class);
-
- Predicate predicate = buildQueryCriteria(Map.of("depositId", List.of(depositId)), criteriaBuilder, root);
- criteriaUpdate.where(predicate);
-
- criteriaUpdate.set("location", currentParentPath.toString());
-
- var query = currentSession().createQuery(criteriaUpdate);
- return Optional.of(query.executeUpdate());
- }
-
}
diff --git a/src/main/java/nl/knaw/dans/managedeposit/resources/DepositPropertiesResource.java b/src/main/java/nl/knaw/dans/managedeposit/resources/DepositPropertiesResource.java
index aef3290..343132c 100644
--- a/src/main/java/nl/knaw/dans/managedeposit/resources/DepositPropertiesResource.java
+++ b/src/main/java/nl/knaw/dans/managedeposit/resources/DepositPropertiesResource.java
@@ -40,25 +40,27 @@ public DepositPropertiesResource(DepositPropertiesDAO depositPropertiesDAO) {
private String writeHelpInfoText() {
return
- "DD Manage Deposit is running. \n" +
- "Usage: \n" +
- " - Create reports: GET basePath/report \n" +
- " - Clean database: POST basePath/delete-deposit \n" +
- " Query string parameters: user, state, startdate, enddate \n" +
- " 'startdate'/'enddate' format: yyyy-MM-dd \n" +
- " Possible 'state' value: ARCHIVED, DRAFT, FAILED, FINALIZING, INVALID, REJECTED, SUBMITTED, UPLOADED, PUBLISHED \n" +
- " Examples: \n" +
- " curl -i -X GET basePath/report?startdate=yyyy-MM-dd \n" +
- " curl -i -X GET basePath/delete-deposit?user=XXX&state=REJECTED \n" +
- " curl -i -X POST basePath/delete-deposit?user=XXX \n" +
- " curl -i -X POST basePath/delete-deposit?user=XXX&state=REJECTED";
+ """
+ DD Manage Deposit is running.\s
+ Usage:\s
+ - Create reports: GET basePath/report\s
+ - Clean database: POST basePath/delete-deposit\s
+ - Query string parameters: user, state, startdate, enddate, deleted\s
+ - 'startdate'/'enddate' format: yyyy-MM-dd\s
+ - 'deleted' is a boolean with values: 'true' or 'false'\s
+ - Possible 'state' values: ARCHIVED, DRAFT, FAILED, FINALIZING, INVALID, REJECTED, SUBMITTED, UPLOADED, PUBLISHED\s
+ - To give an undefined parameter (when column's value is empty or null): 'parameterName=' (ex. 'user=')\s
+ Examples:\s
+ curl -i -X GET basePath/report?startdate=yyyy-MM-dd\s
+ curl -i -X GET basePath/delete-deposit?user=XXX&state=REJECTED\s
+ curl -i -X POST basePath/delete-deposit?user=XXX\s
+ curl -i -X POST basePath/delete-deposit?user=XXX&state=REJECTED""";
}
@GET
@UnitOfWork
public Response getApiInformation() {
-
return Response
.status(Response.Status.OK)
.entity(this.helpInfo)
diff --git a/src/test/java/nl/knaw/dans/managedeposit/AbstractDatabaseTest.java b/src/test/java/nl/knaw/dans/managedeposit/AbstractDatabaseTest.java
new file mode 100644
index 0000000..c9e88bb
--- /dev/null
+++ b/src/test/java/nl/knaw/dans/managedeposit/AbstractDatabaseTest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nl.knaw.dans.managedeposit;
+
+import io.dropwizard.testing.junit5.DAOTestExtension;
+import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
+import nl.knaw.dans.managedeposit.core.DepositProperties;
+import nl.knaw.dans.managedeposit.db.DepositPropertiesDAO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(DropwizardExtensionsSupport.class)
+public abstract class AbstractDatabaseTest extends AbstractTestWithTestDir {
+ protected final DAOTestExtension daoTestExtension = DAOTestExtension.newBuilder()
+ .addEntityClass(DepositProperties.class)
+ .build();
+ protected DepositPropertiesDAO dao;
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ super.setUp();
+ dao = new DepositPropertiesDAO(daoTestExtension.getSessionFactory());
+ }
+}
diff --git a/src/test/java/nl/knaw/dans/managedeposit/AbstractTestWithTestDir.java b/src/test/java/nl/knaw/dans/managedeposit/AbstractTestWithTestDir.java
new file mode 100644
index 0000000..950d084
--- /dev/null
+++ b/src/test/java/nl/knaw/dans/managedeposit/AbstractTestWithTestDir.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nl.knaw.dans.managedeposit;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.BeforeEach;
+
+import java.nio.file.Path;
+
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+public class AbstractTestWithTestDir {
+ protected final Path testDir = Path.of("target/test")
+ .resolve(getClass().getSimpleName());
+
+ @BeforeEach
+ public void setUp() throws Exception {
+ FileUtils.deleteDirectory(testDir.toFile());
+ }
+
+ /**
+ * Assume that a bug is not yet fixed. This allows to skip assertions while still showing the code covered by the test.
+ *
+ * @param message the message to display
+ */
+ public void assumeNotYetFixed (String message) {
+ assumeTrue(false, message);
+ }
+}
diff --git a/src/test/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdaterOnDepositUpdateTest.java b/src/test/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdaterOnDepositUpdateTest.java
new file mode 100644
index 0000000..6a841ca
--- /dev/null
+++ b/src/test/java/nl/knaw/dans/managedeposit/core/service/DepositStatusUpdaterOnDepositUpdateTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2023 DANS - Data Archiving and Networked Services (info@dans.knaw.nl)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package nl.knaw.dans.managedeposit.core.service;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import nl.knaw.dans.managedeposit.AbstractDatabaseTest;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.time.OffsetDateTime;
+
+import static java.nio.file.Files.createDirectories;
+import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
+
+public class DepositStatusUpdaterOnDepositUpdateTest extends AbstractDatabaseTest {
+ private ListAppender listAppender;
+
+ @BeforeEach
+ public void setup() throws Exception {
+ super.setUp();
+ var logger = (Logger) LoggerFactory.getLogger(DepositStatusUpdater.class);
+ listAppender = new ListAppender<>();
+ listAppender.start();
+ logger.setLevel(Level.DEBUG);
+ logger.addAppender(listAppender);
+ }
+
+ @Test
+ public void onCreateDeposit_should_add_a_db_record() throws IOException {
+ var depositStatusUpdater = new DepositStatusUpdater(dao);
+
+ // Prepare test data
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.writeString(propertiesFile, """
+ creation.timestamp = 2023-08-16T17:40:41.390209+02:00
+ depositor.userId = user001
+ bag-store.bag-name = revision03
+ """);
+
+ // Call the method under test
+ depositStatusUpdater.onDepositCreate(propertiesFile.toFile());
+
+ // Check the logs
+ var formattedMessage = listAppender.list.get(0).getFormattedMessage();
+ assertThat(formattedMessage).startsWith("onDepositCreate: A new deposit has been registered '");
+ assertThat(formattedMessage).endsWith("DepositStatusUpdaterOnDepositUpdateTest/bag'");
+
+ // Check the database
+ var maybeDepositProperties = daoTestExtension.inTransaction(() ->
+ dao.findById("bag")
+ );
+ assertThat(maybeDepositProperties).isNotEmpty().get()
+ .hasFieldOrPropertyWithValue("depositId", "bag")
+ .hasFieldOrPropertyWithValue("depositor", "user001")
+ .hasFieldOrPropertyWithValue("bagName", "revision03")
+ .hasFieldOrPropertyWithValue("depositState", "")
+ .hasFieldOrPropertyWithValue("depositCreationTimestamp", OffsetDateTime.parse("2023-08-16T17:40:41.390209+02:00"))
+ .hasFieldOrPropertyWithValue("location", testDir.toAbsolutePath().toString())
+ .hasFieldOrPropertyWithValue("storageInBytes", 113L);
+
+ var depositPropertiesList = daoTestExtension.inTransaction(() ->
+ dao.findAll()
+ );
+ assertThat(depositPropertiesList).hasSize(1);
+ }
+
+ @Test
+ public void onCreateDeposit_should_create_new_db_record_when_creation_timestamp_is_empty() throws IOException {
+ var depositStatusUpdater = new DepositStatusUpdater(dao);
+
+ // Prepare test data
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.writeString(propertiesFile, """
+ creation.timestamp
+ depositor.userId = user001
+ bag-store.bag-name = revision03
+ """);
+
+ // Call the method under test
+ depositStatusUpdater.onDepositCreate(propertiesFile.toFile());
+
+ // Check the logs
+ var formattedMessage = listAppender.list.get(0).getFormattedMessage();
+ assertThat(formattedMessage).startsWith("onDepositCreate: A new deposit has been registered '");
+ assertThat(formattedMessage).endsWith("DepositStatusUpdaterOnDepositUpdateTest/bag'");
+
+ // Check the database
+ var maybeDepositProperties = daoTestExtension.inTransaction(() -> dao.findById("bag"));
+ assertThat(maybeDepositProperties).isNotEmpty();
+ }
+
+ @Test
+ public void onCreateDeposit_should_create_new_db_record_when_creation_timestamp_is_not_present() throws IOException {
+ var depositStatusUpdater = new DepositStatusUpdater(dao);
+
+ // Prepare test data
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.writeString(propertiesFile, """
+ depositor.userId = user001
+ bag-store.bag-name = revision03
+ """);
+
+ // Call the method under test
+ depositStatusUpdater.onDepositCreate(propertiesFile.toFile());
+
+ // Check the logs
+ var formattedMessage = listAppender.list.get(0).getFormattedMessage();
+ assertThat(formattedMessage).startsWith("onDepositCreate: A new deposit has been registered '");
+ assertThat(formattedMessage).endsWith("DepositStatusUpdaterOnDepositUpdateTest/bag'");
+
+ // Check the database
+ var maybeDepositProperties = daoTestExtension.inTransaction(() -> dao.findById("bag"));
+ assertThat(maybeDepositProperties).isNotEmpty();
+ }
+
+ @Test
+ public void onCreateDeposit_should_not_create_new_db_record_when_creation_timestamp_is_invalid() throws IOException {
+ var depositStatusUpdater = new DepositStatusUpdater(dao);
+
+ // Prepare test data
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.writeString(propertiesFile, """
+ creation.timestamp = 2023-08-16 17:40:41.390209+02:00
+ depositor.userId = user001
+ bag-store.bag-name = revision03
+ """);
+
+ // Call the method under test
+ depositStatusUpdater.onDepositCreate(propertiesFile.toFile());
+
+ // Check the logs
+ var formattedMessage = listAppender.list.get(0).getFormattedMessage();
+ assertThat(formattedMessage).startsWith("Error creating / Updating deposit record: '");
+ assertThat(formattedMessage).endsWith("DepositStatusUpdaterOnDepositUpdateTest/bag'");
+
+ // Check the database
+ var maybeDepositProperties = daoTestExtension.inTransaction(() -> dao.findById("bag"));
+ assertThat(maybeDepositProperties).isEmpty();
+ }
+
+ // TODO: other scenario's and test classes for onChangeDeposit and onDeleteDeposit
+}
\ No newline at end of file
diff --git a/src/test/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitorTest.java b/src/test/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitorTest.java
index 27a320a..b7d2a39 100644
--- a/src/test/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitorTest.java
+++ b/src/test/java/nl/knaw/dans/managedeposit/core/service/IngestPathMonitorTest.java
@@ -15,6 +15,135 @@
*/
package nl.knaw.dans.managedeposit.core.service;
-public class IngestPathMonitorTest {
- // Stub
-}
+import nl.knaw.dans.managedeposit.AbstractTestWithTestDir;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.nio.file.Files;
+
+import static java.nio.file.Files.createDirectories;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class IngestPathMonitorTest extends AbstractTestWithTestDir {
+
+ private IngestPathMonitor startMonitor(DepositStatusUpdater mockUpdater, int pollingInterval) throws Exception {
+ var monitor = new IngestPathMonitor(singletonList(testDir), mockUpdater, pollingInterval);
+ monitor.start();
+ Thread.sleep(60); // wait for the monitor to get ready
+ return monitor;
+ }
+
+ @Test
+ public void should_ignore_properties_in_root() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 50);
+
+ createDirectories(testDir);
+ var propertiesFile = Files.createFile(testDir.resolve("deposit.properties"));
+ Thread.sleep(70);// Wait for the monitor to pick up the new file
+
+ Mockito.verify(mockUpdater, Mockito.times(0))
+ .onDepositCreate(propertiesFile.toFile());
+ Mockito.verifyNoMoreInteractions(mockUpdater);
+
+ monitor.stop();
+ }
+
+ @Test
+ public void should_pick_up_new_properties_in_bag() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 50);
+
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.createFile(propertiesFile);
+ Thread.sleep(70);// Wait for the monitor to pick up the new file
+
+ Mockito.verify(mockUpdater, Mockito.times(1)).onDepositCreate(propertiesFile.toFile());
+ Mockito.verifyNoMoreInteractions(mockUpdater);
+
+ monitor.stop();
+ }
+
+ @Test
+ public void should_ignore_properties_in_bag_content() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 50);
+
+ var propertiesFile = testDir.resolve("bag/content/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.createFile(propertiesFile);
+ Thread.sleep(70);// Wait for the monitor to pick up the new file
+
+ Mockito.verify(mockUpdater, Mockito.times(0)).onDepositCreate(propertiesFile.toFile());
+ Mockito.verifyNoMoreInteractions(mockUpdater);
+
+ monitor.stop();
+ }
+
+
+ @Test
+ public void should_pick_up_deleted_bag() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 50);
+
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.createFile(propertiesFile);
+ Thread.sleep(70);
+ FileUtils.deleteDirectory(propertiesFile.getParent().toFile());
+ Thread.sleep(70);
+
+ Mockito.verify(mockUpdater, Mockito.times(1)).onDepositDelete(propertiesFile.toFile());
+
+ monitor.stop();
+ }
+
+ @Test
+ public void should_pick_up_changed_properties() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 20);
+
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.createFile(propertiesFile);
+ Thread.sleep(30);
+ Files.writeString(propertiesFile, "just some garbage");
+ Thread.sleep(30);
+
+ Mockito.verify(mockUpdater, Mockito.times(1)).onDepositChange(propertiesFile.toFile());
+
+ monitor.stop();
+ }
+
+ @Test
+ public void should_pick_up_deleted_root() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 20);
+
+ var propertiesFile = testDir.resolve("bag/deposit.properties");
+ createDirectories(propertiesFile.getParent());
+ Files.createFile(propertiesFile);
+ Thread.sleep(30);
+ FileUtils.deleteDirectory(testDir.toFile());
+ Thread.sleep(30);
+
+ Mockito.verify(mockUpdater, Mockito.times(1)).onDepositCreate(propertiesFile.toFile());
+ Mockito.verifyNoMoreInteractions(mockUpdater);
+ assumeNotYetFixed("The monitor should pick up the deletion of the root folder, it might imply deletion of many bags");
+ monitor.stop();
+ }
+
+ @Test
+ public void should_throw_when_stopping_a_stopped_monitor() throws Exception {
+ var mockUpdater = Mockito.mock(DepositStatusUpdater.class);
+ var monitor = startMonitor(mockUpdater, 20);
+ monitor.stop();
+
+ assertThatThrownBy(monitor::stop)
+ .isInstanceOf(RuntimeException.class)
+ .hasRootCauseInstanceOf(IllegalStateException.class);
+ }
+}
\ No newline at end of file