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