001/*-
002 * #%L
003 * HAPI FHIR Storage api
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.binary.api;
021
022import ca.uhn.fhir.model.api.IModelJson;
023import ca.uhn.fhir.rest.server.util.JsonDateDeserializer;
024import ca.uhn.fhir.rest.server.util.JsonDateSerializer;
025import com.fasterxml.jackson.annotation.JsonProperty;
026import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
027import com.fasterxml.jackson.databind.annotation.JsonSerialize;
028import com.google.common.hash.HashingInputStream;
029import jakarta.annotation.Nonnull;
030import org.apache.commons.lang3.builder.ToStringBuilder;
031
032import java.util.Date;
033
034public class StoredDetails implements IModelJson {
035
036        @JsonProperty(value = "binaryContentId")
037        private String myBinaryContentId;
038
039        /**
040         * This field exists to fix a break that changing this property name caused.
041         * in 7.2.0 we went from blobId to binaryContentId. However this did not consider installations using filesystem
042         * mode storage in which the data on disk was not updated, and needed to be Serialized/Deserialized at runtime.
043         * Existing stored details used `blobId`. This causes Jackson deserialization failures which are tough to recover
044         * from without manually modifying all those stored details
045         * on disk.
046         * 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
047         * into `myBinaryContentId` when needed.
048         */
049        @JsonProperty(value = "blobId")
050        private String myBlobId;
051
052        @JsonProperty("bytes")
053        private long myBytes;
054
055        @JsonProperty("contentType")
056        private String myContentType;
057
058        @JsonProperty("hash")
059        private String myHash;
060
061        @JsonProperty("published")
062        @JsonSerialize(using = JsonDateSerializer.class)
063        @JsonDeserialize(using = JsonDateDeserializer.class)
064        private Date myPublished;
065
066        /**
067         * Constructor
068         */
069        @SuppressWarnings("unused")
070        public StoredDetails() {
071                super();
072        }
073
074        /**
075         * Constructor
076         */
077        public StoredDetails(
078                        @Nonnull String theBinaryContentId,
079                        long theBytes,
080                        @Nonnull String theContentType,
081                        HashingInputStream theIs,
082                        Date thePublished) {
083                myBinaryContentId = theBinaryContentId;
084                myBytes = theBytes;
085                myContentType = theContentType;
086                myHash = theIs.hash().toString();
087                myPublished = thePublished;
088        }
089
090        @Override
091        public String toString() {
092                return new ToStringBuilder(this)
093                                .append("binaryContentId", getBinaryContentId())
094                                .append("bytes", myBytes)
095                                .append("contentType", myContentType)
096                                .append("hash", myHash)
097                                .append("published", myPublished)
098                                .toString();
099        }
100
101        public String getHash() {
102                return myHash;
103        }
104
105        public StoredDetails setHash(String theHash) {
106                myHash = theHash;
107                return this;
108        }
109
110        public Date getPublished() {
111                return myPublished;
112        }
113
114        public StoredDetails setPublished(Date thePublished) {
115                myPublished = thePublished;
116                return this;
117        }
118
119        @Nonnull
120        public String getContentType() {
121                return myContentType;
122        }
123
124        public StoredDetails setContentType(String theContentType) {
125                myContentType = theContentType;
126                return this;
127        }
128
129        @Nonnull
130        public String getBinaryContentId() {
131                if (myBinaryContentId == null && myBlobId != null) {
132                        return myBlobId;
133                } else {
134                        return myBinaryContentId;
135                }
136        }
137
138        public StoredDetails setBinaryContentId(String theBinaryContentId) {
139                myBinaryContentId = theBinaryContentId;
140                return this;
141        }
142
143        public long getBytes() {
144                return myBytes;
145        }
146
147        public StoredDetails setBytes(long theBytes) {
148                myBytes = theBytes;
149                return this;
150        }
151}