From 6dba75bee6954a0185515b6da63b592a511b3169 Mon Sep 17 00:00:00 2001 From: Thorsten Vitt <thorsten.vitt@uni-wuerzburg.de> Date: Sun, 19 Jan 2014 18:25:37 +0100 Subject: [PATCH] Adjusted StylesheetManager to Cache API. --- .../aggregator/StylesheetManager.java | 90 ++++++++++++++----- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/src/main/java/info/textgrid/services/aggregator/StylesheetManager.java b/src/main/java/info/textgrid/services/aggregator/StylesheetManager.java index 3e369d7..cce4c65 100644 --- a/src/main/java/info/textgrid/services/aggregator/StylesheetManager.java +++ b/src/main/java/info/textgrid/services/aggregator/StylesheetManager.java @@ -11,6 +11,8 @@ import java.net.URL; import java.text.MessageFormat; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import java.util.logging.Logger; @@ -23,6 +25,7 @@ import net.sf.saxon.s9api.XsltExecutable; import com.google.common.base.Optional; +import com.google.common.base.Throwables; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -35,7 +38,8 @@ /** * Manages and caches XSLT stylesheets across service calls, also maintains * processor policies etc. All stylesheets invoked throughout the service should - * be invoked through the manager's {@link #getStylesheet(URI, Optional, boolean, boolean)} method. + * be invoked through the manager's + * {@link #getStylesheet(URI, Optional, boolean, boolean)} method. */ public class StylesheetManager { private static final Logger logger = Logger @@ -132,25 +136,50 @@ public XsltExecutable load(final URI url) throws Exception { public XsltExecutable getStylesheet(final URI uri, final Optional<String> sid, final boolean forceLoad, final boolean frequentlyUsed) throws SaxonApiException, IOException { - XsltExecutable executable = null; - // (1) try cached version, if it exists - if (!forceLoad) { - executable = stylesheets.getIfPresent(uri); + if (frequentlyUsed) + importantStylesheets.add(uri); + + try { + if (forceLoad) + stylesheets.invalidate(uri); + + return stylesheets.get(uri, new StylesheetLoader(uri, sid)); + } catch (final ExecutionException e) { + final Throwable cause = e.getCause(); + if (cause instanceof PrivateResourceException) + return ((PrivateResourceException) cause).getXsltExecutable(); + else { + Throwables.propagateIfPossible(cause, SaxonApiException.class, + IOException.class); + return null; // will never be reached + } + } - if (executable == null) { - if (frequentlyUsed) - importantStylesheets.add(uri); + } - final XsltCompiler compiler = xsltProcessor.newXsltCompiler(); + private final class StylesheetLoader implements Callable<XsltExecutable> { + private final URI uri; + private final Optional<String> sid; - // (2) it's internal, load & cache it from the servlet + private StylesheetLoader(final URI uri, final Optional<String> sid) { + this.uri = uri; + this.sid = sid; + } + + @Override + public XsltExecutable call() throws Exception { + final XsltCompiler compiler = xsltProcessor.newXsltCompiler(); + final XsltExecutable executable; if (uri.getScheme() == null || uri.getScheme() == "file") { + // (1) Internal final URL resource = resolveInternalPath(uri.getPath()); - executable = compiler.compile(new StreamSource(resource.openStream(), resource.toExternalForm())); - stylesheets.put(uri, executable); - logger.log(Level.INFO, "Cached internal stylesheet {0}", resource); + executable = compiler.compile(new StreamSource(resource + .openStream(), resource.toExternalForm())); + logger.log(Level.INFO, "Cached internal stylesheet {0}", + resource); + return executable; } else if (TGUriResolver.isResolveable(uri)) { // (3/4) it's a TextGrid object, load it from TG-crud. @@ -161,26 +190,39 @@ public XsltExecutable getStylesheet(final URI uri, if (isPublic(xsltSupplier.getMetadata())) { // (3) it's public -> we can cache it. - stylesheets.put(uri, executable); logger.log(Level.INFO, "Cached public stylesheet {0}", uri); + return executable; } else { - // (4) it's private -> no caching logger.log(Level.INFO, "Loaded private stylesheet {0}", uri); + throw new PrivateResourceException(executable); } } else { // (2) it's non-TextGrid -- load & cache it. executable = compiler.compile(new StreamSource(uri.toString())); - stylesheets.put(uri, executable); logger.log(Level.INFO, "Cached external stylesheet {0}", uri); + return executable; } - } else { - logger.log(Level.INFO, "Reusing cached stylesheed {0}", uri); } + } + + private static class PrivateResourceException extends Exception { - return executable; + private static final long serialVersionUID = -3506322226718139009L; + private XsltExecutable xsltExecutable; + + public XsltExecutable getXsltExecutable() { + return xsltExecutable; + } + + public PrivateResourceException(final XsltExecutable xsltExecutable) { + super("The source object is private and will not be cached."); + this.xsltExecutable = xsltExecutable; + } } - private URL resolveInternalPath(final String path) throws MalformedURLException { + + private URL resolveInternalPath(final String path) + throws MalformedURLException { URL stylesheet; if (servlet == null) { logger.info("No servlet context, trying fallback property"); @@ -192,7 +234,8 @@ private URL resolveInternalPath(final String path) throws MalformedURLException } else { stylesheet = servlet.getResource(path); } - logger.fine("Resolved internal stylesheet: " + stylesheet.toExternalForm()); + logger.fine("Resolved internal stylesheet: " + + stylesheet.toExternalForm()); return stylesheet; } @@ -208,7 +251,9 @@ private static boolean isPublic(final ObjectType metadata) { @Override protected void finalize() throws Throwable { if (stylesheets != null) - logger.log(Level.INFO, "Shutting down stylesheet manager. Stats: {0}", stylesheets.stats()); + logger.log(Level.INFO, + "Shutting down stylesheet manager. Stats: {0}", + stylesheets.stats()); super.finalize(); } @@ -216,5 +261,4 @@ public CacheStats stats() { return stylesheets.stats(); } - } \ No newline at end of file -- GitLab