Mercurial > dive4elements > framework
diff artifact-database/src/main/java/org/dive4elements/artifactdatabase/ArtifactDatabaseImpl.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/ArtifactDatabaseImpl.java@8d8aed23c323 |
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/ArtifactDatabaseImpl.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,1976 @@ +/* + * Copyright (c) 2010 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.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.artifactdatabase.Backend.PersistentArtifact; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.ArtifactSerializer; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.CollectionItem; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.Hook; +import de.intevation.artifacts.Message; +import de.intevation.artifacts.Service; +import de.intevation.artifacts.ServiceFactory; +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + +import java.io.IOException; +import java.io.OutputStream; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.xpath.XPathConstants; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * The core implementation of artifact database. This layer exposes + * the needed methods to the artifact runtime system which e.g. may + * expose them via REST. The concrete persistent representation of the + * artifacts is handled by the {@link Backend backend}. + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class ArtifactDatabaseImpl +implements ArtifactDatabase, + DatabaseCleaner.LockedIdsProvider, + Backend.FactoryLookup +{ + private static Logger logger = + Logger.getLogger(ArtifactDatabaseImpl.class); + + /** The key under which the artifact database is stored in the global + * context.*/ + public static final String GLOBAL_CONTEXT_KEY = "global.artifact.database"; + + /** Message that is returned if an operation was successful.*/ + public static final String OPERATION_SUCCESSFUL = + "SUCCESS"; + + /** Message that is returned if an operation failed.*/ + public static final String OPERATION_FAILURE = + "FAILURE"; + + /** + * Error message issued if a requested artifact factory + * is not registered to this database. + */ + public static final String NO_SUCH_FACTORY = + "No such factory"; + + /** + * Error message issued if a requested artifact is not found + * in this database. + */ + public static final String NO_SUCH_ARTIFACT = + "No such artifact"; + + /** + * Error message issued if a requested artifact is not found + * in this database. + */ + public static final String NO_SUCH_COLLECTION = + "No such collection"; + + /** + * Error message issued if the creation of an artifact failed. + */ + public static final String CREATION_FAILED = + "Creation of artifact failed"; + + /** + * Error message if an severe internal error occurred. + */ + public static final String INTERNAL_ERROR = + "Creation of artifact failed"; + + /** + * Error message issued if a requested service is not + * offered by this database. + */ + public static final String NO_SUCH_SERVICE = + "No such service"; + + /** + * Default digest hash to be used while im-/exporting artifacts. + */ + public static final String DIGEST_ALGORITHM = + "SHA-1"; + + /** + * XPath to get the checksum from an XML representation of + * an exported artifact. + */ + public static final String XPATH_IMPORT_CHECKSUM = + "/art:action/art:data/@checksum"; + + /** + * XPath to get the name of the factory which should be + * used to revive an antrifact that is going to be imported. + */ + public static final String XPATH_IMPORT_FACTORY = + "/art:action/art:data/@factory"; + + /** + * XPath to get the base64 encoded data of an artifact + * that is going to be imported. + */ + public static final String XPATH_IMPORT_DATA = + "/art:action/art:data/text()"; + + /** + * Error message issued if the checksum of an + * artifact to be imported has an invalid syntax. + */ + public static final String INVALID_CHECKSUM = + "Invalid checksum"; + + /** + * Error message issued the checksum validation + * of an artifact to be imported fails. + */ + public static final String CHECKSUM_MISMATCH = + "Mismatching checksum"; + + /** + * Error message issued if an artifact to be imported + * does not have any data. + */ + public static final String NO_DATA = + "No data"; + + /** + * Error message issued if the deserialization of + * an artifact to be imported fails. + */ + public static final String INVALID_ARTIFACT = + "Invalid artifact"; + + + // User constants + + /** + * Error message issued if the creation of a user failed. + */ + public static final String USER_CREATION_FAILED = + "Creation of user failed."; + + /** XPath to figure out the name of a new user.*/ + public static final String XPATH_USERNAME = + "/art:action/art:user/@name"; + + /** XPath to figure out the role of a new user.*/ + public static final String XPATH_USERROLE = + "/art:action/art:user/art:role"; + + /** XPath to figure out the account of a new user.*/ + public static final String XPATH_USERACCOUNT = + "/art:action/art:user/art:account/@name"; + + /** XPath to figure out the account of when searching for a user .*/ + public static final String XPATH_USERACCOUNT_FIND = + "/art:action/art:account/@name"; + + /** Error message if a specified user does not exist.*/ + public static final String NO_SUCH_USER = + "No such user"; + + /** Error message if no username is given for user creation.*/ + public static final String NO_USERNAME = + "Invalid username"; + + /** Error message if no user account is given for user creation.*/ + public static final String NO_USERACCOUNT = + "Invalid user account name"; + + // Collection constants + + /** + * Error message issued if the creation of a collection failed. + */ + public static final String COLLECTION_CREATION_FAILED = + "Creation of collection failed"; + + /** + * XPath to figure out the name of a collection described in the incoming + * document. + */ + public static final String XPATH_COLLECTION_NAME = + "/art:action/art:type/art:collection/@name"; + + /** + * XPath to figure out the attributes for a collection. + */ + public static final String XPATH_COLLECTION_ATTRIBUTE = + "/art:action/art:type/art:collection/art:attribute"; + + /** + * XPath to figure out the attributes for an artifact that is put into a + * collection. + */ + public static final String XPATH_COLLECTION_ITEM_ATTRIBUTE = + "/art:action/art:type/art:artifact/art:attribute"; + + /** + * XPath to figure out the time to live value for setting a new TTL. + */ + public static final String XPATH_COLLECTION_TTL = + "/art:action/art:type/art:ttl/@value"; + + + /** + * This inner class allows the deferral of writing the output + * of the artifact's out() call. + */ + public class DeferredOutputImpl + implements DeferredOutput + { + /** + * The persistence wrapper around a living artifact. + */ + protected PersistentArtifact artifact; + /** + * The output type. + */ + protected String type; + /** + * The input document for the artifact's out() call. + */ + protected Document format; + /** + * The meta information of the artifact's out() call. + */ + protected CallMeta callMeta; + + /** + * Default constructor. + */ + public DeferredOutputImpl() { + } + + /** + * Constructor to create a deferred execution unit for + * the artifact's out() call given an artifact, an input document + * an the meta information. + * @param artifact The persistence wrapper around a living artifact. + * @param format The input document for the artifact's out() call. + * @param callMeta The meta information of the artifact's out() call. + */ + public DeferredOutputImpl( + PersistentArtifact artifact, + String type, + Document format, + CallMeta callMeta + ) { + this.artifact = artifact; + this.type = type; + this.format = format; + this.callMeta = callMeta; + } + + public void write(OutputStream output) throws IOException { + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + artifact); + + try { + artifact.getArtifact().out(type, format, output, cc); + } + finally { + cc.postCall(); + } + } + } // class DeferredOutputImpl + + + /** + * This inner class allows the deferral of writing the output + * of the artifact's out() call. + */ + public class DeferredCollectionOutputImpl + implements DeferredOutput + { + /** + * The persistence wrapper around a living collection. + */ + protected ArtifactCollection collection; + /** + * The output type. + */ + protected String type; + /** + * The input document for the collection's out() call. + */ + protected Document format; + /** + * The meta information of the collection's out() call. + */ + protected CallMeta callMeta; + + /** + * Default constructor. + */ + public DeferredCollectionOutputImpl() { + } + + /** + * Constructor to create a deferred execution unit for + * the collection's out() call given a collection, an input document + * an the meta information. + * @param collection The collection. + * @param format The input document for the collection's out() call. + * @param callMeta The meta information of the collection's out() call. + */ + public DeferredCollectionOutputImpl( + ArtifactCollection collection, + String type, + Document format, + CallMeta callMeta + ) { + this.collection = collection; + this.type = type; + this.format = format; + this.callMeta = callMeta; + } + + public void write(OutputStream output) throws IOException { + + CollectionCallContext cc = new CollectionCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + collection); + + try { + collection.out(type, format, output, cc); + } + finally { + cc.postCall(); + } + } + } // class DeferredCollectionOutputImpl + + /** + * List of name/description pairs needed for + * {@link #artifactFactoryNamesAndDescriptions() }. + */ + protected String [][] factoryNamesAndDescription; + /** + * Map to access artifact factories by there name. + */ + protected HashMap name2factory; + + /** + * List of name/description pairs needed for + * {@link #serviceNamesAndDescriptions() }. + */ + protected String [][] serviceNamesAndDescription; + /** + * Map to access services by there name. + */ + protected HashMap name2service; + + /** + * The factory that is used to create new artifact collections. + */ + protected ArtifactCollectionFactory collectionFactory; + + /** + * The factory that is used to create and list users. + */ + protected UserFactory userFactory; + + /** + * Reference to the storage backend. + */ + protected Backend backend; + /** + * Reference of the global context of the artifact runtime system. + */ + protected GlobalContext context; + + /** + * The signing secret to be used for ex-/importing artifacts. + */ + protected byte [] exportSecret; + + /** + * A set of ids of artifact which currently running in background. + * This artifacts should not be removed from the database by the + * database cleaner. + */ + protected HashSet<Integer> backgroundIds; + + /** + * A list of background messages for Artifacts and Collections. + */ + protected Map<String, LinkedList<Message>> backgroundMsgs; + + + protected CallContext.Listener callContextListener; + + /** + * Hooks that are executed after an artifact has been fed. + */ + protected List<Hook> postFeedHooks; + + /** + * Hooks that are executed after an artifact has advanced. + */ + protected List<Hook> postAdvanceHooks; + + /** + * Hooks that are executed after an artifact's describe() operation was + * called. + */ + protected List<Hook> postDescribeHooks; + + protected List<LifetimeListener> lifetimeListeners; + + /** + * Default constructor. + */ + public ArtifactDatabaseImpl() { + } + + /** + * Constructor to create a artifact database with the given + * bootstrap parameters like artifact- and service factories et. al. + * Created this way the artifact database has no backend. + * @param bootstrap The parameters to start this artifact database. + */ + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { + this(bootstrap, null); + } + + /** + * Constructor to create a artifact database with the a given + * backend and + * bootstrap parameters like artifact- and service factories et. al. + * @param bootstrap The parameters to start this artifact database. + * @param backend The storage backend. + */ + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { + + logger.debug("new ArtifactDatabaseImpl"); + + backgroundIds = new HashSet<Integer>(); + backgroundMsgs = new HashMap<String, LinkedList<Message>>(); + + setupArtifactCollectionFactory(bootstrap); + setupArtifactFactories(bootstrap); + setupServices(bootstrap); + setupUserFactory(bootstrap); + setupCallContextListener(bootstrap); + setupHooks(bootstrap); + setupLifetimeListeners(bootstrap); + + context = bootstrap.getContext(); + context.put(GLOBAL_CONTEXT_KEY, this); + + exportSecret = bootstrap.getExportSecret(); + + wireWithBackend(backend, bootstrap); + } + + public CallContext.Listener getCallContextListener() { + return callContextListener; + } + + public void setCallContextListener( + CallContext.Listener callContextListener + ) { + this.callContextListener = callContextListener; + } + + + public void setPostFeedHook(List<Hook> postFeedHooks) { + this.postFeedHooks = postFeedHooks; + } + + public void setPostAdvanceHook(List<Hook> postAdvanceHooks) { + this.postAdvanceHooks = postAdvanceHooks; + } + + public void setPostDescribeHook(List<Hook> postDescribeHooks) { + this.postDescribeHooks = postDescribeHooks; + } + + /** + * Used to extract the artifact collection factory from bootstrap. + * + * @param bootstrap The bootstrap parameters. + */ + protected void setupArtifactCollectionFactory(FactoryBootstrap bootstrap) { + collectionFactory = bootstrap.getArtifactCollectionFactory(); + } + + /** + * Used to extract the artifact factories from the bootstrap + * parameters and building the internal lookup tables. + * @param bootstrap The bootstrap parameters. + */ + protected void setupArtifactFactories(FactoryBootstrap bootstrap) { + name2factory = new HashMap(); + + ArtifactFactory [] factories = bootstrap.getArtifactFactories(); + factoryNamesAndDescription = new String[factories.length][]; + + for (int i = 0; i < factories.length; ++i) { + + ArtifactFactory factory = factories[i]; + + String name = factory.getName(); + String description = factory.getDescription(); + + factoryNamesAndDescription[i] = + new String [] { name, description }; + + name2factory.put(name, factory); + } + } + + /** + * Used to extract the callContextListener from the bootstrap. + * + * @param bootstrap The bootstrap parameters. + */ + protected void setupCallContextListener(FactoryBootstrap bootstrap) { + setCallContextListener(bootstrap.getCallContextListener()); + } + + + protected void setupHooks(FactoryBootstrap bootstrap) { + setPostFeedHook(bootstrap.getPostFeedHooks()); + setPostAdvanceHook(bootstrap.getPostAdvanceHooks()); + setPostDescribeHook(bootstrap.getPostDescribeHooks()); + } + + protected void setupBackendListeners(FactoryBootstrap bootstrap) { + logger.debug("setupBackendListeners"); + List<BackendListener> bls = bootstrap.getBackendListeners(); + if (bls != null && !bls.isEmpty()) { + for (BackendListener listener: bls) { + listener.setup(context); + } + backend.addAllListeners(bls); + } + } + + protected void setupLifetimeListeners(FactoryBootstrap bootstrap) { + this.lifetimeListeners = bootstrap.getLifetimeListeners(); + } + + /** + * Used to extract the user factory from the bootstrap. + */ + protected void setupUserFactory(FactoryBootstrap bootstrap) { + userFactory = bootstrap.getUserFactory(); + } + + /** + * Used to extract the service factories from the bootstrap + * parameters, setting up the services and building the internal + * lookup tables. + * @param bootstrap The bootstrap parameters. + */ + protected void setupServices(FactoryBootstrap bootstrap) { + + name2service = new HashMap(); + + ServiceFactory [] serviceFactories = + bootstrap.getServiceFactories(); + + serviceNamesAndDescription = + new String[serviceFactories.length][]; + + for (int i = 0; i < serviceFactories.length; ++i) { + ServiceFactory factory = serviceFactories[i]; + + String name = factory.getName(); + String description = factory.getDescription(); + + serviceNamesAndDescription[i] = + new String [] { name, description }; + + name2service.put( + name, + factory.createService(bootstrap.getContext())); + } + + } + + /** + * Wires a storage backend to this artifact database and + * establishes a callback to be able to revive artifacts + * via the serializers of this artifact factories. + * @param backend The backend to be wired with this artifact database. + */ + public void wireWithBackend(Backend backend, FactoryBootstrap bootstrap) { + logger.debug("wireWithBackend"); + if (backend != null) { + this.backend = backend; + backend.setFactoryLookup(this); + setupBackendListeners(bootstrap); + } + } + + /** + * Called after an backgrounded artifact signals its + * will to be written back to the backend. + * @param artifact The persistence wrapper around + * the backgrounded artifact. + * @param action The action to be performed. + */ + 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()); + removeBackgroundMessages(artifact.getArtifact().identifier()); + } + + /** + * Removes an artifact's database id from the set of backgrounded + * artifacts. The database cleaner is now able to remove it safely + * from the database again. + * @param id The database id of the artifact. + */ + protected void removeIdFromBackground(int id) { + synchronized (backgroundIds) { + backgroundIds.remove(id); + } + } + + + /** + * Removes all messages that have been added to the <i>backgroundMsgs</i> + * list. + * + * @param uuid The UUID of an artifact or collection. + */ + protected void removeBackgroundMessages(String uuid) { + logger.debug("Remove background messages for: " + uuid); + + synchronized (backgroundMsgs) { + backgroundMsgs.remove(uuid); + } + } + + /** + * Adds an artifact's database id to the set of artifacts + * running in backgroound. To be in this set prevents the + * artifact to be removed from the database by the database cleaner. + * @param id The database id of the artifact to be protected + * from being removed from the database. + */ + protected void addIdToBackground(int id) { + synchronized (backgroundIds) { + backgroundIds.add(Integer.valueOf(id)); + } + } + + /** + * Adds a <i>Message</i> to the background messages list of the Artifact or + * Collection. + * + * @param uuid The UUID of the Artifact or Collection. + * @param msg The message that should be added to the background messages + * list. + */ + public void addBackgroundMessage(String uuid, Message msg) { + logger.debug("Add new background messsage for: " + uuid); + + synchronized (backgroundMsgs) { + LinkedList<Message> messages = backgroundMsgs.get(uuid); + + if (messages == null) { + messages = new LinkedList<Message>(); + backgroundMsgs.put(uuid, messages); + } + + messages.addLast(msg); + } + } + + public Set<Integer> getLockedIds() { + synchronized (backgroundIds) { + return new HashSet<Integer>(backgroundIds); + } + } + + /** + * Returns the background <i>Message</i>s for a specific Artifact or + * Collection. + * + * @param uuid The Artifact's or Collection's UUID. + * + * @return a <i>List</i> of <i>Message</i>s or null if no messages are + * existing. + */ + public LinkedList<Message> getBackgroundMessages(String uuid) { + logger.debug("Retrieve background message for: " + uuid); + + synchronized (backgroundMsgs) { + return backgroundMsgs.get(uuid); + } + } + + public String [][] artifactFactoryNamesAndDescriptions() { + return factoryNamesAndDescription; + } + + public ArtifactFactory getInternalArtifactFactory(String factoryName) { + return getArtifactFactory(factoryName); + } + + public ArtifactFactory getArtifactFactory(String factoryName) { + return (ArtifactFactory)name2factory.get(factoryName); + } + + public UserFactory getUserFactory() { + return userFactory; + } + + public ArtifactCollectionFactory getArtifactCollectionFactory() { + return collectionFactory; + } + + public Document createArtifactWithFactory( + String factoryName, + CallMeta callMeta, + Document data + ) + throws ArtifactDatabaseException + { + logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory " + + factoryName); + ArtifactFactory factory = getArtifactFactory(factoryName); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + Artifact artifact = factory.createArtifact( + backend.newIdentifier(), + context, + callMeta, + data); + + if (artifact == null) { + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + PersistentArtifact persistentArtifact; + + try { + persistentArtifact = backend.storeInitially( + artifact, + factory, + factory.timeToLiveUntouched(artifact, context)); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + persistentArtifact); + + try { + return artifact.describe(null, cc); + } + finally { + cc.postCall(); + } + } + + + public Artifact getRawArtifact(String identifier) + throws ArtifactDatabaseException + { + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return artifact.getArtifact(); + } + + + public Document describe( + String identifier, + Document data, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.describe(data, cc); + + if (postDescribeHooks != null) { + for (Hook hook: postDescribeHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public Document advance( + String identifier, + Document target, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.STORE, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.advance(target, cc); + + if (postAdvanceHooks != null) { + for (Hook hook: postAdvanceHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public Document feed(String identifier, Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.STORE, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.feed(data, cc); + + if (postFeedHooks != null) { + for (Hook hook: postFeedHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public DeferredOutput out( + String identifier, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + return out(identifier, null, format, callMeta); + } + + public DeferredOutput out( + String identifier, + String type, + Document format, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return new DeferredOutputImpl(artifact, type, format, callMeta); + } + + public Document exportArtifact(String artifact, CallMeta callMeta) + throws ArtifactDatabaseException + { + final String [] factoryName = new String[1]; + + byte [] bytes = (byte [])backend.loadArtifact( + artifact, + new Backend.ArtifactLoader() { + public Object load( + ArtifactFactory factory, + Long ttl, + byte [] bytes, + int id + ) { + factoryName[0] = factory.getName(); + + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(bytes); + artifact.cleanup(context); + + return serializer.toBytes(artifact); + } + }); + + if (bytes == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return createExportDocument( + factoryName[0], + bytes, + exportSecret); + } + + /** + * Creates an exteral XML representation of an artifact. + * @param factoryName The name of the factory which is responsible + * for the serialized artifact. + * @param artifact The byte data of the artifact itself. + * @param secret The signing secret. + * @return An XML document containing the external representation + * of the artifact. + */ + protected static Document createExportDocument( + String factoryName, + byte [] artifact, + byte [] secret + ) { + Document document = XMLUtils.newDocument(); + + MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } + catch (NoSuchAlgorithmException nsae) { + logger.error(nsae.getLocalizedMessage(), nsae); + return document; + } + + md.update(artifact); + md.update(secret); + + String checksum = Hex.encodeHexString(md.digest()); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("action"); + document.appendChild(root); + + Element type = ec.create("type"); + ec.addAttr(type, "name", "export", true); + root.appendChild(type); + + Element data = ec.create("data"); + ec.addAttr(data, "checksum", checksum, true); + ec.addAttr(data, "factory", factoryName, true); + data.setTextContent(Base64.encodeBase64String(artifact)); + + root.appendChild(data); + + return document; + } + + public Document importArtifact(Document input, CallMeta callMeta) + throws ArtifactDatabaseException + { + String factoryName = XMLUtils.xpathString( + input, + XPATH_IMPORT_FACTORY, + ArtifactNamespaceContext.INSTANCE); + + ArtifactFactory factory; + + if (factoryName == null + || (factoryName = factoryName.trim()).length() == 0 + || (factory = getArtifactFactory(factoryName)) == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String checksumString = XMLUtils.xpathString( + input, + XPATH_IMPORT_CHECKSUM, + ArtifactNamespaceContext.INSTANCE); + + byte [] checksum; + + if (checksumString == null + || (checksumString = checksumString.trim()).length() == 0 + || (checksum = StringUtils.decodeHex(checksumString)) == null + ) { + throw new ArtifactDatabaseException(INVALID_CHECKSUM); + } + + checksumString = null; + + String dataString = XMLUtils.xpathString( + input, + XPATH_IMPORT_DATA, + ArtifactNamespaceContext.INSTANCE); + + if (dataString == null + || (dataString = dataString.trim()).length() == 0) { + throw new ArtifactDatabaseException(NO_DATA); + } + + byte [] data = Base64.decodeBase64(dataString); + + dataString = null; + + MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } + catch (NoSuchAlgorithmException nsae) { + logger.error(nsae.getLocalizedMessage(), nsae); + return XMLUtils.newDocument(); + } + + md.update(data); + md.update(exportSecret); + + byte [] digest = md.digest(); + + if (!Arrays.equals(checksum, digest)) { + throw new ArtifactDatabaseException(CHECKSUM_MISMATCH); + } + + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(data); data = null; + + if (artifact == null) { + throw new ArtifactDatabaseException(INVALID_ARTIFACT); + } + + artifact.setIdentifier(backend.newIdentifier()); + PersistentArtifact persistentArtifact; + + try { + persistentArtifact = backend.storeOrReplace( + artifact, + factory, + factory.timeToLiveUntouched(artifact, context)); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + persistentArtifact); + + try { + return artifact.describe(input, cc); + } + finally { + cc.postCall(); + } + } + + public String [][] serviceNamesAndDescriptions() { + return serviceNamesAndDescription; + } + + public Service.Output process( + String serviceName, + Document input, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + Service service = (Service)name2service.get(serviceName); + + if (service == null) { + throw new ArtifactDatabaseException(NO_SUCH_SERVICE); + } + + return service.process(input, context, callMeta); + } + + // User API + + /** Returns user(s) elements. */ + public Document listUsers(CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + User [] users = backend.getUsers(factory, context); + + if (users != null) { + logger.debug(users.length + " users found in the backend."); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("users"); + result.appendChild(root); + + if(users != null) { + for (User user: users) { + Element ue = ec.create("user"); + ec.addAttr(ue, "uuid", user.identifier(), true); + ec.addAttr(ue, "name", user.getName(), true); + Element ua = ec.create("account"); + ec.addAttr(ua, "name", user.getAccount(), true); + ue.appendChild(ua); + + Document role = user.getRole(); + + if (role != null) { + ue.appendChild(result.importNode(role.getFirstChild(), true)); + } + + root.appendChild(ue); + } + } + + return result; + } + + /** Search for a user. */ + public Document findUser(Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String account = XMLUtils.xpathString( + data, XPATH_USERACCOUNT_FIND, ArtifactNamespaceContext.INSTANCE); + + if (account == null || account.length() == 0) { + logger.warn("Can't find user without account!"); + throw new ArtifactDatabaseException(NO_USERACCOUNT); + } + + User user = backend.findUser(account, factory, context); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element ue = ec.create("user"); + + if (user != null) { + logger.debug(user + " user found in the backend."); + + ec.addAttr(ue, "uuid", user.identifier(), true); + ec.addAttr(ue, "name", user.getName(), true); + Element ua = ec.create("account"); + ec.addAttr(ua, "name", user.getAccount(), true); + ue.appendChild(ua); + + Document role = user.getRole(); + + if (role != null) { + ue.appendChild(result.importNode(role.getFirstChild(), true)); + } + } + + result.appendChild(ue); + + return result; + } + + public Document createUser(Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String name = XMLUtils.xpathString( + data, XPATH_USERNAME, ArtifactNamespaceContext.INSTANCE); + + if (name == null || name.length() == 0) { + logger.warn("User without username not accepted!"); + throw new ArtifactDatabaseException(NO_USERNAME); + } + + String account = XMLUtils.xpathString( + data, XPATH_USERACCOUNT, ArtifactNamespaceContext.INSTANCE); + + if (account == null || account.length() == 0) { + logger.warn("User without account not accepted!"); + throw new ArtifactDatabaseException(NO_USERACCOUNT); + } + + Node tmp = (Node) XMLUtils.xpath( + data, + XPATH_USERROLE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + Document role = XMLUtils.newDocument(); + + if (tmp != null) { + Node clone = role.importNode(tmp, true); + role.appendChild(clone); + } + + User newUser = null; + + try { + newUser = backend.createUser(name, account, role, userFactory, context); + } + catch (Exception e) { + logger.error(e.getMessage(), e); + throw new ArtifactDatabaseException(USER_CREATION_FAILED); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + + if (newUser != null) { + root.setTextContent(OPERATION_SUCCESSFUL); + } + else { + root.setTextContent(OPERATION_FAILURE); + } + + result.appendChild(root); + + return result; + } + + public Document deleteUser(String userId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Delete user: " + userId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.deleteUser(userId); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + // Collection API + + public Document getCollectionsMasterArtifact( + String collectionId, + CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + String masterUUID = backend.getMasterArtifact(collectionId); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + Element root = ec.create("artifact-collection"); + ec.addAttr(root, "name", c.getName(), true); + ec.addAttr(root, "uuid", c.identifier(), true); + ec.addAttr(root, "ttl", String.valueOf(c.getTTL()), true); + + Date creationTime = c.getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + ec.addAttr(root, "creation", creation, true); + result.appendChild(root); + + if (masterUUID == null || masterUUID.length() == 0) { + logger.debug("No master for the collection existing."); + return result; + } + + Element master = ec.create("artifact"); + ec.addAttr(master, "uuid", masterUUID, true); + + root.appendChild(master); + + return result; + } + + public Document listCollections(String userId, CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + UserFactory uf = getUserFactory(); + + if (acf == null || uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + logger.debug("Fetch the list of collection for user: " + userId); + + ArtifactCollection [] ac = backend.listCollections( + userId, + null, // XXX: fetch from REST + acf, uf, + context); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("artifact-collections"); + result.appendChild(root); + + if (ac == null || ac.length == 0) { + logger.debug("No collections for the user existing."); + + return result; + } + + logger.debug("Found " + ac.length + " collections of the user."); + + for (ArtifactCollection c: ac) { + Element collection = ec.create("artifact-collection"); + ec.addAttr(collection, "name", c.getName(), true); + ec.addAttr(collection, "uuid", c.identifier(), true); + ec.addAttr(collection, "ttl", String.valueOf(c.getTTL()), true); + + Date creationTime = c.getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + ec.addAttr(collection, "creation", creation, true); + + root.appendChild(collection); + } + + return result; + } + + public Document createCollection(String ownerId, Document data, + CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String name = XMLUtils.xpathString( + data, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); + + logger.debug("Create new collection with name: " + name); + + Document attr = null; + + Node attrNode = (Node) XMLUtils.xpath( + data, + XPATH_COLLECTION_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attrNode != null) { + attr = XMLUtils.newDocument(); + attr.appendChild(attr.importNode(attrNode, true)); + } + + ArtifactCollection ac = backend.createCollection( + ownerId, name, acf, attr, context); + + if (ac == null) { + throw new ArtifactDatabaseException(COLLECTION_CREATION_FAILED); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + Element acElement = ec.create("artifact-collection"); + ec.addAttr(acElement, "uuid", ac.identifier(), true); + ec.addAttr(acElement, "ttl", String.valueOf(ac.getTTL()), true); + + root.appendChild(acElement); + + return result; + } + + public Document deleteCollection(String collectionId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Delete collection: " + collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.deleteCollection(collectionId); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document describeCollection(String collectionId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Describe collection: " + collectionId); + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + CollectionCallContext cc = new CollectionCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + c); + + try { + return c.describe(cc); + } + finally { + cc.postCall(); + } + } + + + public Document getCollectionAttribute(String collectionId, CallMeta meta) + throws ArtifactDatabaseException + { + logger.debug("Fetch collection attribute for: " + collectionId); + + return backend.getCollectionAttribute(collectionId); + } + + + public Document setCollectionAttribute( + String collectionId, + CallMeta meta, + Document attribute) + throws ArtifactDatabaseException + { + logger.debug("Set new attribute for the collection: " + collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.setCollectionAttribute( + collectionId, attribute); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document getCollectionItemAttribute(String collectionId, String artifactId, + CallMeta callMeta) throws ArtifactDatabaseException + { + logger.debug("Fetch the attribute for the artifact: " + artifactId); + + return backend.getCollectionItemAttribute(collectionId, artifactId); + } + + public Document setCollectionItemAttribute(String collectionId, String artifactId, + Document source, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Set the attribute for the artifact: " + artifactId); + + Document attribute = null; + + Node attr = (Node) XMLUtils.xpath( + source, + XPATH_COLLECTION_ITEM_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attr != null) { + attribute = XMLUtils.newDocument(); + attribute.appendChild(attribute.importNode(attr, true)); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.setCollectionItemAttribute( + collectionId, artifactId, attribute); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document addCollectionArtifact( + String collectionId, + String artifactId, + Document input, + CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug( + "Add artifact '" + artifactId + "' collection '" +collectionId+"'"); + + Document attr = XMLUtils.newDocument(); + + Node attrNode = (Node) XMLUtils.xpath( + input, + XPATH_COLLECTION_ITEM_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attrNode != null) { + attr.appendChild(attr.importNode(attrNode, true)); + } + + boolean success = backend.addCollectionArtifact( + collectionId, + artifactId, + attr); + + if (!success) { + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + root.setTextContent(OPERATION_FAILURE); + + return result; + } + + return describeCollection(collectionId, callMeta); + } + + public Document removeCollectionArtifact(String collectionId, String artifactId, + CallMeta callMeta) throws ArtifactDatabaseException + { + logger.debug( + "Remove artifact '" + artifactId + "' from collection '" + + collectionId + "'"); + + Document attr = XMLUtils.newDocument(); + + boolean success = backend.removeCollectionArtifact( + collectionId, + artifactId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document listCollectionArtifacts(String collectionId, + CallMeta callMeta) throws ArtifactDatabaseException + { + CollectionItem[] items = backend.listCollectionArtifacts(collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + Element ac = ec.create("artifact-collection"); + ec.addAttr(ac, "uuid", collectionId, true); + + for (CollectionItem item: items) { + Element i = ec.create("collection-item"); + Element attr = ec.create("attribute"); + ec.addAttr(i, "uuid", item.getArtifactIdentifier(), true); + + Document attribute = item.getAttribute(); + if (attribute != null) { + Node firstChild = attribute.getFirstChild(); + attr.appendChild(result.importNode(firstChild, true)); + } + else { + logger.debug("No attributes for the collection item!"); + } + + i.appendChild(attr); + ac.appendChild(i); + } + + root.appendChild(ac); + result.appendChild(root); + + return result; + } + + public Document setCollectionTTL(String uuid, Document doc, CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + String tmp = XMLUtils.xpathString( + doc, XPATH_COLLECTION_TTL, ArtifactNamespaceContext.INSTANCE); + + logger.info("Set TTL of artifact collection '" + uuid + "' to: " + tmp); + + if (tmp == null || tmp.length() == 0) { + logger.warn("No ttl for this collection specified."); + root.setTextContent(OPERATION_FAILURE); + + return result; + } + + Long ttl = null; + if ((tmp = tmp.toUpperCase()).equals("INF")) { + ttl = null; + } + else if (tmp.equals("DEFAULT")) { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + ttl = acf.timeToLiveUntouched(null, context); + } + else { + try { + ttl = Long.valueOf(tmp); + + if (ttl < 0) { + throw new NumberFormatException("Negative value."); + } + } + catch (NumberFormatException nfe) { + logger.error("Could not determine TTL", nfe); + root.setTextContent(OPERATION_FAILURE); + return result; + } + } + + boolean success = backend.setCollectionTTL(uuid, ttl); + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + public Document setCollectionName(String uuid, Document doc, CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + String name = XMLUtils.xpathString( + doc, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); + + logger.info("Set name of collection '" + uuid + "' to: " + name); + + if (name == null || name.length() == 0) { + logger.warn("The new name is emtpy. No new name set!"); + root.setTextContent(OPERATION_FAILURE); + return result; + } + + boolean success = backend.setCollectionName(uuid, name); + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + public DeferredOutput outCollection( + String collectionId, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + return outCollection(collectionId, null, format, callMeta); + } + + public DeferredOutput outCollection( + String collectionId, + String type, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + return new DeferredCollectionOutputImpl(c, type, format, callMeta); + } + + protected void initCallContext(CallContext cc) { + logger.debug("initCallContext"); + if (callContextListener != null) { + callContextListener.init(cc); + } + } + + protected void closeCallContext(CallContext cc) { + logger.debug("closeCallContext"); + if (callContextListener != null) { + callContextListener.close(cc); + } + } + + @Override + public void loadAllArtifacts(ArtifactLoadedCallback callback) + throws ArtifactDatabaseException + { + logger.debug("loadAllArtifacts"); + boolean success = backend.loadAllArtifacts(callback); + if (!success) { + throw new ArtifactDatabaseException(INTERNAL_ERROR); + } + } + + public void start() { + if (lifetimeListeners == null || lifetimeListeners.isEmpty()) { + return; + } + + for (LifetimeListener ltl: lifetimeListeners) { + ltl.systemUp(context); + } + + logger.debug("all lifetime listeners started"); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + for (LifetimeListener ltl: lifetimeListeners) { + ltl.systemDown(context); + } + } + }); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :