1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.common.xml;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.PrintWriter;
24 import java.io.StringWriter;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.net.URL;
28 import java.net.URLDecoder;
29 import java.nio.charset.StandardCharsets;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.nio.file.Paths;
33 import java.time.Instant;
34 import java.time.temporal.ChronoUnit;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.Hashtable;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Optional;
42 import java.util.Set;
43 import java.util.StringTokenizer;
44 import java.util.regex.Matcher;
45 import java.util.regex.Pattern;
46 import java.util.stream.Collectors;
47
48 import javax.xml.parsers.ParserConfigurationException;
49 import javax.xml.transform.Source;
50 import javax.xml.transform.TransformerException;
51 import javax.xml.transform.URIResolver;
52 import javax.xml.transform.sax.SAXSource;
53 import javax.xml.transform.stream.StreamSource;
54
55 import org.apache.http.client.cache.HttpCacheContext;
56 import org.apache.http.client.config.RequestConfig;
57 import org.apache.http.client.methods.CloseableHttpResponse;
58 import org.apache.http.client.methods.HttpGet;
59 import org.apache.http.impl.client.CloseableHttpClient;
60 import org.apache.http.impl.client.cache.CacheConfig;
61 import org.apache.http.impl.client.cache.CachingHttpClients;
62 import org.apache.logging.log4j.LogManager;
63 import org.apache.logging.log4j.Logger;
64 import org.jdom2.Document;
65 import org.jdom2.Element;
66 import org.jdom2.JDOMException;
67 import org.jdom2.Namespace;
68 import org.jdom2.input.SAXBuilder;
69 import org.jdom2.input.sax.XMLReaders;
70 import org.jdom2.transform.JDOMSource;
71 import org.mycore.access.MCRAccessManager;
72 import org.mycore.common.MCRCache;
73 import org.mycore.common.MCRClassTools;
74 import org.mycore.common.MCRConstants;
75 import org.mycore.common.MCRDeveloperTools;
76 import org.mycore.common.MCRException;
77 import org.mycore.common.MCRSessionMgr;
78 import org.mycore.common.MCRUsageException;
79 import org.mycore.common.config.MCRConfiguration2;
80 import org.mycore.common.config.MCRConfigurationDir;
81 import org.mycore.common.content.MCRByteContent;
82 import org.mycore.common.content.MCRContent;
83 import org.mycore.common.content.MCRPathContent;
84 import org.mycore.common.content.MCRSourceContent;
85 import org.mycore.common.content.MCRStreamContent;
86 import org.mycore.common.content.transformer.MCRContentTransformer;
87 import org.mycore.common.content.transformer.MCRParameterizedTransformer;
88 import org.mycore.common.content.transformer.MCRXSLTransformer;
89 import org.mycore.common.events.MCRShutdownHandler;
90 import org.mycore.common.xsl.MCRLazyStreamSource;
91 import org.mycore.common.xsl.MCRParameterCollector;
92 import org.mycore.datamodel.classifications2.MCRCategory;
93 import org.mycore.datamodel.classifications2.MCRCategoryDAO;
94 import org.mycore.datamodel.classifications2.MCRCategoryDAOFactory;
95 import org.mycore.datamodel.classifications2.MCRCategoryID;
96 import org.mycore.datamodel.classifications2.utils.MCRCategoryTransformer;
97 import org.mycore.datamodel.common.MCRAbstractMetadataVersion;
98 import org.mycore.datamodel.common.MCRDataURL;
99 import org.mycore.datamodel.common.MCRXMLMetadataManager;
100 import org.mycore.datamodel.metadata.MCRDerivate;
101 import org.mycore.datamodel.metadata.MCRFileMetadata;
102 import org.mycore.datamodel.metadata.MCRMetadataManager;
103 import org.mycore.datamodel.metadata.MCRObjectDerivate;
104 import org.mycore.datamodel.metadata.MCRObjectID;
105 import org.mycore.datamodel.niofs.MCRPath;
106 import org.mycore.datamodel.niofs.MCRPathXML;
107 import org.mycore.services.http.MCRHttpUtils;
108 import org.mycore.services.i18n.MCRTranslation;
109 import org.mycore.tools.MCRObjectFactory;
110 import org.xml.sax.InputSource;
111 import org.xml.sax.SAXException;
112 import org.xml.sax.XMLReader;
113
114 import jakarta.servlet.ServletContext;
115
116
117
118
119
120
121
122
123
124
125
126 public final class MCRURIResolver implements URIResolver {
127 static final Logger LOGGER = LogManager.getLogger(MCRURIResolver.class);
128
129 static final String SESSION_OBJECT_NAME = "URI_RESOLVER_DEBUG";
130
131 private static final String CONFIG_PREFIX = "MCR.URIResolver.";
132
133 private static Map<String, URIResolver> SUPPORTED_SCHEMES;
134
135 private static MCRResolverProvider EXT_RESOLVER;
136
137 private static MCRURIResolver singleton;
138
139 private static ServletContext context;
140
141 static {
142 try {
143 EXT_RESOLVER = getExternalResolverProvider();
144 singleton = new MCRURIResolver();
145 } catch (Exception exc) {
146 LOGGER.error("Unable to initialize MCRURIResolver", exc);
147 }
148 }
149
150
151
152
153 private MCRURIResolver() {
154 SUPPORTED_SCHEMES = Collections.unmodifiableMap(getResolverMapping());
155 }
156
157 private static MCRResolverProvider getExternalResolverProvider() {
158 return MCRConfiguration2.getClass(CONFIG_PREFIX + "ExternalResolver.Class")
159 .map(c -> {
160 try {
161 return (MCRResolverProvider) c.getDeclaredConstructor().newInstance();
162 } catch (ReflectiveOperationException e) {
163 LOGGER.warn("Could not instantiate external Resolver class", e);
164 return null;
165 }
166 }).orElse(HashMap::new);
167 }
168
169
170
171
172 public static MCRURIResolver instance() {
173 return singleton;
174 }
175
176
177
178
179
180
181
182 public static synchronized void init(ServletContext ctx) {
183 context = ctx;
184 }
185
186 public static Hashtable<String, String> getParameterMap(String key) {
187 String[] param;
188 StringTokenizer tok = new StringTokenizer(key, "&");
189 Hashtable<String, String> params = new Hashtable<>();
190
191 while (tok.hasMoreTokens()) {
192 param = tok.nextToken().split("=");
193 params.put(param[0], param.length >= 2 ? param[1] : "");
194 }
195 return params;
196 }
197
198 static URI resolveURI(String href, String base) {
199 return Optional.ofNullable(base)
200 .map(URI::create)
201 .map(u -> u.resolve(href))
202 .orElse(URI.create(href));
203 }
204
205 public static ServletContext getServletContext() {
206 return context;
207 }
208
209 private HashMap<String, URIResolver> getResolverMapping() {
210 final Map<String, URIResolver> extResolverMapping = EXT_RESOLVER.getURIResolverMapping();
211 extResolverMapping.putAll(new MCRModuleResolverProvider().getURIResolverMapping());
212
213 HashMap<String, URIResolver> supportedSchemes = new HashMap<>(10 + extResolverMapping.size(), 1);
214
215 supportedSchemes.putAll(extResolverMapping);
216 supportedSchemes.put("webapp", new MCRWebAppResolver());
217 supportedSchemes.put("ifs", new MCRIFSResolver());
218 supportedSchemes.put("mcrfile", new MCRMCRFileResolver());
219 supportedSchemes.put("mcrobject", new MCRObjectResolver());
220 supportedSchemes.put("session", new MCRSessionResolver());
221 supportedSchemes.put("access", new MCRACLResolver());
222 supportedSchemes.put("resource", new MCRResourceResolver());
223 supportedSchemes.put("localclass", new MCRLocalClassResolver());
224 supportedSchemes.put("classification", new MCRClassificationResolver());
225 supportedSchemes.put("buildxml", new MCRBuildXMLResolver());
226 supportedSchemes.put("catchEx", new MCRExceptionAsXMLResolver());
227 supportedSchemes.put("notnull", new MCRNotNullResolver());
228 supportedSchemes.put("xslStyle", new MCRXslStyleResolver());
229 supportedSchemes.put("xslTransform", new MCRLayoutTransformerResolver());
230 supportedSchemes.put("xslInclude", new MCRXslIncludeResolver());
231 supportedSchemes.put("xslImport", new MCRXslImportResolver());
232 supportedSchemes.put("versioninfo", new MCRVersionInfoResolver());
233 supportedSchemes.put("deletedMcrObject", new MCRDeletedObjectResolver());
234 supportedSchemes.put("fileMeta", new MCRFileMetadataResolver());
235 supportedSchemes.put("basket", new org.mycore.frontend.basket.MCRBasketResolver());
236 supportedSchemes.put("language", new org.mycore.datamodel.language.MCRLanguageResolver());
237 supportedSchemes.put("chooseTemplate", new MCRChooseTemplateResolver());
238 supportedSchemes.put("redirect", new MCRRedirectResolver());
239 supportedSchemes.put("data", new MCRDataURLResolver());
240 supportedSchemes.put("i18n", new MCRI18NResolver());
241 supportedSchemes.put("checkPermissionChain", new MCRCheckPermissionChainResolver());
242 supportedSchemes.put("checkPermission", new MCRCheckPermissionResolver());
243 MCRRESTResolver restResolver = new MCRRESTResolver();
244 supportedSchemes.put("http", restResolver);
245 supportedSchemes.put("https", restResolver);
246 supportedSchemes.put("file", new MCRFileResolver());
247 supportedSchemes.put("cache", new MCRCachingResolver());
248 return supportedSchemes;
249 }
250
251
252
253
254
255
256 @Override
257 public Source resolve(String href, String base) throws TransformerException {
258 if (LOGGER.isDebugEnabled()) {
259 if (base != null) {
260 LOGGER.debug("Including {} from {}", href, base);
261 addDebugInfo(href, base);
262 } else {
263 LOGGER.debug("Including {}", href);
264 addDebugInfo(href, null);
265 }
266 }
267 if (!href.contains(":")) {
268 return tryResolveXSL(href, base);
269 }
270
271 String scheme = getScheme(href, base);
272
273 URIResolver uriResolver = SUPPORTED_SCHEMES.get(scheme);
274 if (uriResolver != null) {
275 return uriResolver.resolve(href, base);
276 } else {
277 try {
278 InputSource entity = MCREntityResolver.instance().resolveEntity(null, href);
279 if (entity != null) {
280 LOGGER.debug("Resolved via EntityResolver: {}", entity.getSystemId());
281 return new MCRLazyStreamSource(entity::getByteStream, entity.getSystemId());
282 }
283 } catch (IOException e) {
284 LOGGER.debug("Error while resolving uri: {}", href);
285 }
286
287 if (href.endsWith("/") && scheme.equals("file")) {
288
289 return null;
290 }
291 StreamSource streamSource = new StreamSource();
292 streamSource.setSystemId(href);
293 return streamSource;
294 }
295 }
296
297 private Source tryResolveXSL(String href, String base) throws TransformerException {
298 if (href.endsWith(".xsl")) {
299 final String uri = "resource:xsl/" + href;
300 LOGGER.debug("Trying to resolve {} from uri {}", href, uri);
301 return SUPPORTED_SCHEMES.get("resource").resolve(uri, base);
302 }
303 return null;
304 }
305
306 private void addDebugInfo(String href, String base) {
307 MCRURIResolverFilter.uriList.get().add(href + " from " + base);
308 }
309
310
311
312
313
314
315
316
317 public Element resolve(String uri) {
318 if (LOGGER.isDebugEnabled()) {
319 addDebugInfo(uri, "JAVA method invocation");
320 }
321 MCRSourceContent content;
322 try {
323 content = MCRSourceContent.getInstance(uri);
324 return content == null ? null : content.asXML().getRootElement().detach();
325 } catch (Exception e) {
326 throw new MCRException("Error while resolving " + uri, e);
327 }
328 }
329
330
331
332
333
334
335
336
337
338
339 public String getScheme(String uri, String base) {
340 StringTokenizer uriTokenizer = new StringTokenizer(uri, ":");
341 if (uriTokenizer.hasMoreTokens()) {
342 return uriTokenizer.nextToken();
343 }
344 if (base != null) {
345 uriTokenizer = new StringTokenizer(base, ":");
346 if (uriTokenizer.hasMoreTokens()) {
347 return uriTokenizer.nextToken();
348 }
349 }
350 return null;
351 }
352
353 URIResolver getResolver(String scheme) {
354 if (SUPPORTED_SCHEMES.containsKey(scheme)) {
355 return SUPPORTED_SCHEMES.get(scheme);
356 }
357 String msg = "Unsupported scheme type: " + scheme;
358 throw new MCRUsageException(msg);
359 }
360
361
362
363
364
365
366
367
368 protected Element parseStream(InputStream in) throws JDOMException, IOException {
369 SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING);
370 builder.setEntityResolver(MCREntityResolver.instance());
371
372 return builder.build(in).getRootElement();
373 }
374
375
376
377
378
379
380
381
382 public interface MCRResolverProvider {
383
384
385
386
387
388
389
390 Map<String, URIResolver> getURIResolverMapping();
391 }
392
393 public interface MCRXslIncludeHrefs {
394 List<String> getHrefs();
395 }
396
397 private static class MCRModuleResolverProvider implements MCRResolverProvider {
398 private final Map<String, URIResolver> resolverMap = new HashMap<>();
399
400 MCRModuleResolverProvider() {
401 MCRConfiguration2.getSubPropertiesMap(CONFIG_PREFIX + "ModuleResolver.")
402 .forEach(this::registerUriResolver);
403 }
404
405 @Override
406 public Map<String, URIResolver> getURIResolverMapping() {
407 return resolverMap;
408 }
409
410 private void registerUriResolver(String scheme, String className) {
411 try {
412 resolverMap.put(scheme, MCRConfiguration2.instantiateClass(className));
413 } catch (RuntimeException re) {
414 throw new MCRException("Cannot instantiate " + className + " for URI scheme " + scheme, re);
415 }
416 }
417
418 }
419
420 private static class MCRFileResolver implements URIResolver {
421
422 @Override
423 public Source resolve(String href, String base) throws TransformerException {
424 URI hrefURI = MCRURIResolver.resolveURI(href, base);
425 if (!hrefURI.getScheme().equals("file")) {
426 throw new TransformerException("Unsupport file uri scheme: " + hrefURI.getScheme());
427 }
428 Path path = Paths.get(hrefURI);
429 StreamSource source;
430 try {
431 source = new StreamSource(Files.newInputStream(path), hrefURI.toASCIIString());
432 return source;
433 } catch (IOException e) {
434 throw new TransformerException(e);
435 }
436 }
437 }
438
439 private static class MCRRESTResolver implements URIResolver {
440 private static final long MAX_OBJECT_SIZE = MCRConfiguration2.getLong(CONFIG_PREFIX + "REST.MaxObjectSize")
441 .orElse(128 * 1024l);
442
443 private static final int MAX_CACHE_ENTRIES = MCRConfiguration2.getInt(CONFIG_PREFIX + "REST.MaxCacheEntries")
444 .orElse(1000);
445
446 private static final int REQUEST_TIMEOUT = MCRConfiguration2.getInt(CONFIG_PREFIX + "REST.RequestTimeout")
447 .orElse(30000);
448
449 private CloseableHttpClient restClient;
450
451 private org.apache.logging.log4j.Logger logger;
452
453 MCRRESTResolver() {
454 CacheConfig cacheConfig = CacheConfig.custom()
455 .setMaxObjectSize(MAX_OBJECT_SIZE)
456 .setMaxCacheEntries(MAX_CACHE_ENTRIES)
457 .build();
458 RequestConfig requestConfig = RequestConfig.custom()
459 .setConnectTimeout(REQUEST_TIMEOUT)
460 .setSocketTimeout(REQUEST_TIMEOUT)
461 .build();
462 this.restClient = CachingHttpClients.custom()
463 .setCacheConfig(cacheConfig)
464 .setDefaultRequestConfig(requestConfig)
465 .setUserAgent(MCRHttpUtils.getHttpUserAgent())
466 .useSystemProperties()
467 .build();
468 MCRShutdownHandler.getInstance().addCloseable(this::close);
469 this.logger = LogManager.getLogger();
470 }
471
472 public void close() {
473 try {
474 restClient.close();
475 } catch (IOException e) {
476 LogManager.getLogger().warn("Exception while closing http client.", e);
477 }
478 }
479
480 @Override
481 public Source resolve(String href, String base) throws TransformerException {
482 URI hrefURI = MCRURIResolver.resolveURI(href, base);
483 try {
484 HttpCacheContext context = HttpCacheContext.create();
485 HttpGet get = new HttpGet(hrefURI);
486 CloseableHttpResponse response = restClient.execute(get, context);
487 logger.debug(() -> getCacheDebugMsg(hrefURI, context));
488 try (InputStream content = response.getEntity().getContent()) {
489 final Source source = new MCRStreamContent(content).getReusableCopy().getSource();
490 source.setSystemId(hrefURI.toASCIIString());
491 return source;
492 } finally {
493 response.close();
494 get.reset();
495 }
496 } catch (IOException e) {
497 throw new TransformerException(e);
498 }
499 }
500
501 private String getCacheDebugMsg(URI hrefURI, HttpCacheContext context) {
502 String msg = hrefURI.toASCIIString() + ": ";
503 switch (context.getCacheResponseStatus()) {
504 case CACHE_HIT:
505 msg += "A response was generated from the cache with " +
506 "no requests sent upstream";
507 break;
508 case CACHE_MODULE_RESPONSE:
509 msg += "The response was generated directly by the " +
510 "caching module";
511 break;
512 case CACHE_MISS:
513 msg += "The response came from an upstream server";
514 break;
515 case VALIDATED:
516 msg += "The response was generated from the cache " +
517 "after validating the entry with the origin server";
518 break;
519 }
520 return msg;
521 }
522
523 }
524
525 private static class MCRObjectResolver implements URIResolver {
526
527
528
529
530
531
532
533
534 @Override
535 public Source resolve(String href, String base) throws TransformerException {
536 String id = href.substring(href.indexOf(":") + 1);
537 LOGGER.debug("Reading MCRObject with ID {}", id);
538 Map<String, String> params;
539 StringTokenizer tok = new StringTokenizer(id, "?");
540 id = tok.nextToken();
541
542 if (tok.hasMoreTokens()) {
543 params = getParameterMap(tok.nextToken());
544 } else {
545 params = Collections.emptyMap();
546 }
547
548 MCRObjectID mcrid = MCRObjectID.getInstance(id);
549 try {
550 MCRXMLMetadataManager xmlmm = MCRXMLMetadataManager.instance();
551 MCRContent content = params.containsKey("r")
552 ? xmlmm.retrieveContent(mcrid, params.get("r"))
553 : xmlmm.retrieveContent(mcrid);
554 if (content == null) {
555 return null;
556 }
557 LOGGER.debug("end resolving {}", href);
558 return content.getSource();
559 } catch (IOException e) {
560 throw new TransformerException(e);
561 }
562 }
563
564 }
565
566
567
568
569 private static class MCRWebAppResolver implements URIResolver {
570
571 @Override
572 public Source resolve(String href, String base) throws TransformerException {
573 String path = href.substring(href.indexOf(":") + 1);
574 if (path.charAt(0) != '/') {
575 path = '/' + path;
576 }
577
578 if (MCRDeveloperTools.overrideActive()) {
579 final Optional<Path> overriddenFilePath = MCRDeveloperTools.getOverriddenFilePath(path, true);
580 if (overriddenFilePath.isPresent()) {
581 return new StreamSource(overriddenFilePath.get().toFile());
582 }
583 }
584
585 LOGGER.debug("Reading xml from webapp {}", path);
586 try {
587 URL resource = context.getResource(path);
588 if (resource != null) {
589 return new StreamSource(resource.toURI().toASCIIString());
590 }
591 } catch (Exception ex) {
592 throw new TransformerException(ex);
593 }
594 LOGGER.error("File does not exist: {}", context.getRealPath(path));
595 throw new TransformerException("Could not find web resource: " + path);
596 }
597 }
598
599 private static class MCRChooseTemplateResolver implements URIResolver {
600
601 private static Document getStylesheets(List<String> temps) {
602
603 Element rootOut = new Element("stylesheet", MCRConstants.XSL_NAMESPACE).setAttribute("version", "1.0");
604 Document jdom = new Document(rootOut);
605
606 if (temps.isEmpty()) {
607 return jdom;
608 }
609
610 for (String templateName : temps) {
611 rootOut.addContent(
612 new Element("include", MCRConstants.XSL_NAMESPACE).setAttribute("href", templateName + ".xsl"));
613 }
614
615
616 Element template = new Element("template", MCRConstants.XSL_NAMESPACE).setAttribute("name",
617 "chooseTemplate");
618 Element choose = new Element("choose", MCRConstants.XSL_NAMESPACE);
619
620 Element template2 = new Element("template", MCRConstants.XSL_NAMESPACE).setAttribute("name",
621 "get.templates");
622 Element templates = new Element("templates");
623
624 for (String templateName : temps) {
625
626 Element when = new Element("when", MCRConstants.XSL_NAMESPACE).setAttribute("test",
627 "$template = '" + templateName + "'");
628 when.addContent(
629 new Element("call-template", MCRConstants.XSL_NAMESPACE).setAttribute("name", templateName));
630 choose.addContent(when);
631
632
633 templates.addContent(new Element("template").setAttribute("category", "master").setText(templateName));
634 }
635
636
637 template.addContent(choose);
638 rootOut.addContent(template);
639
640 template2.addContent(templates);
641 rootOut.addContent(template2);
642 return jdom;
643 }
644
645 @Override
646 public Source resolve(String href, String base) throws TransformerException {
647 String type = href.substring(href.indexOf(":") + 1);
648 String path = "/templates/" + type + "/";
649 LOGGER.debug("Reading templates from {}", path);
650 Set<String> resourcePaths = context.getResourcePaths(path);
651 ArrayList<String> templates = new ArrayList<>();
652 if (resourcePaths != null) {
653 for (String resourcePath : resourcePaths) {
654 if (!resourcePath.endsWith("/")) {
655
656 continue;
657 }
658 String templateName = resourcePath.substring(path.length(), resourcePath.length() - 1);
659 LOGGER.debug("Checking if template: {}", templateName);
660 if (templateName.contains("/")) {
661 continue;
662 }
663 templates.add(templateName);
664 }
665 Collections.sort(templates);
666 }
667 LOGGER.info("Found theses templates: {}", templates);
668 return new JDOMSource(getStylesheets(templates));
669 }
670
671 }
672
673
674
675
676 private static class MCRResourceResolver implements URIResolver {
677
678 @Override
679 public Source resolve(String href, String base) throws TransformerException {
680 String path = href.substring(href.indexOf(":") + 1);
681 URL resource = MCRConfigurationDir.getConfigResource(path);
682 if (resource != null) {
683
684 if (path.endsWith(".xsl")) {
685 XMLReader reader;
686 try {
687 reader = MCRXMLParserFactory.getNonValidatingParser().getXMLReader();
688 } catch (SAXException | ParserConfigurationException e) {
689 throw new TransformerException(e);
690 }
691 reader.setEntityResolver(MCREntityResolver.instance());
692 InputSource input = new InputSource(resource.toString());
693 SAXSource saxSource = new SAXSource(reader, input);
694 LOGGER.debug("include stylesheet: {}", saxSource.getSystemId());
695 return saxSource;
696 }
697 return MCRURIResolver.instance().resolve(resource.toString(), base);
698 }
699 return null;
700 }
701 }
702
703
704
705
706
707 private static class MCRLocalClassResolver implements URIResolver {
708
709 @Override
710 public Source resolve(String href, String base) throws TransformerException {
711 String classname = href.substring(href.indexOf(":") + 1, href.indexOf("?"));
712 Class<? extends URIResolver> cl = null;
713 LogManager.getLogger(this.getClass()).debug("Loading Class: {}", classname);
714 URIResolver resolver;
715 try {
716 cl = MCRClassTools.forName(classname);
717 resolver = cl.getDeclaredConstructor().newInstance();
718 } catch (Exception e) {
719 throw new TransformerException(e);
720 }
721 return resolver.resolve(href, base);
722 }
723
724 }
725
726 private static class MCRSessionResolver implements URIResolver {
727
728
729
730
731
732
733
734
735
736
737 @Override
738 public Source resolve(String href, String base) throws TransformerException {
739 String key = href.substring(href.indexOf(":") + 1);
740 LOGGER.debug("Reading xml from session using key {}", key);
741 Element value = (Element) MCRSessionMgr.getCurrentSession().get(key);
742 return new JDOMSource(value.clone());
743 }
744
745 }
746
747 private static class MCRIFSResolver implements URIResolver {
748
749
750
751
752
753
754
755
756 @Override
757 public Source resolve(String href, String base) throws TransformerException {
758 LOGGER.debug("Reading xml from url {}", href);
759
760 String path = href.substring(href.indexOf(":") + 1);
761
762 int i = path.indexOf("?host");
763 if (i > 0) {
764 path = path.substring(0, i);
765 }
766 StringTokenizer st = new StringTokenizer(path, "/");
767
768 String ownerID = st.nextToken();
769 try {
770 String aPath = MCRXMLFunctions.decodeURIPath(path.substring(ownerID.length() + 1));
771
772 if (ownerID.endsWith(":")) {
773 ownerID = ownerID.substring(0, ownerID.length() - 1);
774 }
775 LOGGER.debug("Get {} path: {}", ownerID, aPath);
776 return new JDOMSource(MCRPathXML.getDirectoryXML(MCRPath.getPath(ownerID, aPath)));
777 } catch (IOException | URISyntaxException e) {
778 throw new TransformerException(e);
779 }
780 }
781 }
782
783 private static class MCRMCRFileResolver implements URIResolver {
784 @Override
785 public Source resolve(String href, String base) throws TransformerException {
786 LOGGER.debug("Reading xml from MCRFile {}", href);
787 MCRPath file = null;
788 String id = href.substring(href.indexOf(":") + 1);
789 if (id.contains("/")) {
790
791 try {
792 MCRObjectID derivateID = MCRObjectID.getInstance(id.substring(0, id.indexOf("/")));
793 String path = id.substring(id.indexOf("/"));
794 file = MCRPath.getPath(derivateID.toString(), path);
795 } catch (MCRException exc) {
796
797 }
798 }
799 if (file == null) {
800 throw new TransformerException("mcrfile: Resolver needs a path: " + href);
801 }
802 try {
803 return new MCRPathContent(file).getSource();
804 } catch (Exception e) {
805 throw new TransformerException(e);
806 }
807 }
808 }
809
810 private static class MCRACLResolver implements URIResolver {
811
812 private static final String ACTION_PARAM = "action";
813
814 private static final String OBJECT_ID_PARAM = "object";
815
816
817
818
819 @Override
820 public Source resolve(String href, String base) throws TransformerException {
821 String key = href.substring(href.indexOf(":") + 1);
822 LOGGER.debug("Reading xml from query result using key :{}", key);
823
824 String[] param;
825 StringTokenizer tok = new StringTokenizer(key, "&");
826 Hashtable<String, String> params = new Hashtable<>();
827
828 while (tok.hasMoreTokens()) {
829 param = tok.nextToken().split("=");
830 params.put(param[0], param[1]);
831 }
832
833 String action = params.get(ACTION_PARAM);
834 String objId = params.get(OBJECT_ID_PARAM);
835
836 if (action == null || objId == null) {
837 return null;
838 }
839
840 Element container = new Element("servacls").setAttribute("class", "MCRMetaAccessRule");
841
842 if (action.equals("all")) {
843 for (String permission : MCRAccessManager.getPermissionsForID(objId)) {
844
845
846
847 addRule(container, permission, MCRAccessManager.requireRulesInterface().getRule(objId, permission));
848 }
849 } else {
850 addRule(container, action, MCRAccessManager.requireRulesInterface().getRule(objId, action));
851 }
852
853 return new JDOMSource(container);
854 }
855
856 private void addRule(Element root, String pool, Element rule) {
857 if (rule != null && pool != null) {
858 Element poolElement = new Element("servacl").setAttribute("permission", pool);
859 poolElement.addContent(rule);
860 root.addContent(poolElement);
861 }
862 }
863
864 }
865
866 private static class MCRClassificationResolver implements URIResolver {
867
868 private static final Pattern EDITORFORMAT_PATTERN = Pattern.compile("(\\[)([^\\]]*)(\\])");
869
870 private static final String FORMAT_CONFIG_PREFIX = CONFIG_PREFIX + "Classification.Format.";
871
872 private static final String SORT_CONFIG_PREFIX = CONFIG_PREFIX + "Classification.Sort.";
873
874 private static MCRCache<String, Element> categoryCache;
875
876 private static MCRCategoryDAO DAO;
877
878 static {
879 try {
880 DAO = MCRCategoryDAOFactory.getInstance();
881 categoryCache = new MCRCache<>(
882 MCRConfiguration2.getInt(CONFIG_PREFIX + "Classification.CacheSize").orElse(1000),
883 "URIResolver categories");
884 } catch (Exception exc) {
885 LOGGER.error("Unable to initialize classification resolver", exc);
886 }
887 }
888
889 MCRClassificationResolver() {
890 }
891
892 private static String getLabelFormat(String editorString) {
893 Matcher m = EDITORFORMAT_PATTERN.matcher(editorString);
894 if (m.find() && m.groupCount() == 3) {
895 String formatDef = m.group(2);
896 return MCRConfiguration2.getStringOrThrow(FORMAT_CONFIG_PREFIX + formatDef);
897 }
898 return null;
899 }
900
901 private static boolean shouldSortCategories(String classId) {
902 return MCRConfiguration2.getBoolean(SORT_CONFIG_PREFIX + classId).orElse(true);
903 }
904
905 private static long getSystemLastModified() {
906 long xmlLastModified = MCRXMLMetadataManager.instance().getLastModified();
907 long classLastModified = DAO.getLastModified();
908 return Math.max(xmlLastModified, classLastModified);
909 }
910
911
912
913
914
915
916
917
918
919
920
921
922 @Override
923 public Source resolve(String href, String base) throws TransformerException {
924 LOGGER.debug("start resolving {}", href);
925 String cacheKey = getCacheKey(href);
926 Element returns = categoryCache.getIfUpToDate(cacheKey, getSystemLastModified());
927 if (returns == null) {
928 returns = getClassElement(href);
929 if (returns != null) {
930 categoryCache.put(cacheKey, returns);
931 }
932 }
933 return new JDOMSource(returns);
934 }
935
936 protected String getCacheKey(String uri) {
937 return uri;
938 }
939
940 private Element getClassElement(String uri) {
941 StringTokenizer pst = new StringTokenizer(uri, ":", true);
942 if (pst.countTokens() < 9) {
943
944 throw new IllegalArgumentException("Invalid format of uri for retrieval of classification: " + uri);
945 }
946
947 pst.nextToken();
948 pst.nextToken();
949 String format = pst.nextToken();
950 pst.nextToken();
951
952 String levelS = pst.nextToken();
953 pst.nextToken();
954 int levels = "all".equals(levelS) ? -1 : Integer.parseInt(levelS);
955
956 String axis;
957 String token = pst.nextToken();
958 pst.nextToken();
959 boolean emptyLeaves = !"noEmptyLeaves".equals(token);
960 if (!emptyLeaves) {
961 axis = pst.nextToken();
962 pst.nextToken();
963 } else {
964 axis = token;
965 }
966
967 String classID = pst.nextToken();
968 StringBuilder categID = new StringBuilder();
969 if (pst.hasMoreTokens()) {
970 pst.nextToken();
971 while (pst.hasMoreTokens()) {
972 categID.append(pst.nextToken());
973 }
974 }
975
976 String categ;
977 try {
978 categ = MCRXMLFunctions.decodeURIPath(categID.toString());
979 } catch (URISyntaxException e) {
980 categ = categID.toString();
981 }
982 MCRCategory cl = null;
983 LOGGER.debug("categoryCache entry invalid or not found: start MCRClassificationQuery");
984 if (axis.equals("children")) {
985 if (categ.length() > 0) {
986 cl = DAO.getCategory(new MCRCategoryID(classID, categ), levels);
987 } else {
988 cl = DAO.getCategory(MCRCategoryID.rootID(classID), levels);
989 }
990 } else if (axis.equals("parents")) {
991 if (categ.length() == 0) {
992 LOGGER.error("Cannot resolve parent axis without a CategID. URI: {}", uri);
993 throw new IllegalArgumentException(
994 "Invalid format (categID is required in mode 'parents') "
995 + "of uri for retrieval of classification: "
996 + uri);
997 }
998 cl = DAO.getRootCategory(new MCRCategoryID(classID, categ), levels);
999 }
1000 if (cl == null) {
1001 return null;
1002 }
1003
1004 Element returns;
1005 LOGGER.debug("start transformation of ClassificationQuery");
1006 if (format.startsWith("editor")) {
1007 boolean completeId = format.startsWith("editorComplete");
1008 boolean sort = shouldSortCategories(classID);
1009 String labelFormat = getLabelFormat(format);
1010 if (labelFormat == null) {
1011 returns = MCRCategoryTransformer.getEditorItems(cl, sort, emptyLeaves, completeId);
1012 } else {
1013 returns = MCRCategoryTransformer.getEditorItems(cl, labelFormat, sort, emptyLeaves, completeId);
1014 }
1015 } else if (format.equals("metadata")) {
1016 returns = MCRCategoryTransformer.getMetaDataDocument(cl, false).getRootElement().detach();
1017 } else {
1018 LOGGER.error("Unknown target format given. URI: {}", uri);
1019 throw new IllegalArgumentException(
1020 "Invalid target format (" + format + ") in uri for retrieval of classification: " + uri);
1021 }
1022 LOGGER.debug("end resolving {}", uri);
1023 return returns;
1024 }
1025
1026 }
1027
1028 private static class MCRExceptionAsXMLResolver implements URIResolver {
1029
1030 @Override
1031 public Source resolve(String href, String base) throws TransformerException {
1032 String target = href.substring(href.indexOf(":") + 1);
1033
1034 try {
1035 return MCRURIResolver.instance().resolve(target, base);
1036 } catch (Exception ex) {
1037 LOGGER.debug("Caught {}. Put it into XML to process in XSL!", ex.getClass().getName());
1038 Element exception = new Element("exception");
1039 Element message = new Element("message");
1040 Element stacktraceElement = new Element("stacktrace");
1041
1042 stacktraceElement.setAttribute("space", "preserve", Namespace.XML_NAMESPACE);
1043
1044 exception.addContent(message);
1045 exception.addContent(stacktraceElement);
1046
1047 message.setText(ex.getMessage());
1048
1049 try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
1050 ex.printStackTrace(pw);
1051 stacktraceElement.setText(pw.toString());
1052 } catch (IOException e) {
1053 throw new MCRException("Error while writing Exception to String!", e);
1054 }
1055
1056 return new JDOMSource(exception);
1057 }
1058 }
1059 }
1060
1061
1062
1063
1064
1065 private static class MCRNotNullResolver implements URIResolver {
1066
1067 @Override
1068 public Source resolve(String href, String base) throws TransformerException {
1069 String target = href.substring(href.indexOf(":") + 1);
1070
1071 String subUri = target.substring(target.indexOf(":") + 1);
1072 if (subUri.length() == 0) {
1073 return new JDOMSource(new Element("null"));
1074 }
1075
1076 LOGGER.debug("Ensuring xml is not null: {}", target);
1077 try {
1078 Source result = MCRURIResolver.instance().resolve(target, base);
1079 if (result != null) {
1080
1081
1082
1083 MCRContent content = new MCRSourceContent(result).getBaseContent();
1084 Document document = MCRXMLParserFactory.getParser(false, true).parseXML(content);
1085 return new JDOMSource(document.getRootElement().detach());
1086 } else {
1087 LOGGER.debug("MCRNotNullResolver returning empty xml");
1088 return new JDOMSource(new Element("null"));
1089 }
1090 } catch (Exception ex) {
1091 LOGGER.info("MCRNotNullResolver caught exception: {}", ex.getLocalizedMessage());
1092 LOGGER.debug(ex.getStackTrace());
1093 LOGGER.debug("MCRNotNullResolver returning empty xml");
1094 return new JDOMSource(new Element("null"));
1095 }
1096 }
1097 }
1098
1099
1100
1101
1102
1103 private static class MCRXslStyleResolver implements URIResolver {
1104
1105 @Override
1106 public Source resolve(String href, String base) throws TransformerException {
1107 String help = href.substring(href.indexOf(":") + 1);
1108 String stylesheets = new StringTokenizer(help, ":").nextToken();
1109 String target = help.substring(help.indexOf(":") + 1);
1110
1111 String subUri = target.substring(target.indexOf(":") + 1);
1112 if (subUri.length() == 0) {
1113 return new JDOMSource(new Element("null"));
1114 }
1115
1116 Map<String, String> params;
1117 StringTokenizer tok = new StringTokenizer(stylesheets, "?");
1118 stylesheets = tok.nextToken();
1119
1120 if (tok.hasMoreTokens()) {
1121 params = getParameterMap(tok.nextToken());
1122 } else {
1123 params = Collections.emptyMap();
1124 }
1125 Source resolved = MCRURIResolver.instance().resolve(target, base);
1126
1127 try {
1128 if (resolved != null) {
1129 if (resolved.getSystemId() == null) {
1130 resolved.setSystemId(target);
1131 }
1132 MCRSourceContent content = new MCRSourceContent(resolved);
1133 MCRXSLTransformer transformer = getTransformer(stylesheets.split(","));
1134 MCRParameterCollector paramcollector = MCRParameterCollector.getInstanceFromUserSession();
1135 paramcollector.setParameters(params);
1136 MCRContent result = transformer.transform(content, paramcollector);
1137 return result.getSource();
1138 } else {
1139 LOGGER.debug("MCRXslStyleResolver returning empty xml");
1140 return new JDOMSource(new Element("null"));
1141 }
1142 } catch (IOException e) {
1143 Throwable cause = e.getCause();
1144 while (cause != null) {
1145 if (cause instanceof TransformerException) {
1146 throw (TransformerException) cause;
1147 }
1148 cause = cause.getCause();
1149 }
1150 throw new TransformerException(e);
1151 }
1152 }
1153
1154 private MCRXSLTransformer getTransformer(String... stylesheet) {
1155 String[] stylesheets = new String[stylesheet.length];
1156 for (int i = 0; i < stylesheets.length; i++) {
1157 stylesheets[i] = "xsl/" + stylesheet[i] + ".xsl";
1158 }
1159 return MCRXSLTransformer.getInstance(stylesheets);
1160 }
1161 }
1162
1163
1164
1165
1166
1167 private static class MCRLayoutTransformerResolver implements URIResolver {
1168
1169 private static final String TRANSFORMER_FACTORY_PROPERTY = "MCR.Layout.Transformer.Factory";
1170
1171 @Override
1172 public Source resolve(String href, String base) throws TransformerException {
1173 String help = href.substring(href.indexOf(":") + 1);
1174 String transformerId = new StringTokenizer(help, ":").nextToken();
1175 String target = help.substring(help.indexOf(":") + 1);
1176
1177 String subUri = target.substring(target.indexOf(":") + 1);
1178 if (subUri.length() == 0) {
1179 return new JDOMSource(new Element("null"));
1180 }
1181
1182 Map<String, String> params;
1183 StringTokenizer tok = new StringTokenizer(transformerId, "?");
1184 transformerId = tok.nextToken();
1185
1186 if (tok.hasMoreTokens()) {
1187 params = getParameterMap(tok.nextToken());
1188 } else {
1189 params = Collections.emptyMap();
1190 }
1191 Source resolved = MCRURIResolver.instance().resolve(target, base);
1192
1193 try {
1194 if (resolved != null) {
1195 MCRSourceContent content = new MCRSourceContent(resolved);
1196 MCRLayoutTransformerFactory factory = MCRConfiguration2
1197 .<MCRLayoutTransformerFactory>getInstanceOf(TRANSFORMER_FACTORY_PROPERTY)
1198 .orElseGet(MCRLayoutTransformerFactory::new);
1199 MCRContentTransformer transformer = factory.getTransformer(transformerId);
1200 MCRContent result;
1201 if (transformer instanceof MCRParameterizedTransformer) {
1202 MCRParameterCollector paramcollector = MCRParameterCollector.getInstanceFromUserSession();
1203 paramcollector.setParameters(params);
1204 result = ((MCRParameterizedTransformer) transformer).transform(content, paramcollector);
1205 } else {
1206 result = transformer.transform(content);
1207 }
1208 return result.getSource();
1209 } else {
1210 LOGGER.debug("MCRLayoutStyleResolver returning empty xml");
1211 return new JDOMSource(new Element("null"));
1212 }
1213 } catch (Exception e) {
1214 if (e instanceof TransformerException) {
1215 throw (TransformerException) e;
1216 }
1217 Throwable cause = e.getCause();
1218 while (cause != null) {
1219 if (cause instanceof TransformerException) {
1220 throw (TransformerException) cause;
1221 }
1222 cause = cause.getCause();
1223 }
1224 throw new TransformerException(e);
1225 }
1226 }
1227
1228 }
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244 private static class MCRXslIncludeResolver implements URIResolver {
1245 private static Logger LOGGER = LogManager.getLogger(MCRXslIncludeResolver.class);
1246
1247 @Override
1248 public Source resolve(String href, String base) throws TransformerException {
1249 String includePart = href.substring(href.indexOf(":") + 1);
1250 Namespace xslNamespace = Namespace.getNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
1251
1252 Element root = new Element("stylesheet", xslNamespace);
1253 root.setAttribute("version", "1.0");
1254
1255
1256 String propertyName = "MCR.URIResolver.xslIncludes." + includePart;
1257 List<String> propValue;
1258 if (includePart.startsWith("class.")) {
1259 MCRXslIncludeHrefs incHrefClass = MCRConfiguration2
1260 .getOrThrow(propertyName, MCRConfiguration2::instantiateClass);
1261 propValue = incHrefClass.getHrefs();
1262 } else {
1263 propValue = MCRConfiguration2.getString(propertyName)
1264 .map(MCRConfiguration2::splitValue)
1265 .map(s -> s.collect(Collectors.toList()))
1266 .orElseGet(Collections::emptyList);
1267 }
1268
1269 for (String include : propValue) {
1270
1271 Element includeElement = new Element("include", xslNamespace);
1272 includeElement.setAttribute("href", include);
1273 root.addContent(includeElement);
1274 LOGGER.debug("Resolved XSL include: {}", include);
1275 }
1276 return new JDOMSource(root);
1277 }
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291 private static class MCRXslImportResolver implements URIResolver {
1292
1293 URIResolver fallback = new MCRResourceResolver();
1294
1295 @Override
1296 public Source resolve(String href, String base) throws TransformerException {
1297 String importXSL = MCRXMLFunctions.nextImportStep(href.substring(href.indexOf(':') + 1));
1298 if (importXSL.isEmpty()) {
1299 LOGGER.debug("End of import queue: {}", href);
1300 Namespace xslNamespace = Namespace.getNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
1301 Element root = new Element("stylesheet", xslNamespace);
1302 root.setAttribute("version", "1.0");
1303 return new JDOMSource(root);
1304 }
1305 LOGGER.debug("xslImport importing {}", importXSL);
1306 return fallback.resolve("resource:xsl/" + importXSL, base);
1307 }
1308 }
1309
1310
1311
1312
1313
1314
1315
1316 private static class MCRBuildXMLResolver implements URIResolver {
1317
1318 private static Hashtable<String, String> getParameterMap(String key) {
1319 String[] param;
1320 StringTokenizer tok = new StringTokenizer(key, "&");
1321 Hashtable<String, String> params = new Hashtable<>();
1322
1323 while (tok.hasMoreTokens()) {
1324 param = tok.nextToken().split("=");
1325 params.put(URLDecoder.decode(param[0], StandardCharsets.UTF_8),
1326 URLDecoder.decode(param[1], StandardCharsets.UTF_8));
1327 }
1328 return params;
1329 }
1330
1331 private static void constructElement(Element current, String xpath, String value) {
1332 StringTokenizer st = new StringTokenizer(xpath, "/");
1333 String name = null;
1334 while (st.hasMoreTokens()) {
1335 name = st.nextToken();
1336 if (name.startsWith("@")) {
1337 break;
1338 }
1339
1340 String localName = getLocalName(name);
1341 Namespace namespace = getNamespace(name);
1342
1343 Element child = current.getChild(localName, namespace);
1344 if (child == null) {
1345 child = new Element(localName, namespace);
1346 current.addContent(child);
1347 }
1348 current = child;
1349 }
1350
1351 if (name.startsWith("@")) {
1352 name = name.substring(1);
1353 String localName = getLocalName(name);
1354 Namespace namespace = getNamespace(name);
1355 current.setAttribute(localName, value, namespace);
1356 } else {
1357 current.setText(value);
1358 }
1359 }
1360
1361 private static Namespace getNamespace(String name) {
1362 if (!name.contains(":")) {
1363 return Namespace.NO_NAMESPACE;
1364 }
1365 String prefix = name.split(":")[0];
1366 Namespace ns = MCRConstants.getStandardNamespace(prefix);
1367 return ns == null ? Namespace.NO_NAMESPACE : ns;
1368 }
1369
1370 private static String getLocalName(String name) {
1371 if (!name.contains(":")) {
1372 return name;
1373 } else {
1374 return name.split(":")[1];
1375 }
1376 }
1377
1378
1379
1380
1381 @Override
1382 public Source resolve(String href, String base) throws TransformerException {
1383 String key = href.substring(href.indexOf(":") + 1);
1384 LOGGER.debug("Building xml from {}", key);
1385
1386 Hashtable<String, String> params = getParameterMap(key);
1387
1388 Element defaultRoot = new Element("root");
1389 Element root = defaultRoot;
1390 String rootName = params.get("_rootName_");
1391 if (rootName != null) {
1392 root = new Element(getLocalName(rootName), getNamespace(rootName));
1393 params.remove("_rootName_");
1394 }
1395
1396 for (Map.Entry<String, String> entry : params.entrySet()) {
1397 constructElement(root, entry.getKey(), entry.getValue());
1398 }
1399 if (root == defaultRoot && root.getChildren().size() > 1) {
1400 LOGGER.warn("More than 1 root node defined, returning first");
1401 return new JDOMSource(root.getChildren().get(0).detach());
1402 }
1403 return new JDOMSource(root);
1404 }
1405
1406 }
1407
1408 private static class MCRVersionInfoResolver implements URIResolver {
1409
1410 @Override
1411 public Source resolve(String href, String base) throws TransformerException {
1412 String id = href.substring(href.indexOf(":") + 1);
1413 LOGGER.debug("Reading version info of MCRObject with ID {}", id);
1414 MCRObjectID mcrId = MCRObjectID.getInstance(id);
1415 MCRXMLMetadataManager metadataManager = MCRXMLMetadataManager.instance();
1416 try {
1417 List<? extends MCRAbstractMetadataVersion<?>> versions = metadataManager.listRevisions(mcrId);
1418 if (versions != null && !versions.isEmpty()) {
1419 return getSource(versions);
1420 } else {
1421 return getSource(Instant.ofEpochMilli(metadataManager.getLastModified(mcrId))
1422 .truncatedTo(ChronoUnit.MILLIS));
1423 }
1424 } catch (Exception e) {
1425 throw new TransformerException(e);
1426 }
1427 }
1428
1429 private Source getSource(Instant lastModified) throws IOException {
1430 Element e = new Element("versions");
1431 Element v = new Element("version");
1432 e.addContent(v);
1433 v.setAttribute("date", lastModified.toString());
1434 return new JDOMSource(e);
1435 }
1436
1437 private Source getSource(List<? extends MCRAbstractMetadataVersion<?>> versions) {
1438 Element e = new Element("versions");
1439 for (MCRAbstractMetadataVersion<?> version : versions) {
1440 Element v = new Element("version");
1441 v.setAttribute("user", version.getUser());
1442 v.setAttribute("date", MCRXMLFunctions.getISODate(version.getDate(), null));
1443 v.setAttribute("r", version.getRevision());
1444 v.setAttribute("action", Character.toString(version.getType()));
1445 e.addContent(v);
1446 }
1447 return new JDOMSource(e);
1448 }
1449 }
1450
1451 private static class MCRDeletedObjectResolver implements URIResolver {
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462 @Override
1463 public Source resolve(String href, String base) throws TransformerException {
1464 String[] parts = href.split(":");
1465 MCRObjectID mcrId = MCRObjectID.getInstance(parts[parts.length - 1]);
1466 LOGGER.info("Resolving deleted object {}", mcrId);
1467 try {
1468 MCRContent lastPresentVersion = MCRXMLMetadataManager.instance().retrieveContent(mcrId);
1469 if (lastPresentVersion == null) {
1470 LOGGER.warn("Could not resolve deleted object {}", mcrId);
1471 return new JDOMSource(MCRObjectFactory.getSampleObject(mcrId));
1472 }
1473 return lastPresentVersion.getSource();
1474 } catch (IOException e) {
1475 throw new TransformerException(e);
1476 }
1477 }
1478 }
1479
1480 private static class MCRFileMetadataResolver implements URIResolver {
1481
1482 @Override
1483 public Source resolve(String href, String base) throws TransformerException {
1484 String[] parts = href.split(":");
1485 String completePath = parts[1];
1486 String[] pathParts = completePath.split("/", 2);
1487 MCRObjectID derivateID = MCRObjectID.getInstance(pathParts[0]);
1488 MCRDerivate derivate = MCRMetadataManager.retrieveMCRDerivate(derivateID);
1489 MCRObjectDerivate objectDerivate = derivate.getDerivate();
1490 if (pathParts.length == 1) {
1491
1492 Element fileset = new Element("fileset");
1493 if (objectDerivate.getURN() != null) {
1494 fileset.setAttribute("urn", objectDerivate.getURN());
1495 for (MCRFileMetadata fileMeta : objectDerivate.getFileMetadata()) {
1496 fileset.addContent(fileMeta.createXML());
1497 }
1498 }
1499 return new JDOMSource(fileset);
1500 }
1501 MCRFileMetadata fileMetadata = objectDerivate.getOrCreateFileMetadata("/" + pathParts[1]);
1502 return new JDOMSource(fileMetadata.createXML());
1503 }
1504 }
1505
1506
1507
1508
1509
1510 private static class MCRRedirectResolver implements URIResolver {
1511 private static final Logger LOGGER = LogManager.getLogger(MCRRedirectResolver.class);
1512
1513 @Override
1514 public Source resolve(String href, String base) throws TransformerException {
1515 String configsuffix = href.substring(href.indexOf(":") + 1);
1516
1517
1518 String propertyName = "MCR.URIResolver.redirect." + configsuffix;
1519 String propValue = MCRConfiguration2.getStringOrThrow(propertyName);
1520 LOGGER.info("Redirect {} to {}", href, propValue);
1521 return singleton.resolve(propValue, base);
1522 }
1523 }
1524
1525
1526
1527
1528
1529
1530 private static class MCRDataURLResolver implements URIResolver {
1531
1532 @Override
1533 public Source resolve(String href, String base) throws TransformerException {
1534 try {
1535 final MCRDataURL dataURL = MCRDataURL.parse(href);
1536
1537 final MCRByteContent content = new MCRByteContent(dataURL.getData());
1538 content.setSystemId(href);
1539 content.setMimeType(dataURL.getMimeType());
1540 content.setEncoding(dataURL.getCharset().name());
1541
1542 return content.getSource();
1543 } catch (IOException e) {
1544 throw new TransformerException(e);
1545 }
1546 }
1547
1548 }
1549
1550 private static class MCRI18NResolver implements URIResolver {
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583 @Override
1584 public Source resolve(String href, String base) {
1585 String target = href.substring(href.indexOf(":") + 1);
1586
1587 final Element i18nElement = new Element("i18n");
1588 if (!target.contains("*") && !target.contains(",")) {
1589 String translation;
1590 if (target.contains(":")) {
1591 final int i = target.indexOf(":");
1592 translation = MCRTranslation.translate(target.substring(0, i),
1593 (Object[]) target.substring(i + 1).split(":"));
1594 } else {
1595 translation = MCRTranslation.translate(target);
1596 }
1597 i18nElement.addContent(translation);
1598 return new JDOMSource(i18nElement);
1599 }
1600
1601 final String[] translationKeys = target.split(",");
1602
1603
1604 HashMap<String, String> translations = new HashMap<>();
1605 for (String translationKey : translationKeys) {
1606 if (translationKey.endsWith("*")) {
1607 final String prefix = translationKey.substring(0, translationKey.length() - 1);
1608 translations.putAll(MCRTranslation.translatePrefix(prefix));
1609 } else {
1610 translations.put(translationKey,
1611 MCRTranslation.translate(translationKey));
1612 }
1613 }
1614
1615 translations.forEach((key, value) -> {
1616 final Element translation = new Element("translation");
1617 translation.setAttribute("key", key);
1618 translation.setText(value);
1619 i18nElement.addContent(translation);
1620 });
1621
1622 return new JDOMSource(i18nElement);
1623 }
1624 }
1625
1626 private static class MCRCheckPermissionChainResolver implements URIResolver {
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640 @Override
1641 public Source resolve(String href, String base) throws TransformerException {
1642 final String[] split = href.split(":", 4);
1643
1644 if (split.length != 4) {
1645 throw new MCRException(
1646 "Syntax needs to be checkPermissionChain:{?id}:{permission}:{uri} but was " + href);
1647 }
1648
1649 final String permission = split[2];
1650
1651 final String uri = split[3];
1652 final boolean hasAccess;
1653
1654 if (!split[1].isBlank()) {
1655 hasAccess = MCRAccessManager.checkPermission(split[1], permission);
1656 } else {
1657 hasAccess = MCRAccessManager.checkPermission(permission);
1658 }
1659
1660 if (!hasAccess) {
1661 throw new TransformerException("No Access to " + uri + " (" + href + " )");
1662 }
1663
1664 return MCRURIResolver.instance().resolve(uri, base);
1665 }
1666 }
1667
1668 private static class MCRCheckPermissionResolver implements URIResolver {
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682 @Override
1683 public Source resolve(String href, String base) throws TransformerException {
1684 final String[] split = href.split(":");
1685 boolean permission;
1686 switch (split.length) {
1687 case 2:
1688 permission = MCRAccessManager.checkPermission(split[1]);
1689 break;
1690 case 3:
1691 permission = MCRAccessManager.checkPermission(split[1], split[2]);
1692 break;
1693 default:
1694 throw new IllegalArgumentException(
1695 "Invalid format of uri for retrieval of checkPermission: " + href);
1696 }
1697 Element root = new Element("boolean");
1698 root.setText(Boolean.toString(permission));
1699 return new JDOMSource(root);
1700 }
1701 }
1702
1703
1704
1705
1706 private static class MCRCachingResolver implements URIResolver {
1707
1708 private final static Logger LOGGER = LogManager.getLogger();
1709
1710 private final static String CONFIG_PREFIX = "MCR.URIResolver.CachingResolver";
1711
1712 private long maxAge;
1713
1714 private MCRCache<String, Element> cache;
1715
1716 MCRCachingResolver() {
1717 int capacity = MCRConfiguration2.getOrThrow(CONFIG_PREFIX + ".Capacity", Integer::parseInt);
1718 maxAge = MCRConfiguration2.getOrThrow(CONFIG_PREFIX + ".MaxAge", Long::parseLong);
1719 cache = new MCRCache<>(capacity, MCRCachingResolver.class.getName());
1720 }
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732 @Override
1733 public Source resolve(String href, String base) throws TransformerException {
1734 String hrefToCache = href.substring(href.indexOf(":") + 1);
1735 LOGGER.debug("resolving: " + hrefToCache);
1736
1737 long maxDateCached = System.currentTimeMillis() - maxAge;
1738 Element resolvedXML = cache.getIfUpToDate(hrefToCache, maxDateCached);
1739
1740 if (resolvedXML == null) {
1741 LOGGER.debug(hrefToCache + " not in cache, must resolve");
1742 resolvedXML = MCRURIResolver.instance().resolve(hrefToCache);
1743 cache.put(hrefToCache, resolvedXML);
1744 } else {
1745 LOGGER.debug(hrefToCache + " already in cache");
1746 }
1747
1748 return new JDOMSource(resolvedXML);
1749 }
1750 }
1751
1752 }