From 5bfc5f02f6dc61b2f53bab18529c2b4bc2755f16 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Fri, 19 Jul 2024 12:52:20 -0700 Subject: [PATCH] Add fix --- .../api/BinaryContentIdDeserializer.java | 36 +++++++++++++++++++ .../fhir/jpa/binary/api/OldStoredDetails.java | 8 +++-- .../fhir/jpa/binary/api/StoredDetails.java | 24 +++++++++++-- .../FilesystemBinaryStorageSvcImpl.java | 3 +- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/BinaryContentIdDeserializer.java diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/BinaryContentIdDeserializer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/BinaryContentIdDeserializer.java new file mode 100644 index 00000000000..cf28b1dbb3f --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/BinaryContentIdDeserializer.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.jpa.binary.api; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; + +/** + * This deserializer exists to fix a break that changing this property name caused. + * in 7.2.0 we went from blobId to binaryContentId. However this did not consider installations using filesystem + * mode storage in which the data on disk was not updated, and existing stored details used `blobId`. This causes + * Jackson deserialization failures which are tough to recover from without manually modifying all those stored details + * on disk. + *

+ *

+ * This class is a shim to support the old and new names. + */ +class BinaryContentIdDeserializer extends JsonDeserializer { + + @Override + public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException { + JsonNode node = jp.getCodec().readTree(jp); + JsonNode binaryContentIdNode = node.get("binaryContentId"); + if (binaryContentIdNode != null && !binaryContentIdNode.isNull()) { + return binaryContentIdNode.asText(); + } + JsonNode blobIdNode = node.get("blobId"); + if (blobIdNode != null && !blobIdNode.isNull()) { + return blobIdNode.asText(); + } + return null; + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/OldStoredDetails.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/OldStoredDetails.java index f495db925f5..8a7754aacc5 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/OldStoredDetails.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/OldStoredDetails.java @@ -137,9 +137,13 @@ public class OldStoredDetails implements IModelJson { public StoredDetails toDetails() { HashFunction hash = Hashing.sha256(); - StoredDetails storedDetails = new StoredDetails(myBinaryContentId, myBytes, myContentType, new HashingInputStream(hash,new ByteArrayInputStream("whatever".getBytes())), myPublished); + StoredDetails storedDetails = new StoredDetails( + myBinaryContentId, + myBytes, + myContentType, + new HashingInputStream(hash, new ByteArrayInputStream("whatever".getBytes())), + myPublished); storedDetails.setHash(myHash); return storedDetails; - } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java index 14b4b583a2e..003b0e9e261 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java @@ -33,9 +33,22 @@ import java.util.Date; public class StoredDetails implements IModelJson { - @JsonProperty("binaryContentId") + @JsonProperty(value = "binaryContentId") private String myBinaryContentId; + /** + * This field exists to fix a break that changing this property name caused. + * in 7.2.0 we went from blobId to binaryContentId. However this did not consider installations using filesystem + * mode storage in which the data on disk was not updated, and needed to be Serialized/Deserialized at runtime. + * Existing stored details used `blobId`. This causes Jackson deserialization failures which are tough to recover + * from without manually modifying all those stored details + * on disk. + * This field is a relic to support old blobs post-upgrade to 7.2.0. It is not ever surfaced to the user, and is proxied + * into `myBinaryContentId` when needed. + */ + @JsonProperty(value = "blobId") + private String myBlobId; + @JsonProperty("bytes") private long myBytes; @@ -77,7 +90,7 @@ public class StoredDetails implements IModelJson { @Override public String toString() { return new ToStringBuilder(this) - .append("binaryContentId", myBinaryContentId) + .append("binaryContentId", getBinaryContentId()) .append("bytes", myBytes) .append("contentType", myContentType) .append("hash", myHash) @@ -115,7 +128,11 @@ public class StoredDetails implements IModelJson { @Nonnull public String getBinaryContentId() { - return myBinaryContentId; + if (myBinaryContentId == null && myBlobId != null) { + return myBlobId; + } else { + return myBinaryContentId; + } } public StoredDetails setBinaryContentId(String theBinaryContentId) { @@ -131,4 +148,5 @@ public class StoredDetails implements IModelJson { myBytes = theBytes; return this; } + } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java index 136b6037207..2b2f7e090ed 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java @@ -113,7 +113,8 @@ public class FilesystemBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl { long count = countingInputStream.getByteCount(); StoredDetails details = null; if (HapiSystemProperties.isUnitTestModeEnabled()) { - OldStoredDetails oldDetails = new OldStoredDetails(id, count, theContentType, hashingInputStream, new Date()); + OldStoredDetails oldDetails = + new OldStoredDetails(id, count, theContentType, hashingInputStream, new Date()); File descriptorFilename = getDescriptorFilename(storagePath, theResourceId, id); ourLog.info("Writing to file: {}", descriptorFilename.getAbsolutePath()); try (FileWriter writer = new FileWriter(descriptorFilename)) {