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 Sascha L. Teichmann
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:
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:
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 backgroundIds;
ingo@331:
ingo@331: /**
ingo@331: * A list of background messages for Artifacts and Collections.
ingo@331: */
ingo@331: protected Map> 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 postFeedHooks;
ingo@292:
ingo@292: /**
ingo@292: * Hooks that are executed after an artifact has advanced.
ingo@292: */
ingo@292: protected List postAdvanceHooks;
ingo@292:
sascha@304: protected List 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();
ingo@331: backgroundMsgs = new HashMap>();
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 postFeedHooks) {
ingo@292: this.postFeedHooks = postFeedHooks;
ingo@292: }
ingo@292:
ingo@292: public void setPostAdvanceHook(List postAdvanceHooks) {
ingo@292: this.postAdvanceHooks = postAdvanceHooks;
ingo@292: }
ingo@292:
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@292: }
ingo@292:
sascha@311: protected void setupBackendListeners(FactoryBootstrap bootstrap) {
sascha@313: logger.debug("setupBackendListeners");
sascha@311: List 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 backgroundMsgs
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 Message 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 messages = backgroundMsgs.get(uuid);
ingo@331:
ingo@331: if (messages == null) {
ingo@331: messages = new LinkedList();
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 getLockedIds() {
sascha@32: synchronized (backgroundIds) {
sascha@230: return new HashSet(backgroundIds);
sascha@32: }
sascha@32: }
sascha@32:
ingo@331: /**
ingo@331: * Returns the background Messages 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 List of Messages or null if no messages are
ingo@331: * existing.
ingo@331: */
ingo@331: public LinkedList 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: {
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 {
sascha@55: return artifact.getArtifact().describe(data, cc);
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@70: public Document 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:
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);
ingo@150:
ingo@150: for (User user: users) {
ingo@150: Element ue = ec.create("user");
ingo@183: ec.addAttr(ue, "uuid", user.identifier(), true);
ingo@183: ec.addAttr(ue, "name", user.getName(), true);
ingo@150:
ingo@160: Document role = user.getRole();
ingo@160:
ingo@160: if (role != null) {
ingo@160: ue.appendChild(result.importNode(role.getFirstChild(), true));
ingo@160: }
ingo@160:
ingo@150: root.appendChild(ue);
ingo@160:
ingo@150: }
ingo@150:
ingo@150: return result;
sascha@117: }
sascha@117:
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:
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 {
sascha@157: newUser = backend.createUser(name, 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:
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@303: 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 :