1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.restapi.v1.utils;
20
21 import java.io.IOException;
22 import java.io.StringWriter;
23 import java.net.URLDecoder;
24 import java.nio.charset.StandardCharsets;
25 import java.nio.file.DirectoryStream;
26 import java.nio.file.FileVisitOption;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.attribute.BasicFileAttributes;
30 import java.text.ParseException;
31 import java.text.SimpleDateFormat;
32 import java.util.ArrayList;
33 import java.util.Date;
34 import java.util.EnumSet;
35 import java.util.HashSet;
36 import java.util.List;
37 import java.util.Locale;
38 import java.util.Set;
39 import java.util.concurrent.TimeUnit;
40 import java.util.stream.Collectors;
41
42 import org.apache.logging.log4j.LogManager;
43 import org.apache.logging.log4j.Logger;
44 import org.apache.solr.client.solrj.SolrClient;
45 import org.apache.solr.client.solrj.SolrQuery;
46 import org.apache.solr.client.solrj.SolrServerException;
47 import org.apache.solr.client.solrj.response.QueryResponse;
48 import org.apache.solr.common.SolrDocumentList;
49 import org.jdom2.Comment;
50 import org.jdom2.Document;
51 import org.jdom2.Element;
52 import org.jdom2.JDOMException;
53 import org.jdom2.filter.Filters;
54 import org.jdom2.output.Format;
55 import org.jdom2.output.XMLOutputter;
56 import org.jdom2.xpath.XPathExpression;
57 import org.jdom2.xpath.XPathFactory;
58 import org.mycore.common.MCRConstants;
59 import org.mycore.common.MCRException;
60 import org.mycore.common.config.MCRConfiguration2;
61 import org.mycore.common.content.MCRJDOMContent;
62 import org.mycore.common.content.transformer.MCRContentTransformer;
63 import org.mycore.common.content.transformer.MCRContentTransformerFactory;
64 import org.mycore.datamodel.common.MCRObjectIDDate;
65 import org.mycore.datamodel.common.MCRXMLMetadataManager;
66 import org.mycore.datamodel.metadata.MCRDerivate;
67 import org.mycore.datamodel.metadata.MCRMetaClassification;
68 import org.mycore.datamodel.metadata.MCRMetaLinkID;
69 import org.mycore.datamodel.metadata.MCRMetadataManager;
70 import org.mycore.datamodel.metadata.MCRObject;
71 import org.mycore.datamodel.metadata.MCRObjectID;
72 import org.mycore.datamodel.niofs.MCRPath;
73 import org.mycore.datamodel.niofs.MCRPathXML;
74 import org.mycore.frontend.jersey.MCRJerseyUtil;
75 import org.mycore.restapi.v1.MCRRestAPIObjects;
76 import org.mycore.restapi.v1.errors.MCRRestAPIError;
77 import org.mycore.restapi.v1.errors.MCRRestAPIException;
78 import org.mycore.restapi.v1.utils.MCRRestAPISortObject.SortOrder;
79 import org.mycore.solr.MCRSolrClientFactory;
80 import org.xml.sax.SAXException;
81
82 import com.google.gson.stream.JsonWriter;
83
84 import jakarta.ws.rs.core.Application;
85 import jakarta.ws.rs.core.CacheControl;
86 import jakarta.ws.rs.core.Request;
87 import jakarta.ws.rs.core.Response;
88 import jakarta.ws.rs.core.UriInfo;
89 import jakarta.ws.rs.core.Response.ResponseBuilder;
90 import jakarta.ws.rs.core.Response.Status;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public class MCRRestAPIObjectsHelper {
109
110 private static final String GENERAL_ERROR_MSG = "A problem occured while fetching the data.";
111
112 private static Logger LOGGER = LogManager.getLogger(MCRRestAPIObjectsHelper.class);
113
114 private static SimpleDateFormat SDF_UTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
115
116 public static Response showMCRObject(String pathParamId, String queryParamStyle, UriInfo info, Application app)
117 throws MCRRestAPIException {
118
119 MCRObject mcrObj = retrieveMCRObject(pathParamId);
120 Document doc = mcrObj.createXML();
121 Element eStructure = doc.getRootElement().getChild("structure");
122
123 if (queryParamStyle != null && !MCRRestAPIObjects.STYLE_DERIVATEDETAILS.equals(queryParamStyle)) {
124 throw new MCRRestAPIException(Response.Status.BAD_REQUEST,
125 new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER,
126 "The value of parameter {style} is not allowed.",
127 "Allowed values for {style} parameter are: " + MCRRestAPIObjects.STYLE_DERIVATEDETAILS));
128 }
129
130 if (MCRRestAPIObjects.STYLE_DERIVATEDETAILS.equals(queryParamStyle) && eStructure != null) {
131 Element eDerObjects = eStructure.getChild("derobjects");
132 if (eDerObjects != null) {
133 for (Element eDer : eDerObjects.getChildren("derobject")) {
134 String derID = eDer.getAttributeValue("href", MCRConstants.XLINK_NAMESPACE);
135 Element currentDerElement = eDer;
136 try {
137 MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(derID));
138 eDer.addContent(der.createXML().getRootElement().detach());
139
140
141
142
143 currentDerElement = eDer.getChild("mycorederivate").getChild("derivate");
144 Document docContents = listDerivateContentAsXML(
145 MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(derID)), "/", -1, info, app);
146 if (docContents.hasRootElement()) {
147 eDer.addContent(docContents.getRootElement().detach());
148 }
149 } catch (MCRException e) {
150 currentDerElement.addContent(new Comment("Error: Derivate not found."));
151 } catch (IOException e) {
152 currentDerElement
153 .addContent(new Comment("Error: Derivate content could not be listed: " + e.getMessage()));
154 }
155 }
156 }
157 }
158
159 StringWriter sw = new StringWriter();
160 XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
161 try {
162 String filterId = MCRConfiguration2.getString("MCR.RestAPI.v1.Filter.XML").orElse("");
163 if (filterId.length() > 0) {
164 MCRContentTransformer trans = MCRContentTransformerFactory.getTransformer(filterId);
165 Document filteredDoc = trans.transform(new MCRJDOMContent(doc)).asXML();
166 outputter.output(filteredDoc, sw);
167 } else {
168 outputter.output(doc, sw);
169 }
170 } catch (SAXException | JDOMException e) {
171 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR, new MCRRestAPIError(
172 MCRRestAPIError.CODE_INTERNAL_ERROR, "Unable to transform MCRContent to XML document", e.getMessage()));
173 } catch (IOException e) {
174 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR, new MCRRestAPIError(
175 MCRRestAPIError.CODE_INTERNAL_ERROR, "Unable to retrieve/transform MyCoRe object", e.getMessage()));
176 }
177
178 return Response.ok(sw.toString())
179 .type("application/xml")
180 .build();
181 }
182
183 public static Response showMCRDerivate(String pathParamMcrID, String pathParamDerID, UriInfo info, Application app,
184 boolean withDetails) throws MCRRestAPIException {
185
186 MCRObjectID mcrObj = MCRObjectID.getInstance(pathParamMcrID);
187 MCRDerivate derObj = retrieveMCRDerivate(mcrObj, pathParamDerID);
188
189 try {
190 Document doc = derObj.createXML();
191 if (withDetails) {
192 Document docContent = listDerivateContentAsXML(derObj, "/", -1, info, app);
193 if (docContent != null && docContent.hasRootElement()) {
194 doc.getRootElement().addContent(docContent.getRootElement().detach());
195 }
196 }
197
198 StringWriter sw = new StringWriter();
199 XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
200 outputter.output(doc, sw);
201
202 return Response.ok(sw.toString())
203 .type("application/xml")
204 .build();
205 } catch (IOException e) {
206 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
207 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage()));
208 }
209
210
211
212 }
213
214 private static String listDerivateContentAsJson(MCRDerivate derObj, String path, int depth, UriInfo info,
215 Application app)
216 throws IOException {
217 StringWriter sw = new StringWriter();
218 MCRPath root = MCRPath.getPath(derObj.getId().toString(), "/");
219 root = MCRPath.toMCRPath(root.resolve(path));
220 if (depth == -1) {
221 depth = Integer.MAX_VALUE;
222 }
223 if (root != null) {
224 JsonWriter writer = new JsonWriter(sw);
225 Files.walkFileTree(root, EnumSet.noneOf(FileVisitOption.class), depth,
226 new MCRJSONFileVisitor(writer, derObj.getOwnerID(), derObj.getId(), info, app));
227 writer.close();
228 }
229 return sw.toString();
230 }
231
232 private static Document listDerivateContentAsXML(MCRDerivate derObj, String path, int depth, UriInfo info,
233 Application app)
234 throws IOException {
235 Document doc = new Document();
236
237 MCRPath root = MCRPath.getPath(derObj.getId().toString(), "/");
238 root = MCRPath.toMCRPath(root.resolve(path));
239 if (depth == -1) {
240 depth = Integer.MAX_VALUE;
241 }
242 if (root != null) {
243 Element eContents = new Element("contents");
244 eContents.setAttribute("mycoreobject", derObj.getOwnerID().toString());
245 eContents.setAttribute("mycorederivate", derObj.getId().toString());
246 doc.addContent(eContents);
247 if (!path.endsWith("/")) {
248 path += "/";
249 }
250 MCRPath p = MCRPath.getPath(derObj.getId().toString(), path);
251 if (p != null && Files.exists(p)) {
252 Element eRoot = MCRPathXML.getDirectoryXML(p).getRootElement();
253 eContents.addContent(eRoot.detach());
254 createXMLForSubdirectories(p, eRoot, 1, depth);
255 }
256
257
258 String baseURL = MCRJerseyUtil.getBaseURL(info, app)
259 + MCRConfiguration2.getStringOrThrow("MCR.RestAPI.v1.Files.URL.path");
260 baseURL = baseURL.replace("${mcrid}", derObj.getOwnerID().toString()).replace("${derid}",
261 derObj.getId().toString());
262 XPathExpression<Element> xp = XPathFactory.instance().compile(".//child[@type='file']", Filters.element());
263 for (Element e : xp.evaluate(eContents)) {
264 String uri = e.getChildText("uri");
265 if (uri != null) {
266 int pos = uri.lastIndexOf(":/");
267 String subPath = uri.substring(pos + 2);
268 while (subPath.startsWith("/")) {
269 subPath = path.substring(1);
270 }
271 e.setAttribute("href", baseURL + subPath);
272 }
273 }
274 }
275 return doc;
276 }
277
278 private static void createXMLForSubdirectories(MCRPath mcrPath, Element currentElement, int currentDepth,
279 int maxDepth) {
280 if (currentDepth < maxDepth) {
281 XPathExpression<Element> xp = XPathFactory.instance().compile("./children/child[@type='directory']",
282 Filters.element());
283 for (Element e : xp.evaluate(currentElement)) {
284 String name = e.getChildTextNormalize("name");
285 try {
286 MCRPath pChild = (MCRPath) mcrPath.resolve(name);
287 Document doc = MCRPathXML.getDirectoryXML(pChild);
288 Element eChildren = doc.getRootElement().getChild("children");
289 if (eChildren != null) {
290 e.addContent(eChildren.detach());
291 createXMLForSubdirectories(pChild, e, currentDepth + 1, maxDepth);
292 }
293 } catch (IOException ex) {
294
295 }
296
297 }
298 }
299 }
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315 public static Response listObjects(UriInfo info, String format, String filter,
316 String sort) throws MCRRestAPIException {
317 List<MCRRestAPIError> errors = new ArrayList<>();
318
319 MCRRestAPISortObject sortObj = null;
320 try {
321 sortObj = createSortObject(sort);
322 } catch (MCRRestAPIException rae) {
323 errors.addAll(rae.getErrors());
324 }
325
326
327
328 if (!format.equals(MCRRestAPIObjects.FORMAT_JSON) && !format.equals(MCRRestAPIObjects.FORMAT_XML)) {
329 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER, "The parameter 'format' is wrong.",
330 "Allowed values for format are 'json' or 'xml'."));
331 }
332
333
334 List<String> projectIDs = new ArrayList<>();
335 List<String> typeIDs = new ArrayList<>();
336 String lastModifiedBefore = null;
337 String lastModifiedAfter = null;
338 if (filter != null) {
339 for (String s : filter.split(";")) {
340 if (s.startsWith("project:")) {
341 projectIDs.add(s.substring(8));
342 continue;
343 }
344 if (s.startsWith("type:")) {
345 typeIDs.add(s.substring(5));
346 continue;
347 }
348 if (s.startsWith("lastModifiedBefore:")) {
349 if (!validateDateInput(s.substring(19))) {
350 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER,
351 "The parameter 'filter' is wrong.",
352 "The value of lastModifiedBefore could not be parsed. "
353 + "Please use UTC syntax: yyyy-MM-dd'T'HH:mm:ss'Z'."));
354 continue;
355 }
356 if (lastModifiedBefore == null) {
357 lastModifiedBefore = s.substring(19);
358 } else if (s.substring(19).compareTo(lastModifiedBefore) < 0) {
359 lastModifiedBefore = s.substring(19);
360 }
361 continue;
362 }
363
364 if (s.startsWith("lastModifiedAfter:")) {
365 if (!validateDateInput(s.substring(18))) {
366 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER,
367 "The parameter 'filter' is wrong.",
368 "The value of lastModifiedAfter could not be parsed. "
369 + "Please use UTC syntax: yyyy-MM-dd'T'HH:mm:ss'Z'."));
370 continue;
371 }
372 if (lastModifiedAfter == null) {
373 lastModifiedAfter = s.substring(18);
374 } else if (s.substring(18).compareTo(lastModifiedAfter) > 0) {
375 lastModifiedAfter = s.substring(18);
376 }
377 continue;
378 }
379
380 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER, "The parameter 'filter' is wrong.",
381 "The syntax of the filter '" + s
382 + "'could not be parsed. The syntax should be [filterName]:[value]. "
383 + "Allowed filterNames are 'project', 'type', 'lastModifiedBefore' and 'lastModifiedAfter'."));
384 }
385 }
386
387 if (errors.size() > 0) {
388 throw new MCRRestAPIException(Status.BAD_REQUEST, errors);
389 }
390
391
392
393 Set<String> mcrIDs = new HashSet<>();
394 if (projectIDs.isEmpty()) {
395 if (typeIDs.isEmpty()) {
396 mcrIDs = MCRXMLMetadataManager.instance().listIDs().stream().filter(id -> !id.contains("_derivate_"))
397 .collect(Collectors.toSet());
398 } else {
399 for (String t : typeIDs) {
400 mcrIDs.addAll(MCRXMLMetadataManager.instance().listIDsOfType(t));
401 }
402 }
403 } else {
404
405 if (typeIDs.isEmpty()) {
406 for (String id : MCRXMLMetadataManager.instance().listIDs()) {
407 String[] split = id.split("_");
408 if (!split[1].equals("derivate") && projectIDs.contains(split[0])) {
409 mcrIDs.add(id);
410 }
411 }
412 } else {
413 for (String p : projectIDs) {
414 for (String t : typeIDs) {
415 mcrIDs.addAll(MCRXMLMetadataManager.instance().listIDsForBase(p + "_" + t));
416 }
417 }
418 }
419 }
420
421
422 List<String> l = new ArrayList<>();
423 l.addAll(mcrIDs);
424 List<MCRObjectIDDate> objIdDates = new ArrayList<>();
425 try {
426 objIdDates = MCRXMLMetadataManager.instance().retrieveObjectDates(l);
427 } catch (IOException e) {
428
429 }
430 if (lastModifiedAfter != null || lastModifiedBefore != null) {
431 List<MCRObjectIDDate> testObjIdDates = objIdDates;
432 objIdDates = new ArrayList<>();
433 for (MCRObjectIDDate oid : testObjIdDates) {
434 String test = SDF_UTC.format(oid.getLastModified());
435 if (lastModifiedAfter != null && test.compareTo(lastModifiedAfter) < 0) {
436 continue;
437 }
438 if (lastModifiedBefore != null
439 && lastModifiedBefore.compareTo(test.substring(0, lastModifiedBefore.length())) < 0) {
440 continue;
441 }
442 objIdDates.add(oid);
443 }
444 }
445
446
447 if (sortObj != null) {
448 objIdDates.sort(new MCRRestAPISortObjectComparator(sortObj));
449 }
450
451
452 if (MCRRestAPIObjects.FORMAT_XML.equals(format)) {
453 Element eMcrobjects = new Element("mycoreobjects");
454 Document docOut = new Document(eMcrobjects);
455 eMcrobjects.setAttribute("numFound", Integer.toString(objIdDates.size()));
456 for (MCRObjectIDDate oid : objIdDates) {
457 Element eMcrObject = new Element("mycoreobject");
458 eMcrObject.setAttribute("ID", oid.getId());
459 eMcrObject.setAttribute("lastModified", SDF_UTC.format(oid.getLastModified()));
460 eMcrObject.setAttribute("href", info.getAbsolutePathBuilder().path(oid.getId()).build().toString());
461
462 eMcrobjects.addContent(eMcrObject);
463 }
464 try {
465 StringWriter sw = new StringWriter();
466 XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
467 xout.output(docOut, sw);
468 return Response.ok(sw.toString())
469 .type("application/xml; charset=UTF-8")
470 .build();
471 } catch (IOException e) {
472 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
473 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage()));
474 }
475 }
476
477
478 if (MCRRestAPIObjects.FORMAT_JSON.equals(format)) {
479 StringWriter sw = new StringWriter();
480 try {
481 JsonWriter writer = new JsonWriter(sw);
482 writer.setIndent(" ");
483 writer.beginObject();
484 writer.name("numFound").value(objIdDates.size());
485 writer.name("mycoreobjects");
486 writer.beginArray();
487 for (MCRObjectIDDate oid : objIdDates) {
488 writer.beginObject();
489 writer.name("ID").value(oid.getId());
490 writer.name("lastModified").value(SDF_UTC.format(oid.getLastModified()));
491 writer.name("href").value(info.getAbsolutePathBuilder().path(oid.getId()).build().toString());
492 writer.endObject();
493 }
494 writer.endArray();
495 writer.endObject();
496
497 writer.close();
498 return Response.ok(sw.toString())
499 .type("application/json; charset=UTF-8")
500 .build();
501 } catch (IOException e) {
502 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
503 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage()));
504 }
505 }
506 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
507 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, "A problem in programm flow", null));
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 public static Response listDerivates(UriInfo info, String mcrObjID, String format,
525 String sort) throws MCRRestAPIException {
526 List<MCRRestAPIError> errors = new ArrayList<>();
527
528 MCRRestAPISortObject sortObj = null;
529 try {
530 sortObj = createSortObject(sort);
531 } catch (MCRRestAPIException rae) {
532 errors.addAll(rae.getErrors());
533 }
534
535
536
537 if (!format.equals(MCRRestAPIObjects.FORMAT_JSON) && !format.equals(MCRRestAPIObjects.FORMAT_XML)) {
538 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER, "The Parameter format is wrong.",
539 "Allowed values for format are 'json' or 'xml'."));
540 }
541
542 if (errors.size() > 0) {
543 throw new MCRRestAPIException(Status.BAD_REQUEST, errors);
544 }
545
546
547
548 List<MCRObjectIDDate> objIdDates = retrieveMCRObject(mcrObjID).getStructure().getDerivates().stream()
549 .map(MCRMetaLinkID::getXLinkHrefID).filter(MCRMetadataManager::exists).map(id -> new MCRObjectIDDate() {
550 long lastModified;
551 {
552 try {
553 lastModified = MCRXMLMetadataManager.instance().getLastModified(id);
554 } catch (IOException e) {
555 lastModified = 0;
556 LOGGER.error(
557 "Exception while getting last modified of {}",
558 id, e);
559 }
560 }
561
562 @Override
563 public String getId() {
564 return id.toString();
565 }
566
567 @Override
568 public Date getLastModified() {
569 return new Date(lastModified);
570 }
571 }).sorted(new MCRRestAPISortObjectComparator(sortObj)::compare).collect(Collectors.toList());
572
573
574 if (MCRRestAPIObjects.FORMAT_XML.equals(format)) {
575 Element eDerObjects = new Element("derobjects");
576 Document docOut = new Document(eDerObjects);
577 eDerObjects.setAttribute("numFound", Integer.toString(objIdDates.size()));
578 for (MCRObjectIDDate oid : objIdDates) {
579 Element eDerObject = new Element("derobject");
580 eDerObject.setAttribute("ID", oid.getId());
581 MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(oid.getId()));
582 String mcrID = der.getDerivate().getMetaLink().getXLinkHref();
583 eDerObject.setAttribute("metadata", mcrID);
584 if (der.getDerivate().getClassifications().size() > 0) {
585 StringBuffer c = new StringBuffer();
586 for (int i = 0; i < der.getDerivate().getClassifications().size(); i++) {
587 if (i > 0) {
588 c.append(' ');
589 }
590 MCRMetaClassification cl = der.getDerivate().getClassifications().get(i);
591 c.append(cl.getClassId()).append(':').append(cl.getCategId());
592 }
593 eDerObject.setAttribute("classifications", c.toString());
594 }
595 eDerObject.setAttribute("lastModified", SDF_UTC.format(oid.getLastModified()));
596 eDerObject.setAttribute("href", info.getAbsolutePathBuilder().path(oid.getId()).build().toString());
597
598 eDerObjects.addContent(eDerObject);
599 }
600 try {
601 StringWriter sw = new StringWriter();
602 XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
603 xout.output(docOut, sw);
604 return Response.ok(sw.toString())
605 .type("application/xml; charset=UTF-8")
606 .build();
607 } catch (IOException e) {
608 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
609 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage()));
610 }
611 }
612
613
614 if (MCRRestAPIObjects.FORMAT_JSON.equals(format)) {
615 StringWriter sw = new StringWriter();
616 try {
617 JsonWriter writer = new JsonWriter(sw);
618 writer.setIndent(" ");
619 writer.beginObject();
620 writer.name("numFound").value(objIdDates.size());
621 writer.name("mycoreobjects");
622 writer.beginArray();
623 for (MCRObjectIDDate oid : objIdDates) {
624 writer.beginObject();
625 writer.name("ID").value(oid.getId());
626 MCRDerivate der = MCRMetadataManager.retrieveMCRDerivate(MCRObjectID.getInstance(oid.getId()));
627 String mcrID = der.getDerivate().getMetaLink().getXLinkHref();
628 writer.name("metadata").value(mcrID);
629 writer.name("lastModified").value(SDF_UTC.format(oid.getLastModified()));
630 writer.name("href").value(info.getAbsolutePathBuilder().path(oid.getId()).build().toString());
631 writer.endObject();
632 }
633 writer.endArray();
634 writer.endObject();
635
636 writer.close();
637
638 return Response.ok(sw.toString())
639 .type("application/json; charset=UTF-8")
640 .build();
641 } catch (IOException e) {
642 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
643 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG, e.getMessage()));
644 }
645 }
646
647 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
648 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, "Unexepected program flow termination.",
649 "Please contact a developer!"));
650 }
651
652
653
654
655
656
657
658
659
660
661
662
663
664 public static Response listContents(UriInfo info, Application app, Request request, String mcrObjID,
665 String mcrDerID, String format, String path, int depth) throws MCRRestAPIException {
666
667 if (!format.equals(MCRRestAPIObjects.FORMAT_JSON) && !format.equals(MCRRestAPIObjects.FORMAT_XML)) {
668 throw new MCRRestAPIException(Response.Status.BAD_REQUEST,
669 new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_PARAMETER, "The syntax of format parameter is wrong.",
670 "Allowed values for format are 'json' or 'xml'."));
671 }
672 MCRObjectID mcrObj = MCRObjectID.getInstance(mcrObjID);
673 MCRDerivate derObj = retrieveMCRDerivate(mcrObj, mcrDerID);
674
675 try {
676 MCRPath root = MCRPath.getPath(derObj.getId().toString(), "/");
677 BasicFileAttributes readAttributes = Files.readAttributes(root, BasicFileAttributes.class);
678 Date lastModified = new Date(readAttributes.lastModifiedTime().toMillis());
679 ResponseBuilder responseBuilder = request.evaluatePreconditions(lastModified);
680 if (responseBuilder != null) {
681 return responseBuilder.build();
682 }
683 switch (format) {
684 case MCRRestAPIObjects.FORMAT_XML:
685 Document docOut = listDerivateContentAsXML(derObj, path, depth, info, app);
686 try (StringWriter sw = new StringWriter()) {
687 XMLOutputter xout = new XMLOutputter(Format.getPrettyFormat());
688 xout.output(docOut, sw);
689 return response(sw.toString(), "application/xml", lastModified);
690 } catch (IOException e) {
691 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
692 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, GENERAL_ERROR_MSG,
693 e.getMessage()));
694 }
695 case MCRRestAPIObjects.FORMAT_JSON:
696 if (MCRRestAPIObjects.FORMAT_JSON.equals(format)) {
697 String result = listDerivateContentAsJson(derObj, path, depth, info, app);
698 return response(result, "application/json", lastModified);
699 }
700 default:
701 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR,
702 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR,
703 "Unexepected program flow termination.",
704 "Please contact a developer!"));
705 }
706 } catch (IOException e) {
707 throw new MCRRestAPIException(Response.Status.INTERNAL_SERVER_ERROR, new MCRRestAPIError(
708 MCRRestAPIError.CODE_INTERNAL_ERROR, "Unexepected program flow termination.", e.getMessage()));
709 }
710 }
711
712
713
714
715
716
717
718
719
720
721
722 public static String retrieveMaindocURL(UriInfo info, String mcrObjID, String mcrDerID, Application app)
723 throws IOException {
724 try {
725 MCRObjectID mcrObj = MCRObjectID.getInstance(mcrObjID);
726 MCRDerivate derObj = retrieveMCRDerivate(mcrObj, mcrDerID);
727 String maindoc = derObj.getDerivate().getInternals().getMainDoc();
728
729 String baseURL = MCRJerseyUtil.getBaseURL(info, app)
730 + MCRConfiguration2.getStringOrThrow("MCR.RestAPI.v1.Files.URL.path");
731 baseURL = baseURL.replace("${mcrid}", mcrObj.toString()).replace("${derid}",
732 derObj.getId().toString());
733
734 return baseURL + maindoc;
735 } catch (MCRRestAPIException rae) {
736 return null;
737 }
738 }
739
740 private static Response response(String response, String type, Date lastModified) {
741 byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);
742 String mimeType = type + "; charset=UTF-8";
743 CacheControl cacheControl = new CacheControl();
744 cacheControl.setNoTransform(false);
745 cacheControl.setMaxAge(0);
746 return Response.ok(responseBytes, mimeType).lastModified(lastModified)
747 .header("Content-Length", responseBytes.length)
748 .cacheControl(cacheControl).build();
749 }
750
751
752
753
754
755
756 private static boolean validateDateInput(String test) {
757 String base = "0000-00-00T00:00:00Z";
758 if (test.length() > base.length()) {
759 return false;
760 }
761 test = test + base.substring(test.length());
762 try {
763 SDF_UTC.parse(test);
764 } catch (ParseException e) {
765 return false;
766 }
767 return true;
768 }
769
770 private static MCRRestAPISortObject createSortObject(String input) throws MCRRestAPIException {
771 if (input == null) {
772 return null;
773 }
774 List<MCRRestAPIError> errors = new ArrayList<>();
775 MCRRestAPISortObject result = new MCRRestAPISortObject();
776
777 String[] data = input.split(":");
778 if (data.length == 2) {
779 result.setField(data[0].replace("|", ""));
780 String sortOrder = data[1].toLowerCase(Locale.GERMAN).replace("|", "");
781 if (!"ID".equals(result.getField()) && !"lastModified".equals(result.getField())) {
782 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_QUERY_PARAMETER, "The sortField is wrong",
783 "Allowed values are 'ID' and 'lastModified'."));
784 }
785
786 if ("asc".equals(sortOrder)) {
787 result.setOrder(SortOrder.ASC);
788 }
789 if ("desc".equals(sortOrder)) {
790 result.setOrder(SortOrder.DESC);
791 }
792 if (result.getOrder() == null) {
793 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_QUERY_PARAMETER, "The sortOrder is wrong",
794 "Allowed values for sortOrder are 'asc' and 'desc'."));
795 }
796
797 } else {
798 errors.add(new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_QUERY_PARAMETER, "The sort parameter is wrong.",
799 "The syntax should be [sortField]:[sortOrder]."));
800 }
801 if (errors.size() > 0) {
802 throw new MCRRestAPIException(Status.BAD_REQUEST, errors);
803 }
804 return result;
805 }
806
807 private static MCRObject retrieveMCRObject(String idString) throws MCRRestAPIException {
808 String key = "mcr";
809 if (idString.contains(":")) {
810 int pos = idString.indexOf(":");
811 key = idString.substring(0, pos);
812 idString = idString.substring(pos + 1);
813 if (!key.equals("mcr")) {
814 idString = URLDecoder.decode(idString, StandardCharsets.UTF_8);
815
816
817
818
819 }
820 }
821 if (key.equals("mcr")) {
822
823 MCRObjectID mcrID = null;
824 try {
825 mcrID = MCRObjectID.getInstance(idString);
826 } catch (Exception e) {
827 throw new MCRRestAPIException(Response.Status.BAD_REQUEST,
828 new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_ID,
829 "The MyCoRe ID '" + idString
830 + "' is not valid. - Did you use the proper format: '{project}_{type}_{number}'?",
831 e.getMessage()));
832 }
833
834 if (!MCRMetadataManager.exists(mcrID)) {
835 throw new MCRRestAPIException(Response.Status.NOT_FOUND,
836 new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND,
837 "There is no object with the given MyCoRe ID '" + idString + "'.", null));
838 }
839
840 return MCRMetadataManager.retrieveMCRObject(mcrID);
841 } else {
842 SolrClient solrClient = MCRSolrClientFactory.getMainSolrClient();
843 SolrQuery query = new SolrQuery();
844 query.setQuery(key + ":" + idString);
845 try {
846 QueryResponse response = solrClient.query(query);
847 SolrDocumentList solrResults = response.getResults();
848 if (solrResults.getNumFound() == 1) {
849 String id = solrResults.get(0).getFieldValue("returnId").toString();
850 return retrieveMCRObject(id);
851 } else {
852 if (solrResults.getNumFound() == 0) {
853 throw new MCRRestAPIException(Response.Status.NOT_FOUND,
854 new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND,
855 "There is no object with the given ID '" + key + ":" + idString + "'.", null));
856 } else {
857 throw new MCRRestAPIException(Response.Status.NOT_FOUND,
858 new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND,
859 "The ID is not unique. There are " + solrResults.getNumFound()
860 + " objecst fore the given ID '" + key + ":" + idString + "'.",
861 null));
862 }
863 }
864 } catch (SolrServerException | IOException e) {
865 LOGGER.error(e);
866 throw new MCRRestAPIException(Response.Status.BAD_REQUEST,
867 new MCRRestAPIError(MCRRestAPIError.CODE_INTERNAL_ERROR, "Internal server error.", e.getMessage()));
868 }
869 }
870 }
871
872 private static MCRDerivate retrieveMCRDerivate(MCRObjectID parentObjId, String derIDString)
873 throws MCRRestAPIException {
874
875 String derKey = "mcr";
876 if (derIDString.contains(":")) {
877 int pos = derIDString.indexOf(":");
878 derKey = derIDString.substring(0, pos);
879 derIDString = derIDString.substring(pos + 1);
880 if (!derKey.equals("mcr") && !derKey.equals("label")) {
881 throw new MCRRestAPIException(Response.Status.BAD_REQUEST,
882 new MCRRestAPIError(MCRRestAPIError.CODE_WRONG_ID, "The ID is not valid.",
883 "The prefix is unkown. Only 'mcr' or 'label' are allowed."));
884 }
885 }
886
887 String matchedDerID = null;
888 if ("mcr".equals(derKey) &&
889 MCRMetadataManager.getDerivateIds(parentObjId, 0, TimeUnit.SECONDS)
890 .stream()
891 .map(MCRObjectID::toString)
892 .anyMatch(derIDString::equals)) {
893 matchedDerID = derIDString;
894 }
895 if (derKey.equals("label")) {
896 MCRObject mcrObj = MCRMetadataManager.retrieveMCRObject(parentObjId);
897 for (MCRMetaLinkID check : mcrObj.getStructure().getDerivates()) {
898 if (derIDString.equals(check.getXLinkLabel()) || derIDString.equals(check.getXLinkTitle())) {
899 matchedDerID = check.getXLinkHref();
900 break;
901 }
902 }
903 }
904
905 if (matchedDerID == null) {
906 throw new MCRRestAPIException(Response.Status.NOT_FOUND,
907 new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND, "Derivate " + derIDString + " not found.",
908 "The MyCoRe Object with id '" + parentObjId
909 + "' does not contain a derivate with id '" + derIDString + "'."));
910 }
911
912 MCRObjectID derID = MCRObjectID.getInstance(matchedDerID);
913 if (!MCRMetadataManager.exists(derID)) {
914 throw new MCRRestAPIException(Response.Status.NOT_FOUND, new MCRRestAPIError(MCRRestAPIError.CODE_NOT_FOUND,
915 "There is no derivate with the id '" + matchedDerID + "'.", null));
916 }
917 return MCRMetadataManager.retrieveMCRDerivate(derID);
918 }
919
920
921
922
923
924
925
926
927 public static boolean hasChildren(Path p) {
928 try {
929 if (Files.isDirectory(p)) {
930 try (DirectoryStream<Path> ds = Files.newDirectoryStream(p)) {
931 return ds.iterator().hasNext();
932 }
933 }
934 } catch (IOException e) {
935 LOGGER.error(e);
936 }
937 return false;
938 }
939 }