changeset 32:c2d53bd30ab8

Re-factored artifact API for better integration of background processing. artifacts/trunk@78 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sun, 13 Sep 2009 14:50:53 +0000
parents c4d85a8532d1
children 251e8904d6c2
files Changelog TODO artifact-database/src/main/java/de/intevation/artifactdatabase/App.java artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java artifact-database/src/main/java/de/intevation/artifactdatabase/Id.java artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java artifact-database/src/main/resources/sql/org-h2-driver.properties artifacts/src/main/java/de/intevation/artifacts/Artifact.java artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabaseException.java artifacts/src/main/java/de/intevation/artifacts/CallContext.java
diffstat 18 files changed, 640 insertions(+), 310 deletions(-) [+]
line wrap: on
line diff
--- a/Changelog	Sat Sep 12 10:45:28 2009 +0000
+++ b/Changelog	Sun Sep 13 14:50:53 2009 +0000
@@ -1,3 +1,73 @@
+2009-09-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Refactored the persistent handling of the artifacts in the 
+	artifact database. When an artifact is called (describe, feed,
+	advance, out) a new CallContext is given instead of the
+	old Object context. CallContext.globalContext() returns the
+	old Object context now. CallContext.afterCall() may be
+	called from inside describe, feed, advance and out to tell
+	the artifact database what to do after the processing of
+	the specific call.
+	
+	The accepted values are:
+		- NOTHING    for doing nothing
+		- TOUCH      for just update the last access time
+		- STORE      for persisting the artifact.
+		- BACKGROUND to signal that the artifact has started
+		             a background operation.
+
+	After the background operation the artifact has to call
+	CallContext.fromBackground() to signal the artifact 
+	database that the artifact needs to be touched or stored.
+
+	!!! Without this call the artifact database assumes there
+	!!! is still some background operation on this artifact
+	!!! which prevents reclaiming resources
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java:
+	New. New API to cope with background processing.
+
+	* artifacts/src/main/java/de/intevation/artifacts/Artifact.java:
+	Replaced old Object context with CallContext context.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java:
+	Adjusted.
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	For a better encapsulation of the persistence operations of the
+	artifacts the database does not return artifacts any more. It delegates
+	the describe, feed, advance and out call to internal structures.
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabaseException.java:
+	New. Simplifies handling of error conditions inside of artifact database.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	Adjusted to new interface. Internally it handles the background API.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java,
+	artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java,
+	artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java,
+	artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java,
+	artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java:
+	Adjusted to new artifact database interface (which greatly simplifies things)
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	Artifacts in background are not removed from database any longer.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/App.java: Little
+	re-wirering need for new cleanup.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: 
+	Simplified to do the SQL stuff only. The most infrastructure stuff is now done
+	in ArtifactDatabaseImpl.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Id.java:
+	New. Helper base class to enable filtering of background artifacts by there
+	database id.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties:
+	Modified the INSERT statement to write the serialized artifact, too.
+
 2009-09-12	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
 
 	* artifacts/src/main/java/de/intevation/artifacts/Artifact.java(out):
--- a/TODO	Sat Sep 12 10:45:28 2009 +0000
+++ b/TODO	Sun Sep 13 14:50:53 2009 +0000
@@ -1,2 +1,3 @@
 TODO:
 	* Document the XML of the configuration file.
+    * Adjust JavaDoc.
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java	Sun Sep 13 14:50:53 2009 +0000
@@ -40,15 +40,19 @@
 
         bootstrap.boot();
 
+        Backend backend = new Backend();
+
+        ArtifactDatabaseImpl db = new ArtifactDatabaseImpl(
+            bootstrap, backend);
+
         DatabaseCleaner cleaner = new DatabaseCleaner(
             bootstrap.getContext());
 
-        cleaner.start();
+        backend.setCleaner(cleaner);
 
-        Backend backend = new Backend(cleaner);
+        cleaner.setFilter(db);
 
-        ArtifactDatabaseImpl db = new ArtifactDatabaseImpl(
-            bootstrap, backend);
+        cleaner.start();
 
         Standalone.startAsServer(db);
     }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java	Sun Sep 13 14:50:53 2009 +0000
@@ -1,35 +1,163 @@
 package de.intevation.artifactdatabase;
 
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.List;
 
+import de.intevation.artifacts.ArtifactDatabaseException;
 import de.intevation.artifacts.ArtifactDatabase;
 import de.intevation.artifacts.ArtifactFactory;
 import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.Backend.PersistentArtifact;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import java.io.OutputStream;
+import java.io.IOException;
 
 /**
  *  @author Sascha L. Teichmann
  */
 public class ArtifactDatabaseImpl
-implements   ArtifactDatabase
+implements   ArtifactDatabase, Id.Filter
 {
-    protected String [] factoryNames;
-    protected HashMap   name2factory;
+    private static Logger logger =
+        Logger.getLogger(ArtifactDatabaseImpl.class);
 
-    protected Backend   backend;
-    protected Object    context;
+    public static final String NO_SUCH_FACTORY =
+        "No such factory";
+
+    public static final String NO_SUCH_ARTIFACT =
+        "No such artifact";
+
+    public static final String NOT_IN_BACKGROUND =
+        "Not in background";
+
+    public static final String INVALID_CALL_STATE =
+        "Invalid after call state";
+
+    public static final String CREATION_FAILED =
+        "Creation of artifact failed";
+
+    public static final String INTERNAL_ERROR =
+        "Creation of artifact failed";
+
+    public class CallContextImpl
+    implements   CallContext
+    {
+        protected PersistentArtifact artifact;
+        protected int                action;
+
+        public CallContextImpl(PersistentArtifact artifact, int action) {
+            this.artifact = artifact;
+            this.action   = action;
+        }
+
+        public void afterCall(int action) {
+            this.action = action;
+            if (action == BACKGROUND) {
+                addIdToBackground(artifact.getId());
+            }
+        }
+
+        public void afterBackground(int action) {
+            if (this.action != BACKGROUND) {
+                throw new IllegalStateException(NOT_IN_BACKGROUND);
+            }
+            fromBackground(artifact, action);
+        }
+
+        public Object globalContext() {
+            return context;
+        }
+
+        public void postCall() {
+            switch (action) {
+                case NOTHING:
+                    break;
+                case TOUCH:
+                    artifact.touch();
+                    break;
+                case STORE:
+                    artifact.store();
+                    break;
+                case BACKGROUND:
+                    logger.warn("BACKGROUND processing is not fully implemented, yet!");
+                    artifact.store();
+                    break;
+                default:
+                    logger.error(INVALID_CALL_STATE + ": " + action);
+                    throw new IllegalStateException(INVALID_CALL_STATE);
+            }
+        }
+    } // class CallContextImpl
+
+    public class DeferredOutputImpl
+    implements   DeferredOutput
+    {
+        protected PersistentArtifact artifact;
+        protected Document           format;
+
+        public DeferredOutputImpl() {
+        }
+
+        public DeferredOutputImpl(
+            PersistentArtifact artifact, 
+            Document           format
+        ) {
+            this.artifact = artifact;
+            this.format   = format;
+        }
+
+        public void write(OutputStream output) throws IOException {
+
+            CallContextImpl cc = new CallContextImpl(
+                artifact, CallContext.TOUCH);
+
+            try {
+                artifact.getArtifact().out(format, output, cc);
+            }
+            finally {
+                cc.postCall();
+            }
+        }
+    } // class DeferredOutputImpl
+
+    protected String [][] factoryNamesAndDescription;
+    protected HashMap     name2factory;
+
+    protected Backend     backend;
+    protected Object      context;
+
+    protected HashSet     backgroundIds;
 
     public ArtifactDatabaseImpl() {
     }
 
     public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) {
-        name2factory = new HashMap();
+
+        backgroundIds = new HashSet();
+        name2factory  = new HashMap();
 
         ArtifactFactory [] factories = bootstrap.getArtifactFactories();
-        factoryNames = new String[factories.length];
+        factoryNamesAndDescription = new String[factories.length][];
 
         for (int i = 0; i < factories.length; ++i) {
+
             ArtifactFactory factory = factories[i];
-            name2factory.put(factoryNames[i] = factory.getName(), factory);
+
+            String name        = factory.getName();
+            String description = factory.getDescription();
+
+            factoryNamesAndDescription[i] =
+                new String [] { name, description };
+
+            name2factory.put(name, factory);
         }
 
         context = bootstrap.getContext();
@@ -37,23 +165,168 @@
         this.backend = backend;
     }
 
-    public String [] getArtifactFactoryNames() {
-        return factoryNames;
-    }
-
-    public Artifact getArtifact(String identifier) {
-        return backend.getArtifact(identifier);
+    protected void fromBackground(PersistentArtifact artifact, int action) {
+        logger.warn("BACKGROUND processing is not fully implemented, yet!");
+        switch (action) {
+            case CallContext.NOTHING:
+                break;
+            case CallContext.TOUCH:
+                artifact.touch();
+                break;
+            case CallContext.STORE:
+                artifact.store();
+                break;
+            default:
+                logger.warn("operation not allowed in fromBackground");
+        }
+        removeIdFromBackground(artifact.getId());
     }
 
-    public Artifact createArtifactWithFactory(String factoryName) {
-        ArtifactFactory factory = (ArtifactFactory)name2factory.get(factoryName);
-        return  factory == null
-            ? null
-            : backend.createArtifactWithFactory(factory, context);
+    protected void removeIdFromBackground(int id) {
+        synchronized (backgroundIds) {
+            backgroundIds.remove(Integer.valueOf(id));
+        }
     }
 
-    public Object getArtifactContext() {
-        return context;
+    protected void addIdToBackground(int id) {
+        synchronized (backgroundIds) {
+            backgroundIds.add(Integer.valueOf(id));
+        }
+    }
+
+    public List filterIds(List ids) {
+        int N = ids.size();
+        ArrayList out = new ArrayList(N);
+        synchronized (backgroundIds) {
+            for (int i = 0; i < N; ++i) {
+                Id id = (Id)ids.get(i);
+                if (!backgroundIds.contains(Integer.valueOf(id.getId()))) {
+                    out.add(id);
+                }
+            }
+        }
+        return out;
+    }
+
+    public String [][] artifactFactoryNamesAndDescriptions() {
+        return factoryNamesAndDescription;
+    }
+
+    public Document createArtifactWithFactory(String factoryName)
+        throws ArtifactDatabaseException 
+    {
+        ArtifactFactory factory = (ArtifactFactory)name2factory.get(
+            factoryName);
+
+        if (factory == null) {
+            throw new ArtifactDatabaseException(NO_SUCH_FACTORY);
+        }
+
+        Artifact artifact = factory.createArtifact(
+            backend.newIdentifier(),
+            context);
+
+        if (artifact == null) {
+            throw new ArtifactDatabaseException(CREATION_FAILED);
+        }
+
+        PersistentArtifact persistentArtifact;
+
+        try {
+            persistentArtifact = backend.storeInitially(
+                artifact,
+                factory.timeToLiveUntouched(artifact, context));
+        }
+        catch (Exception e) {
+            logger.error(e.getLocalizedMessage(), e);
+            throw new ArtifactDatabaseException(CREATION_FAILED);
+        }
+
+        CallContextImpl cc = new CallContextImpl(
+            persistentArtifact, CallContext.NOTHING);
+
+        try {
+            return artifact.describe(cc);
+        }
+        finally {
+            cc.postCall();
+        }
+    }
+
+    public Document describe(String identifier)
+        throws ArtifactDatabaseException
+    {
+        // TODO: Handle background tasks
+        PersistentArtifact artifact = backend.getArtifact(identifier);
+
+        if (artifact == null) {
+            throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT);
+        }
+
+        CallContextImpl cc = new CallContextImpl(
+            artifact, CallContext.TOUCH);
+
+        try {
+            return artifact.getArtifact().describe(cc);
+        }
+        finally {
+            cc.postCall();
+        }
+    }
+
+    public Document advance(String identifier, Document target)
+        throws ArtifactDatabaseException
+    {
+        // TODO: Handle background tasks
+        PersistentArtifact artifact = backend.getArtifact(identifier);
+
+        if (artifact == null) {
+            throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT);
+        }
+
+        CallContextImpl cc = new CallContextImpl(
+            artifact, CallContext.STORE);
+
+        try {
+            return artifact.getArtifact().advance(target, cc);
+        }
+        finally {
+            cc.postCall();
+        }
+    }
+
+    public Document feed(String identifier, Document data)
+        throws ArtifactDatabaseException
+    {
+        // TODO: Handle background tasks
+        PersistentArtifact artifact = backend.getArtifact(identifier);
+
+        if (artifact == null) {
+            throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT);
+        }
+
+        CallContextImpl cc = new CallContextImpl(
+            artifact, CallContext.STORE);
+
+        try {
+            return artifact.getArtifact().advance(data, cc);
+        }
+        finally {
+            cc.postCall();
+        }
+    }
+
+    public DeferredOutput out(String identifier, Document format)
+        throws ArtifactDatabaseException
+    {
+        // TODO: Handle background tasks
+        PersistentArtifact artifact = backend.getArtifact(identifier);
+
+        if (artifact == null) {
+            throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT);
+        }
+
+        return new DeferredOutputImpl(artifact, format);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java	Sun Sep 13 14:50:53 2009 +0000
@@ -1,7 +1,5 @@
 package de.intevation.artifactdatabase;
 
-import org.w3c.dom.Document;
-
 import java.util.UUID;
 
 import java.sql.Connection;
@@ -17,12 +15,10 @@
 import java.io.ByteArrayOutputStream;
 import java.io.ObjectOutputStream;
 import java.io.ObjectInputStream;
-import java.io.OutputStream;
 
 import java.util.zip.GZIPOutputStream;
 import java.util.zip.GZIPInputStream;
 
-import de.intevation.artifacts.ArtifactFactory;
 import de.intevation.artifacts.Artifact;
 
 import org.apache.log4j.Logger;
@@ -51,113 +47,28 @@
 
     protected DatabaseCleaner cleaner;
 
-    /**
-     * Used to wrap the calls to invole database actions.
-     */
-    public class ArtifactProxy
-    implements   Artifact
+    public final class PersistentArtifact
+    extends            Id
     {
-        protected Artifact original;
-        protected int      id;
-        protected boolean  unwritten;
-
-        public ArtifactProxy() {
-        }
-
-        public ArtifactProxy(Artifact original, int id, boolean unwritten) {
-            this.original  = original;
-            this.id        = id;
-            this.unwritten = unwritten;
-        }
-
-        public Artifact getOriginal() {
-            return original;
-        }
+        private Artifact artifact;
 
-        public int getId() {
-            return id;
-        }
-
-        public boolean isUnwritten() {
-            return unwritten;
-        }
-
-        public String identifier() {
-            return original.identifier();
-        }
-
-        public String hash() {
-            return original.hash();
-        }
-
-        public Document describe(Object context) {
-            try {
-                return original.describe(context);
-            }
-            finally {
-                touch(this);
-            }
+        public PersistentArtifact(Artifact artifact, int id) {
+            super(id);
+            this.artifact = artifact;
         }
 
-        public Document advance(Document target, Object context) {
-            try {
-                return original.advance(target, context);
-            }
-            finally {
-                store(this);
-            }
-        }
-
-        public Document feed(Document data, Object context) {
-            try {
-                return original.feed(data, context);
-            }
-            finally {
-                store(this);
-            }
+        public Artifact getArtifact() {
+            return artifact;
         }
 
-        public void out(
-            Document     format, 
-            OutputStream output,
-            Object       context
-        ) 
-        throws IOException
-        {
-            try {
-                original.out(format, output, context);
-            }
-            finally {
-                touch(this);
-            }
-        }
-
-        public void setup(String identifier, ArtifactFactory factory, Object context) {
-            original.setup(identifier, factory, context);
+        public void store() {
+            Backend.this.store(this);
         }
 
-        public void endOfLife(Object context) {
-            original.endOfLife(context);
+        public void touch() {
+            Backend.this.touch(this);
         }
-
-        public byte [] toBytes() {
-            try {
-                ByteArrayOutputStream bos = new ByteArrayOutputStream();
-                GZIPOutputStream      gos = new GZIPOutputStream(bos);
-                ObjectOutputStream    oos = new ObjectOutputStream(gos);
-
-                oos.writeObject(original);
-                oos.flush();
-                oos.close();
-
-                return bos.toByteArray();
-            }
-            catch (IOException ioe) {
-                logger.error(ioe.getLocalizedMessage(), ioe);
-                throw new RuntimeException(ioe);
-            }
-        }
-    } // class ArtifactProxy
+    } // class ArtifactWithId
 
     public Backend() {
     }
@@ -166,42 +77,37 @@
         this.cleaner = cleaner;
     }
 
-    public Artifact getArtifact(String idenitfier) {
-        UUID uuid;
+    public void setCleaner(DatabaseCleaner cleaner) {
+        this.cleaner = cleaner;
+    }
+
+    public String newIdentifier() {
+        UUID uuid = UUID.randomUUID();
+        // TODO: check database for collisions.
+        return uuid.toString();
+    }
+
+    public PersistentArtifact storeInitially(
+        Artifact artifact,
+        Long     ttl
+    ) 
+    throws Exception
+    {
+        return new PersistentArtifact(
+            artifact,
+            insertDatabase(artifact, ttl));
+    }
+    
+    public PersistentArtifact getArtifact(String identifer) {
 
         try {
-            uuid = UUID.fromString(idenitfier);
+            UUID.fromString(identifer);
         }
         catch (IllegalArgumentException iae) {
             logger.warn(iae.getLocalizedMessage());
             return null;
         }
 
-        return getArtifactByUUID(uuid);
-    }
-
-    public Artifact createArtifactWithFactory(
-        ArtifactFactory factory, Object context
-    ) {
-        UUID uuid = UUID.randomUUID();
-        Artifact artifact = factory.createArtifact(
-            uuid.toString(), context);
-
-        Long ttl = factory.timeToLiveUntouched(
-            artifact, context);
-
-        try {
-            int id = insertDatabase(uuid, ttl);
-            return new ArtifactProxy(artifact, id, true);
-        }
-        catch (Exception e) {
-            logger.error(e.getLocalizedMessage(), e);
-        }
-        return null;
-    }
-
-    protected Artifact getArtifactByUUID(UUID uuid) {
-
         Connection        connection  = null;
         PreparedStatement stmnt_load  = null;
         ResultSet         load_result = null;
@@ -210,7 +116,7 @@
         try {
             connection = dataSource.getConnection();
             stmnt_load = connection.prepareStatement(SQL_LOAD_BY_GID);
-            stmnt_load.setString(1, uuid.toString());
+            stmnt_load.setString(1, identifer);
 
             load_result = stmnt_load.executeQuery();
 
@@ -231,16 +137,11 @@
 
             byte [] bytes = load_result.getBytes(4);
 
-            if (bytes == null) {
-                return null;
-            }
+            Artifact artifact = restoreArtifact(bytes);
 
-            Artifact original = restoreArtifact(bytes);
-            if (original == null) {
-                return null;
-            }
-
-            return new ArtifactProxy(original, id, false);
+            return artifact == null
+                ? null
+                : new PersistentArtifact(artifact, id);
         }
         catch (SQLException sqle) {
             logger.error(sqle.getLocalizedMessage(), sqle);
@@ -262,6 +163,24 @@
         return null;
     }
 
+    public static byte [] toBytes(Artifact artifact) {
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            GZIPOutputStream      gos = new GZIPOutputStream(bos);
+            ObjectOutputStream    oos = new ObjectOutputStream(gos);
+
+            oos.writeObject(artifact);
+            oos.flush();
+            oos.close();
+
+            return bos.toByteArray();
+        }
+        catch (IOException ioe) {
+            logger.error(ioe.getLocalizedMessage(), ioe);
+            throw new RuntimeException(ioe);
+        }
+    }
+
     public static Artifact restoreArtifact(byte [] bytes) {
 
         if (bytes == null) {
@@ -303,7 +222,10 @@
         }
     }
 
-    protected int insertDatabase(UUID uuid, Long ttl) {
+    protected int insertDatabase(Artifact artifact, Long ttl) {
+
+        String uuid = artifact.identifier();
+
         Connection        connection    = null;
         PreparedStatement stmnt_next_id = null;
         PreparedStatement stmnt_insert  = null;
@@ -327,7 +249,7 @@
                 int id = res_id.getInt(1);
 
                 stmnt_insert.setInt(1, id);
-                stmnt_insert.setString(2, uuid.toString());
+                stmnt_insert.setString(2, uuid);
                 if (ttl == null) {
                     stmnt_insert.setNull(3, Types.BIGINT);
                 }
@@ -335,6 +257,8 @@
                     stmnt_insert.setLong(3, ttl.longValue());
                 }
 
+                stmnt_insert.setBytes(4, toBytes(artifact));
+
                 stmnt_insert.execute();
 
                 connection.commit();
@@ -370,12 +294,7 @@
         throw new RuntimeException("failed insert artifact into database");
     }
 
-    public void touch(ArtifactProxy proxy) {
-
-        if (proxy.isUnwritten()) {
-            store(proxy);
-            return;
-        }
+    public void touch(PersistentArtifact artifact) {
 
         try {
             Connection        connection  = null;
@@ -386,7 +305,7 @@
                 try {
                     connection.setAutoCommit(false);
                     stmnt_touch = connection.prepareStatement(SQL_UPDATE);
-                    stmnt_touch.setInt(1, proxy.getId());
+                    stmnt_touch.setInt(1, artifact.getId());
                     stmnt_touch.execute();
                     connection.commit();
                 }
@@ -413,7 +332,7 @@
         }
     }
 
-    public void store(ArtifactProxy proxy) {
+    public void store(PersistentArtifact artifact) {
 
         try {
             Connection        connection   = null;
@@ -424,9 +343,9 @@
                 try {
                     connection.setAutoCommit(false);
                     stmnt_update = connection.prepareStatement(SQL_UPDATE);
-                    stmnt_update.setInt(2, proxy.getId());
+                    stmnt_update.setInt(2, artifact.getId());
 
-                    byte [] bytes = proxy.toBytes();
+                    byte [] bytes = toBytes(artifact.getArtifact());
 
                     stmnt_update.setBytes(1, bytes);
                     stmnt_update.execute();
@@ -454,6 +373,5 @@
             logger.error(e.getLocalizedMessage(), e);
         }
     }
-
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Sun Sep 13 14:50:53 2009 +0000
@@ -12,6 +12,7 @@
 import org.apache.log4j.Logger;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  *  @author Sascha L. Teichmann
@@ -41,6 +42,8 @@
 
     protected Object context;
 
+    protected Id.Filter filter;
+
     public DatabaseCleaner() {
     }
 
@@ -50,6 +53,10 @@
         this.context = context;
     }
 
+    public void setFilter(Id.Filter filter) {
+        this.filter = filter;
+    }
+
     public void wakeup() {
         synchronized (sleepLock) {
             sleepLock.notify();
@@ -72,13 +79,13 @@
         return SLEEP_DEFAULT;
     }
 
-    private static final class IdData {
-
-        int     id;
+    private static final class IdData 
+    extends                    Id 
+    {
         byte [] data;
 
         public IdData(int id, byte [] data) {
-            this.id   = id;
+            super(id);
             this.data = data;
         }
     } // class IdData
@@ -115,7 +122,7 @@
             fetchIds.setMaxRows(MAX_ROWS);
 
             for (;;) {
-                ArrayList ids = new ArrayList();
+                List ids = new ArrayList();
 
                 result = fetchIds.executeQuery();
 
@@ -130,6 +137,10 @@
                     break;
                 }
 
+                if (filter != null) {
+                    ids = filter.filterIds(ids);
+                }
+
                 for (int i = ids.size()-1; i >= 0; --i) {
                     IdData idData = (IdData)ids.get(i);
                     Artifact artifact = Backend.restoreArtifact(
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Sun Sep 13 14:50:53 2009 +0000
@@ -3,6 +3,7 @@
 import org.w3c.dom.Document;
 
 import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
 import de.intevation.artifacts.ArtifactFactory;
 
 import org.apache.log4j.Logger;
@@ -31,22 +32,22 @@
         return String.valueOf(hashCode());
     }
 
-    public Document describe(Object context) {
+    public Document describe(CallContext context) {
         return XMLUtils.newDocument();
     }
 
-    public Document advance(Document target, Object context) {
+    public Document advance(Document target, CallContext context) {
         return XMLUtils.newDocument();
     }
 
-    public Document feed(Document target, Object context) {
+    public Document feed(Document target, CallContext context) {
         return XMLUtils.newDocument();
     }
 
     public void out(
         Document     format,
         OutputStream out,
-        Object       context
+        CallContext  context
     )
     throws IOException
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Id.java	Sun Sep 13 14:50:53 2009 +0000
@@ -0,0 +1,27 @@
+package de.intevation.artifactdatabase;
+
+import java.util.List;
+
+/**
+ *  @author Sascha L. Teichmann
+ */
+public class Id
+{
+    public interface Filter {
+        List filterIds(List ids);
+    }
+
+    protected int id;
+
+    public Id() {
+    }
+
+    public Id(int id) {
+        this.id = id;
+    }
+
+    public int getId() {
+        return id;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java	Sun Sep 13 14:50:53 2009 +0000
@@ -18,8 +18,8 @@
 import de.intevation.artifactdatabase.XMLUtils;
 
 import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.ArtifactDatabaseException;
 import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.Artifact;
 
 import org.w3c.dom.Document;
 
@@ -68,15 +68,6 @@
             logger.debug("looking for artifact id '" + identifier + "'");
         }
 
-        Artifact artifact = db.getArtifact(identifier);
-
-        if (artifact == null) {
-            Response response = getResponse();
-            response.setStatus(
-                Status.CLIENT_ERROR_NOT_FOUND, ArtifactResource.NO_ARTIFACT_FOUND);
-            return new EmptyRepresentation();
-        }
-
         String mimeTypeString = XMLUtils.xpathString(
             inputDocument,
             XPATH_MIME_TYPE,
@@ -93,8 +84,17 @@
             }
         }
 
-        return new OutRepresentation(
-            mimeType, artifact, inputDocument, db.getArtifactContext());
+        try {
+            return new OutRepresentation(
+                mimeType,
+                db.out(identifier, inputDocument));
+        }
+        catch (ArtifactDatabaseException adbe) {
+            Response response = getResponse();
+            response.setStatus(
+                Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage());
+            return new EmptyRepresentation();
+        }
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java	Sun Sep 13 14:50:53 2009 +0000
@@ -10,6 +10,7 @@
 import org.restlet.ext.xml.DomRepresentation;
 
 import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
 import de.intevation.artifacts.Artifact;
 import de.intevation.artifacts.ArtifactNamespaceContext;
 
@@ -60,22 +61,46 @@
         ArtifactDatabase db = (ArtifactDatabase)getContext()
             .getAttributes().get("database");
 
-        Artifact artifact = db.getArtifact(identifier);
+        try {
+            return new DomRepresentation(
+                MediaType.APPLICATION_XML,
+                db.describe(identifier));
+        }
+        catch (ArtifactDatabaseException adbe) {
+            Response response = getResponse();
+            response.setStatus(
+                Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage());
+            return new EmptyRepresentation();
+        }
+    }
 
-        if (artifact == null) {
+    protected Representation dispatch(
+        String           identifier,
+        String           action,
+        Document         source,
+        ArtifactDatabase db
+    ) {
+        Document out = null;
+
+        try {
+            if (action.equals(FEED)) {
+                out = db.feed(identifier, source);
+            }
+            else if (action.equals(ADVANCE)) {
+                out = db.advance(identifier, source);
+            }
+            else {
+                throw new ArtifactDatabaseException(NO_SUCH_ACTION_MESSAGE);
+            }
+        }
+        catch (ArtifactDatabaseException adbe) {
             Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_NOT_FOUND, NO_ARTIFACT_FOUND);
+            response.setStatus(
+                Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage());
             return new EmptyRepresentation();
         }
 
-        Document description = artifact.describe(db.getArtifactContext());
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("out document: " + description);
-        }
-
-        return new DomRepresentation(
-            MediaType.APPLICATION_XML, description);
+        return new DomRepresentation(MediaType.APPLICATION_XML, out);
     }
 
     @Post
@@ -103,52 +128,17 @@
 
         if (action == null || action.length() == 0) {
             Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MESSAGE);
-            return new EmptyRepresentation();
-        }
-
-        int actionType = -1;
-
-             if (FEED   .equals(action)) actionType = 0;
-        else if (ADVANCE.equals(action)) actionType = 1;
-        else {
-            Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, NO_SUCH_ACTION_MESSAGE);
+            response.setStatus(
+                Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MESSAGE);
             return new EmptyRepresentation();
         }
 
         String identifier = (String)request.getAttributes().get("uuid");
 
-        if (logger.isDebugEnabled()) {
-            logger.debug("looking for artifact id '" + identifier + "'");
-        }
-
         ArtifactDatabase db = (ArtifactDatabase)getContext()
             .getAttributes().get("database");
 
-        Artifact artifact = db.getArtifact(identifier);
-
-        if (artifact == null) {
-            Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_NOT_FOUND, NO_ARTIFACT_FOUND);
-            return new EmptyRepresentation();
-        }
-
-        Document document = null;
-
-        switch (actionType) {
-            case 0:
-                document = artifact.feed(inputDocument, db.getArtifactContext());
-                break;
-            case 1:
-                document = artifact.advance(inputDocument, db.getArtifactContext());
-                break;
-            default:
-                // should not happen
-                return new EmptyRepresentation();
-        }
-
-        return new DomRepresentation(MediaType.APPLICATION_XML, document);
+        return dispatch(identifier, action, inputDocument, db);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java	Sun Sep 13 14:50:53 2009 +0000
@@ -23,8 +23,8 @@
 import java.io.IOException;
 
 import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.ArtifactDatabaseException;
 import de.intevation.artifacts.ArtifactDatabase;
-import de.intevation.artifacts.Artifact;
 
 /**
  * @author Sascha L. Teichmann (sascha.teichmann@intevation)
@@ -68,7 +68,8 @@
 
         if (factory == null || factory.length() == 0) {
             Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, NO_FACTORY_MESSAGE);
+            response.setStatus(
+                Status.CLIENT_ERROR_BAD_REQUEST, NO_FACTORY_MESSAGE);
             return new EmptyRepresentation();
         }
 
@@ -79,19 +80,17 @@
         ArtifactDatabase db = (ArtifactDatabase)getContext()
             .getAttributes().get("database");
 
-        Artifact artifact = db.createArtifactWithFactory(factory);
-
-        if (artifact == null) {
+        try {
+            return new DomRepresentation(
+                MediaType.APPLICATION_XML,
+                db.createArtifactWithFactory(factory));
+        }
+        catch (ArtifactDatabaseException adbe) {
             Response response = getResponse();
-            response.setStatus(Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, NO_ARTIFACT_CREATED);
+            response.setStatus(
+                Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage());
             return new EmptyRepresentation();
         }
-        
-        Document outputDocument = artifact.describe(
-            db.getArtifactContext());
-
-        return new DomRepresentation(
-            MediaType.APPLICATION_XML, outputDocument);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java	Sun Sep 13 14:50:53 2009 +0000
@@ -53,11 +53,13 @@
         Element factories = ec.create("factories");
         root.appendChild(factories);
 
-        String [] factoryNames = db.getArtifactFactoryNames();
+        String [][] factoryNames = db.artifactFactoryNamesAndDescriptions();
 
         for (int i = 0; i < factoryNames.length; ++i) {
+            String [] nd = factoryNames[i];
             Element factoryElement = ec.create("factory");
-            ec.addAttr(factoryElement, "name", factoryNames[i]);
+            ec.addAttr(factoryElement, "name", nd[0]);
+            ec.addAttr(factoryElement, "description", nd[1]);
             factories.appendChild(factoryElement);
         }
 
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java	Sun Sep 13 14:50:53 2009 +0000
@@ -2,14 +2,12 @@
 
 import org.restlet.representation.OutputRepresentation;
 
-import de.intevation.artifacts.Artifact;
-
 import org.restlet.data.MediaType;
 
-import java.io.OutputStream;
+import de.intevation.artifacts.ArtifactDatabase.DeferredOutput;
+
 import java.io.IOException;
-
-import org.w3c.dom.Document;
+import java.io.OutputStream;
 
 /**
  * @author Sascha L. Teichmann (sascha.teichmann@intevation)
@@ -17,24 +15,15 @@
 public class OutRepresentation
 extends      OutputRepresentation
 {
-    protected Artifact artifact;
-    protected Document document;
-    protected Object   context;
+    protected DeferredOutput out;
 
-    public OutRepresentation(
-        MediaType mediaType, 
-        Artifact  artifact, 
-        Document  document,
-        Object    context
-    ) {
+    public OutRepresentation(MediaType mediaType, DeferredOutput out) {
         super(mediaType);
-        this.artifact = artifact;
-        this.document = document;
-        this.context  = context;
+        this.out = out;
     }
 
-    public void write(OutputStream outputStream) throws IOException {
-        artifact.out(document, outputStream, context);
+    public void write(OutputStream output) throws IOException {
+        out.write(output);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/resources/sql/org-h2-driver.properties	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifact-database/src/main/resources/sql/org-h2-driver.properties	Sun Sep 13 14:50:53 2009 +0000
@@ -1,8 +1,8 @@
 artifacts.id.nextval=SELECT NEXTVAL('ARTIFACTS_ID_SEQ')
 
 artifacts.insert=INSERT INTO artifacts \
-    (id, gid, creation, last_access, ttl) \
-    VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?)
+    (id, gid, creation, last_access, ttl, data) \
+    VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, data)
 
 artifacts.update=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP, \
     data = ? WHERE id = ?
--- a/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Sun Sep 13 14:50:53 2009 +0000
@@ -21,7 +21,7 @@
  *   <li>{@link #advance(Document, Object) advance()}: Advances this artifact
  *       to the next internal state</li>
  *   <li>{@link #feed(Document, Object) feed()}: Feed new data into this artifact.</li>
- *   <li>{@link #out(Document, Object) out()}: Produces output for this artifact.</li>
+ *   <li>{@link #out(Document, OutputStream, CallContext) out()}: Produces output for this artifact.</li>
  * </ol>
  *
  * There are two more methods involved with the life cycle of the are:
@@ -42,21 +42,21 @@
      * Identify this artifact.
      * @return Returns unique string to identify this artifact globally.
      */
-    public String identifier();
+    String identifier();
 
     /**
      * Internal hash of this artifact.
      * @return Returns hash that should stay the same if the internal
      *         value has not changed. Useful for caching
      */
-    public String hash();
+    String hash();
 
     /**
      * A description used to build a interface to interact with this artifact.
      * @param context The global context of the runtime system.
      * @return An XML representation of the current state of the artifact.
      */
-    public Document describe(Object context);
+    Document describe(CallContext context);
 
     /**
      * Change the internal state of the artifact.
@@ -64,7 +64,7 @@
      * @param target Target of internal state to move to.
      * @param context The global context of the runtime system.
      */
-    public Document advance(Document target, Object context);
+    Document advance(Document target, CallContext context);
 
     /**
      * Feed data into this artifact.
@@ -72,17 +72,17 @@
      * @param context The global context of the runtime system.
      * @return An XML representation of the success of the feeding.
      */
-    public Document feed(Document data, Object context);
+    Document feed(Document data, CallContext context);
 
     /**
      * Produce output from this artifact.
      * @param format Specifies the format of the output.
      * @param context The global context of the runtime system.
      */
-    public void out(
+    void out(
         Document     format,
         OutputStream out, 
-        Object       context)
+        CallContext  context)
     throws IOException;
 
     /**
@@ -92,7 +92,10 @@
      * @param factory    The factory which created this artifact.
      * @param context    The global context of the runtime system.
      */
-    public void setup(String identifier, ArtifactFactory factory, Object context);
+    public void setup(
+        String          identifier, 
+        ArtifactFactory factory,
+        Object          context);
 
     /**
      * Called from artifact database when an artifact is
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Sat Sep 12 10:45:28 2009 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Sun Sep 13 14:50:53 2009 +0000
@@ -1,5 +1,10 @@
 package de.intevation.artifacts;
 
+import org.w3c.dom.Document;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
 /**
  * Interface of an artifact managing database.
  *
@@ -7,29 +12,32 @@
  */
 public interface ArtifactDatabase
 {
-    /**
-     * List of artifact factories names accessible through the database.
-     * @return names of the factories.
-     */
-    String [] getArtifactFactoryNames();
+    public interface DeferredOutput {
 
-    /**
-     * Look up an artifact by its identifier.
-     * @return the artifact. null if the artifact is not found.
-     */
-    Artifact getArtifact(String identifier);
+        void write(OutputStream output) throws IOException;
+
+    } // interface DeferredOut
 
     /**
-     * Creates new artifact with a certain factory.
-     * @param factoryName the name of the factory. Name out of {@link #getArtifactFactoryNames() getArtifactFactoryNames()}.
+     * List of artifact factories names accessible through the database.
+     * @return pairs of names and descriptions of the factories.
      */
-    Artifact createArtifactWithFactory(String factoryName);
+    String [][] artifactFactoryNamesAndDescriptions();
 
+    Document createArtifactWithFactory(String factory)
+        throws ArtifactDatabaseException;
 
-    /**
-     * Returns the global artifact runtime context.
-     * @return the runtime context
-     */
-    Object getArtifactContext();
+    Document describe(String artifact)
+        throws ArtifactDatabaseException;
+
+    Document advance(String artifact, Document target)
+        throws ArtifactDatabaseException;
+
+    Document feed(String artifact, Document data)
+        throws ArtifactDatabaseException;
+
+    DeferredOutput out(String artifact, Document format)
+        throws ArtifactDatabaseException;
+
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabaseException.java	Sun Sep 13 14:50:53 2009 +0000
@@ -0,0 +1,16 @@
+package de.intevation.artifacts;
+
+/**
+ * @author Sascha L. Teichmann (sascha.teichmann@intevation.de)
+ */
+public class ArtifactDatabaseException
+extends      Exception
+{
+    public ArtifactDatabaseException() {
+    }
+
+    public ArtifactDatabaseException(String msg) {
+        super(msg);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/CallContext.java	Sun Sep 13 14:50:53 2009 +0000
@@ -0,0 +1,18 @@
+package de.intevation.artifacts;
+
+public interface CallContext
+{
+    int NOTHING    = 0;
+    int TOUCH      = 1;
+    int STORE      = 2;
+    int BACKGROUND = 3;
+    // int DELETE     = 4;
+    // int FOREVER    = 5;
+
+    void afterCall(int action);
+
+    void afterBackground(int action);
+
+    Object globalContext();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org