View Javadoc
1   package de.dlr.shepard.data.file.services;
2   
3   import static com.mongodb.client.model.Filters.eq;
4   
5   import com.mongodb.client.MongoCollection;
6   import com.mongodb.client.MongoDatabase;
7   import com.mongodb.client.gridfs.GridFSBucket;
8   import com.mongodb.client.gridfs.GridFSBuckets;
9   import de.dlr.shepard.common.mongoDB.NamedInputStream;
10  import de.dlr.shepard.common.util.DateHelper;
11  import de.dlr.shepard.common.util.UUIDHelper;
12  import de.dlr.shepard.data.file.entities.ShepardFile;
13  import io.quarkus.logging.Log;
14  import jakarta.enterprise.context.RequestScoped;
15  import jakarta.inject.Inject;
16  import jakarta.inject.Named;
17  import jakarta.ws.rs.InternalServerErrorException;
18  import jakarta.ws.rs.NotFoundException;
19  import jakarta.xml.bind.DatatypeConverter;
20  import java.io.InputStream;
21  import java.security.DigestInputStream;
22  import java.security.MessageDigest;
23  import java.security.NoSuchAlgorithmException;
24  import org.bson.Document;
25  import org.bson.types.ObjectId;
26  
27  @RequestScoped
28  public class FileService {
29  
30    private static final int CHUNK_SIZE_BYTES = 1024 * 1024; // 1 MiB
31    private static final String ID_ATTR = "_id";
32    private static final String FILENAME_ATTR = "name";
33    private static final String FILEID_ATTR = "FileMongoId";
34    private static final String CREATEDAT_ATTR = "createdAt";
35    private static final String MD5_ATTR = "md5";
36  
37    @Inject
38    @Named("mongoDatabase")
39    MongoDatabase mongoDatabase;
40  
41    @Inject
42    UUIDHelper uuidHelper;
43  
44    @Inject
45    DateHelper dateHelper;
46  
47    public String createFileContainer() {
48      String oid = "FileContainer" + uuidHelper.getUUID().toString();
49      mongoDatabase.createCollection(oid);
50      return oid;
51    }
52  
53    /**
54     * Creates a new file in file container
55     *
56     * @param mongoId
57     * @param fileName
58     * @param inputStream
59     * @return ShepardFile
60     */
61    public ShepardFile createFile(String mongoId, String fileName, InputStream inputStream) {
62      MongoCollection<Document> collection;
63      try {
64        collection = mongoDatabase.getCollection(mongoId);
65      } catch (IllegalArgumentException e) {
66        String errorMsg = String.format("Could not find container with mongoId: %s", mongoId);
67        Log.error(errorMsg);
68        throw new NotFoundException(errorMsg);
69      }
70  
71      MessageDigest md;
72      try {
73        md = MessageDigest.getInstance("MD5");
74      } catch (NoSuchAlgorithmException e) {
75        String errorMsg = "No Such Algorithm while uploading file";
76        Log.error(errorMsg);
77        throw new InternalServerErrorException(errorMsg);
78      }
79  
80      DigestInputStream dis = new DigestInputStream(inputStream, md);
81      String fileMongoId = createBucket()
82        .withChunkSizeBytes(CHUNK_SIZE_BYTES)
83        .uploadFromStream(fileName, dis)
84        .toHexString();
85      var file = new ShepardFile(dateHelper.getDate(), fileName, DatatypeConverter.printHexBinary(md.digest()));
86      var doc = toDocument(file).append(FILEID_ATTR, fileMongoId);
87      collection.insertOne(doc);
88      file.setOid(doc.getObjectId(ID_ATTR).toHexString());
89      return file;
90    }
91  
92    /**
93     * Returns the payload as an InputStream for a given file.
94     *
95     * @param containerId
96     * @param fileOid
97     * @return NamedInputStream
98     * @throws NotFoundException if container could not be found by MongoId or document could be found by Oid
99     */
100   public NamedInputStream getPayload(String containerId, String fileOid) {
101     MongoCollection<Document> collection;
102     try {
103       collection = mongoDatabase.getCollection(containerId);
104     } catch (IllegalArgumentException e) {
105       String errorMsg = String.format("Could not find container with mongoId: %s", containerId);
106       Log.error(errorMsg);
107       throw new NotFoundException(errorMsg);
108     }
109     var oid = new ObjectId(fileOid);
110     var payloadDocument = collection.find(eq(ID_ATTR, oid)).first();
111     if (payloadDocument == null) {
112       String errorMsg = String.format("Could not find document with oid: %s", fileOid);
113       Log.error(errorMsg);
114       throw new NotFoundException(errorMsg);
115     }
116     var fileId = new ObjectId(payloadDocument.getString(FILEID_ATTR));
117     var filename = payloadDocument.getString(FILENAME_ATTR);
118     var gridBucket = createBucket();
119     var gridFsFile = gridBucket.find(eq(ID_ATTR, fileId)).first();
120     var inputStream = gridBucket.openDownloadStream(fileId);
121 
122     return new NamedInputStream(fileOid, inputStream, filename, gridFsFile.getLength());
123   }
124 
125   /**
126    * Returns a ShepardFile for a given container and oid
127    *
128    * @param containerId
129    * @param fileOid
130    * @return ShepardFile
131    * @throws NotFoundException if container could not be found by MongoId or file could be found by Oid
132    */
133   public ShepardFile getFile(String containerId, String fileOid) {
134     MongoCollection<Document> collection;
135     try {
136       collection = mongoDatabase.getCollection(containerId);
137     } catch (IllegalArgumentException e) {
138       String errorMsg = String.format("Could not find container with mongoId: %s", containerId);
139       Log.error(errorMsg);
140       throw new NotFoundException(errorMsg);
141     }
142     var doc = collection.find(eq(ID_ATTR, new ObjectId(fileOid))).first();
143     if (doc == null) {
144       String errorMsg = String.format("Could not find file with oid: %s", fileOid);
145       Log.error(errorMsg);
146       throw new NotFoundException(errorMsg);
147     }
148     return toShepardFile(doc);
149   }
150 
151   /**
152    *
153    * @param mongoId
154    * @throws NotFoundException if deleting container fails
155    */
156   public void deleteFileContainer(String mongoId) {
157     MongoCollection<Document> toDelete;
158     try {
159       toDelete = mongoDatabase.getCollection(mongoId);
160     } catch (IllegalArgumentException e) {
161       String errorMsg = String.format("Could not delete container with mongoid: %s", mongoId);
162       Log.error(errorMsg);
163       throw new NotFoundException(errorMsg);
164     }
165     GridFSBucket gridBucket = createBucket();
166     for (Document doc : toDelete.find()) {
167       gridBucket.delete(new ObjectId(doc.getString(FILEID_ATTR)));
168     }
169     toDelete.drop();
170   }
171 
172   public void deleteFile(String mongoId, String fileOid) {
173     MongoCollection<Document> collection;
174     try {
175       collection = mongoDatabase.getCollection(mongoId);
176     } catch (IllegalArgumentException e) {
177       String errorMsg = String.format("Could not find container with mongoId: %s", mongoId);
178       Log.error(errorMsg);
179       throw new NotFoundException(errorMsg);
180     }
181     var doc = collection.findOneAndDelete(eq(ID_ATTR, new ObjectId(fileOid)));
182     if (doc == null) {
183       String errorMsg = String.format("Could not find and delete file with oid: %s", fileOid);
184       Log.error(errorMsg);
185       throw new NotFoundException(errorMsg);
186     }
187     var gridBucket = createBucket();
188     gridBucket.delete(new ObjectId(doc.getString(FILEID_ATTR)));
189   }
190 
191   private static ShepardFile toShepardFile(Document doc) {
192     var file = new ShepardFile(
193       doc.getObjectId(ID_ATTR).toHexString(),
194       doc.getDate(CREATEDAT_ATTR),
195       doc.getString(FILENAME_ATTR),
196       doc.getString(MD5_ATTR)
197     );
198     return file;
199   }
200 
201   private static Document toDocument(ShepardFile file) {
202     var doc = new Document()
203       .append(CREATEDAT_ATTR, file.getCreatedAt())
204       .append(FILENAME_ATTR, file.getFilename())
205       .append(MD5_ATTR, file.getMd5());
206     if (file.getOid() != null) doc.append(ID_ATTR, new ObjectId(file.getOid()));
207     return doc;
208   }
209 
210   private GridFSBucket createBucket() {
211     return GridFSBuckets.create(mongoDatabase);
212   }
213 }