changeset 972:0c8aca463bd4

Added caching support for the static part of the datacage. flys-artifacts/trunk@2398 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 22 Jul 2011 16:55:36 +0000
parents d0c9a5f32c30
children c30ada285d45
files flys-artifacts/ChangeLog flys-artifacts/doc/conf/cache.xml flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java
diffstat 7 files changed, 165 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/ChangeLog	Fri Jul 22 16:55:36 2011 +0000
@@ -1,3 +1,24 @@
+2011-07-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/cache.xml: Added configuration for static datacage db access.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/App.java: Using
+	  caches seems to need an explicit System.exit().
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java:
+	  Added support for caching the SQL statements and there results.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java: Some
+	  clean up. Reordered code for performance. Strip SQL statements more
+	  aggressively.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java:
+	  Made it Serializable.
+
+	* src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java:
+	  Introduced system property 'flys.artifacts.cache.config.file' to make
+	  the caching configurable without pulling up the whole stack.
+
 2011-07-22  Ingo Weinzierl <ingo@intevation.de>
 
 	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
--- a/flys-artifacts/doc/conf/cache.xml	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/doc/conf/cache.xml	Fri Jul 22 16:55:36 2011 +0000
@@ -46,4 +46,12 @@
            diskPersistent="true"
            memoryStoreEvictionPolicy="LRU"
            />
+
+    <!-- This one is used for the SQL statements used by the static datacage -->
+    <cache name="datacage.db"
+           maxElementsInMemory="2000"
+           eternal="false"
+           timeToLiveSeconds="7200"
+           memoryStoreEvictionPolicy="LFU"
+           />
 </ehcache>
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java	Fri Jul 22 16:55:36 2011 +0000
@@ -12,6 +12,9 @@
 {
     private static Logger log = Logger.getLogger(CacheFactory.class);
 
+    public static final String CACHE_CONFIG_FILE_PROPERTY =
+        "flys.artifacts.cache.config.file";
+
     public static final String XPATH_CACHE_CONFIG_FILE =
         "/artifact-database/cache/config-file/text()";
 
@@ -26,12 +29,27 @@
         return getCache(Cache.DEFAULT_CACHE_NAME);
     }
 
+    public static final String getConfigFile() {
+        String configFile = System.getProperty(CACHE_CONFIG_FILE_PROPERTY);
+
+        if (configFile != null) {
+            return configFile;
+        }
+
+        configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
+
+        if (configFile != null) {
+            configFile = Config.replaceConfigDir(configFile);
+        }
+
+        return configFile;
+    }
+
     public static final synchronized Cache getCache(String cacheName) {
         if (!initialized) {
             initialized = true; // try only once
-            String configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
+            String configFile = getConfigFile();
             if (configFile != null) {
-                configFile = Config.replaceConfigDir(configFile);
                 try {
                     cacheManager = CacheManager.create(configFile);
                     //System.setProperty(
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/App.java	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/App.java	Fri Jul 22 16:55:36 2011 +0000
@@ -112,6 +112,7 @@
                 }
             }
         }
+        System.exit(0);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java	Fri Jul 22 16:55:36 2011 +0000
@@ -1,5 +1,6 @@
 package de.intevation.flys.artifacts.services.meta;
 
+import java.util.regex.Pattern;
 import java.util.regex.Matcher;
 
 import java.util.ArrayList;
@@ -28,6 +29,9 @@
 {
     private static Logger log = Logger.getLogger(Builder.class);
 
+    public static final Pattern STRIP_LINE_INDENT =
+        Pattern.compile("\\s*\\r?\\n\\s*");
+
     public static final String DC_NAMESPACE_URI =
         "http://www.intevation.org/2011/Datacage";
 
@@ -39,11 +43,6 @@
 
     protected Document template;
 
-    protected static Document getOwnerDocument(Node node) {
-        Document document = node.getOwnerDocument();
-        return document != null ? document : (Document)node;
-    }
-
 
     public class BuildHelper
     {
@@ -109,7 +108,7 @@
             String stmntText = stmntNode.item(0).getTextContent();
 
             if (stmntText == null
-            || (stmntText = stmntText.trim()).length() == 0) {
+            || (stmntText = trimStatement(stmntText)).length() == 0) {
                 log.warn("dc:context: no sql statement found -> ignored");
                 return;
             }
@@ -335,17 +334,23 @@
                 }
                 else {
                     String localName = current.getLocalName();
-                    if ("context".equals(localName)) {
+                    if ("attribute".equals(localName)) {
+                        attribute(parent, (Element)current);
+                    }
+                    else if ("context".equals(localName)) {
                         context(parent, (Element)current);
                     }
+                    else if ("if".equals(localName)) {
+                        ifClause(parent, (Element)current);
+                    }
                     else if ("choose".equals(localName)) {
                         choose(parent, (Element)current);
                     }
-                    else if ("if".equals(localName)) {
-                        ifClause(parent, (Element)current);
+                    else if ("call-macro".equals(localName)) {
+                        callMacro(parent, (Element)current);
                     }
-                    else if ("attribute".equals(localName)) {
-                        attribute(parent, (Element)current);
+                    else if ("macro".equals(localName)) {
+                        // simply ignore the definition.
                     }
                     else if ("element".equals(localName)) {
                         element(parent, (Element)current);
@@ -356,12 +361,6 @@
                     else if ("convert".equals(localName)) {
                         convert(parent, (Element)current);
                     }
-                    else if ("call-macro".equals(localName)) {
-                        callMacro(parent, (Element)current);
-                    }
-                    else if ("macro".equals(localName)) {
-                        // simply ignore the definition.
-                    }
                     else {
                         log.warn("unknown '" + localName + "' -> ignore");
                     }
@@ -411,6 +410,16 @@
         return elements;
     }
 
+    protected static final String trimStatement(String stmnt) {
+        //XXX: Maybe a bit to radical for multiline strings?
+        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
+    }
+
+    protected static Document getOwnerDocument(Node node) {
+        Document document = node.getOwnerDocument();
+        return document != null ? document : (Document)node;
+    }
+
     public void build(
         Connection          connection,
         Node                output,
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java	Fri Jul 22 16:55:36 2011 +0000
@@ -5,7 +5,7 @@
 
 import java.util.List;
 import java.util.Map;
-import java.util.HashMap;
+import java.util.TreeMap;
 import java.util.ArrayList;
 
 import java.sql.PreparedStatement;
@@ -13,8 +13,16 @@
 import java.sql.Connection;
 import java.sql.ResultSet;
 
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
 public class CompiledStatement
 {
+    public static final String DATACAGE_DB_CACHE =
+        "datacage.db";
+
     public static final Pattern VAR =
         Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
 
@@ -25,12 +33,15 @@
 
     protected PreparedStatement preparedStatement;
 
+    protected int numVars;
+
     public CompiledStatement() {
     }
 
     public CompiledStatement(String original) {
         this.original = original;
-        positions = new HashMap<String, List<Integer>>();
+        // TreeMap to ensure order
+        positions = new TreeMap<String, List<Integer>>();
         compile();
     }
 
@@ -40,7 +51,7 @@
 
         Matcher m = VAR.matcher(original);
 
-        int index = 1;
+        int index = 0;
 
         while (m.find()) {
             String key = m.group(1);
@@ -56,6 +67,8 @@
 
         m.appendTail(sb);
 
+        numVars = index;
+
         statement = sb.toString();
     }
 
@@ -63,7 +76,65 @@
         return statement;
     }
 
-    public ResultData execute(Connection connection, StackFrames frames)
+    protected ResultData executeCached(
+        Cache       cache,
+        Connection  connection,
+        StackFrames frames
+    )
+    throws SQLException
+    {
+        Object [] values = new Object[numVars];
+
+        StringBuilder sb = new StringBuilder(original);
+
+        for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+            String key   = entry.getKey();
+            Object value = frames.get(key);
+            sb.append(';').append(key).append(':').append(value);
+            for (Integer index: entry.getValue()) {
+                values[index] = value;
+            }
+        }
+
+        // XXX: Maybe too many collisions?
+        // String key = original + Arrays.hashCode(values);
+        String key = sb.toString();
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            return (ResultData)element.getValue();
+        }
+
+        if (preparedStatement == null) {
+            preparedStatement = connection.prepareStatement(statement);
+        }
+
+        for (int i = 0; i < values.length; ++i) {
+            preparedStatement.setObject(i+1, values[i]);
+        }
+
+        ResultData data;
+
+        ResultSet result = preparedStatement.executeQuery();
+        try {
+            data = new ResultData(preparedStatement.getMetaData())
+                .addAll(result);
+        }
+        finally {
+            result.close();
+        }
+
+        element = new Element(key, data);
+        cache.put(element);
+
+        return data;
+    }
+
+    protected ResultData executeUncached(
+        Connection  connection,
+        StackFrames frames
+    ) 
     throws SQLException
     {
         if (preparedStatement == null) {
@@ -73,7 +144,7 @@
         for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
             Object value = frames.get(entry.getKey());
             for (Integer index: entry.getValue()) {
-                preparedStatement.setObject(index, value);
+                preparedStatement.setObject(index+1, value);
             }
         }
 
@@ -87,6 +158,16 @@
         }
     }
 
+    public ResultData execute(Connection connection, StackFrames frames)
+    throws SQLException
+    {
+        Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
+
+        return cache != null
+            ? executeCached(cache, connection, frames)
+            : executeUncached(connection, frames);
+    }
+
     public void close() {
         if (preparedStatement != null) {
             try {
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java	Fri Jul 22 11:18:00 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java	Fri Jul 22 16:55:36 2011 +0000
@@ -1,5 +1,7 @@
 package de.intevation.flys.artifacts.services.meta;
 
+import java.io.Serializable;
+
 import java.sql.ResultSetMetaData;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -8,6 +10,7 @@
 import java.util.ArrayList;
 
 public class ResultData
+implements   Serializable
 {
     protected String [] columns;
 

http://dive4elements.wald.intevation.org