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