1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.mycore.restapi.v1.utils;
19
20 import static org.mycore.access.MCRAccessManager.PERMISSION_WRITE;
21
22 import java.io.BufferedInputStream;
23 import java.io.BufferedWriter;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.StringWriter;
27 import java.nio.charset.StandardCharsets;
28 import java.nio.file.Files;
29 import java.nio.file.Paths;
30 import java.nio.file.StandardCopyOption;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.SortedMap;
36 import java.util.TreeMap;
37 import java.util.function.Predicate;
38 import java.util.stream.Collectors;
39 import java.util.stream.Stream;
40 import java.util.zip.ZipEntry;
41 import java.util.zip.ZipInputStream;
42
43 import org.apache.logging.log4j.LogManager;
44 import org.apache.logging.log4j.Logger;
45 import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
46 import org.jdom2.Document;
47 import org.jdom2.input.SAXBuilder;
48 import org.jdom2.output.Format;
49 import org.jdom2.output.XMLOutputter;
50 import org.mycore.access.MCRAccessException;
51 import org.mycore.access.MCRAccessManager;
52 import org.mycore.common.MCRPersistenceException;
53 import org.mycore.common.config.MCRConfiguration2;
54 import org.mycore.datamodel.classifications2.MCRCategoryDAO;
55 import org.mycore.common.MCRUtils;
56 import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
57 import org.mycore.datamodel.classifications2.MCRCategoryID;
58 import org.mycore.datamodel.metadata.MCRDerivate;
59 import org.mycore.datamodel.metadata.MCRMetaClassification;
60 import org.mycore.datamodel.metadata.MCRMetaEnrichedLinkID;
61 import org.mycore.datamodel.metadata.MCRMetaEnrichedLinkIDFactory;
62 import org.mycore.datamodel.metadata.MCRMetaIFS;
63 import org.mycore.datamodel.metadata.MCRMetaLangText;
64 import org.mycore.datamodel.metadata.MCRMetaLinkID;
65 import org.mycore.datamodel.metadata.MCRMetadataManager;
66 import org.mycore.datamodel.metadata.MCRObject;
67 import org.mycore.datamodel.metadata.MCRObjectID;
68 import org.mycore.datamodel.niofs.MCRPath;
69 import org.mycore.datamodel.niofs.utils.MCRRecursiveDeleter;
70 import org.mycore.datamodel.niofs.utils.MCRTreeCopier;
71 import org.mycore.frontend.cli.MCRObjectCommands;
72 import org.mycore.restapi.v1.errors.MCRRestAPIError;
73 import org.mycore.restapi.v1.errors.MCRRestAPIException;
74
75 import jakarta.servlet.http.HttpServletRequest;
76 import jakarta.ws.rs.core.Response;
77 import jakarta.ws.rs.core.UriInfo;
78 import jakarta.ws.rs.core.Response.Status;
79
80 public class MCRRestAPIUploadHelper {
81 private static final Logger LOGGER = LogManager.getLogger(MCRRestAPIUploadHelper.class);
82
83 private static java.nio.file.Path UPLOAD_DIR = Paths
84 .get(MCRConfiguration2.getStringOrThrow("MCR.RestAPI.v1.Upload.Directory"));
85
86 static {
87 if (!Files.exists(UPLOAD_DIR)) {
88 try {
89 Files.createDirectories(UPLOAD_DIR);
90 } catch (IOException e) {
91 LOGGER.error(e);
92 }
93 }
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109 public static Response uploadObject(UriInfo info, HttpServletRequest request, InputStream uploadedInputStream,
110 FormDataContentDisposition fileDetails) throws MCRRestAPIException {
111
112 java.nio.file.Path fXML = null;
113 try {
114 SAXBuilder sb = new SAXBuilder();
115 Document docOut = sb.build(uploadedInputStream);
116
117 MCRObjectID mcrID = MCRObjectID.getInstance(docOut.getRootElement().getAttributeValue("ID"));
118 if (mcrID.getNumberAsInteger() == 0) {
119 mcrID = MCRObjectID.getNextFreeId(mcrID.getBase());
120 }
121
122 fXML = UPLOAD_DIR.resolve(mcrID + ".xml");
123
124 docOut.getRootElement().setAttribute("ID", mcrID.toString());
125 docOut.getRootElement().setAttribute("label", mcrID.toString());
126 XMLOutputter xmlOut = new XMLOutputter(Format.getPrettyFormat());
127 try (BufferedWriter bw = Files.newBufferedWriter(fXML, StandardCharsets.UTF_8)) {
128 xmlOut.output(docOut, bw);
129 }
130
131 MCRObjectCommands.updateFromFile(fXML.toString(), false);
132
133 return Response.created(info.getBaseUriBuilder().path("objects/" + mcrID).build())
134 .type("application/xml; charset=UTF-8")
135 .build();
136 } catch (Exception e) {
137 LOGGER.error("Unable to Upload file: {}", String.valueOf(fXML), e);
138 throw new MCRRestAPIException(Status.BAD_REQUEST, new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER,
139 "Unable to Upload file: " + fXML, e.getMessage()));
140 } finally {
141 if (fXML != null) {
142 try {
143 Files.delete(fXML);
144 } catch (IOException e) {
145 LOGGER.error("Unable to delete temporary workflow file: {}", String.valueOf(fXML), e);
146 }
147 }
148 }
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162 public static Response uploadDerivate(UriInfo info, HttpServletRequest request, String mcrObjID, String label,
163 String classifications, boolean overwriteOnExisting) throws MCRRestAPIException {
164 Response response = Response.status(Status.INTERNAL_SERVER_ERROR).build();
165
166
167 MCRObjectID mcrObjIDObj = MCRObjectID.getInstance(mcrObjID);
168
169 try {
170 MCRObject mcrObj = MCRMetadataManager.retrieveMCRObject(mcrObjIDObj);
171 MCRObjectID derID = null;
172 final MCRCategoryDAO dao = MCRCategoryDAOFactory.getInstance();
173 if (overwriteOnExisting) {
174 final List<MCRMetaEnrichedLinkID> currentDerivates = mcrObj.getStructure().getDerivates();
175 if (label != null && label.length() > 0) {
176 for (MCRMetaLinkID derLink : currentDerivates) {
177 if (label.equals(derLink.getXLinkLabel()) || label.equals(derLink.getXLinkTitle())) {
178 derID = derLink.getXLinkHrefID();
179 }
180 }
181 }
182 if (derID == null && classifications != null && classifications.length() > 0) {
183 final List<MCRCategoryID> categories = Stream.of(classifications.split(" "))
184 .map(MCRCategoryID::fromString)
185 .collect(Collectors.toList());
186
187 final List<MCRCategoryID> notExisting = categories.stream().filter(Predicate.not(dao::exist))
188 .collect(Collectors.toList());
189
190 if (notExisting.size() > 0) {
191 final String missingIDS = notExisting.stream()
192 .map(MCRCategoryID::toString).collect(Collectors.joining(", "));
193 throw new MCRRestAPIException(Status.NOT_FOUND,
194 new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND, "Classification not found.",
195 "There are no classification with the IDs: " + missingIDS));
196 }
197 final Optional<MCRMetaEnrichedLinkID> matchingDerivate = currentDerivates.stream()
198 .filter(derLink -> {
199 final Set<MCRCategoryID> clazzSet = new HashSet<>(derLink.getClassifications());
200 return categories.stream().allMatch(clazzSet::contains);
201 }).findFirst();
202 if (matchingDerivate.isPresent()) {
203 derID = matchingDerivate.get().getXLinkHrefID();
204 }
205 }
206 }
207
208 if (derID == null) {
209 derID = MCRObjectID.getNextFreeId(mcrObjIDObj.getProjectId() + "_derivate");
210 MCRDerivate mcrDerivate = new MCRDerivate();
211 if (label != null && label.length() > 0) {
212 mcrDerivate.getDerivate().getTitles()
213 .add(new MCRMetaLangText("title", null, null, 0, null, label));
214 }
215 mcrDerivate.setId(derID);
216 mcrDerivate.setSchema("datamodel-derivate.xsd");
217 mcrDerivate.getDerivate().setLinkMeta(new MCRMetaLinkID("linkmeta", mcrObjIDObj, null, null));
218 mcrDerivate.getDerivate().setInternals(new MCRMetaIFS("internal", null));
219
220 if (classifications != null && classifications.length() > 0) {
221 final List<MCRMetaClassification> currentClassifications;
222 currentClassifications = mcrDerivate.getDerivate().getClassifications();
223 Stream.of(classifications.split(" "))
224 .map(MCRCategoryID::fromString)
225 .filter(dao::exist)
226 .map(categoryID -> new MCRMetaClassification("classification", 0, null, categoryID))
227 .forEach(currentClassifications::add);
228 }
229
230 MCRMetadataManager.create(mcrDerivate);
231 MCRMetadataManager.addOrUpdateDerivateToObject(mcrObjIDObj,
232 MCRMetaEnrichedLinkIDFactory.getInstance().getDerivateLink(mcrDerivate));
233 }
234
235 response = Response
236 .created(info.getBaseUriBuilder().path("objects/" + mcrObjID + "/derivates/" + derID).build())
237 .type("application/xml; charset=UTF-8")
238 .build();
239 } catch (Exception e) {
240 LOGGER.error("Exeption while uploading derivate", e);
241 }
242 return response;
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261 public static Response uploadFile(UriInfo info, HttpServletRequest request, String pathParamMcrObjID,
262 String pathParamMcrDerID, InputStream uploadedInputStream, FormDataContentDisposition fileDetails,
263 String formParamPath, boolean formParamMaindoc, boolean formParamUnzip, String formParamMD5,
264 Long formParamSize) throws MCRRestAPIException {
265
266 SortedMap<String, String> parameter = new TreeMap<>();
267 parameter.put("mcrObjectID", pathParamMcrObjID);
268 parameter.put("mcrDerivateID", pathParamMcrDerID);
269 parameter.put("path", formParamPath);
270 parameter.put("maindoc", Boolean.toString(formParamMaindoc));
271 parameter.put("unzip", Boolean.toString(formParamUnzip));
272 parameter.put("md5", formParamMD5);
273 parameter.put("size", Long.toString(formParamSize));
274
275 MCRObjectID objID = MCRObjectID.getInstance(pathParamMcrObjID);
276 MCRObjectID derID = MCRObjectID.getInstance(pathParamMcrDerID);
277
278 if (!MCRAccessManager.checkPermission(derID.toString(), PERMISSION_WRITE)) {
279 throw new MCRRestAPIException(Status.FORBIDDEN,
280 new MCRRestAPIError(MCRRestAPIError.CODE_ACCESS_DENIED, "Could not add file to derivate",
281 "You do not have the permission to write to " + derID));
282 }
283 MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(derID);
284
285 java.nio.file.Path derDir = null;
286
287 String path = null;
288 if (!der.getOwnerID().equals(objID)) {
289 throw new MCRRestAPIException(Status.INTERNAL_SERVER_ERROR,
290 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, "Derivate object mismatch",
291 "Derivate " + derID + " belongs to a different object: " + objID));
292 }
293 try {
294 derDir = UPLOAD_DIR.resolve(derID.toString());
295 if (Files.exists(derDir)) {
296 Files.walkFileTree(derDir, MCRRecursiveDeleter.instance());
297 }
298 path = formParamPath.replace("\\", "/").replace("../", "");
299 while (path.startsWith("/")) {
300 path = path.substring(1);
301 }
302
303 MCRPath derRoot = MCRPath.getPath(derID.toString(), "/");
304 if (Files.notExists(derRoot)) {
305 derRoot.getFileSystem().createRoot(derID.toString());
306 }
307
308 if (formParamUnzip) {
309 String maindoc = null;
310 try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(uploadedInputStream))) {
311 ZipEntry entry;
312 while ((entry = zis.getNextEntry()) != null) {
313 LOGGER.debug("Unzipping: {}", entry.getName());
314 java.nio.file.Path target = MCRUtils.safeResolve(derDir, entry.getName());
315 Files.createDirectories(target.getParent());
316 Files.copy(zis, target, StandardCopyOption.REPLACE_EXISTING);
317 if (maindoc == null && !entry.isDirectory()) {
318 maindoc = entry.getName();
319 }
320 }
321 } catch (IOException e) {
322 LOGGER.error(e);
323 }
324
325 Files.walkFileTree(derDir, new MCRTreeCopier(derDir, derRoot, true));
326 if (formParamMaindoc) {
327 der.getDerivate().getInternals().setMainDoc(maindoc);
328 }
329 } else {
330 java.nio.file.Path saveFile = MCRUtils.safeResolve(derDir, path);
331 Files.createDirectories(saveFile.getParent());
332 Files.copy(uploadedInputStream, saveFile, StandardCopyOption.REPLACE_EXISTING);
333
334 Files.walkFileTree(derDir, new MCRTreeCopier(derDir, derRoot, true));
335 if (formParamMaindoc) {
336 der.getDerivate().getInternals().setMainDoc(path);
337 }
338 }
339
340 MCRMetadataManager.update(der);
341 Files.walkFileTree(derDir, MCRRecursiveDeleter.instance());
342 } catch (IOException | MCRPersistenceException | MCRAccessException e) {
343 LOGGER.error(e);
344 throw new MCRRestAPIException(Status.INTERNAL_SERVER_ERROR,
345 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, "Internal error", e.getMessage()));
346 }
347 return Response
348 .created(info.getBaseUriBuilder().path("objects/" + objID + "/derivates/" + derID + "/contents").build())
349 .type("application/xml; charset=UTF-8").build();
350 }
351
352
353
354
355
356
357
358
359
360
361 public static Response deleteAllFiles(UriInfo info, HttpServletRequest request, String pathParamMcrObjID,
362 String pathParamMcrDerID) throws MCRRestAPIException {
363
364 SortedMap<String, String> parameter = new TreeMap<>();
365 parameter.put("mcrObjectID", pathParamMcrObjID);
366 parameter.put("mcrDerivateID", pathParamMcrDerID);
367
368 MCRObjectID objID = MCRObjectID.getInstance(pathParamMcrObjID);
369 MCRObjectID derID = MCRObjectID.getInstance(pathParamMcrDerID);
370
371
372 MCRAccessManager.invalidPermissionCache(derID.toString(), PERMISSION_WRITE);
373 if (MCRAccessManager.checkPermission(derID.toString(), PERMISSION_WRITE)) {
374 MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(derID);
375
376 final MCRPath rootPath = MCRPath.getPath(der.getId().toString(), "/");
377 try {
378 Files.walkFileTree(rootPath, MCRRecursiveDeleter.instance());
379 Files.createDirectory(rootPath);
380 } catch (IOException e) {
381 LOGGER.error(e);
382 }
383 }
384
385 return Response
386 .created(info.getBaseUriBuilder()
387 .path("objects/" + objID + "/derivates/" + derID + "/contents")
388 .build())
389 .type("application/xml; charset=UTF-8")
390 .build();
391 }
392
393
394
395
396
397
398
399
400
401
402 public static Response deleteDerivate(UriInfo info, HttpServletRequest request, String pathParamMcrObjID,
403 String pathParamMcrDerID) throws MCRRestAPIException {
404
405 MCRObjectID objID = MCRObjectID.getInstance(pathParamMcrObjID);
406 MCRObjectID derID = MCRObjectID.getInstance(pathParamMcrDerID);
407
408 try {
409 MCRMetadataManager.deleteMCRDerivate(derID);
410 return Response
411 .created(info.getBaseUriBuilder().path("objects/" + objID + "/derivates").build())
412 .type("application/xml; charset=UTF-8")
413 .build();
414 } catch (MCRAccessException e) {
415 throw new MCRRestAPIException(Status.FORBIDDEN,
416 new MCRRestAPIError(MCRRestAPIError.CODE_ACCESS_DENIED, "Could not delete derivate", e.getMessage()));
417 }
418 }
419
420
421
422
423
424
425 public static String generateMessagesFromProperties(SortedMap<String, String> data) {
426 StringWriter sw = new StringWriter();
427 sw.append("{");
428 for (String key : data.keySet()) {
429 sw.append("\"").append(key).append("\"").append(":").append("\"").append(data.get(key)).append("\"")
430 .append(",");
431 }
432 String result = sw.toString();
433 if (result.length() > 1) {
434 result = result.substring(0, result.length() - 1);
435 }
436 result = result + "}";
437
438 return result;
439 }
440
441 }