Mercurial > dive4elements > framework
diff artifact-database/src/main/java/org/dive4elements/artifactdatabase/Backend.java @ 473:d0ac790a6c89 dive4elements-move
Moved directories to org.dive4elements
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 10:57:18 +0200 |
parents | artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java@e79ad624f94c |
children | 415df0fc4fa1 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/Backend.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,1914 @@ +/* + * Copyright (c) 2010, 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactDatabase.ArtifactLoadedCallback; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactSerializer; +import de.intevation.artifacts.CollectionItem; +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + +import de.intevation.artifacts.common.utils.StringUtils; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.LRUCache; + +import de.intevation.artifactdatabase.db.SQLExecutor; +import de.intevation.artifactdatabase.db.SQL; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; + +import java.util.List; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * The backend implements the low level layer used to store artifacts + * in a SQL database. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class Backend +implements DatabaseCleaner.ArtifactReviver +{ + private static Logger logger = Logger.getLogger(Backend.class); + + /** + * The SQL statement to create new artifact id inside the database. + */ + public String SQL_NEXT_ID; + + /** + * The SQL statement to insert an artifact into the database. + */ + public String SQL_INSERT; + + /** + * The SQL statement to update some columns of an existing + * artifact in the database. + */ + public String SQL_UPDATE; + + /** + * The SQL statement to touch the access time of an + * artifact inside the database. + */ + public String SQL_TOUCH; + + /** + * The SQL statement to load an artifact by a given + * identifier from the database. + */ + public String SQL_LOAD_BY_GID; + + /** + * The SQL statement to get the database id of an artifact + * identified by the identifier. + */ + public String SQL_GET_ID; + + /** + * The SQL statement to replace the content of an + * existing artifact inside the database. + */ + public String SQL_REPLACE; + + // USER SQL + + public String SQL_USERS_NEXT_ID; + public String SQL_USERS_INSERT; + public String SQL_USERS_SELECT_ID_BY_GID; + public String SQL_USERS_SELECT_GID; + public String SQL_USERS_SELECT_ACCOUNT; + public String SQL_USERS_DELETE_ID; + public String SQL_USERS_DELETE_COLLECTIONS; + public String SQL_USERS_SELECT_ALL; + public String SQL_USERS_COLLECTIONS; + public String SQL_USERS_COLLECTION_IDS; + public String SQL_USERS_DELETE_ALL_COLLECTIONS; + public String SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY; + public String SQL_OUTDATE_ARTIFACTS_COLLECTION; + public String SQL_UPDATE_COLLECTION_TTL; + public String SQL_UPDATE_COLLECTION_NAME; + public String SQL_OUTDATE_ARTIFACTS_USER; + public String SQL_DELETE_USER_COLLECTION_ITEMS; + public String SQL_COLLECTIONS_NEXT_ID; + public String SQL_COLLECTIONS_INSERT; + public String SQL_COLLECTIONS_SELECT_USER; + public String SQL_COLLECTIONS_SELECT_ALL; + public String SQL_COLLECTIONS_SELECT_GID; + public String SQL_COLLECTIONS_CREATION_TIME; + public String SQL_COLLECTIONS_ID_BY_GID; + public String SQL_COLLECTIONS_OLDEST_ARTIFACT; + public String SQL_DELETE_COLLECTION_ITEMS; + public String SQL_DELETE_COLLECTION; + public String SQL_COLLECTION_CHECK_ARTIFACT; + public String SQL_COLLECTION_ITEMS_ID_NEXTVAL; + public String SQL_COLLECTION_ITEMS_INSERT; + public String SQL_COLLECTION_GET_ATTRIBUTE; + public String SQL_COLLECTION_SET_ATTRIBUTE; + public String SQL_COLLECTION_ITEM_GET_ATTRIBUTE; + public String SQL_COLLECTION_ITEM_SET_ATTRIBUTE; + public String SQL_COLLECTIONS_TOUCH_BY_GID; + public String SQL_COLLECTION_ITEM_ID_CID_AID; + public String SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT; + public String SQL_COLLECTION_ITEM_DELETE; + public String SQL_COLLECTIONS_TOUCH_BY_ID; + public String SQL_COLLECTION_ITEMS_LIST_GID; + public String SQL_ALL_ARTIFACTS; + + /** The singleton.*/ + protected static Backend instance; + + protected SQLExecutor sqlExecutor; + + protected List<BackendListener> listeners; + + protected DBConfig config; + + /** + * The database cleaner. Reference is stored here because + * the cleaner is woken up if the backend finds an outdated + * artifact. This artifact should be removed as soon as + * possible. + */ + protected DatabaseCleaner cleaner; + + /** + * To revive an artifact from the bytes coming from the database + * we need the artifact factory which references the artifact + * serializer which is able to do the reviving job. + */ + protected FactoryLookup factoryLookup; + + /** + * Little helper interface to decouple the ArtifactDatabase + * from the Backend. A ArtifactDatabase should depend on a + * Backend but a Backend not from an ArtifactDatabase. + */ + public interface FactoryLookup { + + /** + * Returns an ArtifactFactory which is bound to a given name. + * @param factoryName The name of the artifact factory. + * @return The ArtifactFactory bound to the factory name or + * null if not matching factory is found. + */ + ArtifactFactory getArtifactFactory(String factoryName); + + } // interface FactoryLookup + + /** + * Inner class that brigdes between the persisten form of the + * artifact and the living one inside the artifact database. + * After the describe(), feed(), advance() and out() operations + * of the artifact it must be possible to write to modified artifact + * back into the database. + */ + public final class PersistentArtifact + { + private int id; + private Artifact artifact; + private ArtifactSerializer serializer; + private Long ttl; + + /** + * Cronstructor to create a persistent artifact. + * @param artifact The living artifact. + * @param serializer The serializer to store the artifact + * after the operations. + * @param ttl The time to life of the artifact. + * @param id The database id of the artifact. + */ + public PersistentArtifact( + Artifact artifact, + ArtifactSerializer serializer, + Long ttl, + int id + ) { + this.id = id; + this.artifact = artifact; + this.serializer = serializer; + this.ttl = ttl; + } + + public int getId() { + return id; + } + + /** + * Returns the wrapped living artifact. + * @return the living artifact. + */ + public Artifact getArtifact() { + return artifact; + } + + /** + * Returns the serialized which is able to write a + * modified artifact back into the database. + * @return The serializer. + */ + public ArtifactSerializer getSerializer() { + return serializer; + } + + /** + * The time to life of the artifact. + * @return The time to live. + */ + public Long getTTL() { + return ttl; + } + + /** + * Stores the living artifact back into the database. + */ + public void store() { + if (logger.isDebugEnabled()) { + logger.debug("storing artifact id = " + getId()); + } + Backend.this.store(this); + } + + /** + * Only touches the access time of the artifact. + */ + public void touch() { + if (logger.isDebugEnabled()) { + logger.debug("touching artifact id = " + getId()); + } + Backend.this.touch(this); + } + } // class ArtifactWithId + + /** + * Default constructor + */ + public Backend() { + listeners = new CopyOnWriteArrayList<BackendListener>(); + } + + public Backend(DBConfig config) { + this(); + this.config = config; + sqlExecutor = new SQLExecutor(config.getDBConnection()); + setupSQL(config.getSQL()); + } + + /** + * Constructor to create a backend with a link to the database cleaner. + * @param cleaner The clean which periodically removes outdated + * artifacts from the database. + */ + public Backend(DBConfig config, DatabaseCleaner cleaner) { + this(config); + this.cleaner = cleaner; + } + + public DBConfig getConfig() { + return config; + } + + /** + * Returns the singleton of this Backend. + * + * @return the backend. + */ + public static synchronized Backend getInstance() { + if (instance == null) { + instance = new Backend(DBConfig.getInstance()); + } + + return instance; + } + + protected void setupSQL(SQL sql) { + SQL_NEXT_ID = sql.get("artifacts.id.nextval"); + SQL_INSERT = sql.get("artifacts.insert"); + SQL_UPDATE = sql.get("artifacts.update"); + SQL_TOUCH = sql.get("artifacts.touch"); + SQL_LOAD_BY_GID = sql.get("artifacts.select.gid"); + SQL_GET_ID = sql.get("artifacts.get.id"); + SQL_REPLACE = sql.get("artifacts.replace"); + SQL_USERS_NEXT_ID = sql.get("users.id.nextval"); + SQL_USERS_INSERT = sql.get("users.insert"); + SQL_USERS_SELECT_ID_BY_GID = sql.get("users.select.id.by.gid"); + SQL_USERS_SELECT_GID = sql.get("users.select.gid"); + SQL_USERS_SELECT_ACCOUNT = sql.get("users.select.account"); + SQL_USERS_DELETE_ID = sql.get("users.delete.id"); + SQL_USERS_DELETE_COLLECTIONS = sql.get("users.delete.collections"); + SQL_USERS_SELECT_ALL = sql.get("users.select.all"); + SQL_USERS_COLLECTIONS = sql.get("users.collections"); + SQL_USERS_COLLECTION_IDS = sql.get("users.collection.ids"); + SQL_USERS_DELETE_ALL_COLLECTIONS = + sql.get("users.delete.collections"); + SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY = + sql.get("artifacts.in.one.collection.only"); + SQL_OUTDATE_ARTIFACTS_COLLECTION = + sql.get("outdate.artifacts.collection"); + SQL_UPDATE_COLLECTION_TTL = sql.get("collections.update.ttl"); + SQL_UPDATE_COLLECTION_NAME = sql.get("collections.update.name"); + SQL_OUTDATE_ARTIFACTS_USER = sql.get("outdate.artifacts.user"); + SQL_DELETE_USER_COLLECTION_ITEMS = + sql.get("delete.user.collection.items"); + SQL_COLLECTIONS_NEXT_ID = sql.get("collections.id.nextval"); + SQL_COLLECTIONS_INSERT = sql.get("collections.insert"); + SQL_COLLECTIONS_SELECT_USER = sql.get("collections.select.user"); + SQL_COLLECTIONS_SELECT_ALL = sql.get("collections.select.all"); + SQL_COLLECTIONS_SELECT_GID = sql.get("collections.select.by.gid"); + SQL_COLLECTIONS_CREATION_TIME = sql.get("collection.creation.time"); + SQL_COLLECTIONS_OLDEST_ARTIFACT = sql.get("collections.artifacts.oldest"); + SQL_COLLECTIONS_ID_BY_GID = sql.get("collections.id.by.gid"); + SQL_DELETE_COLLECTION_ITEMS = sql.get("delete.collection.items"); + SQL_DELETE_COLLECTION = sql.get("delete.collection"); + SQL_COLLECTION_CHECK_ARTIFACT = sql.get("collection.check.artifact"); + SQL_COLLECTION_ITEMS_ID_NEXTVAL = + sql.get("collection.items.id.nextval"); + SQL_COLLECTION_ITEMS_INSERT = sql.get("collection.items.insert"); + SQL_COLLECTION_GET_ATTRIBUTE = sql.get("collection.get.attribute"); + SQL_COLLECTION_SET_ATTRIBUTE = sql.get("collection.set.attribute"); + SQL_COLLECTION_ITEM_GET_ATTRIBUTE = + sql.get("collection.item.get.attribute"); + SQL_COLLECTION_ITEM_SET_ATTRIBUTE = + sql.get("collection.item.set.attribute"); + SQL_COLLECTIONS_TOUCH_BY_GID = sql.get("collections.touch.by.gid"); + SQL_COLLECTION_ITEM_ID_CID_AID = sql.get("collection.item.id.cid.aid"); + SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT = + sql.get("collection.item.outdate.artifact"); + SQL_COLLECTION_ITEM_DELETE = sql.get("collection.item.delete"); + SQL_COLLECTIONS_TOUCH_BY_ID = sql.get("collections.touch.by.id"); + SQL_COLLECTION_ITEMS_LIST_GID = sql.get("collection.items.list.gid"); + SQL_ALL_ARTIFACTS = sql.get("all.artifacts"); + } + + public void addListener(BackendListener listener) { + listeners.add(listener); + logger.debug("# listeners: " + listeners.size()); + } + + public void addAllListeners(List<BackendListener> others) { + listeners.addAll(others); + logger.debug("# listeners: " + listeners.size()); + } + + /** + * Sets the factory lookup mechanism to decouple ArtifactDatabase + * and Backend. + * @param factoryLookup + */ + public void setFactoryLookup(FactoryLookup factoryLookup) { + this.factoryLookup = factoryLookup; + } + + /** + * Sets the database cleaner explicitly. + * @param cleaner The database cleaner + */ + public void setCleaner(DatabaseCleaner cleaner) { + this.cleaner = cleaner; + } + + /** + * Returns a new unique identifier to external identify + * the artifact across the system. This implementation + * uses random UUIDs v4 to achieve this target. + * @return the new identifier + */ + public String newIdentifier() { + // TODO: check database for collisions. + return StringUtils.newUUID(); + } + + public boolean isValidIdentifier(String identifier) { + return StringUtils.checkUUID(identifier); + } + + /** + * Stores a new artifact into the database. + * @param artifact The artifact to be stored + * @param factory The factory which build the artifact + * @param ttl The initial time to life of the artifact. + * @return A persistent wrapper around the living + * artifact to be able to write modification later. + * @throws Exception Thrown if something went wrong with the + * storage process. + */ + public PersistentArtifact storeInitially( + Artifact artifact, + ArtifactFactory factory, + Long ttl + ) + throws Exception + { + return new PersistentArtifact( + artifact, + factory.getSerializer(), + ttl, + insertDatabase(artifact, factory, ttl)); + } + + /** + * Stores an artifact into database if it does not exist there. + * If it exists there it is only updated. + * @param artifact The artifact to store/update. + * @param factory The factory which created the artifact. + * @param ttl The initial time to live of the artifact. + * @return A persistent version of the artifact to be able + * to store a modification later. + * @throws Exception Thrown if something went wrong during + * storing/updating. + */ + public PersistentArtifact storeOrReplace( + Artifact artifact, + ArtifactFactory factory, + Long ttl + ) + throws Exception + { + return new PersistentArtifact( + artifact, + factory.getSerializer(), + ttl, + storeOrReplaceDatabase(artifact, factory, ttl)); + } + + /** + * Implementors of this interface are able to process the raw + * artifact data from the database for loading. + */ + public interface ArtifactLoader { + + /** + * Creates a custom object from the raw artifact database data. + * @param factory The factory that created this artifact. + * @param ttl The current time to life of the artifact. + * @param bytes The raw artifact bytes from the database. + * @param id The database id of the artifact. + * @return The custom object created by the implementation. + */ + Object load(ArtifactFactory factory, Long ttl, byte [] bytes, int id); + + } // interface ArtifactLoader + + /** + * Fetches an artifact from the database identified by the + * given identifier. + * @param identifer The identifier of the artifact. + * @return A persistent wrapper around the found artifact + * to be able to write back a modifaction later or null + * if no artifact is found for this identifier. + */ + public PersistentArtifact getArtifact(String identifer) { + + return (PersistentArtifact)loadArtifact( + identifer, + new ArtifactLoader() { + + public Object load( + ArtifactFactory factory, + Long ttl, + byte [] bytes, + int id + ) { + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(bytes); + + return artifact == null + ? null + : new PersistentArtifact(artifact, serializer, ttl, id); + } + }); + } + + /** + * More general loading mechanism for artifacts. The concrete + * load processing is delegated to the given loader. + * @param identifer The identifier of the artifact. + * @param loader The loader which processes the raw database data. + * @return The object created by the loader. + */ + public Object loadArtifact( + final String identifer, + final ArtifactLoader loader + ) { + if (!isValidIdentifier(identifer)) { + return null; + } + + if (factoryLookup == null) { + logger.error("factory lookup == null"); + return false; + } + + final Object [] loaded = new Object[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_LOAD_BY_GID); + stmnt.setString(1, identifer); + + result = stmnt.executeQuery(); + + if (!result.next()) { + return false; + } + + int id = result.getInt(1); + long ttlX = result.getLong(2); + Long ttl = result.wasNull() ? null : ttlX; + + String factoryName = result.getString(3); + + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error("factory '" + factoryName + "' not found"); + return false; + } + + byte [] bytes = result.getBytes(4); + + loaded[0] = loader.load(factory, ttl, bytes, id); + return true; + } + }; + + return exec.runRead() ? loaded[0] : null; + } + + /** + * Called if the load mechanism found an outdated artifact. + * It wakes up the database cleaner. + * @param id The id of the outdated artifact. + */ + protected void artifactOutdated(int id) { + if (logger.isDebugEnabled()) { + logger.info("artifactOutdated: id = " + id); + } + if (cleaner != null) { + cleaner.wakeup(); + } + } + + 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); + } + + /** + * Internal method to store/replace an artifact inside the database. + * If an artifact with the given identifier does not exists it is + * created else only the content data is updated. + * @param artifact The artifact to be store/update inside the database. + * @param factory The factory that created the artifact. + * @param ttl The initial time to life of the artifact. + * @return The database id of the stored/updated artifact. + */ + protected int storeOrReplaceDatabase( + final Artifact artifact, + final ArtifactFactory factory, + final Long ttl + ) { + final String uuid = artifact.identifier(); + + if (!isValidIdentifier(uuid)) { + throw new RuntimeException("No valid UUID"); + } + + final int [] id = new int[1]; + final boolean [] stored = new boolean[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_GET_ID); + stmnt.setString(1, uuid); + result = stmnt.executeQuery(); + + Integer ID = result.next() + ? Integer.valueOf(result.getInt(1)) + : null; + + reset(); + + if (stored[0] = ID != null) { // already in database + prepareStatement(SQL_REPLACE); + + if (ttl == null) { + stmnt.setNull(1, Types.BIGINT); + } + else { + stmnt.setLong(1, ttl.longValue()); + } + + stmnt.setString(2, factory.getName()); + stmnt.setBytes( + 3, + factory.getSerializer().toBytes(artifact)); + id[0] = ID.intValue(); + stmnt.setInt(4, id[0]); + } + else { // new artifact + prepareStatement(SQL_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + logger.error("No id generated"); + return false; + } + + reset(); + + prepareStatement(SQL_INSERT); + + id[0] = result.getInt(1); + stmnt.setInt(1, id[0]); + stmnt.setString(2, uuid); + if (ttl == null) { + stmnt.setNull(3, Types.BIGINT); + } + else { + stmnt.setLong(3, ttl.longValue()); + } + + stmnt.setString(4, factory.getName()); + + stmnt.setBytes( + 5, + factory.getSerializer().toBytes(artifact)); + } + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + throw new RuntimeException("failed insert artifact into database"); + } + + if (stored[0]) { + fireStoredArtifact(artifact); + } + else { + fireCreatedArtifact(artifact); + } + + return id[0]; + } + + /** + * Internal method to store an artifact inside the database. + * @param artifact The artifact to be stored. + * @param factory The factory which created the artifact. + * @param ttl The initial time to live of the artifact. + * @return The database id of the stored artifact. + */ + protected int insertDatabase( + final Artifact artifact, + final ArtifactFactory factory, + final Long ttl + ) { + final int [] id = new int[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + logger.error("No id generated"); + return false; + } + + id[0] = result.getInt(1); + + reset(); + prepareStatement(SQL_INSERT); + + String uuid = artifact.identifier(); + stmnt.setInt(1, id[0]); + stmnt.setString(2, uuid); + if (ttl == null) { + stmnt.setNull(3, Types.BIGINT); + } + else { + stmnt.setLong(3, ttl.longValue()); + } + + stmnt.setString(4, factory.getName()); + + stmnt.setBytes( + 5, + factory.getSerializer().toBytes(artifact)); + + stmnt.execute(); + + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + throw new RuntimeException("failed insert artifact into database"); + } + + fireCreatedArtifact(artifact); + + return id[0]; + } + + protected void fireCreatedArtifact(Artifact artifact) { + for (BackendListener listener: listeners) { + listener.createdArtifact(artifact, this); + } + } + + /** + * Touches the access timestamp of a given artifact to prevent + * that it will be removed from the database by the database cleaner. + * @param artifact The persistent wrapper around the living artifact. + */ + public void touch(final PersistentArtifact artifact) { + sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_TOUCH); + stmnt.setInt(1, artifact.getId()); + stmnt.execute(); + conn.commit(); + return true; + } + }.runWrite(); + } + + /** + * Writes modification of an artifact back to the database. + * @param artifact The persistent wrapper around a living + * artifact. + */ + public void store(final PersistentArtifact artifact) { + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE); + stmnt.setInt(2, artifact.getId()); + + byte [] bytes = artifact + .getSerializer() + .toBytes(artifact.getArtifact()); + + stmnt.setBytes(1, bytes); + stmnt.execute(); + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireStoredArtifact(artifact.getArtifact()); + } + } + + protected void fireStoredArtifact(Artifact artifact) { + for (BackendListener listener: listeners) { + listener.storedArtifact(artifact, this); + } + } + + + public User createUser( + final String name, + final String account, + final Document role, + final UserFactory factory, + final Object context + ) { + final User [] user = new User[1]; + + final byte [] roleData = XMLUtils.toByteArray(role, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_USERS_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + return false; + } + + int id = result.getInt(1); + + reset(); + + String identifier = newIdentifier(); + + prepareStatement(SQL_USERS_INSERT); + + stmnt.setInt(1, id); + stmnt.setString(2, identifier); + stmnt.setString(3, name); + stmnt.setString(4, account); + + if (roleData == null) { + stmnt.setNull(5, Types.BIGINT); + } + else { + stmnt.setBytes(5, roleData); + } + + stmnt.execute(); + conn.commit(); + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireCreatedUser(user[0]); + return user[0]; + } + + return null; + } + + protected void fireCreatedUser(User user) { + for (BackendListener listener: listeners) { + listener.createdUser(user, this); + } + } + + public boolean deleteUser(final String identifier) { + + if (!isValidIdentifier(identifier)) { + return false; + } + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ID_BY_GID); + + stmnt.setString(1, identifier); + result = stmnt.executeQuery(); + + if (!result.next()) { // No such user + return false; + } + + int id = result.getInt(1); + + reset(); + + // outdate the artifacts exclusively used by the user + + prepareStatement(SQL_OUTDATE_ARTIFACTS_USER); + stmnt.setInt(1, id); + stmnt.setInt(2, id); + stmnt.execute(); + + reset(); + + // delete the collection items of the user + + prepareStatement(SQL_DELETE_USER_COLLECTION_ITEMS); + stmnt.setInt(1, id); + stmnt.execute(); + + reset(); + + // delete the collections of the user + + prepareStatement(SQL_USERS_DELETE_COLLECTIONS); + stmnt.setInt(1, id); + stmnt.execute(); + + reset(); + + // delete the user + + prepareStatement(SQL_USERS_DELETE_ID); + stmnt.setInt(1, id); + stmnt.execute(); + + conn.commit(); + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireDeletedUser(identifier); + } + + return success; + } + + protected void fireDeletedUser(String identifier) { + for (BackendListener listener: listeners) { + listener.deletedUser(identifier, this); + } + } + + public User getUser( + final String identifier, + final UserFactory factory, + final Object context + ) { + if (!isValidIdentifier(identifier)) { + logger.debug("Invalid UUID: '" + identifier + "'"); + return null; + } + + final User [] user = new User[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_GID); + stmnt.setString(1, identifier); + result = stmnt.executeQuery(); + if (!result.next()) { // no such user + return false; + } + // omit id + String name = result.getString(2); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); + + Document role = null; + if (roleData != null) { + role = XMLUtils.fromByteArray(roleData, true); + } + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + return exec.runRead() ? user[0] : null; + } + + /** + * Find/Get user by account. + */ + public User findUser( + final String account, + final UserFactory factory, + final Object context + ) { + + final User [] user = new User[1]; + logger.debug("Trying to find user by account " + account); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ACCOUNT); + stmnt.setString(1, account); + result = stmnt.executeQuery(); + if (!result.next()) { // no such user + logger.debug("No user found."); + return false; + } + String identifier = result.getString(1); + String name = result.getString(2); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); + + Document role = null; + if (roleData != null) { + role = XMLUtils.fromByteArray(roleData, true); + } + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + return exec.runRead() ? user[0] : null; + } + + public User [] getUsers( + final UserFactory factory, + final Object context + ) { + final ArrayList<User> users = new ArrayList<User>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ALL); + result = stmnt.executeQuery(); + + while (result.next()) { + // omit id + String identifier = result.getString(2); + String name = result.getString(3); + String account = result.getString(4); + byte [] roleData = result.getBytes(5); + + Document role = XMLUtils.fromByteArray(roleData, true); + User user = factory.createUser( + identifier, name, account, role, context); + users.add(user); + } + return true; + } + }; + + return exec.runRead() + ? users.toArray(new User[users.size()]) + : null; + } + + public ArtifactCollection createCollection( + final String ownerIdentifier, + final String name, + final ArtifactCollectionFactory factory, + final Document attribute, + final Object context + ) { + if (name == null) { + logger.debug("Name is null"); + return null; + } + + if (!isValidIdentifier(ownerIdentifier)) { + logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); + return null; + } + + final ArtifactCollection [] collection = new ArtifactCollection[1]; + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch owner id + prepareStatement(SQL_USERS_SELECT_ID_BY_GID); + stmnt.setString(1, ownerIdentifier); + result = stmnt.executeQuery(); + + if (!result.next()) { // no such user + return false; + } + + int ownerId = result.getInt(1); + reset(); + + // fetch new collection seq number. + prepareStatement(SQL_COLLECTIONS_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { // no identifier generated + return false; + } + + int id = result.getInt(1); + reset(); + + String identifier = newIdentifier(); + + prepareStatement(SQL_COLLECTIONS_INSERT); + + stmnt.setInt(1, id); + stmnt.setString(2, identifier); + stmnt.setString(3, name); + stmnt.setInt(4, ownerId); + + // XXX: A bit odd: we don't have a collection, yet. + Long ttl = factory.timeToLiveUntouched(null, context); + + if (ttl == null) { + stmnt.setNull(5, Types.BIGINT); + } + else { + stmnt.setLong(5, ttl); + } + + if (data == null) { + stmnt.setNull(6, Types.BINARY); + } + else { + stmnt.setBytes(6, data); + } + + stmnt.execute(); + conn.commit(); + + reset(); + + // fetch creation time from database + // done this way to use the time system + // of the database. + + prepareStatement(SQL_COLLECTIONS_CREATION_TIME); + stmnt.setInt(1, id); + + result = stmnt.executeQuery(); + + Date creationTime = null; + + if (result.next()) { + Timestamp timestamp = result.getTimestamp(1); + creationTime = new Date(timestamp.getTime()); + } + + collection[0] = factory.createCollection( + identifier, name, creationTime, ttl, attribute, context); + + if (collection[0] != null) { + // XXX: Little hack to make the listeners happy + collection[0].setUser(new DefaultUser(ownerIdentifier)); + } + + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireCreatedCollection(collection[0]); + return collection[0]; + } + return null; + } + + protected void fireCreatedCollection(ArtifactCollection collection) { + for (BackendListener listener: listeners) { + listener.createdCollection(collection, this); + } + } + + public ArtifactCollection getCollection( + final String collectionId, + final ArtifactCollectionFactory collectionFactory, + final UserFactory userFactory, + final Object context + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return null; + } + + final ArtifactCollection[] ac = new ArtifactCollection[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_COLLECTIONS_SELECT_GID); + stmnt.setString(1, collectionId); + + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection"); + return false; + } + + String collectionName = result.getString(2); + String ownerId = result.getString(3); + Date creationTime = + new Date(result.getTimestamp(4).getTime()); + Date lastAccess = + new Date(result.getTimestamp(5).getTime()); + Document attr = + XMLUtils.fromByteArray(result.getBytes(6), true); + long ttl = result.getLong(7); + + ArtifactCollection collection = + collectionFactory.createCollection( + collectionId, + collectionName, + creationTime, + ttl, + attr, + context); + + if (ownerId != null) { + collection.setUser(new LazyBackendUser( + ownerId, userFactory, Backend.this, context)); + } + + ac[0] = collection; + + return true; + } + }; + + return exec.runRead() ? ac[0] : null; + } + + public ArtifactCollection [] listCollections( + final String ownerIdentifier, + final Document data, + final ArtifactCollectionFactory collectionFactory, + final UserFactory userFactory, + final Object context + ) { + if (ownerIdentifier != null + && !isValidIdentifier(ownerIdentifier)) { + logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); + return null; + } + + final ArrayList<ArtifactCollection> collections = + new ArrayList<ArtifactCollection>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + + public boolean doIt() throws SQLException { + + if (ownerIdentifier != null) { + prepareStatement(SQL_COLLECTIONS_SELECT_USER); + stmnt.setString(1, ownerIdentifier); + } + else { + prepareStatement(SQL_COLLECTIONS_SELECT_ALL); + } + + result = stmnt.executeQuery(); + + HashMap<String, LazyBackendUser> users = + new HashMap<String, LazyBackendUser>(); + + while (result.next()) { + String collectionIdentifier = result.getString(1); + String collectionName = result.getString(2); + Date creationTime = + new Date(result.getTimestamp(3).getTime()); + String userIdentifier = result.getString(4); + long ttl = result.getLong(5); + + ArtifactCollection collection = + collectionFactory.createCollection( + collectionIdentifier, + collectionName, + creationTime, + ttl, + data, + context); + + if (userIdentifier != null) { + LazyBackendUser user = users.get(userIdentifier); + if (user == null) { + user = new LazyBackendUser( + userIdentifier, userFactory, + Backend.this, context); + users.put(userIdentifier, user); + } + collection.setUser(user); + } + + collections.add(collection); + } + return true; + } + }; + + return exec.runRead() + ? collections.toArray(new ArtifactCollection[collections.size()]) + : null; + } + + + public String getMasterArtifact(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return null; + } + final String [] uuid = new String[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // Fetch masters (oldest artifact) id. + prepareStatement(SQL_COLLECTIONS_OLDEST_ARTIFACT); + stmnt.setString(1, collectionId); + stmnt.setMaxRows(1); // + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + return false; + } + uuid[0] = result.getString(1); + if (logger.isDebugEnabled()) { + logger.debug("getMasterArtifact result.getString " + + uuid[0]); + } + return true; + } + }; + return exec.runRead() ? uuid[0] : null; + } + + public boolean deleteCollection(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch collection id + prepareStatement(SQL_COLLECTIONS_ID_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + return false; + } + int id = result.getInt(1); + reset(); + + // outdate artifacts that are only in this collection + logger.info("Outdate Artifacts that belong to collection: " + id); + + prepareStatement(SQL_OUTDATE_ARTIFACTS_COLLECTION); + stmnt.setInt(1, id); + stmnt.setInt(2, id); + stmnt.execute(); + reset(); + + // delete the collection items + prepareStatement(SQL_DELETE_COLLECTION_ITEMS); + stmnt.setInt(1, id); + stmnt.execute(); + reset(); + + // delete the collection + prepareStatement(SQL_DELETE_COLLECTION); + stmnt.setInt(1, id); + stmnt.execute(); + conn.commit(); + return true; + } + }; + boolean success = exec.runWrite(); + + if (success) { + fireDeletedCollection(collectionId); + } + + return success; + } + + protected void fireDeletedCollection(String identifier) { + for (BackendListener listener: listeners) { + listener.deletedCollection(identifier, this); + } + } + + public Document getCollectionAttribute(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + } + + final byte[][] data = new byte[1][1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_GET_ATTRIBUTE); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection."); + return false; + } + + data[0] = result.getBytes(1); + return true; + } + }; + + return exec.runRead() + ? XMLUtils.fromByteArray(data[0], true) + : null; + } + + public boolean setCollectionAttribute( + final String collectionId, + Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // set the column in collection items + prepareStatement(SQL_COLLECTION_SET_ATTRIBUTE); + if (data == null) { + stmnt.setNull(1, Types.BINARY); + } + else { + stmnt.setBytes(1, data); + } + stmnt.setString(2, collectionId); + stmnt.execute(); + reset(); + + // touch the collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); + stmnt.setString(1, collectionId); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireChangedCollectionAttribute(collectionId, attribute); + } + + return success; + } + + protected void fireChangedCollectionAttribute( + String collectionId, + Document document + ) { + for (BackendListener listener: listeners) { + listener.changedCollectionAttribute(collectionId, document, this); + } + } + + public Document getCollectionItemAttribute( + final String collectionId, + final String artifactId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return null; + } + if (!isValidIdentifier(artifactId)) { + logger.debug("artifact id is not valid: " + artifactId); + return null; + } + + final byte [][] data = new byte[1][1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ITEM_GET_ATTRIBUTE); + stmnt.setString(1, collectionId); + stmnt.setString(2, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection item"); + return false; + } + data[0] = result.getBytes(1); + return true; + } + }; + + return exec.runRead() + ? XMLUtils.fromByteArray(data[0], true) + : null; + } + + public boolean setCollectionItemAttribute( + final String collectionId, + final String artifactId, + Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return false; + } + if (!isValidIdentifier(artifactId)) { + logger.debug("artifact id is not valid: " + artifactId); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // set the column in collection items + prepareStatement(SQL_COLLECTION_ITEM_SET_ATTRIBUTE); + if (data == null) { + stmnt.setNull(1, Types.BINARY); + } + else { + stmnt.setBytes(1, data); + } + stmnt.setString(2, collectionId); + stmnt.setString(3, artifactId); + stmnt.execute(); + reset(); + + // touch the collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); + stmnt.setString(1, collectionId); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireChangedCollectionItemAttribute( + collectionId, artifactId, attribute); + } + + return success; + } + + protected void fireChangedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document + ) { + for (BackendListener listener: listeners) { + listener.changedCollectionItemAttribute( + collectionId, artifactId, document, this); + } + } + + public boolean addCollectionArtifact( + final String collectionId, + final String artifactId, + final Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + + if (!isValidIdentifier(artifactId)) { + logger.debug("Invalid artifact id: '" + artifactId + "'"); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch artifact id + prepareStatement(SQL_GET_ID); + stmnt.setString(1, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such artifact: " + artifactId); + return false; + } + int aid = result.getInt(1); + reset(); + + // fetch collection id + prepareStatement(SQL_COLLECTIONS_ID_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + } + int cid = result.getInt(1); + reset(); + + // check if artifact is already in collection + prepareStatement(SQL_COLLECTION_CHECK_ARTIFACT); + stmnt.setInt(1, aid); + stmnt.setInt(2, cid); + result = stmnt.executeQuery(); + if (result.next()) { + logger.debug("artifact already in collection"); + return false; + } + reset(); + + // fetch fresh id for new collection item + prepareStatement(SQL_COLLECTION_ITEMS_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("no collection item id generated"); + return false; + } + int ci_id = result.getInt(1); + reset(); + + // insert new collection item + prepareStatement(SQL_COLLECTION_ITEMS_INSERT); + stmnt.setInt(1, ci_id); + stmnt.setInt(2, cid); + stmnt.setInt(3, aid); + + if (data == null) { + stmnt.setNull(4, Types.BINARY); + } + else { + stmnt.setBytes(4, data); + } + stmnt.execute(); + conn.commit(); + + return true; + } + }; + boolean success = exec.runWrite(); + + if (success) { + fireAddedArtifactToCollection(artifactId, collectionId); + } + + return success; + } + + protected void fireAddedArtifactToCollection( + String artifactId, + String collectionId + ) { + for (BackendListener listener: listeners) { + listener.addedArtifactToCollection( + artifactId, collectionId, this); + } + } + + public boolean removeCollectionArtifact( + final String collectionId, + final String artifactId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // fetch id, collection id and artitfact id + prepareStatement(SQL_COLLECTION_ITEM_ID_CID_AID); + stmnt.setString(1, collectionId); + stmnt.setString(2, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection item"); + return false; + } + int id = result.getInt(1); + int cid = result.getInt(2); + int aid = result.getInt(3); + reset(); + + // outdate artifact iff it is only in this collection + prepareStatement(SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT); + stmnt.setInt(1, aid); + stmnt.setInt(2, cid); + stmnt.setInt(3, aid); + stmnt.execute(); + reset(); + + // delete collection item + prepareStatement(SQL_COLLECTION_ITEM_DELETE); + stmnt.setInt(1, id); + stmnt.execute(); + reset(); + + // touch collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_ID); + stmnt.setInt(1, cid); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireRemovedArtifactFromCollection(artifactId, collectionId); + } + + return success; + } + + protected void fireRemovedArtifactFromCollection( + String artifactId, + String collectionId + ) { + for (BackendListener listener: listeners) { + listener.removedArtifactFromCollection( + artifactId, collectionId, this); + } + } + + public CollectionItem [] listCollectionArtifacts( + final String collectionId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return null; + } + + final ArrayList<CollectionItem> collectionItems = + new ArrayList<CollectionItem>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ITEMS_LIST_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + while (result.next()) { + CollectionItem item = new DefaultCollectionItem( + result.getString(1), + result.getBytes(2)); + collectionItems.add(item); + } + return true; + } + }; + + return exec.runRead() + ? collectionItems.toArray( + new CollectionItem[collectionItems.size()]) + : null; + } + + + public boolean setCollectionTTL(final String uuid, final Long ttl) { + if (!isValidIdentifier(uuid)) { + logger.debug("Invalid collection id: '" + uuid + "'"); + return false; + } + + return sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE_COLLECTION_TTL); + if (ttl == null) { + stmnt.setNull(1, Types.BIGINT); + } + else { + stmnt.setLong(1, ttl); + } + stmnt.setString(2, uuid); + stmnt.execute(); + conn.commit(); + + return true; + } + }.runWrite(); + } + + + public boolean setCollectionName(final String uuid, final String name) { + if (!isValidIdentifier(uuid)) { + logger.debug("Invalid collection id: '" + uuid + "'"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE_COLLECTION_NAME); + stmnt.setString(1, name); + stmnt.setString(2, uuid); + stmnt.execute(); + conn.commit(); + + return true; + } + }.runWrite(); + + if (success) { + fireSetCollectionName(uuid, name); + } + + return success; + } + + protected void fireSetCollectionName(String identifier, String name) { + for (BackendListener listener: listeners) { + listener.setCollectionName(identifier, name); + } + } + + public boolean loadAllArtifacts(final ArtifactLoadedCallback alc) { + + logger.debug("loadAllArtifacts"); + + if (factoryLookup == null) { + logger.error("factory lookup == null"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + // a little cache to avoid too much deserializations. + LRUCache<String, Artifact> alreadyLoaded = + new LRUCache<String, Artifact>(200); + + prepareStatement(SQL_ALL_ARTIFACTS); + result = stmnt.executeQuery(); + while (result.next()) { + String userId = result.getString("u_gid"); + String collectionId = result.getString("c_gid"); + String collectionName = result.getString("c_name"); + String artifactId = result.getString("a_gid"); + String factoryName = result.getString("factory"); + Date collectionCreated = + new Date(result.getTimestamp("c_creation").getTime()); + Date artifactCreated = + new Date(result.getTimestamp("a_creation").getTime()); + + Artifact artifact = alreadyLoaded.get(artifactId); + + if (artifact != null) { + alc.artifactLoaded( + userId, + collectionId, collectionName, + collectionCreated, + artifactId, artifactCreated, artifact); + continue; + } + + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error("factory '" + factoryName + "' not found"); + continue; + } + + byte [] bytes = result.getBytes("data"); + + artifact = factory.getSerializer().fromBytes(bytes); + + if (artifact != null) { + alc.artifactLoaded( + userId, + collectionId, collectionName, collectionCreated, + artifactId, artifactCreated, artifact); + } + + alreadyLoaded.put(artifactId, artifact); + } + return true; + } + }.runRead(); + + if (logger.isDebugEnabled()) { + logger.debug("loadAllArtifacts success: " + success); + } + + return success; + } + + @Override + public void killedArtifacts(List<String> identifiers) { + logger.debug("killedArtifacts"); + for (BackendListener listener: listeners) { + listener.killedArtifacts(identifiers, this); + } + } + + @Override + public void killedCollections(List<String> identifiers) { + logger.debug("killedCollections"); + for (BackendListener listener: listeners) { + listener.killedCollections(identifiers, this); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :