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: ingo@348: /** ingo@348: * Hooks that are executed after an artifact's describe() operation was ingo@348: * called. ingo@348: */ ingo@348: protected List postDescribeHooks; ingo@348: 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: ingo@348: public void setPostDescribeHook(List 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 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: { 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@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: 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@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 :