# HG changeset patch # User Sascha L. Teichmann # Date 1252853453 0 # Node ID c2d53bd30ab84429dfe7a01d1c48636868cccc87 # Parent c4d85a8532d1c1f5502cffcfdbe8650de43cefec Re-factored artifact API for better integration of background processing. artifacts/trunk@78 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r c4d85a8532d1 -r c2d53bd30ab8 Changelog --- 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 + + 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 * artifacts/src/main/java/de/intevation/artifacts/Artifact.java(out): diff -r c4d85a8532d1 -r c2d53bd30ab8 TODO --- 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. diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/App.java --- 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); } diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java --- 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( diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java --- 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 { diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/Id.java --- /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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java --- 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); } diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifact-database/src/main/resources/sql/org-h2-driver.properties --- 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 = ? diff -r c4d85a8532d1 -r c2d53bd30ab8 artifacts/src/main/java/de/intevation/artifacts/Artifact.java --- 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 @@ *
  • {@link #advance(Document, Object) advance()}: Advances this artifact * to the next internal state
  • *
  • {@link #feed(Document, Object) feed()}: Feed new data into this artifact.
  • - *
  • {@link #out(Document, Object) out()}: Produces output for this artifact.
  • + *
  • {@link #out(Document, OutputStream, CallContext) out()}: Produces output for this artifact.
  • * * * 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 diff -r c4d85a8532d1 -r c2d53bd30ab8 artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java --- 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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabaseException.java --- /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: diff -r c4d85a8532d1 -r c2d53bd30ab8 artifacts/src/main/java/de/intevation/artifacts/CallContext.java --- /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: