changeset 10:e8626caac353

* Made Artifact life cycle symmetric: setup/endOfLife. * Implement defaults for Artifact and ArtifactFactory. * Added connection pooling from apache commons dbcp * Made sql schema of artifact database more compatible. * Improve example config. * Made artifactdb start with 'mvn exec:exec' * minor fixes. artifacts/trunk@25 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sun, 06 Sep 2009 12:00:56 +0000
parents a5a279a0ee35
children af07d004d320
files Changelog TODO artifact-database/doc/artifactdb-example-conf.xml artifact-database/doc/schema.sql artifact-database/pom.xml artifact-database/src/main/java/de/intevation/artifactdatabase/Config.java artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java artifacts/src/main/java/de/intevation/artifacts/Artifact.java artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java
diffstat 12 files changed, 447 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/Changelog	Fri Sep 04 16:06:44 2009 +0000
+++ b/Changelog	Sun Sep 06 12:00:56 2009 +0000
@@ -1,3 +1,51 @@
+2009-09-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/Artifact.java(setup):
+	Added the setup() method to have symmetric counter part to endOfLife().
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java(timeToLiveUntouched):
+	Added this method to let the factory decide how long an artifact should live in ms.
+	This is not a part of the Artifact itself because this is only evaluated once when
+	the artifact is created.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java:
+	New. Simple base class implementation of the Artifact interface.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java:
+	New. Simple base class implementation of the ArtifactFactory interface. When setup()
+	on instances of this class is called, it pull ttl, name, description and
+	the artifact class name from the node given. See artifactdb-example-conf.xml
+	for examples.
+
+	* artifact-database/pom.xml: Cleaned up XML. 
+	Introduced dependency to apache commons dbcp, used for pooling of the 
+	database connections to artifact db. 
+	Added parameters for the exec:exec goal to make the project
+	startable without building packages.
+
+	* artifact-database/doc/schema.sql: Removed AUTO_INCREMENT from primary key
+	to avoid compatibility issues with other non-H2 databases (PostgreSQL, Oracle, ...)
+	which have no or limited support for generated keys in the JDBC driver. Now
+	using an explicit sequence. TTL ist now big int to bring the resolution to ms.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Config.java:
+	Refactored a bit to make the XPath access function usable on arbitrary XML
+	documents and parts of.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java:
+	New. Exposes DataSource from a apache dbcp connection pool configured by the
+	global configuration file. See artifactdb-example-conf.xml for examples.
+	TODO: Write some documentation about this.
+
+	* artifact-database/doc/artifactdb-example-conf.xml: Adjusted to be a more realistic
+	example config file. Added references to DefaultArtifact/DefaultArtifactFactory and
+	demonstrate how to configure the connection pool.
+
+	* TODO: Add remark to document the connection file.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	Do not crash when config does not contain any factories.
+
 2009-09-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	* artifact-database/src/main/java/de/intevation/artifactdatabase/Config.java (getNodeXPath):
--- a/TODO	Fri Sep 04 16:06:44 2009 +0000
+++ b/TODO	Sun Sep 06 12:00:56 2009 +0000
@@ -1,2 +1,3 @@
 TODO:
     * integrate logging into artifact database.
+	* Document the XML of the configuration file.
--- a/artifact-database/doc/artifactdb-example-conf.xml	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifact-database/doc/artifactdb-example-conf.xml	Sun Sep 06 12:00:56 2009 +0000
@@ -1,11 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <artifact-database>
     <factories>
-        <context-factory>com.example.Context</context-factory>
+        <context-factory>de.intevation.artifactdatabase.DefaultArtifactContextFactory</context-factory>
         <artifact-factories>
-            <artifact-factory>com.example.ArtifactExampleFactoryOne</artifact-factory>
-            <artifact-factory>com.example.ArtifactExampleFactoryTwo</artifact-factory>
-            <artifact-factory>com.example.ArtifactExampleFactoryThree</artifact-factory>
+            <artifact-factory
+             name="dummy-1" description="dummy description 1" ttl="60000"
+             artifact="de.intevation.artifactdatabase.DefaultArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory
+             name="dummy-2" description="dummy description 2" ttl="1800000"
+             artifact="de.intevation.artifactdatabase.DefaultArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+            <artifact-factory
+             name="dummy-3" description="dummy description 3"
+             artifact="de.intevation.artifactdatabase.DefaultArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
         </artifact-factories>
     </factories>
+    <database>
+        <user></user>
+        <password></password>
+        <!-- <url>jdbc:h2:artifacts.db</url> -->
+        <!-- <driver>org.h2.Driver</driver> -->
+        <!-- <sql></sql> -->
+    </database>
 </artifact-database>
--- a/artifact-database/doc/schema.sql	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifact-database/doc/schema.sql	Sun Sep 06 12:00:56 2009 +0000
@@ -4,12 +4,16 @@
 
 BEGIN;
 
+-- not using AUTO_INCREMENT to be more compatible with
+-- other dbms.
+CREATE SEQUENCE ARTIFACTS_ID_SEQ;
+
 CREATE TABLE artifacts (
-    id          INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
-    gid         UUID      NOT NULL UNIQUE,
-    creation    TIMESTAMP NOT NULL,
-    last_access TIMESTAMP NOT NULL,
-    ttl         TIME, -- NULL means eternal
+    id          INT PRIMARY KEY NOT NULL,
+    gid         UUID            NOT NULL UNIQUE,
+    creation    TIMESTAMP       NOT NULL,
+    last_access TIMESTAMP       NOT NULL,
+    ttl         BIGINT, -- NULL means eternal
     data        BINARY
 );
 
--- a/artifact-database/pom.xml	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifact-database/pom.xml	Sun Sep 06 12:00:56 2009 +0000
@@ -1,4 +1,5 @@
-<?xml version="1.0"?><project>
+<?xml version="1.0"?>
+<project>
   <parent>
     <artifactId>artifact-system</artifactId>
     <groupId>de.intevation.bsh</groupId>
@@ -12,35 +13,53 @@
   <url>http://maven.apache.org</url>
   <build>
     <plugins>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-jar-plugin</artifactId>
-          <version>2.2</version>
-          <configuration>
-            <archive>
-                <manifest>
-                    <mainClass>de.intevation.artifactdatabase.App</mainClass>
-                </manifest>
-            </archive>
-          </configuration>
-        </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>2.2</version>
+        <configuration>
+          <archive>
+            <manifest>
+              <mainClass>de.intevation.artifactdatabase.App</mainClass>
+            </manifest>
+          </archive>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>exec-maven-plugin</artifactId>
+        <version>1.1</version>
+        <configuration>
+          <executable>java</executable>
+          <arguments>
+            <argument>-classpath</argument>
+            <classpath/>
+            <argument>de.intevation.artifactdatabase.App</argument>
+          </arguments>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
   <dependencies>
     <dependency>
-        <groupId>de.intevation.bsh.artifacts</groupId>
-        <artifactId>artifacts</artifactId>
-        <version>1.0-SNAPSHOT</version>
+      <groupId>de.intevation.bsh.artifacts</groupId>
+      <artifactId>artifacts</artifactId>
+      <version>1.0-SNAPSHOT</version>
     </dependency>
-    <dependency>  
-      <groupId>org.restlet</groupId>  
-      <artifactId>org.restlet</artifactId>  
-      <version>2.0-M3</version>  
-    </dependency> 
     <dependency>
-        <groupId>com.h2database</groupId>
-        <artifactId>h2</artifactId>
-        <version>1.1.117</version>
+      <groupId>org.restlet</groupId>
+      <artifactId>org.restlet</artifactId>
+      <version>2.0-M3</version>
+    </dependency>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <version>1.1.117</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-dbcp</groupId>
+      <artifactId>commons-dbcp</artifactId>
+      <version>1.2.2</version>
     </dependency>
   </dependencies>
 </project>
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Config.java	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Config.java	Sun Sep 06 12:00:56 2009 +0000
@@ -20,6 +20,9 @@
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathConstants;
 
+/**
+ *  @author Sascha L. Teichmann
+ */
 public final class Config
 {
     public static final String CONFIG_PROPERTY = "artifact.database.config";
@@ -71,6 +74,38 @@
         return null;
     }
 
+    public static final Object getXPath(
+        Object root, String query, QName returnType
+    ) {
+        if (root == null) {
+            return null;
+        }
+
+        XPathFactory factory = XPathFactory.newInstance();
+        XPath        xpath   = factory.newXPath();
+
+        try {
+            return xpath.evaluate(query, root, returnType);
+        }
+        catch (XPathExpressionException xpee) {
+            xpee.printStackTrace(System.err);
+        }
+
+        return null;
+    }
+
+    public static final Object getXPath(String query, QName returnType) {
+        return getXPath(getConfig(), query, returnType);
+    }
+
+    public static final NodeList getNodeSetXPath(String query) {
+        return (NodeList)getXPath(query, XPathConstants.NODESET);
+    }
+
+    public static final Node getNodeXPath(String query) {
+        return (Node)getXPath(query, XPathConstants.NODE);
+    }
+
     public static final String getStringXPath(String xpath) {
         return getStringXPath(xpath, null);
     }
@@ -82,31 +117,25 @@
             : s;
     }
 
-    public static final Object getXPath(String query, QName returnType) {
-        Document document = getConfig();
-        if (document == null) {
-            return null;
-        }
-
-        XPathFactory factory = XPathFactory.newInstance();
-        XPath        xpath   = factory.newXPath();
-
-        try {
-            return xpath.evaluate(query, document, returnType);
-        }
-        catch (XPathExpressionException xpee) {
-            xpee.printStackTrace(System.err);
-        }
-
-        return null;
+    public static final NodeList getNodeSetXPath(Object root, String query) {
+        return (NodeList)getXPath(root, query, XPathConstants.NODESET);
     }
 
-    public static final NodeList getNodeSetXPath(String query) {
-        return (NodeList)getXPath(query, XPathConstants.NODESET);
+    public static final Node getNodeXPath(Object root, String query) {
+        return (Node)getXPath(root, query, XPathConstants.NODE);
     }
 
-    public static final Node getNodeXPath(String query) {
-        return (Node)getXPath(query, XPathConstants.NODE);
+    public static final String getStringXPath(Object root, String xpath) {
+        return getStringXPath(root, xpath, null);
+    }
+
+    public static final String getStringXPath(
+        Object root, String query, String def
+    ) {
+        String s = (String)getXPath(root, query, XPathConstants.STRING);
+        return s == null || s.length() == 0
+            ? def
+            : s;
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java	Sun Sep 06 12:00:56 2009 +0000
@@ -0,0 +1,78 @@
+package de.intevation.artifactdatabase;
+
+import javax.sql.DataSource;
+
+import java.sql.SQLException;
+
+import org.apache.commons.dbcp.BasicDataSource;
+
+/**
+ *  @author Sascha L. Teichmann
+ */
+public class DBConnection
+{
+    public static final String DB_DRIVER =
+        "/artifacts-database/database/driver/text()";
+    public static final String DB_URL =
+        "/artifacts-database/database/url/text()";
+    public static final String DB_USER =
+        "/artifacts-database/database/driver/user/text()";
+    public static final String DB_PASSWORD =
+        "/artifacts-database/database/driver/password()";
+
+    public static final String DEFAULT_DRIVER =
+        "org.h2.Driver";
+
+    public static final String DEFAULT_URL =
+        "jdbc:h2:artifacts.db";
+
+    public static final String DEFAULT_USER     = "";
+    public static final String DEFAULT_PASSWORD = "";
+
+    private DBConnection() {
+    }
+
+    private static BasicDataSource dataSource;
+
+    private static final void addShutdownHook() {
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            public void run() {
+                if (dataSource != null) {
+                    try {
+                        dataSource.close();
+                    }
+                    catch (SQLException sqle) {
+                    }
+                    dataSource = null;
+                }
+            }
+        });
+    }
+
+    public static synchronized DataSource getDataSource() {
+        if (dataSource == null) {
+            dataSource = new BasicDataSource();
+
+            String driver = Config.getStringXPath(
+                DB_DRIVER, DEFAULT_DRIVER);
+
+            String url = Config.getStringXPath(
+                DB_URL, DEFAULT_URL);
+
+            String user = Config.getStringXPath(
+                DB_USER, DEFAULT_USER);
+
+            String password = Config.getStringXPath(
+                DB_PASSWORD, DEFAULT_PASSWORD);
+
+            dataSource.setDriverClassName(driver);
+            dataSource.setUsername(user);
+            dataSource.setPassword(password);
+            dataSource.setUrl(url);
+            addShutdownHook();
+        }
+
+        return dataSource;
+    }
+}
+//  vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Sun Sep 06 12:00:56 2009 +0000
@@ -0,0 +1,65 @@
+package de.intevation.artifactdatabase;
+
+import org.w3c.dom.Document;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import de.intevation.artifacts.Artifact;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class DefaultArtifact
+implements   Artifact
+{
+    protected String identifier;
+
+    public DefaultArtifact() {
+    }
+
+    protected Document newDocument() {
+        try {
+            return DocumentBuilderFactory
+                .newInstance()
+                .newDocumentBuilder()
+                .newDocument();
+        }
+        catch (ParserConfigurationException pce) {
+            pce.printStackTrace(System.err);
+        }
+        return null;
+    }
+
+    public String identifier() {
+        return this.identifier;
+    }
+
+    public String hash() {
+        return String.valueOf(hashCode());
+    }
+
+    public Document describe(Object context) {
+        return newDocument();
+    }
+
+    public Document advance(Document target, Object context) {
+        return newDocument();
+    }
+
+    public Document feed(Document target, Object context) {
+        return newDocument();
+    }
+
+    public byte [] out(Document format, Object context) {
+        return new byte[0];
+    }
+
+    public void setup(String identifier, Object context) {
+        this.identifier = identifier;
+    }
+
+    public void endOfLife(Object context) {
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java	Sun Sep 06 12:00:56 2009 +0000
@@ -0,0 +1,102 @@
+package de.intevation.artifactdatabase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.Artifact;
+
+public class DefaultArtifactFactory
+implements   ArtifactFactory
+{
+    public static final String XPATH_TTL         = "@ttl";
+    public static final String XPATH_NAME        = "@name";
+    public static final String XPATH_DESCRIPTION = "@description";
+    public static final String XPATH_ARTIFACT    = "@artifact";
+
+    public static final String DEFAULT_DESCRIPTION =
+        "No description available";
+
+    public static final String DEFAULT_ARTIFACT =
+        "de.intevation.artifactdatabase.DefaultArtifact";
+
+    protected Long   ttl;
+
+    protected String name;
+
+    protected String description;
+
+    protected Class  artifactClass;
+
+    public DefaultArtifactFactory() {
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public Artifact createArtifact(String identifier, Object context) {
+
+        try {
+            Artifact artifact =
+                (Artifact)artifactClass.newInstance();
+
+            artifact.setup(identifier, context);
+
+            return artifact;
+        }
+        catch (InstantiationException ie) {
+            ie.printStackTrace(System.err);
+        }
+        catch (IllegalAccessException iae) {
+            iae.printStackTrace(System.err);
+        }
+        catch (ClassCastException cce) {
+            cce.printStackTrace(System.err);
+        }
+
+        return null;
+    }
+
+    public void setup(Document document, Node factoryNode) {
+
+        String ttlString = Config.getStringXPath(factoryNode, XPATH_TTL);
+        if (ttlString != null) {
+            try {
+                ttl = Long.valueOf(ttlString);
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace(System.err);
+            }
+        }
+
+        description = Config.getStringXPath(
+            factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION);
+
+        name = Config.getStringXPath(
+            factoryNode, XPATH_NAME, toString());
+
+        String artifact = Config.getStringXPath(
+            factoryNode, XPATH_ARTIFACT, DEFAULT_ARTIFACT);
+
+        try {
+            artifactClass = Class.forName(artifact);
+        }
+        catch (ClassNotFoundException cnfe) {
+            cnfe.printStackTrace(System.err);
+        }
+
+        if (artifactClass == null) {
+            artifactClass = DefaultArtifact.class;
+        }
+    }
+
+    public Long timeToLiveUntouched(Artifact artifact, Object context) {
+        return ttl;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java	Sun Sep 06 12:00:56 2009 +0000
@@ -66,6 +66,11 @@
 
         NodeList nodes = Config.getNodeSetXPath(ARTIFACT_FACTORIES);
 
+        if (nodes == null) {
+            System.err.println("ERROR: no factories found");
+            return;
+        }
+
         Document config = Config.getConfig();
 
         for (int i = 0, N = nodes.getLength(); i < N; ++i) {
--- a/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Sun Sep 06 12:00:56 2009 +0000
@@ -22,6 +22,15 @@
  *   <li>{@link #out(Document, Object) out()}: Produces output for this artifact.</li>
  * </ol>
  *
+ * There are two more methods involved with the life cycle of the are:
+ * <ol>
+ *   <li>{@link #setup(String, Object) setup()}: Called after created by the 
+ *                                               factory.</li>
+ *   <li>{@link #endOfLife(Object) endOfLife()}: Called when the artifact
+ *                                               is going to be removed from
+ *                                               system. Useful to clean up.</li>
+ * </ol>
+ *
  * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
  */
 public interface Artifact
@@ -72,6 +81,14 @@
     public byte [] out(Document format, Object context);
 
     /**
+     * When created by a factory this method is called to
+     * initialize the artifact.
+     * @param identifier The identifier from artifact database
+     * @param context    The global context of the runtime system.
+     */
+    public void setup(String identifier, Object context);
+
+    /**
      * Called from artifact database when an artifact is
      * going to be removed from system.
      * @param context The global context of the runtime system.
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Fri Sep 04 16:06:44 2009 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Sun Sep 06 12:00:56 2009 +0000
@@ -37,5 +37,17 @@
      * @param factoryNode the ConfigurationNode of this Factory
      */
     void setup(Document config, Node factoryNode);
+
+    /**
+     * Tells how long an artifact should survive if it is
+     * not touched. This is put in the factory because
+     * life time is nothing a artifact should handle it self.
+     * This method is only called once directly after the
+     * artifact is created.
+     * @param artifact The artifact to be rated.
+     * @param context  The global context.
+     * return time to live in ms. null means eternal.
+     */
+    Long timeToLiveUntouched(Artifact artifact, Object context);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org