From 8075060c3b4e65f45efdc4c1b711409dddbd989e Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Thu, 23 Nov 2023 16:05:06 +0100
Subject: [PATCH 1/6] Rework logging in object-archives to use prefix-loggers
 with contexts

---
 .../conf/ObjectArchiveSingleton.java          |  4 +++-
 .../impl/DigitalObjectArchiveBase.java        | 20 ++++++++++++++++
 .../impl/DigitalObjectFileArchive.java        | 15 ++++++++----
 .../impl/DigitalObjectMETSFileArchive.java    | 10 ++++----
 .../impl/DigitalObjectS3Archive.java          | 23 ++++++-------------
 5 files changed, 46 insertions(+), 26 deletions(-)
 create mode 100644 src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectArchiveBase.java

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
index 4319d16844..f454afcb64 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
@@ -65,7 +65,9 @@ import de.bwl.bwfla.objectarchive.impl.DigitalObjectFileArchive;
 public class ObjectArchiveSingleton
 		implements IMigratable
 {
-	protected static final Logger				LOG	= Logger.getLogger(ObjectArchiveSingleton.class.getName());
+	public static final String LOGGER_NAME = "OBJECT-ARCHIVE";
+	protected static final Logger LOG = Logger.getLogger(LOGGER_NAME);
+
 	public static volatile boolean 				confValid = false;
 	//public static volatile ObjectArchiveConf	CONF;
 	public static ConcurrentHashMap<String, DigitalObjectArchive> archiveMap = null;
diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectArchiveBase.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectArchiveBase.java
new file mode 100644
index 0000000000..3fd22ced72
--- /dev/null
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectArchiveBase.java
@@ -0,0 +1,20 @@
+package de.bwl.bwfla.objectarchive.impl;
+
+import de.bwl.bwfla.common.logging.PrefixLogger;
+import de.bwl.bwfla.common.logging.PrefixLoggerContext;
+import de.bwl.bwfla.objectarchive.conf.ObjectArchiveSingleton;
+import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectArchive;
+
+
+public abstract class DigitalObjectArchiveBase implements DigitalObjectArchive
+{
+    protected final PrefixLogger log;
+
+    protected DigitalObjectArchiveBase(String type)
+    {
+        final var logctx = new PrefixLoggerContext()
+                .add("type", type);
+
+        this.log = new PrefixLogger(ObjectArchiveSingleton.LOGGER_NAME, logctx);
+    }
+}
diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectFileArchive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectFileArchive.java
index 9292297fce..5888c0a6c5 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectFileArchive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectFileArchive.java
@@ -39,7 +39,6 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.logging.Level;
-import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -75,10 +74,9 @@ import static de.bwl.bwfla.common.utils.METS.MetsUtil.MetsEaasConstant.FILE_GROU
 
 
 // FIXME: this class should be implemented in a style of a "Builder" pattern
-public class DigitalObjectFileArchive implements Serializable, DigitalObjectArchive
+public class DigitalObjectFileArchive extends DigitalObjectArchiveBase implements Serializable
 {
 	private static final long	serialVersionUID	= -3958997016973537612L;
-	protected final Logger log	= Logger.getLogger(this.getClass().getName());
 
 	private String name;
 	private String localPath;
@@ -109,13 +107,20 @@ public class DigitalObjectFileArchive implements Serializable, DigitalObjectArch
 	 */
 	public DigitalObjectFileArchive(String name, String localPath, boolean defaultArchive)
 	{
+		this();
 		this.init(name, localPath, defaultArchive);
 	}
 
-	protected DigitalObjectFileArchive() {}
+	protected DigitalObjectFileArchive()
+	{
+		super("file");
+	}
 
 	protected void init(String name, String localPath, boolean defaultArchive)
 	{
+		log.getContext()
+				.add("name", name);
+
 		var httpExport = ConfigurationProvider.getConfiguration()
 				.get("objectarchive.httpexport");
 
@@ -633,7 +638,7 @@ public class DigitalObjectFileArchive implements Serializable, DigitalObjectArch
 
 	@Override
 	public String resolveObjectResource(String objectId, String resourceId, String method) throws BWFLAException {
-		final var url = DigitalObjectArchive.super.resolveObjectResource(objectId, resourceId, method);
+		final var url = super.resolveObjectResource(objectId, resourceId, method);
 		if (url == null || DataResolver.isAbsoluteUrl(url))
 			return url;
 
diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
index c8f05ac1ff..442398daf2 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
@@ -43,14 +43,12 @@ import java.nio.file.Paths;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.logging.Level;
-import java.util.logging.Logger;
 import java.util.stream.Stream;
 
 
-public class DigitalObjectMETSFileArchive implements Serializable, DigitalObjectArchive
+public class DigitalObjectMETSFileArchive extends DigitalObjectArchiveBase implements Serializable
 {
 	private static final long	serialVersionUID	= -3958997016973537612L;
-	protected final Logger log	= Logger.getLogger(this.getClass().getName());
 
 	final private String name;
 	final private File metaDataDir;
@@ -60,6 +58,10 @@ public class DigitalObjectMETSFileArchive implements Serializable, DigitalObject
 	private Map<String, MetsObject> objects;
 
 	public DigitalObjectMETSFileArchive(String name, String metaDataPath, String dataPath, boolean defaultArchive) throws BWFLAException {
+		super("mets");
+		log.getContext()
+				.add("name", name);
+
 		this.name = name;
 		this.metaDataDir = new File(metaDataPath);
 		if(!metaDataDir.exists() && !metaDataDir.isDirectory())
@@ -216,7 +218,7 @@ public class DigitalObjectMETSFileArchive implements Serializable, DigitalObject
 
 	@Override
 	public String resolveObjectResource(String objectId, String resourceId, String method) throws BWFLAException {
-		final var url = DigitalObjectArchive.super.resolveObjectResource(objectId, resourceId, method);
+		final var url = super.resolveObjectResource(objectId, resourceId, method);
 		if (url == null || DataResolver.isAbsoluteUrl(url))
 			return url;
 
diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
index 6892af659b..af95a68ff7 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
@@ -40,7 +40,6 @@ import de.bwl.bwfla.emucomp.api.EmulatorUtils;
 import de.bwl.bwfla.emucomp.api.FileCollection;
 import de.bwl.bwfla.emucomp.api.FileCollectionEntry;
 import de.bwl.bwfla.objectarchive.conf.ObjectArchiveSingleton;
-import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectArchive;
 import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectFileMetadata;
 import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectS3ArchiveDescriptor;
 import de.bwl.bwfla.objectarchive.datatypes.MetsObject;
@@ -78,10 +77,8 @@ import java.util.stream.Stream;
 import static de.bwl.bwfla.objectarchive.impl.DigitalObjectFileArchive.UpdateCounts;
 
 
-public class DigitalObjectS3Archive implements Serializable, DigitalObjectArchive
+public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements Serializable
 {
-	protected final Logger log = Logger.getLogger(this.getClass().getName());
-
 	private DigitalObjectS3ArchiveDescriptor descriptor;
 	private Bucket bucket;
 	private String basename;
@@ -111,17 +108,8 @@ public class DigitalObjectS3Archive implements Serializable, DigitalObjectArchiv
 	public DigitalObjectS3Archive(DigitalObjectS3ArchiveDescriptor descriptor)
 			throws BWFLAException
 	{
-		this.init(descriptor);
-	}
-
-	protected DigitalObjectS3Archive()
-	{
-		// Empty!
-	}
+		super("s3");
 
-	protected void init(DigitalObjectS3ArchiveDescriptor descriptor)
-			throws BWFLAException
-	{
 		ConfigurationInjection.getConfigurationInjector()
 				.configure(this);
 
@@ -139,6 +127,9 @@ public class DigitalObjectS3Archive implements Serializable, DigitalObjectArchiv
 					.toString();
 		}
 
+		log.getContext()
+				.add("name", basename);
+
 		this.driveMapper = new DriveMapper();
 		this.cache = this.load();
 	}
@@ -670,7 +661,7 @@ public class DigitalObjectS3Archive implements Serializable, DigitalObjectArchiv
 	@Override
 	public String resolveObjectResource(String objectId, String resourceId, String method) throws BWFLAException
 	{
-		var url = DigitalObjectArchive.super.resolveObjectResource(objectId, resourceId, method);
+		var url = super.resolveObjectResource(objectId, resourceId, method);
 		if (url == null || DataResolver.isAbsoluteUrl(url))
 			return url;
 
@@ -682,7 +673,7 @@ public class DigitalObjectS3Archive implements Serializable, DigitalObjectArchiv
 	@Override
 	public String resolveObjectResourceInternally(String objectId, String resourceId, String method) throws BWFLAException
 	{
-		var url = DigitalObjectArchive.super.resolveObjectResourceInternally(objectId, resourceId, method);
+		var url = super.resolveObjectResourceInternally(objectId, resourceId, method);
 		if (url == null || DataResolver.isAbsoluteUrl(url)){
 			return url;
 		}
-- 
GitLab


From de37a4d8ee21d0b4783f80deeb0506dad049daf4 Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Thu, 23 Nov 2023 16:28:41 +0100
Subject: [PATCH 2/6] Fix handling of custom path prefix in S3-based
 object-archive

---
 .../objectarchive/impl/DigitalObjectS3Archive.java   | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
index af95a68ff7..fe0c92cea7 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
@@ -82,6 +82,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 	private DigitalObjectS3ArchiveDescriptor descriptor;
 	private Bucket bucket;
 	private String basename;
+	private String basepath;
 	private DriveMapper driveMapper;
 	private Map<String, MetsObject> cache;
 
@@ -123,9 +124,12 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 		this.bucket = blobstore.bucket(descriptor.getBucket());
 		this.basename = DigitalObjectS3Archive.strSaveFilename(descriptor.getName());
 		if (descriptor.getPath() != null) {
-			this.basename = BlobStore.path(descriptor.getPath(), this.basename)
+			this.basepath = BlobStore.path(descriptor.getPath(), this.basename)
 					.toString();
 		}
+		else {
+			this.basepath = this.basename;
+		}
 
 		log.getContext()
 				.add("name", basename);
@@ -170,7 +174,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 
 	private BlobStore.Path location(String id)
 	{
-		return BlobStore.path(basename, id);
+		return BlobStore.path(basepath, id);
 	}
 
 	private BlobStore.Path resolveTarget(String id, ResourceType rt)
@@ -347,7 +351,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 
 	private Stream<String> listObjectIds()
 	{
-		final var prefix = basename + "/";
+		final var prefix = basepath + "/";
 
 		try {
 			return bucket.list(prefix)
@@ -1023,7 +1027,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 		final var ocounter = UpdateCounts.counter();
 
 		final Function<Path, Integer> fuploader = (path) -> {
-			final var name = basename + "/" + basedir.relativize(path);
+			final var name = basepath + "/" + basedir.relativize(path);
 			final var contentType = (name.endsWith(METS_MD_FILENAME)) ?
 					MediaType.APPLICATION_XML : MediaType.APPLICATION_OCTET_STREAM;
 
-- 
GitLab


From 9e9a6f7bee2564de2b69442b5d52a124cb2a242f Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Thu, 23 Nov 2023 16:42:13 +0100
Subject: [PATCH 3/6] Log storage bucket and path configured for S3-based
 object-archive

---
 .../bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java   | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
index fe0c92cea7..12f4056f6a 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
@@ -157,6 +157,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 			}
 		};
 
+		log.info("Loading objects from bucket: s3://" + bucket.name() + "/" + basepath);
 		this.listObjectIds()
 				.forEach(downloader);
 
@@ -470,7 +471,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 
 		cache.put(mets.getID(), new MetsObject(metsdata));
 
-		log.info("Object metadata uploaded to: " + blob.name());
+		log.info("Object metadata uploaded to: s3://" + bucket.name() + "/" + blob.name());
 	}
 
 	@Override
-- 
GitLab


From 80a3490ef736133e03a84e651bd1cd0985b14f8a Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Thu, 23 Nov 2023 16:48:18 +0100
Subject: [PATCH 4/6] Log storage path configured for METS-based object-archive

---
 .../bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
index 442398daf2..36e2e43500 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectMETSFileArchive.java
@@ -132,6 +132,7 @@ public class DigitalObjectMETSFileArchive extends DigitalObjectArchiveBase imple
 		if (objects == null)
 			objects = Collections.emptyMap();
 
+		log.info("Loading objects from path: " + metaDataDir.getAbsolutePath());
 		for(File mets: metaDataDir.listFiles())
 		{
 			try {
-- 
GitLab


From 84589c8f03bbb46b6e93f623e7782893090ba2a7 Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Thu, 30 Nov 2023 20:31:11 +0100
Subject: [PATCH 5/6] Avoid modification of cached METS objects when exporting
 metadata

---
 .../de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
index 12f4056f6a..8d22f27d00 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
@@ -588,7 +588,7 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 	private DigitalObjectMetadata getMetadataHelper(String objectId, BiFunction<String, String, String> metsExportPrefixer) throws BWFLAException
 	{
 		final var data = this.loadMetsData(objectId);
-		final var mets = MetsUtil.export(data.getMets(), metsExportPrefixer);
+		final var mets = MetsUtil.export(data.getMets(), metsExportPrefixer, metsExportPrefixer != null);
 		final var md = new DigitalObjectMetadata(mets);
 		md.markAsSoftware(data.isSoftware());
 		try {
-- 
GitLab


From f5cd83b5df8b7f68c0bb402917ec93c56f861cc1 Mon Sep 17 00:00:00 2001
From: Oleg Stobbe <oleg@openslx.com>
Date: Tue, 12 Dec 2023 16:40:47 +0100
Subject: [PATCH 6/6] Add migration 'fix-mets-file-location-urls'

---
 .../conf/ObjectArchiveSingleton.java          |  11 ++
 .../impl/DigitalObjectS3Archive.java          | 103 ++++++++++++++++++
 2 files changed, 114 insertions(+)

diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
index f454afcb64..524d879d0d 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/conf/ObjectArchiveSingleton.java
@@ -256,6 +256,7 @@ public class ObjectArchiveSingleton
 		migrations.register("cleanup-legacy-object-files", this::cleanupLegacyObjectFiles);
 		migrations.register("rename-user-object-archives", this::renameUserArchives);
 		migrations.register("import-legacy-object-archives-v1", this::importLegacyArchivesV1);
+		migrations.register("fix-mets-file-location-urls", this::fixMetsFileLocationUrls);
 	}
 
 	private interface IHandler<T>
@@ -355,4 +356,14 @@ public class ObjectArchiveSingleton
 			usrarchive.importLegacyArchive(mc, Path.of(usrbasedir, name));
 		}
 	}
+
+	private void fixMetsFileLocationUrls(MigrationConfig mc) throws Exception
+	{
+		final IHandler<DigitalObjectArchive> migration = (archive) -> {
+			if (archive instanceof DigitalObjectS3Archive)
+				((DigitalObjectS3Archive) archive).fixFileLocationUrls(mc);
+		};
+
+		this.execute(migration);
+	}
 }
diff --git a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
index 8d22f27d00..512624c521 100644
--- a/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
+++ b/src/objectarchive/src/main/java/de/bwl/bwfla/objectarchive/impl/DigitalObjectS3Archive.java
@@ -44,6 +44,7 @@ import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectFileMetadata;
 import de.bwl.bwfla.objectarchive.datatypes.DigitalObjectS3ArchiveDescriptor;
 import de.bwl.bwfla.objectarchive.datatypes.MetsObject;
 import gov.loc.mets.Mets;
+import gov.loc.mets.MetsType;
 import org.apache.tamaya.inject.ConfigurationInjection;
 import org.apache.tamaya.inject.api.Config;
 
@@ -74,6 +75,7 @@ import java.util.logging.Logger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static de.bwl.bwfla.common.utils.METS.MetsUtil.MetsEaasConstant.FILE_GROUP_OBJECTS;
 import static de.bwl.bwfla.objectarchive.impl.DigitalObjectFileArchive.UpdateCounts;
 
 
@@ -1114,4 +1116,105 @@ public class DigitalObjectS3Archive extends DigitalObjectArchiveBase implements
 
 		this.sync();
 	}
+
+	public void fixFileLocationUrls(MigrationConfig mc) throws Exception
+	{
+		final var digitalObjectsGroupName = FILE_GROUP_OBJECTS.toString();
+		final var dataResolverEndpoint = DataResolver.getDefaultEndpoint();
+		final var counter = UpdateCounts.counter();
+		final var guard = new Object();
+
+		final BiFunction<MetsType.FileSec, String, MetsType.FileSec.FileGrp> fgfinder = (fsec, fgname) -> {
+			final var fgroups = (fsec != null) ? fsec.getFileGrp() : Collections.<MetsType.FileSec.FileGrp>emptyList();
+			for (final var fgroup : fgroups) {
+				if (fgname.equals(fgroup.getUSE()))
+					return fgroup;
+			}
+
+			return null;
+		};
+
+		final Consumer<String> fixer = (objectId) -> {
+			try {
+				final var mets = this.loadMetsData(objectId)
+						.getMets();
+
+				final var fsec = mets.getFileSec();
+				if (fsec == null)
+					return;
+
+				final var fgroup = fgfinder.apply(fsec, digitalObjectsGroupName);
+				if (fgroup == null)
+					return;
+
+				final var updatemsgs = new ArrayList<String>();
+				FileCollection description = null;
+
+				final var files = fgroup.getFile();
+				for (final var fit = files.iterator(); fit.hasNext();) {
+					final var flocations = fit.next().getFLocat();
+					for (final var flit = flocations.iterator(); flit.hasNext(); ) {
+						final var flocat = flit.next();
+						final var oldurl = flocat.getHref();
+
+						// should the url be fixed?
+						if (!oldurl.startsWith(dataResolverEndpoint))
+							continue;  // no, skip this file
+
+						final var filename = flocat.getTitle();
+						if (filename == null)
+							throw new IllegalStateException("Filename is missing!");
+
+						// generate description from storage layout
+						if (description == null)
+							description = this.describe(objectId);
+
+						// find current file's url by its filename...
+						String newurl = null;
+						for (final var fce : description.files) {
+							final var fceurl = fce.getUrl();
+							if (fceurl.endsWith(filename)) {
+								newurl = fceurl;
+								break;
+							}
+						}
+
+						if (newurl == null)
+							throw new IllegalStateException("No matching URL found for file '" + filename + "'!");
+
+						flocat.setHref(newurl);
+						updatemsgs.add("FLocat-URL: " + oldurl + " -> " + flocat.getHref());
+					}
+				}
+
+				if (updatemsgs.isEmpty())
+					return;
+
+				synchronized (guard) {
+					log.info("Updates for object '" + objectId + "':");
+					for (final var msg : updatemsgs)
+						log.info("  " + msg);
+				}
+
+				this.writeMetsFile(mets);
+				counter.increment(UpdateCounts.UPDATED);
+			}
+			catch (Exception error) {
+				log.log(Level.WARNING, "Fixing object '" + objectId + "' failed!", error);
+				counter.increment(UpdateCounts.FAILED);
+			}
+		};
+
+		log.info("Fixing object file-locations in archive '" + this.getName() + "'...");
+		try (final var ids = this.getObjectIds()) {
+			ParallelProcessors.consumer(fixer)
+					.consume(ids, ObjectArchiveSingleton.executor());
+		}
+
+		final var numFixed = counter.get(UpdateCounts.UPDATED);
+		final var numFailed = counter.get(UpdateCounts.FAILED);
+		log.info("Fixed " + numFixed + " object(s), failed " + numFailed);
+		if (!MigrationUtils.acceptable(numFixed + numFailed, numFailed, MigrationUtils.getFailureRate(mc)))
+			throw new BWFLAException("Fixing object file-locations failed!");
+	}
 }
-- 
GitLab