# HG changeset patch # User Sascha L. Teichmann # Date 1253724912 0 # Node ID 5e4bc24ea4387933fc836b29858d91fb258c8747 # Parent af22d4de275c7ba379466cc1947a8f07d22d4430 Made serilization more flexible. DB update required!!! Fixed problem with touching artifacts in database. artifacts/trunk@119 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r af22d4de275c -r 5e4bc24ea438 Changelog --- a/Changelog Wed Sep 23 08:27:35 2009 +0000 +++ b/Changelog Wed Sep 23 16:55:12 2009 +0000 @@ -1,3 +1,44 @@ +2009-09-23 Sascha L. Teichmann + + * artifact-database/doc/schema-pg.sql, artifact-database/doc/schema-h2.sql: + Added column 'factory' to artifacts table to store the name of the + creating factory. + + !!! + !!! THIS BREAKS OLD ARTIFACT DATABASES! + !!! + !!! To update old databases issue at database prompt: + !!! ALTER TABLE artifacts ADD COLUMN factory VARCHAR(256) NOT NULL; + !!! + + * artifact-database/src/main/resources/sql/org-h2-driver.properties, + artifact-database/src/main/resources/sql/org-postgresql-driver.properties: + Fixed issue in SQL statements for touching artifacts. Fill factory name + into artifact relation. + + * artifacts/src/main/java/de/intevation/artifacts/ArtifactSerializer.java: + To allow different serialization models each ArtifactFactory has + serializer for the artifacts. + + * artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java: + Add method to return ArtifactSerializer. + + * artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactSerializer.java: + Uses Java standard object serialisation. + + * artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java: + Uses th DefaultArtifactSerializer as ArtifactSerializer + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: + The serialisation method is select via the 'factory' column of the 'artifacts' + table. + + * artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java, + artifact-database/src/main/java/de/intevation/artifactdatabase/App.java, + artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java, + artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java: + Adjusted to fit the new serialisation model. + 2009-09-23 Sascha L. Teichmann * artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseResource.java: diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/doc/schema-h2.sql --- a/artifact-database/doc/schema-h2.sql Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/doc/schema-h2.sql Wed Sep 23 16:55:12 2009 +0000 @@ -14,6 +14,7 @@ creation TIMESTAMP NOT NULL, last_access TIMESTAMP NOT NULL, ttl BIGINT, -- NULL means eternal + factory VARCHAR(256) NOT NULL, data BINARY ); diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/doc/schema-pg.sql --- a/artifact-database/doc/schema-pg.sql Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/doc/schema-pg.sql Wed Sep 23 16:55:12 2009 +0000 @@ -14,6 +14,7 @@ creation timestamp NOT NULL, last_access timestamp NOT NULL, ttl bigint, -- NULL means eternal + factory VARCHAR(256) NOT NULL, data bytea ); diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/App.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java Wed Sep 23 16:55:12 2009 +0000 @@ -46,7 +46,7 @@ bootstrap, backend); DatabaseCleaner cleaner = new DatabaseCleaner( - bootstrap.getContext()); + bootstrap.getContext(), backend); backend.setCleaner(cleaner); diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java Wed Sep 23 16:55:12 2009 +0000 @@ -24,7 +24,7 @@ * @author Sascha L. Teichmann */ public class ArtifactDatabaseImpl -implements ArtifactDatabase, Id.Filter +implements ArtifactDatabase, Id.Filter, Backend.FactoryLookup { private static Logger logger = Logger.getLogger(ArtifactDatabaseImpl.class); @@ -139,6 +139,10 @@ public ArtifactDatabaseImpl() { } + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { + this(bootstrap, null); + } + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { backgroundIds = new HashSet(); @@ -162,7 +166,14 @@ context = bootstrap.getContext(); - this.backend = backend; + wireWithBackend(backend); + } + + public void wireWithBackend(Backend backend) { + if (backend != null) { + this.backend = backend; + backend.setFactoryLookup(this); + } } protected void fromBackground(PersistentArtifact artifact, int action) { @@ -212,11 +223,14 @@ return factoryNamesAndDescription; } + public ArtifactFactory getArtifactFactory(String factoryName) { + return (ArtifactFactory)name2factory.get(factoryName); + } + public Document createArtifactWithFactory(String factoryName) throws ArtifactDatabaseException { - ArtifactFactory factory = (ArtifactFactory)name2factory.get( - factoryName); + ArtifactFactory factory = getArtifactFactory(factoryName); if (factory == null) { throw new ArtifactDatabaseException(NO_SUCH_FACTORY); @@ -235,6 +249,7 @@ try { persistentArtifact = backend.storeInitially( artifact, + factory, factory.timeToLiveUntouched(artifact, context)); } catch (Exception e) { diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java Wed Sep 23 16:55:12 2009 +0000 @@ -20,6 +20,8 @@ import java.util.zip.GZIPInputStream; import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactSerializer; import org.apache.log4j.Logger; @@ -27,6 +29,7 @@ * @author Sascha L. Teichmann */ public class Backend +implements DatabaseCleaner.ArtifactReviver { private static Logger logger = Logger.getLogger(Backend.class); @@ -47,20 +50,38 @@ protected DatabaseCleaner cleaner; + protected FactoryLookup factoryLookup; + + public interface FactoryLookup { + + ArtifactFactory getArtifactFactory(String factoryName); + + } // interface FactoryLookup + public final class PersistentArtifact extends Id { - private Artifact artifact; + private Artifact artifact; + private ArtifactSerializer serializer; - public PersistentArtifact(Artifact artifact, int id) { + public PersistentArtifact( + Artifact artifact, + ArtifactSerializer serializer, + int id + ) { super(id); - this.artifact = artifact; + this.artifact = artifact; + this.serializer = serializer; } public Artifact getArtifact() { return artifact; } + public ArtifactSerializer getSerializer() { + return serializer; + } + public void store() { if (logger.isDebugEnabled()) { logger.debug("storing artifact id = " + getId()); @@ -83,6 +104,10 @@ this.cleaner = cleaner; } + public void setFactoryLookup(FactoryLookup factoryLookup) { + this.factoryLookup = factoryLookup; + } + public void setCleaner(DatabaseCleaner cleaner) { this.cleaner = cleaner; } @@ -94,14 +119,16 @@ } public PersistentArtifact storeInitially( - Artifact artifact, - Long ttl + Artifact artifact, + ArtifactFactory factory, + Long ttl ) throws Exception { return new PersistentArtifact( artifact, - insertDatabase(artifact, ttl)); + factory.getSerializer(), + insertDatabase(artifact, factory, ttl)); } public PersistentArtifact getArtifact(String identifer) { @@ -141,13 +168,31 @@ } } - byte [] bytes = load_result.getBytes(4); + String factoryName = load_result.getString(4); - Artifact artifact = restoreArtifact(bytes); + if (factoryLookup == null) { + logger.error("factory lookup == null"); + return null; + } + + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error("factory '" + factoryName + "' not found"); + return null; + } + + ArtifactSerializer serializer = + factory.getSerializer(); + + byte [] bytes = load_result.getBytes(5); + + Artifact artifact = serializer.fromBytes(bytes); return artifact == null ? null - : new PersistentArtifact(artifact, id); + : new PersistentArtifact(artifact, serializer, id); } catch (SQLException sqle) { logger.error(sqle.getLocalizedMessage(), sqle); @@ -169,58 +214,6 @@ 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) { - return null; - } - - ObjectInputStream ois = null; - - try { - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - GZIPInputStream gis = new GZIPInputStream(bis); - ois = new ObjectInputStream(gis); - - return (Artifact)ois.readObject(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - finally { - if (ois != null) { - try { ois.close(); } - catch (IOException ioe) { } - } - } - - return null; - } - protected void artifactOutdated(int id) { logger.info("artifactOutdated: id = " + id); if (cleaner != null) { @@ -228,8 +221,29 @@ } } - protected int insertDatabase(Artifact artifact, Long ttl) { + public Artifact reviveArtifact(String factoryName, byte [] bytes) { + if (factoryLookup == null) { + logger.error("reviveArtifact: factory lookup == null"); + return null; + } + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + if (factory == null) { + logger.error("reviveArtifact: no factory '" + factoryName + "' found"); + return null; + } + + ArtifactSerializer serializer = factory.getSerializer(); + + return serializer.fromBytes(bytes); + } + + protected int insertDatabase( + Artifact artifact, + ArtifactFactory factory, + Long ttl + ) { String uuid = artifact.identifier(); Connection connection = null; @@ -263,7 +277,11 @@ stmnt_insert.setLong(3, ttl.longValue()); } - stmnt_insert.setBytes(4, toBytes(artifact)); + stmnt_insert.setString(4, factory.getName()); + + stmnt_insert.setBytes( + 5, + factory.getSerializer().toBytes(artifact)); stmnt_insert.execute(); @@ -310,7 +328,7 @@ connection = dataSource.getConnection(); try { connection.setAutoCommit(false); - stmnt_touch = connection.prepareStatement(SQL_UPDATE); + stmnt_touch = connection.prepareStatement(SQL_TOUCH); stmnt_touch.setInt(1, artifact.getId()); stmnt_touch.execute(); connection.commit(); @@ -351,7 +369,9 @@ stmnt_update = connection.prepareStatement(SQL_UPDATE); stmnt_update.setInt(2, artifact.getId()); - byte [] bytes = toBytes(artifact.getArtifact()); + byte [] bytes = artifact + .getSerializer() + .toBytes(artifact.getArtifact()); stmnt_update.setBytes(1, bytes); stmnt_update.execute(); diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java Wed Sep 23 16:55:12 2009 +0000 @@ -20,6 +20,12 @@ public class DatabaseCleaner extends Thread { + public interface ArtifactReviver { + + Artifact reviveArtifact(String factoryName, byte [] bytes); + + } // interface ArtifactReviver + private static Logger logger = Logger.getLogger(DatabaseCleaner.class); public static final int MAX_ROWS = 50; @@ -44,13 +50,16 @@ protected Id.Filter filter; + protected ArtifactReviver reviver; + public DatabaseCleaner() { } - public DatabaseCleaner(Object context) { + public DatabaseCleaner(Object context, ArtifactReviver reviver) { setDaemon(true); sleepTime = getSleepTime(); this.context = context; + this.reviver = reviver; } public void setFilter(Id.Filter filter) { @@ -83,10 +92,12 @@ extends Id { byte [] data; + String factoryName; - public IdData(int id, byte [] data) { + public IdData(int id, String factoryName, byte [] data) { super(id); - this.data = data; + this.factoryName = factoryName; + this.data = data; } } // class IdData @@ -128,7 +139,9 @@ while (result.next()) { ids.add(new IdData( - result.getInt(1), result.getBytes(2))); + result.getInt(1), + result.getString(2), + result.getBytes(3))); } result.close(); result = null; @@ -143,8 +156,8 @@ for (int i = ids.size()-1; i >= 0; --i) { IdData idData = (IdData)ids.get(i); - Artifact artifact = Backend.restoreArtifact( - idData.data); + Artifact artifact = reviver.reviveArtifact( + idData.factoryName, idData.data); idData.data = null; deleteId.setInt(1, idData.id); diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java Wed Sep 23 16:55:12 2009 +0000 @@ -5,6 +5,7 @@ import de.intevation.artifacts.ArtifactFactory; import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactSerializer; import org.apache.log4j.Logger; @@ -106,5 +107,9 @@ public Long timeToLiveUntouched(Artifact artifact, Object context) { return ttl; } + + public ArtifactSerializer getSerializer() { + return DefaultArtifactSerializer.INSTANCE; + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactSerializer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactSerializer.java Wed Sep 23 16:55:12 2009 +0000 @@ -0,0 +1,98 @@ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactSerializer; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; + +import java.util.zip.GZIPOutputStream; +import java.util.zip.GZIPInputStream; + +import org.apache.log4j.Logger; + +/** + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + */ +public class DefaultArtifactSerializer +implements ArtifactSerializer +{ + private static Logger logger = + Logger.getLogger(DefaultArtifactSerializer.class); + + public static final ArtifactSerializer INSTANCE = + new DefaultArtifactSerializer(); + + public DefaultArtifactSerializer() { + } + + public Artifact fromBytes(byte [] bytes) { + + if (bytes == null) { + return null; + } + + ObjectInputStream ois = null; + + try { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + GZIPInputStream gis = new GZIPInputStream(bis); + ois = getObjectInputStream(gis); + + return (Artifact)ois.readObject(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + finally { + if (ois != null) { + try { ois.close(); } + catch (IOException ioe) { } + } + } + + return null; + } + + public byte [] toBytes(Artifact artifact) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + GZIPOutputStream gos = new GZIPOutputStream(bos); + ObjectOutputStream oos = getObjectOutputStream(gos); + + oos.writeObject(artifact); + oos.flush(); + oos.close(); + + return bos.toByteArray(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + throw new RuntimeException(ioe); + } + } + + protected ObjectInputStream getObjectInputStream(InputStream is) + throws IOException + { + return new ObjectInputStream(is); + } + + protected ObjectOutputStream getObjectOutputStream(OutputStream os) + throws IOException + { + return new ObjectOutputStream(os); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java Wed Sep 23 16:55:12 2009 +0000 @@ -62,6 +62,8 @@ factories.appendChild(factoryElement); } + document.normalizeDocument(); + return new DomRepresentation( MediaType.APPLICATION_XML, document); } diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/resources/sql/org-h2-driver.properties --- a/artifact-database/src/main/resources/sql/org-h2-driver.properties Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/resources/sql/org-h2-driver.properties Wed Sep 23 16:55:12 2009 +0000 @@ -1,17 +1,17 @@ artifacts.id.nextval=SELECT NEXTVAL('ARTIFACTS_ID_SEQ') artifacts.insert=INSERT INTO artifacts \ - (id, gid, creation, last_access, ttl, data) \ - VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?) + (id, gid, creation, last_access, ttl, factory, data) \ + VALUES (?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?, ?) artifacts.update=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP, \ data = ? WHERE id = ? -artifacts.touch=UPDATE last_access = CURRENT_TIMESTAMP WHERE id = ? +artifacts.touch=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP WHERE id = ? -artifacts.outdated=SELECT id, data FROM artifacts WHERE ttl IS NOT NULL \ +artifacts.outdated=SELECT id, factory, data FROM artifacts WHERE ttl IS NOT NULL \ AND CURRENT_TIMESTAMP - last_access > ttl LIMIT 50 -artifacts.select.gid=SELECT id, last_access, ttl, data FROM artifacts WHERE gid = ? +artifacts.select.gid=SELECT id, last_access, ttl, factory, data FROM artifacts WHERE gid = ? artifacts.delete=DELETE FROM artifacts WHERE id = ? diff -r af22d4de275c -r 5e4bc24ea438 artifact-database/src/main/resources/sql/org-postgresql-driver.properties --- a/artifact-database/src/main/resources/sql/org-postgresql-driver.properties Wed Sep 23 08:27:35 2009 +0000 +++ b/artifact-database/src/main/resources/sql/org-postgresql-driver.properties Wed Sep 23 16:55:12 2009 +0000 @@ -1,17 +1,17 @@ artifacts.id.nextval=SELECT nextval('ARTIFACTS_ID_SEQ') artifacts.insert=INSERT INTO artifacts \ - (id, gid, creation, last_access, ttl, data) \ - VALUES (?, ?::uuid, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?) + (id, gid, creation, last_access, ttl, factory, data) \ + VALUES (?, ?::uuid, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, ?, ?, ?) artifacts.update=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP, \ data = ? WHERE id = ? -artifacts.touch=UPDATE last_access = CURRENT_TIMESTAMP WHERE id = ? +artifacts.touch=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP WHERE id = ? -artifacts.outdated=SELECT id, data FROM artifacts WHERE ttl IS NOT NULL \ +artifacts.outdated=SELECT id, factory, data FROM artifacts WHERE ttl IS NOT NULL \ AND CURRENT_TIMESTAMP - last_access > (ttl || ' microseconds')::interval LIMIT 50 -artifacts.select.gid=SELECT id, last_access, ttl, data FROM artifacts WHERE gid = ?::uuid +artifacts.select.gid=SELECT id, last_access, ttl, factory, data FROM artifacts WHERE gid = ?::uuid artifacts.delete=DELETE FROM artifacts WHERE id = ? diff -r af22d4de275c -r 5e4bc24ea438 artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java --- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java Wed Sep 23 08:27:35 2009 +0000 +++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java Wed Sep 23 16:55:12 2009 +0000 @@ -51,5 +51,7 @@ * return time to live in ms. null means eternal. */ Long timeToLiveUntouched(Artifact artifact, Object context); + + ArtifactSerializer getSerializer(); } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: diff -r af22d4de275c -r 5e4bc24ea438 artifacts/src/main/java/de/intevation/artifacts/ArtifactSerializer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactSerializer.java Wed Sep 23 16:55:12 2009 +0000 @@ -0,0 +1,13 @@ +package de.intevation.artifacts; + +/** + * Interface to make artifact persistent. + * + * @author Sascha L. Teichmann (sascha.teichmann@intevation.de) + */ +public interface ArtifactSerializer +{ + Artifact fromBytes(byte [] bytes); + byte [] toBytes(Artifact artifact); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: