sascha@13: package de.intevation.artifactdatabase;
sascha@13: 
sascha@14: import java.util.UUID;
sascha@14: 
sascha@14: import java.sql.Connection;
sascha@14: import java.sql.SQLException;
sascha@14: import java.sql.PreparedStatement;
sascha@14: import java.sql.Types;
sascha@14: import java.sql.ResultSet;
sascha@14: 
sascha@14: import javax.sql.DataSource;
sascha@14: 
sascha@14: import java.io.IOException;
sascha@15: import java.io.ByteArrayInputStream;
sascha@14: import java.io.ByteArrayOutputStream;
sascha@14: import java.io.ObjectOutputStream;
sascha@15: import java.io.ObjectInputStream;
sascha@14: 
sascha@14: import java.util.zip.GZIPOutputStream;
sascha@15: import java.util.zip.GZIPInputStream;
sascha@14: 
sascha@13: import de.intevation.artifacts.Artifact;
sascha@41: import de.intevation.artifacts.ArtifactFactory;
sascha@41: import de.intevation.artifacts.ArtifactSerializer;
sascha@13: 
sascha@17: import org.apache.log4j.Logger;
sascha@17: 
sascha@13: /**
sascha@13:  *  @author Sascha L. Teichmann
sascha@13:  */
sascha@13: public class Backend
sascha@41: implements   DatabaseCleaner.ArtifactReviver
sascha@13: {
sascha@17:     private static Logger logger = Logger.getLogger(Backend.class);
sascha@17: 
sascha@14:     public static final String SQL_NEXT_ID =
sascha@14:         SQL.get("artifacts.id.nextval");
sascha@14: 
sascha@14:     public static final String SQL_INSERT =
sascha@14:         SQL.get("artifacts.insert");
sascha@14: 
sascha@14:     public static final String SQL_UPDATE =
sascha@14:         SQL.get("artifacts.update");
sascha@14: 
sascha@14:     public static final String SQL_TOUCH =
sascha@14:         SQL.get("artifacts.touch");
sascha@14: 
sascha@15:     public static final String SQL_LOAD_BY_GID =
sascha@15:         SQL.get("artifacts.select.gid");
sascha@15: 
sascha@30:     protected DatabaseCleaner cleaner;
sascha@30: 
sascha@41:     protected FactoryLookup   factoryLookup;
sascha@41: 
sascha@41:     public interface FactoryLookup {
sascha@41: 
sascha@41:         ArtifactFactory getArtifactFactory(String factoryName);
sascha@41: 
sascha@41:     } // interface FactoryLookup
sascha@41: 
sascha@32:     public final class PersistentArtifact
sascha@32:     extends            Id
sascha@14:     {
sascha@41:         private Artifact           artifact;
sascha@41:         private ArtifactSerializer serializer;
sascha@14: 
sascha@41:         public PersistentArtifact(
sascha@41:             Artifact           artifact, 
sascha@41:             ArtifactSerializer serializer,
sascha@41:             int                id
sascha@41:         ) {
sascha@32:             super(id);
sascha@41:             this.artifact   = artifact;
sascha@41:             this.serializer = serializer;
sascha@14:         }
sascha@14: 
sascha@32:         public Artifact getArtifact() {
sascha@32:             return artifact;
sascha@14:         }
sascha@14: 
sascha@41:         public ArtifactSerializer getSerializer() {
sascha@41:             return serializer;
sascha@41:         }
sascha@41: 
sascha@32:         public void store() {
sascha@38:             if (logger.isDebugEnabled()) {
sascha@38:                 logger.debug("storing artifact id = " + getId());
sascha@38:             }
sascha@32:             Backend.this.store(this);
sascha@14:         }
sascha@14: 
sascha@32:         public void touch() {
sascha@38:             if (logger.isDebugEnabled()) {
sascha@38:                 logger.debug("touching artifact id = " + getId());
sascha@38:             }
sascha@32:             Backend.this.touch(this);
sascha@14:         }
sascha@32:     } // class ArtifactWithId
sascha@14: 
sascha@13:     public Backend() {
sascha@13:     }
sascha@13: 
sascha@30:     public Backend(DatabaseCleaner cleaner) {
sascha@30:         this.cleaner = cleaner;
sascha@30:     }
sascha@30: 
sascha@41:     public void setFactoryLookup(FactoryLookup factoryLookup) {
sascha@41:         this.factoryLookup = factoryLookup;
sascha@41:     }
sascha@41: 
sascha@32:     public void setCleaner(DatabaseCleaner cleaner) {
sascha@32:         this.cleaner = cleaner;
sascha@32:     }
sascha@32: 
sascha@32:     public String newIdentifier() {
sascha@32:         UUID uuid = UUID.randomUUID();
sascha@32:         // TODO: check database for collisions.
sascha@32:         return uuid.toString();
sascha@32:     }
sascha@32: 
sascha@32:     public PersistentArtifact storeInitially(
sascha@41:         Artifact        artifact,
sascha@41:         ArtifactFactory factory,
sascha@41:         Long            ttl
sascha@32:     ) 
sascha@32:     throws Exception
sascha@32:     {
sascha@32:         return new PersistentArtifact(
sascha@32:             artifact,
sascha@41:             factory.getSerializer(),
sascha@41:             insertDatabase(artifact, factory, ttl));
sascha@32:     }
sascha@32:     
sascha@32:     public PersistentArtifact getArtifact(String identifer) {
sascha@15: 
sascha@15:         try {
sascha@32:             UUID.fromString(identifer);
sascha@15:         }
sascha@15:         catch (IllegalArgumentException iae) {
sascha@17:             logger.warn(iae.getLocalizedMessage());
sascha@15:             return null;
sascha@15:         }
sascha@15: 
sascha@15:         Connection        connection  = null;
sascha@15:         PreparedStatement stmnt_load  = null;
sascha@15:         ResultSet         load_result = null;
sascha@15: 
sascha@15:         DataSource dataSource = DBConnection.getDataSource();
sascha@15:         try {
sascha@15:             connection = dataSource.getConnection();
sascha@15:             stmnt_load = connection.prepareStatement(SQL_LOAD_BY_GID);
sascha@32:             stmnt_load.setString(1, identifer);
sascha@15: 
sascha@15:             load_result = stmnt_load.executeQuery();
sascha@15: 
sascha@15:             if (!load_result.next()) {
sascha@15:                 return null;
sascha@15:             }
sascha@15: 
sascha@15:             int  id  = load_result.getInt(1);
sascha@15:             long ttl = load_result.getLong(3);
sascha@15: 
sascha@15:             if (!load_result.wasNull()) { // real time to life
sascha@15:                 long last_access = load_result.getTimestamp(2).getTime();
tim@34:                 if (last_access + ttl < System.currentTimeMillis()) {
sascha@15:                     artifactOutdated(id);
sascha@15:                     return null;
sascha@15:                 }
sascha@15:             }
sascha@15: 
sascha@41:             String factoryName = load_result.getString(4);
sascha@15: 
sascha@41:             if (factoryLookup == null) {
sascha@41:                 logger.error("factory lookup == null");
sascha@41:                 return null;
sascha@41:             }
sascha@41: 
sascha@41:             ArtifactFactory factory = factoryLookup
sascha@41:                 .getArtifactFactory(factoryName);
sascha@41: 
sascha@41:             if (factory == null) {
sascha@41:                 logger.error("factory '" + factoryName + "' not found");
sascha@41:                 return null;
sascha@41:             }
sascha@41: 
sascha@41:             ArtifactSerializer serializer =
sascha@41:                 factory.getSerializer();
sascha@41: 
sascha@41:             byte [] bytes = load_result.getBytes(5);
sascha@41: 
sascha@41:             Artifact artifact = serializer.fromBytes(bytes);
sascha@15: 
sascha@32:             return artifact == null
sascha@32:                 ? null
sascha@41:                 : new PersistentArtifact(artifact, serializer, id);
sascha@15:         }
sascha@15:         catch (SQLException sqle) {
sascha@17:             logger.error(sqle.getLocalizedMessage(), sqle);
sascha@15:         }
sascha@15:         finally {
sascha@15:             if (load_result != null) {
sascha@15:                 try { load_result.close(); }
sascha@15:                 catch (SQLException sqle) {}
sascha@15:             }
sascha@15:             if (stmnt_load != null) {
sascha@15:                 try { load_result.close(); }
sascha@15:                 catch (SQLException sqle) {}
sascha@15:             }
sascha@15:             if (connection != null) {
sascha@15:                 try { connection.close(); } 
sascha@15:                 catch (SQLException sqle) {}
sascha@15:             }
sascha@15:         }
sascha@15:         return null;
sascha@15:     }
sascha@15: 
sascha@15:     protected void artifactOutdated(int id) {
sascha@17:         logger.info("artifactOutdated: id = " + id);
sascha@30:         if (cleaner != null) {
sascha@30:             cleaner.wakeup();
sascha@30:         }
sascha@15:     }
sascha@15: 
sascha@41:     public Artifact reviveArtifact(String factoryName, byte [] bytes) {
sascha@41:         if (factoryLookup == null) {
sascha@41:             logger.error("reviveArtifact: factory lookup == null");
sascha@41:             return null;
sascha@41:         }
sascha@41:         ArtifactFactory factory = factoryLookup
sascha@41:             .getArtifactFactory(factoryName);
sascha@32: 
sascha@41:         if (factory == null) {
sascha@41:             logger.error("reviveArtifact: no factory '" + factoryName + "' found");
sascha@41:             return null;
sascha@41:         }
sascha@41: 
sascha@41:         ArtifactSerializer serializer = factory.getSerializer();
sascha@41: 
sascha@41:         return serializer.fromBytes(bytes);
sascha@41:     }
sascha@41: 
sascha@41:     protected int insertDatabase(
sascha@41:         Artifact        artifact, 
sascha@41:         ArtifactFactory factory,
sascha@41:         Long            ttl
sascha@41:     ) {
sascha@32:         String uuid = artifact.identifier();
sascha@32: 
sascha@14:         Connection        connection    = null;
sascha@14:         PreparedStatement stmnt_next_id = null;
sascha@14:         PreparedStatement stmnt_insert  = null;
sascha@14:         ResultSet         res_id        = null;
sascha@14: 
sascha@14:         DataSource dataSource = DBConnection.getDataSource();
sascha@14:         try {
sascha@14:             connection = dataSource.getConnection();
sascha@14:             try {
sascha@14:                 connection.setAutoCommit(false);
sascha@14: 
sascha@14:                 stmnt_next_id = connection.prepareStatement(SQL_NEXT_ID);
sascha@14:                 stmnt_insert  = connection.prepareStatement(SQL_INSERT);
sascha@14: 
sascha@14:                 res_id = stmnt_next_id.executeQuery();
sascha@14: 
sascha@14:                 if (!res_id.next()) {
sascha@14:                     throw new RuntimeException("No id generated");
sascha@14:                 }
sascha@14: 
sascha@14:                 int id = res_id.getInt(1);
sascha@14: 
sascha@14:                 stmnt_insert.setInt(1, id);
sascha@32:                 stmnt_insert.setString(2, uuid);
sascha@14:                 if (ttl == null) {
sascha@14:                     stmnt_insert.setNull(3, Types.BIGINT);
sascha@14:                 }
sascha@14:                 else {
sascha@14:                     stmnt_insert.setLong(3, ttl.longValue());
sascha@14:                 }
sascha@14: 
sascha@41:                 stmnt_insert.setString(4, factory.getName());
sascha@41: 
sascha@41:                 stmnt_insert.setBytes(
sascha@41:                     5,
sascha@41:                     factory.getSerializer().toBytes(artifact));
sascha@32: 
sascha@14:                 stmnt_insert.execute();
sascha@14: 
sascha@14:                 connection.commit();
sascha@14: 
sascha@14:                 return id;
sascha@14:             }
sascha@14:             catch (SQLException sqle) {
sascha@14:                 connection.rollback();
sascha@14:                 throw sqle;
sascha@14:             }
sascha@14:         }
sascha@14:         catch (SQLException sqle) {
sascha@17:             logger.error(sqle.getLocalizedMessage(), sqle);
sascha@14:         }
sascha@14:         finally {
sascha@14:             if (res_id != null) {
sascha@14:                 try { res_id.close(); }
sascha@14:                 catch (SQLException sqle) {}
sascha@14:             }
sascha@14:             if (stmnt_insert != null) {
sascha@14:                 try { stmnt_insert.close(); }
sascha@14:                 catch (SQLException sqle) {}
sascha@14:             }
sascha@14:             if (stmnt_next_id != null) {
sascha@14:                 try { stmnt_next_id.close(); }
sascha@14:                 catch (SQLException sqle) {}
sascha@14:             }
sascha@14:             if (connection != null) {
sascha@14:                 try { connection.close(); }
sascha@14:                 catch (SQLException sqle) {}
sascha@14:             }
sascha@14:         }
sascha@14:         throw new RuntimeException("failed insert artifact into database");
sascha@14:     }
sascha@14: 
sascha@32:     public void touch(PersistentArtifact artifact) {
sascha@26: 
sascha@14:         try {
sascha@26:             Connection        connection  = null;
sascha@26:             PreparedStatement stmnt_touch = null;
sascha@26:             DataSource        dataSource  = DBConnection.getDataSource();
sascha@14:             try {
sascha@26:                 connection = dataSource.getConnection();
sascha@26:                 try {
sascha@26:                     connection.setAutoCommit(false);
sascha@41:                     stmnt_touch = connection.prepareStatement(SQL_TOUCH);
sascha@32:                     stmnt_touch.setInt(1, artifact.getId());
sascha@26:                     stmnt_touch.execute();
sascha@26:                     connection.commit();
sascha@26:                 }
sascha@26:                 catch (SQLException sqle) {
sascha@26:                     connection.rollback();
sascha@26:                 }
sascha@14:             }
sascha@14:             catch (SQLException sqle) {
sascha@26:                 logger.error(sqle.getLocalizedMessage(), sqle);
sascha@26:             }
sascha@26:             finally {
sascha@26:                 if (stmnt_touch != null) {
sascha@26:                     try { stmnt_touch.close(); }
sascha@26:                     catch (SQLException sqle) {}
sascha@26:                 }
sascha@26:                 if (connection != null) {
sascha@26:                     try { connection.close(); }
sascha@26:                     catch (SQLException sqle) {}
sascha@26:                 }
sascha@14:             }
sascha@14:         }
sascha@26:         catch (Exception e) {
sascha@26:             logger.error(e.getLocalizedMessage(), e);
sascha@14:         }
sascha@14:     }
sascha@14: 
sascha@32:     public void store(PersistentArtifact artifact) {
sascha@26: 
sascha@14:         try {
sascha@26:             Connection        connection   = null;
sascha@26:             PreparedStatement stmnt_update = null;
sascha@26:             DataSource        dataSource   = DBConnection.getDataSource();
sascha@14:             try {
sascha@26:                 connection = dataSource.getConnection();
sascha@26:                 try {
sascha@26:                     connection.setAutoCommit(false);
sascha@26:                     stmnt_update = connection.prepareStatement(SQL_UPDATE);
sascha@32:                     stmnt_update.setInt(2, artifact.getId());
sascha@26: 
sascha@41:                     byte [] bytes = artifact
sascha@41:                         .getSerializer()
sascha@41:                         .toBytes(artifact.getArtifact());
sascha@26: 
sascha@26:                     stmnt_update.setBytes(1, bytes);
sascha@26:                     stmnt_update.execute();
sascha@26:                     connection.commit();
sascha@26:                 }
sascha@26:                 catch (SQLException sqle) {
sascha@26:                     connection.rollback();
sascha@26:                 }
sascha@14:             }
sascha@14:             catch (SQLException sqle) {
sascha@26:                 logger.error(sqle.getLocalizedMessage(), sqle);
sascha@26:             }
sascha@26:             finally {
sascha@26:                 if (stmnt_update != null) {
sascha@26:                     try { stmnt_update.close(); }
sascha@26:                     catch (SQLException sqle) {}
sascha@26:                 }
sascha@26:                 if (connection != null) {
sascha@26:                     try { connection.close(); }
sascha@26:                     catch (SQLException sqle) {}
sascha@26:                 }
sascha@14:             }
sascha@14:         }
sascha@26:         catch (Exception e) {
sascha@26:             logger.error(e.getLocalizedMessage(), e);
sascha@14:         }
sascha@14:     }
sascha@13: }
sascha@13: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8: