Mercurial > dive4elements > framework
changeset 473:d0ac790a6c89 dive4elements-move
Moved directories to org.dive4elements
line wrap: on
line diff
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2010, 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import org.apache.log4j.Logger; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.HashMap; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.DataProvider; - - -/** - * Abstract class that implements some basic methods of a CallContext. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public abstract class AbstractCallContext implements CallContext { - - Logger logger = Logger.getLogger(AbstractCallContext.class); - - /** - * The ArtifactDatabase instance. - */ - protected ArtifactDatabaseImpl database; - - /** - * The action to be performed after the artifacts or collections calls. - */ - protected int action; - - /** - * The meta information of the concrete call (preferred languages et. al.) - */ - protected CallMeta callMeta; - - /** - * Map to act like a clipboard when nesting calls like a proxy artifact. - */ - protected Map customValues; - - /** - * Map to act like a clipboard when nesting calls like a proxy artifact. - */ - protected Map<Object, List<DataProvider>> dataProviders; - - - /** - * The default constructor of this abstract CallContext. - * - * @param artifactDatabase The artifact database. - * @param action The action. - * @param callMeta The CallMeta object. - */ - public AbstractCallContext( - ArtifactDatabaseImpl artifactDatabase, - int action, - CallMeta callMeta - ) { - this.database = artifactDatabase; - this.action = action; - this.callMeta = callMeta; - - database.initCallContext(this); - } - - - public void postCall() { - database.closeCallContext(this); - } - - public abstract void afterCall(int action); - - public abstract Long getTimeToLive(); - - public abstract void afterBackground(int action); - - - public Object globalContext() { - return database.context; - } - - - public ArtifactDatabase getDatabase() { - return database; - } - - - public CallMeta getMeta() { - return callMeta; - } - - - public Object getContextValue(Object key) { - return customValues != null - ? customValues.get(key) - : null; - } - - public Object putContextValue(Object key, Object value) { - if (customValues == null) { - customValues = new HashMap(); - } - return customValues.put(key, value); - } - - /** - * Get list of DataProviders that registered for given key. - * @return list (empty list if none found, never null). - */ - public List<DataProvider> getDataProvider(Object key) { - if (dataProviders != null) { - List<DataProvider> list = dataProviders.get(key); - return list != null - ? list - : java.util.Collections.<DataProvider>emptyList(); - } - return java.util.Collections.<DataProvider>emptyList(); - } - - - /** - * Let a DataProvider register itself with given key. - * Multiple DataProvider can register under the same key. - */ - public Object registerDataProvider(Object key, DataProvider value) { - List<DataProvider> providers = null; - if (dataProviders == null) { - dataProviders = new HashMap(); - providers = new ArrayList<DataProvider>(); - providers.add(value); - return dataProviders.put(key, providers); - } - providers = dataProviders.get(key); - - if (providers == null) { - providers = new ArrayList<DataProvider>(); - } - providers.add(value); - return dataProviders.put(key, providers); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import de.intevation.artifactdatabase.rest.HTTPServer; - -import java.io.File; - -import java.net.MalformedURLException; - -import org.apache.log4j.PropertyConfigurator; - -import org.slf4j.bridge.SLF4JBridgeHandler; - -/** - * Starting point of the artifact database. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class App -{ - /** - * The logging is done via Log4j. To configure the logging - * a file 'log4j.properties' is search in the configuration directory. - */ - public static final String LOG4J_PROPERTIES = - "log4j.properties"; - - /** - * Trys to load the Log4j configuration from ${config.dir}/log4j.properties. - */ - public static final void configureLogging() { - File configDir = Config.getConfigDirectory(); - File propFile = new File(configDir, LOG4J_PROPERTIES); - - if (propFile.isFile() && propFile.canRead()) { - try { - PropertyConfigurator.configure(propFile.toURI().toURL()); - SLF4JBridgeHandler.install(); - } - catch (MalformedURLException mue) { - mue.printStackTrace(System.err); - } - } - } - - /** - * Starts the artifact database. - * @param args The commandline arguments. Unused. - */ - public static void main(String[] args) { - - configureLogging(); - - FactoryBootstrap bootstrap = new FactoryBootstrap(); - - bootstrap.boot(); - - Backend backend = Backend.getInstance(); - - ArtifactDatabaseImpl db = new ArtifactDatabaseImpl( - bootstrap, backend); - - DatabaseCleaner cleaner = new DatabaseCleaner( - bootstrap.getContext(), backend, backend.getConfig()); - - HTTPServer httpServer = bootstrap.getHTTPServer(); - - bootstrap = null; - - backend.setCleaner(cleaner); - - cleaner.setLockedIdsProvider(db); - - cleaner.start(); - - db.start(); - - httpServer.startAsServer(db); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2010, 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import java.util.LinkedList; - -import org.apache.log4j.Logger; - -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.Message; - -import de.intevation.artifactdatabase.Backend.PersistentArtifact; - - -/** - * Class that implements the call context handed to the methods calls - * describe(), feed(), etc. of the artifact. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class ArtifactCallContext extends AbstractCallContext { - - private static Logger logger = Logger.getLogger(ArtifactCallContext.class); - - - /** - * Error message issued if an artifact wants to translate itself - * into a none valid persistent state. - */ - public static final String INVALID_CALL_STATE = "Invalid after call state"; - - /** - * Error message issued if one tries to remove a requested artifact - * from the list of artifacts running in background which is - * not in this list. - */ - public static final String NOT_IN_BACKGROUND = "Not in background"; - - - /** - * The persistence wrapper around the living artifact - */ - protected PersistentArtifact artifact; - - - public ArtifactCallContext( - ArtifactDatabaseImpl artifactDatabase, - int action, - CallMeta callMeta, - PersistentArtifact artifact) - { - super(artifactDatabase, action, callMeta); - - this.artifact = artifact; - } - - - public void afterCall(int action) { - this.action = action; - if (action == BACKGROUND) { - database.addIdToBackground(artifact.getId()); - } - } - - - public void afterBackground(int action) { - if (this.action != BACKGROUND) { - throw new IllegalStateException(NOT_IN_BACKGROUND); - } - database.fromBackground(artifact, action); - } - - - public boolean isInBackground() { - return database.getLockedIds().contains(artifact.getId()); - } - - - public void addBackgroundMessage(Message msg) { - database.addBackgroundMessage(artifact.getArtifact().identifier(), msg); - } - - - public LinkedList<Message> getBackgroundMessages() { - return database.getBackgroundMessages( - artifact.getArtifact().identifier()); - } - - - public Long getTimeToLive() { - return artifact.getTTL(); - } - - - /** - * Dispatches and executes the persistence action after - * the return of the concrete artifact call. - */ - public void postCall() { - try { - switch (action) { - case NOTHING: - break; - case TOUCH: - artifact.touch(); - break; - case STORE: - artifact.store(); - break; - case BACKGROUND: - logger.warn( - "BACKGROUND processing is not fully implemented, yet!"); - artifact.store(); - break; - default: - logger.error(INVALID_CALL_STATE + ": " + action); - throw new IllegalStateException(INVALID_CALL_STATE); - } - } - finally { - super.postCall(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1976 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.XMLUtils; -import de.intevation.artifacts.common.utils.StringUtils; - -import de.intevation.artifactdatabase.Backend.PersistentArtifact; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.ArtifactCollectionFactory; -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.ArtifactFactory; -import de.intevation.artifacts.ArtifactNamespaceContext; -import de.intevation.artifacts.ArtifactSerializer; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.CollectionItem; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.Hook; -import de.intevation.artifacts.Message; -import de.intevation.artifacts.Service; -import de.intevation.artifacts.ServiceFactory; -import de.intevation.artifacts.User; -import de.intevation.artifacts.UserFactory; - -import java.io.IOException; -import java.io.OutputStream; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.xml.xpath.XPathConstants; - -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * The core implementation of artifact database. This layer exposes - * the needed methods to the artifact runtime system which e.g. may - * expose them via REST. The concrete persistent representation of the - * artifacts is handled by the {@link Backend backend}. - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class ArtifactDatabaseImpl -implements ArtifactDatabase, - DatabaseCleaner.LockedIdsProvider, - Backend.FactoryLookup -{ - private static Logger logger = - Logger.getLogger(ArtifactDatabaseImpl.class); - - /** The key under which the artifact database is stored in the global - * context.*/ - public static final String GLOBAL_CONTEXT_KEY = "global.artifact.database"; - - /** Message that is returned if an operation was successful.*/ - public static final String OPERATION_SUCCESSFUL = - "SUCCESS"; - - /** Message that is returned if an operation failed.*/ - public static final String OPERATION_FAILURE = - "FAILURE"; - - /** - * Error message issued if a requested artifact factory - * is not registered to this database. - */ - public static final String NO_SUCH_FACTORY = - "No such factory"; - - /** - * Error message issued if a requested artifact is not found - * in this database. - */ - public static final String NO_SUCH_ARTIFACT = - "No such artifact"; - - /** - * Error message issued if a requested artifact is not found - * in this database. - */ - public static final String NO_SUCH_COLLECTION = - "No such collection"; - - /** - * Error message issued if the creation of an artifact failed. - */ - public static final String CREATION_FAILED = - "Creation of artifact failed"; - - /** - * Error message if an severe internal error occurred. - */ - public static final String INTERNAL_ERROR = - "Creation of artifact failed"; - - /** - * Error message issued if a requested service is not - * offered by this database. - */ - public static final String NO_SUCH_SERVICE = - "No such service"; - - /** - * Default digest hash to be used while im-/exporting artifacts. - */ - public static final String DIGEST_ALGORITHM = - "SHA-1"; - - /** - * XPath to get the checksum from an XML representation of - * an exported artifact. - */ - public static final String XPATH_IMPORT_CHECKSUM = - "/art:action/art:data/@checksum"; - - /** - * XPath to get the name of the factory which should be - * used to revive an antrifact that is going to be imported. - */ - public static final String XPATH_IMPORT_FACTORY = - "/art:action/art:data/@factory"; - - /** - * XPath to get the base64 encoded data of an artifact - * that is going to be imported. - */ - public static final String XPATH_IMPORT_DATA = - "/art:action/art:data/text()"; - - /** - * Error message issued if the checksum of an - * artifact to be imported has an invalid syntax. - */ - public static final String INVALID_CHECKSUM = - "Invalid checksum"; - - /** - * Error message issued the checksum validation - * of an artifact to be imported fails. - */ - public static final String CHECKSUM_MISMATCH = - "Mismatching checksum"; - - /** - * Error message issued if an artifact to be imported - * does not have any data. - */ - public static final String NO_DATA = - "No data"; - - /** - * Error message issued if the deserialization of - * an artifact to be imported fails. - */ - public static final String INVALID_ARTIFACT = - "Invalid artifact"; - - - // User constants - - /** - * Error message issued if the creation of a user failed. - */ - public static final String USER_CREATION_FAILED = - "Creation of user failed."; - - /** XPath to figure out the name of a new user.*/ - public static final String XPATH_USERNAME = - "/art:action/art:user/@name"; - - /** XPath to figure out the role of a new user.*/ - public static final String XPATH_USERROLE = - "/art:action/art:user/art:role"; - - /** XPath to figure out the account of a new user.*/ - public static final String XPATH_USERACCOUNT = - "/art:action/art:user/art:account/@name"; - - /** XPath to figure out the account of when searching for a user .*/ - public static final String XPATH_USERACCOUNT_FIND = - "/art:action/art:account/@name"; - - /** Error message if a specified user does not exist.*/ - public static final String NO_SUCH_USER = - "No such user"; - - /** Error message if no username is given for user creation.*/ - public static final String NO_USERNAME = - "Invalid username"; - - /** Error message if no user account is given for user creation.*/ - public static final String NO_USERACCOUNT = - "Invalid user account name"; - - // Collection constants - - /** - * Error message issued if the creation of a collection failed. - */ - public static final String COLLECTION_CREATION_FAILED = - "Creation of collection failed"; - - /** - * XPath to figure out the name of a collection described in the incoming - * document. - */ - public static final String XPATH_COLLECTION_NAME = - "/art:action/art:type/art:collection/@name"; - - /** - * XPath to figure out the attributes for a collection. - */ - public static final String XPATH_COLLECTION_ATTRIBUTE = - "/art:action/art:type/art:collection/art:attribute"; - - /** - * XPath to figure out the attributes for an artifact that is put into a - * collection. - */ - public static final String XPATH_COLLECTION_ITEM_ATTRIBUTE = - "/art:action/art:type/art:artifact/art:attribute"; - - /** - * XPath to figure out the time to live value for setting a new TTL. - */ - public static final String XPATH_COLLECTION_TTL = - "/art:action/art:type/art:ttl/@value"; - - - /** - * This inner class allows the deferral of writing the output - * of the artifact's out() call. - */ - public class DeferredOutputImpl - implements DeferredOutput - { - /** - * The persistence wrapper around a living artifact. - */ - protected PersistentArtifact artifact; - /** - * The output type. - */ - protected String type; - /** - * The input document for the artifact's out() call. - */ - protected Document format; - /** - * The meta information of the artifact's out() call. - */ - protected CallMeta callMeta; - - /** - * Default constructor. - */ - public DeferredOutputImpl() { - } - - /** - * Constructor to create a deferred execution unit for - * the artifact's out() call given an artifact, an input document - * an the meta information. - * @param artifact The persistence wrapper around a living artifact. - * @param format The input document for the artifact's out() call. - * @param callMeta The meta information of the artifact's out() call. - */ - public DeferredOutputImpl( - PersistentArtifact artifact, - String type, - Document format, - CallMeta callMeta - ) { - this.artifact = artifact; - this.type = type; - this.format = format; - this.callMeta = callMeta; - } - - public void write(OutputStream output) throws IOException { - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.TOUCH, - callMeta, - artifact); - - try { - artifact.getArtifact().out(type, format, output, cc); - } - finally { - cc.postCall(); - } - } - } // class DeferredOutputImpl - - - /** - * This inner class allows the deferral of writing the output - * of the artifact's out() call. - */ - public class DeferredCollectionOutputImpl - implements DeferredOutput - { - /** - * The persistence wrapper around a living collection. - */ - protected ArtifactCollection collection; - /** - * The output type. - */ - protected String type; - /** - * The input document for the collection's out() call. - */ - protected Document format; - /** - * The meta information of the collection's out() call. - */ - protected CallMeta callMeta; - - /** - * Default constructor. - */ - public DeferredCollectionOutputImpl() { - } - - /** - * Constructor to create a deferred execution unit for - * the collection's out() call given a collection, an input document - * an the meta information. - * @param collection The collection. - * @param format The input document for the collection's out() call. - * @param callMeta The meta information of the collection's out() call. - */ - public DeferredCollectionOutputImpl( - ArtifactCollection collection, - String type, - Document format, - CallMeta callMeta - ) { - this.collection = collection; - this.type = type; - this.format = format; - this.callMeta = callMeta; - } - - public void write(OutputStream output) throws IOException { - - CollectionCallContext cc = new CollectionCallContext( - ArtifactDatabaseImpl.this, - CallContext.TOUCH, - callMeta, - collection); - - try { - collection.out(type, format, output, cc); - } - finally { - cc.postCall(); - } - } - } // class DeferredCollectionOutputImpl - - /** - * List of name/description pairs needed for - * {@link #artifactFactoryNamesAndDescriptions() }. - */ - protected String [][] factoryNamesAndDescription; - /** - * Map to access artifact factories by there name. - */ - protected HashMap name2factory; - - /** - * List of name/description pairs needed for - * {@link #serviceNamesAndDescriptions() }. - */ - protected String [][] serviceNamesAndDescription; - /** - * Map to access services by there name. - */ - protected HashMap name2service; - - /** - * The factory that is used to create new artifact collections. - */ - protected ArtifactCollectionFactory collectionFactory; - - /** - * The factory that is used to create and list users. - */ - protected UserFactory userFactory; - - /** - * Reference to the storage backend. - */ - protected Backend backend; - /** - * Reference of the global context of the artifact runtime system. - */ - protected GlobalContext context; - - /** - * The signing secret to be used for ex-/importing artifacts. - */ - protected byte [] exportSecret; - - /** - * A set of ids of artifact which currently running in background. - * This artifacts should not be removed from the database by the - * database cleaner. - */ - protected HashSet<Integer> backgroundIds; - - /** - * A list of background messages for Artifacts and Collections. - */ - protected Map<String, LinkedList<Message>> backgroundMsgs; - - - protected CallContext.Listener callContextListener; - - /** - * Hooks that are executed after an artifact has been fed. - */ - protected List<Hook> postFeedHooks; - - /** - * Hooks that are executed after an artifact has advanced. - */ - protected List<Hook> postAdvanceHooks; - - /** - * Hooks that are executed after an artifact's describe() operation was - * called. - */ - protected List<Hook> postDescribeHooks; - - protected List<LifetimeListener> lifetimeListeners; - - /** - * Default constructor. - */ - public ArtifactDatabaseImpl() { - } - - /** - * Constructor to create a artifact database with the given - * bootstrap parameters like artifact- and service factories et. al. - * Created this way the artifact database has no backend. - * @param bootstrap The parameters to start this artifact database. - */ - public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { - this(bootstrap, null); - } - - /** - * Constructor to create a artifact database with the a given - * backend and - * bootstrap parameters like artifact- and service factories et. al. - * @param bootstrap The parameters to start this artifact database. - * @param backend The storage backend. - */ - public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { - - logger.debug("new ArtifactDatabaseImpl"); - - backgroundIds = new HashSet<Integer>(); - backgroundMsgs = new HashMap<String, LinkedList<Message>>(); - - setupArtifactCollectionFactory(bootstrap); - setupArtifactFactories(bootstrap); - setupServices(bootstrap); - setupUserFactory(bootstrap); - setupCallContextListener(bootstrap); - setupHooks(bootstrap); - setupLifetimeListeners(bootstrap); - - context = bootstrap.getContext(); - context.put(GLOBAL_CONTEXT_KEY, this); - - exportSecret = bootstrap.getExportSecret(); - - wireWithBackend(backend, bootstrap); - } - - public CallContext.Listener getCallContextListener() { - return callContextListener; - } - - public void setCallContextListener( - CallContext.Listener callContextListener - ) { - this.callContextListener = callContextListener; - } - - - public void setPostFeedHook(List<Hook> postFeedHooks) { - this.postFeedHooks = postFeedHooks; - } - - public void setPostAdvanceHook(List<Hook> postAdvanceHooks) { - this.postAdvanceHooks = postAdvanceHooks; - } - - public void setPostDescribeHook(List<Hook> postDescribeHooks) { - this.postDescribeHooks = postDescribeHooks; - } - - /** - * Used to extract the artifact collection factory from bootstrap. - * - * @param bootstrap The bootstrap parameters. - */ - protected void setupArtifactCollectionFactory(FactoryBootstrap bootstrap) { - collectionFactory = bootstrap.getArtifactCollectionFactory(); - } - - /** - * Used to extract the artifact factories from the bootstrap - * parameters and building the internal lookup tables. - * @param bootstrap The bootstrap parameters. - */ - protected void setupArtifactFactories(FactoryBootstrap bootstrap) { - name2factory = new HashMap(); - - ArtifactFactory [] factories = bootstrap.getArtifactFactories(); - factoryNamesAndDescription = new String[factories.length][]; - - for (int i = 0; i < factories.length; ++i) { - - ArtifactFactory factory = factories[i]; - - String name = factory.getName(); - String description = factory.getDescription(); - - factoryNamesAndDescription[i] = - new String [] { name, description }; - - name2factory.put(name, factory); - } - } - - /** - * Used to extract the callContextListener from the bootstrap. - * - * @param bootstrap The bootstrap parameters. - */ - protected void setupCallContextListener(FactoryBootstrap bootstrap) { - setCallContextListener(bootstrap.getCallContextListener()); - } - - - protected void setupHooks(FactoryBootstrap bootstrap) { - setPostFeedHook(bootstrap.getPostFeedHooks()); - setPostAdvanceHook(bootstrap.getPostAdvanceHooks()); - setPostDescribeHook(bootstrap.getPostDescribeHooks()); - } - - protected void setupBackendListeners(FactoryBootstrap bootstrap) { - logger.debug("setupBackendListeners"); - List<BackendListener> bls = bootstrap.getBackendListeners(); - if (bls != null && !bls.isEmpty()) { - for (BackendListener listener: bls) { - listener.setup(context); - } - backend.addAllListeners(bls); - } - } - - protected void setupLifetimeListeners(FactoryBootstrap bootstrap) { - this.lifetimeListeners = bootstrap.getLifetimeListeners(); - } - - /** - * Used to extract the user factory from the bootstrap. - */ - protected void setupUserFactory(FactoryBootstrap bootstrap) { - userFactory = bootstrap.getUserFactory(); - } - - /** - * Used to extract the service factories from the bootstrap - * parameters, setting up the services and building the internal - * lookup tables. - * @param bootstrap The bootstrap parameters. - */ - protected void setupServices(FactoryBootstrap bootstrap) { - - name2service = new HashMap(); - - ServiceFactory [] serviceFactories = - bootstrap.getServiceFactories(); - - serviceNamesAndDescription = - new String[serviceFactories.length][]; - - for (int i = 0; i < serviceFactories.length; ++i) { - ServiceFactory factory = serviceFactories[i]; - - String name = factory.getName(); - String description = factory.getDescription(); - - serviceNamesAndDescription[i] = - new String [] { name, description }; - - name2service.put( - name, - factory.createService(bootstrap.getContext())); - } - - } - - /** - * Wires a storage backend to this artifact database and - * establishes a callback to be able to revive artifacts - * via the serializers of this artifact factories. - * @param backend The backend to be wired with this artifact database. - */ - public void wireWithBackend(Backend backend, FactoryBootstrap bootstrap) { - logger.debug("wireWithBackend"); - if (backend != null) { - this.backend = backend; - backend.setFactoryLookup(this); - setupBackendListeners(bootstrap); - } - } - - /** - * Called after an backgrounded artifact signals its - * will to be written back to the backend. - * @param artifact The persistence wrapper around - * the backgrounded artifact. - * @param action The action to be performed. - */ - protected void fromBackground(PersistentArtifact artifact, int action) { - logger.warn("BACKGROUND processing is not fully implemented, yet!"); - switch (action) { - case CallContext.NOTHING: - break; - case CallContext.TOUCH: - artifact.touch(); - break; - case CallContext.STORE: - artifact.store(); - break; - default: - logger.warn("operation not allowed in fromBackground"); - } - removeIdFromBackground(artifact.getId()); - removeBackgroundMessages(artifact.getArtifact().identifier()); - } - - /** - * Removes an artifact's database id from the set of backgrounded - * artifacts. The database cleaner is now able to remove it safely - * from the database again. - * @param id The database id of the artifact. - */ - protected void removeIdFromBackground(int id) { - synchronized (backgroundIds) { - backgroundIds.remove(id); - } - } - - - /** - * Removes all messages that have been added to the <i>backgroundMsgs</i> - * list. - * - * @param uuid The UUID of an artifact or collection. - */ - protected void removeBackgroundMessages(String uuid) { - logger.debug("Remove background messages for: " + uuid); - - synchronized (backgroundMsgs) { - backgroundMsgs.remove(uuid); - } - } - - /** - * Adds an artifact's database id to the set of artifacts - * running in backgroound. To be in this set prevents the - * artifact to be removed from the database by the database cleaner. - * @param id The database id of the artifact to be protected - * from being removed from the database. - */ - protected void addIdToBackground(int id) { - synchronized (backgroundIds) { - backgroundIds.add(Integer.valueOf(id)); - } - } - - /** - * Adds a <i>Message</i> to the background messages list of the Artifact or - * Collection. - * - * @param uuid The UUID of the Artifact or Collection. - * @param msg The message that should be added to the background messages - * list. - */ - public void addBackgroundMessage(String uuid, Message msg) { - logger.debug("Add new background messsage for: " + uuid); - - synchronized (backgroundMsgs) { - LinkedList<Message> messages = backgroundMsgs.get(uuid); - - if (messages == null) { - messages = new LinkedList<Message>(); - backgroundMsgs.put(uuid, messages); - } - - messages.addLast(msg); - } - } - - public Set<Integer> getLockedIds() { - synchronized (backgroundIds) { - return new HashSet<Integer>(backgroundIds); - } - } - - /** - * Returns the background <i>Message</i>s for a specific Artifact or - * Collection. - * - * @param uuid The Artifact's or Collection's UUID. - * - * @return a <i>List</i> of <i>Message</i>s or null if no messages are - * existing. - */ - public LinkedList<Message> getBackgroundMessages(String uuid) { - logger.debug("Retrieve background message for: " + uuid); - - synchronized (backgroundMsgs) { - return backgroundMsgs.get(uuid); - } - } - - public String [][] artifactFactoryNamesAndDescriptions() { - return factoryNamesAndDescription; - } - - public ArtifactFactory getInternalArtifactFactory(String factoryName) { - return getArtifactFactory(factoryName); - } - - public ArtifactFactory getArtifactFactory(String factoryName) { - return (ArtifactFactory)name2factory.get(factoryName); - } - - public UserFactory getUserFactory() { - return userFactory; - } - - public ArtifactCollectionFactory getArtifactCollectionFactory() { - return collectionFactory; - } - - public Document createArtifactWithFactory( - String factoryName, - CallMeta callMeta, - Document data - ) - throws ArtifactDatabaseException - { - logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory " - + factoryName); - ArtifactFactory factory = getArtifactFactory(factoryName); - - if (factory == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - Artifact artifact = factory.createArtifact( - backend.newIdentifier(), - context, - callMeta, - data); - - if (artifact == null) { - throw new ArtifactDatabaseException(CREATION_FAILED); - } - - PersistentArtifact persistentArtifact; - - try { - persistentArtifact = backend.storeInitially( - artifact, - factory, - factory.timeToLiveUntouched(artifact, context)); - } - catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - throw new ArtifactDatabaseException(CREATION_FAILED); - } - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.NOTHING, - callMeta, - persistentArtifact); - - try { - return artifact.describe(null, cc); - } - finally { - cc.postCall(); - } - } - - - public Artifact getRawArtifact(String identifier) - throws ArtifactDatabaseException - { - PersistentArtifact artifact = backend.getArtifact(identifier); - - if (artifact == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - return artifact.getArtifact(); - } - - - public Document describe( - String identifier, - Document data, - CallMeta callMeta - ) - throws ArtifactDatabaseException - { - // TODO: Handle background tasks - PersistentArtifact artifact = backend.getArtifact(identifier); - - if (artifact == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.TOUCH, - callMeta, - artifact); - - try { - Artifact art = artifact.getArtifact(); - Document res = art.describe(data, cc); - - if (postDescribeHooks != null) { - for (Hook hook: postDescribeHooks) { - hook.execute(art, cc, res); - } - } - - return res; - } - finally { - cc.postCall(); - } - } - - public Document advance( - String identifier, - Document target, - CallMeta callMeta - ) - throws ArtifactDatabaseException - { - // TODO: Handle background tasks - PersistentArtifact artifact = backend.getArtifact(identifier); - - if (artifact == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.STORE, - callMeta, - artifact); - - try { - Artifact art = artifact.getArtifact(); - Document res = art.advance(target, cc); - - if (postAdvanceHooks != null) { - for (Hook hook: postAdvanceHooks) { - hook.execute(art, cc, res); - } - } - - return res; - } - finally { - cc.postCall(); - } - } - - public Document feed(String identifier, Document data, CallMeta callMeta) - throws ArtifactDatabaseException - { - // TODO: Handle background tasks - PersistentArtifact artifact = backend.getArtifact(identifier); - - if (artifact == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.STORE, - callMeta, - artifact); - - try { - Artifact art = artifact.getArtifact(); - Document res = art.feed(data, cc); - - if (postFeedHooks != null) { - for (Hook hook: postFeedHooks) { - hook.execute(art, cc, res); - } - } - - return res; - } - finally { - cc.postCall(); - } - } - - public DeferredOutput out( - String identifier, - Document format, - CallMeta callMeta) - throws ArtifactDatabaseException - { - return out(identifier, null, format, callMeta); - } - - public DeferredOutput out( - String identifier, - String type, - Document format, - CallMeta callMeta - ) - throws ArtifactDatabaseException - { - // TODO: Handle background tasks - PersistentArtifact artifact = backend.getArtifact(identifier); - - if (artifact == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - return new DeferredOutputImpl(artifact, type, format, callMeta); - } - - public Document exportArtifact(String artifact, CallMeta callMeta) - throws ArtifactDatabaseException - { - final String [] factoryName = new String[1]; - - byte [] bytes = (byte [])backend.loadArtifact( - artifact, - new Backend.ArtifactLoader() { - public Object load( - ArtifactFactory factory, - Long ttl, - byte [] bytes, - int id - ) { - factoryName[0] = factory.getName(); - - ArtifactSerializer serializer = factory.getSerializer(); - - Artifact artifact = serializer.fromBytes(bytes); - artifact.cleanup(context); - - return serializer.toBytes(artifact); - } - }); - - if (bytes == null) { - throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); - } - - return createExportDocument( - factoryName[0], - bytes, - exportSecret); - } - - /** - * Creates an exteral XML representation of an artifact. - * @param factoryName The name of the factory which is responsible - * for the serialized artifact. - * @param artifact The byte data of the artifact itself. - * @param secret The signing secret. - * @return An XML document containing the external representation - * of the artifact. - */ - protected static Document createExportDocument( - String factoryName, - byte [] artifact, - byte [] secret - ) { - Document document = XMLUtils.newDocument(); - - MessageDigest md; - try { - md = MessageDigest.getInstance(DIGEST_ALGORITHM); - } - catch (NoSuchAlgorithmException nsae) { - logger.error(nsae.getLocalizedMessage(), nsae); - return document; - } - - md.update(artifact); - md.update(secret); - - String checksum = Hex.encodeHexString(md.digest()); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - document, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("action"); - document.appendChild(root); - - Element type = ec.create("type"); - ec.addAttr(type, "name", "export", true); - root.appendChild(type); - - Element data = ec.create("data"); - ec.addAttr(data, "checksum", checksum, true); - ec.addAttr(data, "factory", factoryName, true); - data.setTextContent(Base64.encodeBase64String(artifact)); - - root.appendChild(data); - - return document; - } - - public Document importArtifact(Document input, CallMeta callMeta) - throws ArtifactDatabaseException - { - String factoryName = XMLUtils.xpathString( - input, - XPATH_IMPORT_FACTORY, - ArtifactNamespaceContext.INSTANCE); - - ArtifactFactory factory; - - if (factoryName == null - || (factoryName = factoryName.trim()).length() == 0 - || (factory = getArtifactFactory(factoryName)) == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - String checksumString = XMLUtils.xpathString( - input, - XPATH_IMPORT_CHECKSUM, - ArtifactNamespaceContext.INSTANCE); - - byte [] checksum; - - if (checksumString == null - || (checksumString = checksumString.trim()).length() == 0 - || (checksum = StringUtils.decodeHex(checksumString)) == null - ) { - throw new ArtifactDatabaseException(INVALID_CHECKSUM); - } - - checksumString = null; - - String dataString = XMLUtils.xpathString( - input, - XPATH_IMPORT_DATA, - ArtifactNamespaceContext.INSTANCE); - - if (dataString == null - || (dataString = dataString.trim()).length() == 0) { - throw new ArtifactDatabaseException(NO_DATA); - } - - byte [] data = Base64.decodeBase64(dataString); - - dataString = null; - - MessageDigest md; - try { - md = MessageDigest.getInstance(DIGEST_ALGORITHM); - } - catch (NoSuchAlgorithmException nsae) { - logger.error(nsae.getLocalizedMessage(), nsae); - return XMLUtils.newDocument(); - } - - md.update(data); - md.update(exportSecret); - - byte [] digest = md.digest(); - - if (!Arrays.equals(checksum, digest)) { - throw new ArtifactDatabaseException(CHECKSUM_MISMATCH); - } - - ArtifactSerializer serializer = factory.getSerializer(); - - Artifact artifact = serializer.fromBytes(data); data = null; - - if (artifact == null) { - throw new ArtifactDatabaseException(INVALID_ARTIFACT); - } - - artifact.setIdentifier(backend.newIdentifier()); - PersistentArtifact persistentArtifact; - - try { - persistentArtifact = backend.storeOrReplace( - artifact, - factory, - factory.timeToLiveUntouched(artifact, context)); - } - catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - throw new ArtifactDatabaseException(CREATION_FAILED); - } - - ArtifactCallContext cc = new ArtifactCallContext( - ArtifactDatabaseImpl.this, - CallContext.NOTHING, - callMeta, - persistentArtifact); - - try { - return artifact.describe(input, cc); - } - finally { - cc.postCall(); - } - } - - public String [][] serviceNamesAndDescriptions() { - return serviceNamesAndDescription; - } - - public Service.Output process( - String serviceName, - Document input, - CallMeta callMeta - ) - throws ArtifactDatabaseException - { - Service service = (Service)name2service.get(serviceName); - - if (service == null) { - throw new ArtifactDatabaseException(NO_SUCH_SERVICE); - } - - return service.process(input, context, callMeta); - } - - // User API - - /** Returns user(s) elements. */ - public Document listUsers(CallMeta callMeta) - throws ArtifactDatabaseException - { - UserFactory factory = getUserFactory(); - - if (factory == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - User [] users = backend.getUsers(factory, context); - - if (users != null) { - logger.debug(users.length + " users found in the backend."); - } - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("users"); - result.appendChild(root); - - if(users != null) { - for (User user: users) { - Element ue = ec.create("user"); - ec.addAttr(ue, "uuid", user.identifier(), true); - ec.addAttr(ue, "name", user.getName(), true); - Element ua = ec.create("account"); - ec.addAttr(ua, "name", user.getAccount(), true); - ue.appendChild(ua); - - Document role = user.getRole(); - - if (role != null) { - ue.appendChild(result.importNode(role.getFirstChild(), true)); - } - - root.appendChild(ue); - } - } - - return result; - } - - /** Search for a user. */ - public Document findUser(Document data, CallMeta callMeta) - throws ArtifactDatabaseException - { - UserFactory factory = getUserFactory(); - - if (factory == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - String account = XMLUtils.xpathString( - data, XPATH_USERACCOUNT_FIND, ArtifactNamespaceContext.INSTANCE); - - if (account == null || account.length() == 0) { - logger.warn("Can't find user without account!"); - throw new ArtifactDatabaseException(NO_USERACCOUNT); - } - - User user = backend.findUser(account, factory, context); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element ue = ec.create("user"); - - if (user != null) { - logger.debug(user + " user found in the backend."); - - ec.addAttr(ue, "uuid", user.identifier(), true); - ec.addAttr(ue, "name", user.getName(), true); - Element ua = ec.create("account"); - ec.addAttr(ua, "name", user.getAccount(), true); - ue.appendChild(ua); - - Document role = user.getRole(); - - if (role != null) { - ue.appendChild(result.importNode(role.getFirstChild(), true)); - } - } - - result.appendChild(ue); - - return result; - } - - public Document createUser(Document data, CallMeta callMeta) - throws ArtifactDatabaseException - { - UserFactory factory = getUserFactory(); - - if (factory == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - String name = XMLUtils.xpathString( - data, XPATH_USERNAME, ArtifactNamespaceContext.INSTANCE); - - if (name == null || name.length() == 0) { - logger.warn("User without username not accepted!"); - throw new ArtifactDatabaseException(NO_USERNAME); - } - - String account = XMLUtils.xpathString( - data, XPATH_USERACCOUNT, ArtifactNamespaceContext.INSTANCE); - - if (account == null || account.length() == 0) { - logger.warn("User without account not accepted!"); - throw new ArtifactDatabaseException(NO_USERACCOUNT); - } - - Node tmp = (Node) XMLUtils.xpath( - data, - XPATH_USERROLE, - XPathConstants.NODE, - ArtifactNamespaceContext.INSTANCE); - - Document role = XMLUtils.newDocument(); - - if (tmp != null) { - Node clone = role.importNode(tmp, true); - role.appendChild(clone); - } - - User newUser = null; - - try { - newUser = backend.createUser(name, account, role, userFactory, context); - } - catch (Exception e) { - logger.error(e.getMessage(), e); - throw new ArtifactDatabaseException(USER_CREATION_FAILED); - } - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - - if (newUser != null) { - root.setTextContent(OPERATION_SUCCESSFUL); - } - else { - root.setTextContent(OPERATION_FAILURE); - } - - result.appendChild(root); - - return result; - } - - public Document deleteUser(String userId, CallMeta callMeta) - throws ArtifactDatabaseException - { - logger.debug("Delete user: " + userId); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - boolean success = backend.deleteUser(userId); - - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - - // Collection API - - public Document getCollectionsMasterArtifact( - String collectionId, - CallMeta meta) - throws ArtifactDatabaseException - { - Document result = XMLUtils.newDocument(); - String masterUUID = backend.getMasterArtifact(collectionId); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - - if (acf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - UserFactory uf = getUserFactory(); - if (uf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - ArtifactCollection c = backend.getCollection( - collectionId, acf, uf, context); - - if (c == null) { - logger.warn("No collection found with identifier: " + collectionId); - throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); - } - - Element root = ec.create("artifact-collection"); - ec.addAttr(root, "name", c.getName(), true); - ec.addAttr(root, "uuid", c.identifier(), true); - ec.addAttr(root, "ttl", String.valueOf(c.getTTL()), true); - - Date creationTime = c.getCreationTime(); - String creation = creationTime != null - ? Long.toString(creationTime.getTime()) - : ""; - - ec.addAttr(root, "creation", creation, true); - result.appendChild(root); - - if (masterUUID == null || masterUUID.length() == 0) { - logger.debug("No master for the collection existing."); - return result; - } - - Element master = ec.create("artifact"); - ec.addAttr(master, "uuid", masterUUID, true); - - root.appendChild(master); - - return result; - } - - public Document listCollections(String userId, CallMeta callMeta) - throws ArtifactDatabaseException - { - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - UserFactory uf = getUserFactory(); - - if (acf == null || uf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - logger.debug("Fetch the list of collection for user: " + userId); - - ArtifactCollection [] ac = backend.listCollections( - userId, - null, // XXX: fetch from REST - acf, uf, - context); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("artifact-collections"); - result.appendChild(root); - - if (ac == null || ac.length == 0) { - logger.debug("No collections for the user existing."); - - return result; - } - - logger.debug("Found " + ac.length + " collections of the user."); - - for (ArtifactCollection c: ac) { - Element collection = ec.create("artifact-collection"); - ec.addAttr(collection, "name", c.getName(), true); - ec.addAttr(collection, "uuid", c.identifier(), true); - ec.addAttr(collection, "ttl", String.valueOf(c.getTTL()), true); - - Date creationTime = c.getCreationTime(); - String creation = creationTime != null - ? Long.toString(creationTime.getTime()) - : ""; - - ec.addAttr(collection, "creation", creation, true); - - root.appendChild(collection); - } - - return result; - } - - public Document createCollection(String ownerId, Document data, - CallMeta callMeta) - throws ArtifactDatabaseException - { - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - - if (acf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - String name = XMLUtils.xpathString( - data, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); - - logger.debug("Create new collection with name: " + name); - - Document attr = null; - - Node attrNode = (Node) XMLUtils.xpath( - data, - XPATH_COLLECTION_ATTRIBUTE, - XPathConstants.NODE, - ArtifactNamespaceContext.INSTANCE); - - if (attrNode != null) { - attr = XMLUtils.newDocument(); - attr.appendChild(attr.importNode(attrNode, true)); - } - - ArtifactCollection ac = backend.createCollection( - ownerId, name, acf, attr, context); - - if (ac == null) { - throw new ArtifactDatabaseException(COLLECTION_CREATION_FAILED); - } - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - Element acElement = ec.create("artifact-collection"); - ec.addAttr(acElement, "uuid", ac.identifier(), true); - ec.addAttr(acElement, "ttl", String.valueOf(ac.getTTL()), true); - - root.appendChild(acElement); - - return result; - } - - public Document deleteCollection(String collectionId, CallMeta callMeta) - throws ArtifactDatabaseException - { - logger.debug("Delete collection: " + collectionId); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - boolean success = backend.deleteCollection(collectionId); - - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - public Document describeCollection(String collectionId, CallMeta callMeta) - throws ArtifactDatabaseException - { - logger.debug("Describe collection: " + collectionId); - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - - if (acf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - UserFactory uf = getUserFactory(); - if (uf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - ArtifactCollection c = backend.getCollection( - collectionId, acf, uf, context); - - if (c == null) { - logger.warn("No collection found with identifier: " + collectionId); - throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); - } - - CollectionCallContext cc = new CollectionCallContext( - ArtifactDatabaseImpl.this, - CallContext.NOTHING, - callMeta, - c); - - try { - return c.describe(cc); - } - finally { - cc.postCall(); - } - } - - - public Document getCollectionAttribute(String collectionId, CallMeta meta) - throws ArtifactDatabaseException - { - logger.debug("Fetch collection attribute for: " + collectionId); - - return backend.getCollectionAttribute(collectionId); - } - - - public Document setCollectionAttribute( - String collectionId, - CallMeta meta, - Document attribute) - throws ArtifactDatabaseException - { - logger.debug("Set new attribute for the collection: " + collectionId); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - boolean success = backend.setCollectionAttribute( - collectionId, attribute); - - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - public Document getCollectionItemAttribute(String collectionId, String artifactId, - CallMeta callMeta) throws ArtifactDatabaseException - { - logger.debug("Fetch the attribute for the artifact: " + artifactId); - - return backend.getCollectionItemAttribute(collectionId, artifactId); - } - - public Document setCollectionItemAttribute(String collectionId, String artifactId, - Document source, CallMeta callMeta) - throws ArtifactDatabaseException - { - logger.debug("Set the attribute for the artifact: " + artifactId); - - Document attribute = null; - - Node attr = (Node) XMLUtils.xpath( - source, - XPATH_COLLECTION_ITEM_ATTRIBUTE, - XPathConstants.NODE, - ArtifactNamespaceContext.INSTANCE); - - if (attr != null) { - attribute = XMLUtils.newDocument(); - attribute.appendChild(attribute.importNode(attr, true)); - } - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - boolean success = backend.setCollectionItemAttribute( - collectionId, artifactId, attribute); - - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - public Document addCollectionArtifact( - String collectionId, - String artifactId, - Document input, - CallMeta callMeta) - throws ArtifactDatabaseException - { - logger.debug( - "Add artifact '" + artifactId + "' collection '" +collectionId+"'"); - - Document attr = XMLUtils.newDocument(); - - Node attrNode = (Node) XMLUtils.xpath( - input, - XPATH_COLLECTION_ITEM_ATTRIBUTE, - XPathConstants.NODE, - ArtifactNamespaceContext.INSTANCE); - - if (attrNode != null) { - attr.appendChild(attr.importNode(attrNode, true)); - } - - boolean success = backend.addCollectionArtifact( - collectionId, - artifactId, - attr); - - if (!success) { - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - root.setTextContent(OPERATION_FAILURE); - - return result; - } - - return describeCollection(collectionId, callMeta); - } - - public Document removeCollectionArtifact(String collectionId, String artifactId, - CallMeta callMeta) throws ArtifactDatabaseException - { - logger.debug( - "Remove artifact '" + artifactId + "' from collection '" + - collectionId + "'"); - - Document attr = XMLUtils.newDocument(); - - boolean success = backend.removeCollectionArtifact( - collectionId, - artifactId); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - public Document listCollectionArtifacts(String collectionId, - CallMeta callMeta) throws ArtifactDatabaseException - { - CollectionItem[] items = backend.listCollectionArtifacts(collectionId); - - Document result = XMLUtils.newDocument(); - - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - Element ac = ec.create("artifact-collection"); - ec.addAttr(ac, "uuid", collectionId, true); - - for (CollectionItem item: items) { - Element i = ec.create("collection-item"); - Element attr = ec.create("attribute"); - ec.addAttr(i, "uuid", item.getArtifactIdentifier(), true); - - Document attribute = item.getAttribute(); - if (attribute != null) { - Node firstChild = attribute.getFirstChild(); - attr.appendChild(result.importNode(firstChild, true)); - } - else { - logger.debug("No attributes for the collection item!"); - } - - i.appendChild(attr); - ac.appendChild(i); - } - - root.appendChild(ac); - result.appendChild(root); - - return result; - } - - public Document setCollectionTTL(String uuid, Document doc, CallMeta meta) - throws ArtifactDatabaseException - { - Document result = XMLUtils.newDocument(); - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - String tmp = XMLUtils.xpathString( - doc, XPATH_COLLECTION_TTL, ArtifactNamespaceContext.INSTANCE); - - logger.info("Set TTL of artifact collection '" + uuid + "' to: " + tmp); - - if (tmp == null || tmp.length() == 0) { - logger.warn("No ttl for this collection specified."); - root.setTextContent(OPERATION_FAILURE); - - return result; - } - - Long ttl = null; - if ((tmp = tmp.toUpperCase()).equals("INF")) { - ttl = null; - } - else if (tmp.equals("DEFAULT")) { - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - ttl = acf.timeToLiveUntouched(null, context); - } - else { - try { - ttl = Long.valueOf(tmp); - - if (ttl < 0) { - throw new NumberFormatException("Negative value."); - } - } - catch (NumberFormatException nfe) { - logger.error("Could not determine TTL", nfe); - root.setTextContent(OPERATION_FAILURE); - return result; - } - } - - boolean success = backend.setCollectionTTL(uuid, ttl); - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - - public Document setCollectionName(String uuid, Document doc, CallMeta meta) - throws ArtifactDatabaseException - { - Document result = XMLUtils.newDocument(); - XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( - result, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element root = ec.create("result"); - result.appendChild(root); - - String name = XMLUtils.xpathString( - doc, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); - - logger.info("Set name of collection '" + uuid + "' to: " + name); - - if (name == null || name.length() == 0) { - logger.warn("The new name is emtpy. No new name set!"); - root.setTextContent(OPERATION_FAILURE); - return result; - } - - boolean success = backend.setCollectionName(uuid, name); - root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); - - return result; - } - - - public DeferredOutput outCollection( - String collectionId, - Document format, - CallMeta callMeta) - throws ArtifactDatabaseException - { - return outCollection(collectionId, null, format, callMeta); - } - - public DeferredOutput outCollection( - String collectionId, - String type, - Document format, - CallMeta callMeta) - throws ArtifactDatabaseException - { - ArtifactCollectionFactory acf = getArtifactCollectionFactory(); - - if (acf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - UserFactory uf = getUserFactory(); - if (uf == null) { - throw new ArtifactDatabaseException(NO_SUCH_FACTORY); - } - - ArtifactCollection c = backend.getCollection( - collectionId, acf, uf, context); - - if (c == null) { - logger.warn("No collection found with identifier: " + collectionId); - throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); - } - - return new DeferredCollectionOutputImpl(c, type, format, callMeta); - } - - protected void initCallContext(CallContext cc) { - logger.debug("initCallContext"); - if (callContextListener != null) { - callContextListener.init(cc); - } - } - - protected void closeCallContext(CallContext cc) { - logger.debug("closeCallContext"); - if (callContextListener != null) { - callContextListener.close(cc); - } - } - - @Override - public void loadAllArtifacts(ArtifactLoadedCallback callback) - throws ArtifactDatabaseException - { - logger.debug("loadAllArtifacts"); - boolean success = backend.loadAllArtifacts(callback); - if (!success) { - throw new ArtifactDatabaseException(INTERNAL_ERROR); - } - } - - public void start() { - if (lifetimeListeners == null || lifetimeListeners.isEmpty()) { - return; - } - - for (LifetimeListener ltl: lifetimeListeners) { - ltl.systemUp(context); - } - - logger.debug("all lifetime listeners started"); - - Runtime.getRuntime().addShutdownHook(new Thread() { - @Override - public void run() { - for (LifetimeListener ltl: lifetimeListeners) { - ltl.systemDown(context); - } - } - }); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1914 +0,0 @@ -/* - * Copyright (c) 2010, 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.ArtifactCollectionFactory; -import de.intevation.artifacts.ArtifactDatabase.ArtifactLoadedCallback; -import de.intevation.artifacts.ArtifactFactory; -import de.intevation.artifacts.ArtifactSerializer; -import de.intevation.artifacts.CollectionItem; -import de.intevation.artifacts.User; -import de.intevation.artifacts.UserFactory; - -import de.intevation.artifacts.common.utils.StringUtils; -import de.intevation.artifacts.common.utils.XMLUtils; -import de.intevation.artifacts.common.utils.LRUCache; - -import de.intevation.artifactdatabase.db.SQLExecutor; -import de.intevation.artifactdatabase.db.SQL; - -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; - -import java.util.List; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; - -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -/** - * The backend implements the low level layer used to store artifacts - * in a SQL database. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class Backend -implements DatabaseCleaner.ArtifactReviver -{ - private static Logger logger = Logger.getLogger(Backend.class); - - /** - * The SQL statement to create new artifact id inside the database. - */ - public String SQL_NEXT_ID; - - /** - * The SQL statement to insert an artifact into the database. - */ - public String SQL_INSERT; - - /** - * The SQL statement to update some columns of an existing - * artifact in the database. - */ - public String SQL_UPDATE; - - /** - * The SQL statement to touch the access time of an - * artifact inside the database. - */ - public String SQL_TOUCH; - - /** - * The SQL statement to load an artifact by a given - * identifier from the database. - */ - public String SQL_LOAD_BY_GID; - - /** - * The SQL statement to get the database id of an artifact - * identified by the identifier. - */ - public String SQL_GET_ID; - - /** - * The SQL statement to replace the content of an - * existing artifact inside the database. - */ - public String SQL_REPLACE; - - // USER SQL - - public String SQL_USERS_NEXT_ID; - public String SQL_USERS_INSERT; - public String SQL_USERS_SELECT_ID_BY_GID; - public String SQL_USERS_SELECT_GID; - public String SQL_USERS_SELECT_ACCOUNT; - public String SQL_USERS_DELETE_ID; - public String SQL_USERS_DELETE_COLLECTIONS; - public String SQL_USERS_SELECT_ALL; - public String SQL_USERS_COLLECTIONS; - public String SQL_USERS_COLLECTION_IDS; - public String SQL_USERS_DELETE_ALL_COLLECTIONS; - public String SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY; - public String SQL_OUTDATE_ARTIFACTS_COLLECTION; - public String SQL_UPDATE_COLLECTION_TTL; - public String SQL_UPDATE_COLLECTION_NAME; - public String SQL_OUTDATE_ARTIFACTS_USER; - public String SQL_DELETE_USER_COLLECTION_ITEMS; - public String SQL_COLLECTIONS_NEXT_ID; - public String SQL_COLLECTIONS_INSERT; - public String SQL_COLLECTIONS_SELECT_USER; - public String SQL_COLLECTIONS_SELECT_ALL; - public String SQL_COLLECTIONS_SELECT_GID; - public String SQL_COLLECTIONS_CREATION_TIME; - public String SQL_COLLECTIONS_ID_BY_GID; - public String SQL_COLLECTIONS_OLDEST_ARTIFACT; - public String SQL_DELETE_COLLECTION_ITEMS; - public String SQL_DELETE_COLLECTION; - public String SQL_COLLECTION_CHECK_ARTIFACT; - public String SQL_COLLECTION_ITEMS_ID_NEXTVAL; - public String SQL_COLLECTION_ITEMS_INSERT; - public String SQL_COLLECTION_GET_ATTRIBUTE; - public String SQL_COLLECTION_SET_ATTRIBUTE; - public String SQL_COLLECTION_ITEM_GET_ATTRIBUTE; - public String SQL_COLLECTION_ITEM_SET_ATTRIBUTE; - public String SQL_COLLECTIONS_TOUCH_BY_GID; - public String SQL_COLLECTION_ITEM_ID_CID_AID; - public String SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT; - public String SQL_COLLECTION_ITEM_DELETE; - public String SQL_COLLECTIONS_TOUCH_BY_ID; - public String SQL_COLLECTION_ITEMS_LIST_GID; - public String SQL_ALL_ARTIFACTS; - - /** The singleton.*/ - protected static Backend instance; - - protected SQLExecutor sqlExecutor; - - protected List<BackendListener> listeners; - - protected DBConfig config; - - /** - * The database cleaner. Reference is stored here because - * the cleaner is woken up if the backend finds an outdated - * artifact. This artifact should be removed as soon as - * possible. - */ - protected DatabaseCleaner cleaner; - - /** - * To revive an artifact from the bytes coming from the database - * we need the artifact factory which references the artifact - * serializer which is able to do the reviving job. - */ - protected FactoryLookup factoryLookup; - - /** - * Little helper interface to decouple the ArtifactDatabase - * from the Backend. A ArtifactDatabase should depend on a - * Backend but a Backend not from an ArtifactDatabase. - */ - public interface FactoryLookup { - - /** - * Returns an ArtifactFactory which is bound to a given name. - * @param factoryName The name of the artifact factory. - * @return The ArtifactFactory bound to the factory name or - * null if not matching factory is found. - */ - ArtifactFactory getArtifactFactory(String factoryName); - - } // interface FactoryLookup - - /** - * Inner class that brigdes between the persisten form of the - * artifact and the living one inside the artifact database. - * After the describe(), feed(), advance() and out() operations - * of the artifact it must be possible to write to modified artifact - * back into the database. - */ - public final class PersistentArtifact - { - private int id; - private Artifact artifact; - private ArtifactSerializer serializer; - private Long ttl; - - /** - * Cronstructor to create a persistent artifact. - * @param artifact The living artifact. - * @param serializer The serializer to store the artifact - * after the operations. - * @param ttl The time to life of the artifact. - * @param id The database id of the artifact. - */ - public PersistentArtifact( - Artifact artifact, - ArtifactSerializer serializer, - Long ttl, - int id - ) { - this.id = id; - this.artifact = artifact; - this.serializer = serializer; - this.ttl = ttl; - } - - public int getId() { - return id; - } - - /** - * Returns the wrapped living artifact. - * @return the living artifact. - */ - public Artifact getArtifact() { - return artifact; - } - - /** - * Returns the serialized which is able to write a - * modified artifact back into the database. - * @return The serializer. - */ - public ArtifactSerializer getSerializer() { - return serializer; - } - - /** - * The time to life of the artifact. - * @return The time to live. - */ - public Long getTTL() { - return ttl; - } - - /** - * Stores the living artifact back into the database. - */ - public void store() { - if (logger.isDebugEnabled()) { - logger.debug("storing artifact id = " + getId()); - } - Backend.this.store(this); - } - - /** - * Only touches the access time of the artifact. - */ - public void touch() { - if (logger.isDebugEnabled()) { - logger.debug("touching artifact id = " + getId()); - } - Backend.this.touch(this); - } - } // class ArtifactWithId - - /** - * Default constructor - */ - public Backend() { - listeners = new CopyOnWriteArrayList<BackendListener>(); - } - - public Backend(DBConfig config) { - this(); - this.config = config; - sqlExecutor = new SQLExecutor(config.getDBConnection()); - setupSQL(config.getSQL()); - } - - /** - * Constructor to create a backend with a link to the database cleaner. - * @param cleaner The clean which periodically removes outdated - * artifacts from the database. - */ - public Backend(DBConfig config, DatabaseCleaner cleaner) { - this(config); - this.cleaner = cleaner; - } - - public DBConfig getConfig() { - return config; - } - - /** - * Returns the singleton of this Backend. - * - * @return the backend. - */ - public static synchronized Backend getInstance() { - if (instance == null) { - instance = new Backend(DBConfig.getInstance()); - } - - return instance; - } - - protected void setupSQL(SQL sql) { - SQL_NEXT_ID = sql.get("artifacts.id.nextval"); - SQL_INSERT = sql.get("artifacts.insert"); - SQL_UPDATE = sql.get("artifacts.update"); - SQL_TOUCH = sql.get("artifacts.touch"); - SQL_LOAD_BY_GID = sql.get("artifacts.select.gid"); - SQL_GET_ID = sql.get("artifacts.get.id"); - SQL_REPLACE = sql.get("artifacts.replace"); - SQL_USERS_NEXT_ID = sql.get("users.id.nextval"); - SQL_USERS_INSERT = sql.get("users.insert"); - SQL_USERS_SELECT_ID_BY_GID = sql.get("users.select.id.by.gid"); - SQL_USERS_SELECT_GID = sql.get("users.select.gid"); - SQL_USERS_SELECT_ACCOUNT = sql.get("users.select.account"); - SQL_USERS_DELETE_ID = sql.get("users.delete.id"); - SQL_USERS_DELETE_COLLECTIONS = sql.get("users.delete.collections"); - SQL_USERS_SELECT_ALL = sql.get("users.select.all"); - SQL_USERS_COLLECTIONS = sql.get("users.collections"); - SQL_USERS_COLLECTION_IDS = sql.get("users.collection.ids"); - SQL_USERS_DELETE_ALL_COLLECTIONS = - sql.get("users.delete.collections"); - SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY = - sql.get("artifacts.in.one.collection.only"); - SQL_OUTDATE_ARTIFACTS_COLLECTION = - sql.get("outdate.artifacts.collection"); - SQL_UPDATE_COLLECTION_TTL = sql.get("collections.update.ttl"); - SQL_UPDATE_COLLECTION_NAME = sql.get("collections.update.name"); - SQL_OUTDATE_ARTIFACTS_USER = sql.get("outdate.artifacts.user"); - SQL_DELETE_USER_COLLECTION_ITEMS = - sql.get("delete.user.collection.items"); - SQL_COLLECTIONS_NEXT_ID = sql.get("collections.id.nextval"); - SQL_COLLECTIONS_INSERT = sql.get("collections.insert"); - SQL_COLLECTIONS_SELECT_USER = sql.get("collections.select.user"); - SQL_COLLECTIONS_SELECT_ALL = sql.get("collections.select.all"); - SQL_COLLECTIONS_SELECT_GID = sql.get("collections.select.by.gid"); - SQL_COLLECTIONS_CREATION_TIME = sql.get("collection.creation.time"); - SQL_COLLECTIONS_OLDEST_ARTIFACT = sql.get("collections.artifacts.oldest"); - SQL_COLLECTIONS_ID_BY_GID = sql.get("collections.id.by.gid"); - SQL_DELETE_COLLECTION_ITEMS = sql.get("delete.collection.items"); - SQL_DELETE_COLLECTION = sql.get("delete.collection"); - SQL_COLLECTION_CHECK_ARTIFACT = sql.get("collection.check.artifact"); - SQL_COLLECTION_ITEMS_ID_NEXTVAL = - sql.get("collection.items.id.nextval"); - SQL_COLLECTION_ITEMS_INSERT = sql.get("collection.items.insert"); - SQL_COLLECTION_GET_ATTRIBUTE = sql.get("collection.get.attribute"); - SQL_COLLECTION_SET_ATTRIBUTE = sql.get("collection.set.attribute"); - SQL_COLLECTION_ITEM_GET_ATTRIBUTE = - sql.get("collection.item.get.attribute"); - SQL_COLLECTION_ITEM_SET_ATTRIBUTE = - sql.get("collection.item.set.attribute"); - SQL_COLLECTIONS_TOUCH_BY_GID = sql.get("collections.touch.by.gid"); - SQL_COLLECTION_ITEM_ID_CID_AID = sql.get("collection.item.id.cid.aid"); - SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT = - sql.get("collection.item.outdate.artifact"); - SQL_COLLECTION_ITEM_DELETE = sql.get("collection.item.delete"); - SQL_COLLECTIONS_TOUCH_BY_ID = sql.get("collections.touch.by.id"); - SQL_COLLECTION_ITEMS_LIST_GID = sql.get("collection.items.list.gid"); - SQL_ALL_ARTIFACTS = sql.get("all.artifacts"); - } - - public void addListener(BackendListener listener) { - listeners.add(listener); - logger.debug("# listeners: " + listeners.size()); - } - - public void addAllListeners(List<BackendListener> others) { - listeners.addAll(others); - logger.debug("# listeners: " + listeners.size()); - } - - /** - * Sets the factory lookup mechanism to decouple ArtifactDatabase - * and Backend. - * @param factoryLookup - */ - public void setFactoryLookup(FactoryLookup factoryLookup) { - this.factoryLookup = factoryLookup; - } - - /** - * Sets the database cleaner explicitly. - * @param cleaner The database cleaner - */ - public void setCleaner(DatabaseCleaner cleaner) { - this.cleaner = cleaner; - } - - /** - * Returns a new unique identifier to external identify - * the artifact across the system. This implementation - * uses random UUIDs v4 to achieve this target. - * @return the new identifier - */ - public String newIdentifier() { - // TODO: check database for collisions. - return StringUtils.newUUID(); - } - - public boolean isValidIdentifier(String identifier) { - return StringUtils.checkUUID(identifier); - } - - /** - * Stores a new artifact into the database. - * @param artifact The artifact to be stored - * @param factory The factory which build the artifact - * @param ttl The initial time to life of the artifact. - * @return A persistent wrapper around the living - * artifact to be able to write modification later. - * @throws Exception Thrown if something went wrong with the - * storage process. - */ - public PersistentArtifact storeInitially( - Artifact artifact, - ArtifactFactory factory, - Long ttl - ) - throws Exception - { - return new PersistentArtifact( - artifact, - factory.getSerializer(), - ttl, - insertDatabase(artifact, factory, ttl)); - } - - /** - * Stores an artifact into database if it does not exist there. - * If it exists there it is only updated. - * @param artifact The artifact to store/update. - * @param factory The factory which created the artifact. - * @param ttl The initial time to live of the artifact. - * @return A persistent version of the artifact to be able - * to store a modification later. - * @throws Exception Thrown if something went wrong during - * storing/updating. - */ - public PersistentArtifact storeOrReplace( - Artifact artifact, - ArtifactFactory factory, - Long ttl - ) - throws Exception - { - return new PersistentArtifact( - artifact, - factory.getSerializer(), - ttl, - storeOrReplaceDatabase(artifact, factory, ttl)); - } - - /** - * Implementors of this interface are able to process the raw - * artifact data from the database for loading. - */ - public interface ArtifactLoader { - - /** - * Creates a custom object from the raw artifact database data. - * @param factory The factory that created this artifact. - * @param ttl The current time to life of the artifact. - * @param bytes The raw artifact bytes from the database. - * @param id The database id of the artifact. - * @return The custom object created by the implementation. - */ - Object load(ArtifactFactory factory, Long ttl, byte [] bytes, int id); - - } // interface ArtifactLoader - - /** - * Fetches an artifact from the database identified by the - * given identifier. - * @param identifer The identifier of the artifact. - * @return A persistent wrapper around the found artifact - * to be able to write back a modifaction later or null - * if no artifact is found for this identifier. - */ - public PersistentArtifact getArtifact(String identifer) { - - return (PersistentArtifact)loadArtifact( - identifer, - new ArtifactLoader() { - - public Object load( - ArtifactFactory factory, - Long ttl, - byte [] bytes, - int id - ) { - ArtifactSerializer serializer = factory.getSerializer(); - - Artifact artifact = serializer.fromBytes(bytes); - - return artifact == null - ? null - : new PersistentArtifact(artifact, serializer, ttl, id); - } - }); - } - - /** - * More general loading mechanism for artifacts. The concrete - * load processing is delegated to the given loader. - * @param identifer The identifier of the artifact. - * @param loader The loader which processes the raw database data. - * @return The object created by the loader. - */ - public Object loadArtifact( - final String identifer, - final ArtifactLoader loader - ) { - if (!isValidIdentifier(identifer)) { - return null; - } - - if (factoryLookup == null) { - logger.error("factory lookup == null"); - return false; - } - - final Object [] loaded = new Object[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_LOAD_BY_GID); - stmnt.setString(1, identifer); - - result = stmnt.executeQuery(); - - if (!result.next()) { - return false; - } - - int id = result.getInt(1); - long ttlX = result.getLong(2); - Long ttl = result.wasNull() ? null : ttlX; - - String factoryName = result.getString(3); - - ArtifactFactory factory = factoryLookup - .getArtifactFactory(factoryName); - - if (factory == null) { - logger.error("factory '" + factoryName + "' not found"); - return false; - } - - byte [] bytes = result.getBytes(4); - - loaded[0] = loader.load(factory, ttl, bytes, id); - return true; - } - }; - - return exec.runRead() ? loaded[0] : null; - } - - /** - * Called if the load mechanism found an outdated artifact. - * It wakes up the database cleaner. - * @param id The id of the outdated artifact. - */ - protected void artifactOutdated(int id) { - if (logger.isDebugEnabled()) { - logger.info("artifactOutdated: id = " + id); - } - if (cleaner != null) { - cleaner.wakeup(); - } - } - - public Artifact reviveArtifact(String factoryName, byte [] bytes) { - if (factoryLookup == null) { - logger.error("reviveArtifact: factory lookup == null"); - return null; - } - ArtifactFactory factory = factoryLookup - .getArtifactFactory(factoryName); - - if (factory == null) { - logger.error( - "reviveArtifact: no factory '" + factoryName + "' found"); - return null; - } - - ArtifactSerializer serializer = factory.getSerializer(); - - return serializer.fromBytes(bytes); - } - - /** - * Internal method to store/replace an artifact inside the database. - * If an artifact with the given identifier does not exists it is - * created else only the content data is updated. - * @param artifact The artifact to be store/update inside the database. - * @param factory The factory that created the artifact. - * @param ttl The initial time to life of the artifact. - * @return The database id of the stored/updated artifact. - */ - protected int storeOrReplaceDatabase( - final Artifact artifact, - final ArtifactFactory factory, - final Long ttl - ) { - final String uuid = artifact.identifier(); - - if (!isValidIdentifier(uuid)) { - throw new RuntimeException("No valid UUID"); - } - - final int [] id = new int[1]; - final boolean [] stored = new boolean[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - prepareStatement(SQL_GET_ID); - stmnt.setString(1, uuid); - result = stmnt.executeQuery(); - - Integer ID = result.next() - ? Integer.valueOf(result.getInt(1)) - : null; - - reset(); - - if (stored[0] = ID != null) { // already in database - prepareStatement(SQL_REPLACE); - - if (ttl == null) { - stmnt.setNull(1, Types.BIGINT); - } - else { - stmnt.setLong(1, ttl.longValue()); - } - - stmnt.setString(2, factory.getName()); - stmnt.setBytes( - 3, - factory.getSerializer().toBytes(artifact)); - id[0] = ID.intValue(); - stmnt.setInt(4, id[0]); - } - else { // new artifact - prepareStatement(SQL_NEXT_ID); - result = stmnt.executeQuery(); - - if (!result.next()) { - logger.error("No id generated"); - return false; - } - - reset(); - - prepareStatement(SQL_INSERT); - - id[0] = result.getInt(1); - stmnt.setInt(1, id[0]); - stmnt.setString(2, uuid); - if (ttl == null) { - stmnt.setNull(3, Types.BIGINT); - } - else { - stmnt.setLong(3, ttl.longValue()); - } - - stmnt.setString(4, factory.getName()); - - stmnt.setBytes( - 5, - factory.getSerializer().toBytes(artifact)); - } - stmnt.execute(); - conn.commit(); - return true; - } - }; - - if (!exec.runWrite()) { - throw new RuntimeException("failed insert artifact into database"); - } - - if (stored[0]) { - fireStoredArtifact(artifact); - } - else { - fireCreatedArtifact(artifact); - } - - return id[0]; - } - - /** - * Internal method to store an artifact inside the database. - * @param artifact The artifact to be stored. - * @param factory The factory which created the artifact. - * @param ttl The initial time to live of the artifact. - * @return The database id of the stored artifact. - */ - protected int insertDatabase( - final Artifact artifact, - final ArtifactFactory factory, - final Long ttl - ) { - final int [] id = new int[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_NEXT_ID); - result = stmnt.executeQuery(); - - if (!result.next()) { - logger.error("No id generated"); - return false; - } - - id[0] = result.getInt(1); - - reset(); - prepareStatement(SQL_INSERT); - - String uuid = artifact.identifier(); - stmnt.setInt(1, id[0]); - stmnt.setString(2, uuid); - if (ttl == null) { - stmnt.setNull(3, Types.BIGINT); - } - else { - stmnt.setLong(3, ttl.longValue()); - } - - stmnt.setString(4, factory.getName()); - - stmnt.setBytes( - 5, - factory.getSerializer().toBytes(artifact)); - - stmnt.execute(); - - conn.commit(); - return true; - } - }; - - if (!exec.runWrite()) { - throw new RuntimeException("failed insert artifact into database"); - } - - fireCreatedArtifact(artifact); - - return id[0]; - } - - protected void fireCreatedArtifact(Artifact artifact) { - for (BackendListener listener: listeners) { - listener.createdArtifact(artifact, this); - } - } - - /** - * Touches the access timestamp of a given artifact to prevent - * that it will be removed from the database by the database cleaner. - * @param artifact The persistent wrapper around the living artifact. - */ - public void touch(final PersistentArtifact artifact) { - sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_TOUCH); - stmnt.setInt(1, artifact.getId()); - stmnt.execute(); - conn.commit(); - return true; - } - }.runWrite(); - } - - /** - * Writes modification of an artifact back to the database. - * @param artifact The persistent wrapper around a living - * artifact. - */ - public void store(final PersistentArtifact artifact) { - boolean success = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_UPDATE); - stmnt.setInt(2, artifact.getId()); - - byte [] bytes = artifact - .getSerializer() - .toBytes(artifact.getArtifact()); - - stmnt.setBytes(1, bytes); - stmnt.execute(); - conn.commit(); - return true; - } - }.runWrite(); - - if (success) { - fireStoredArtifact(artifact.getArtifact()); - } - } - - protected void fireStoredArtifact(Artifact artifact) { - for (BackendListener listener: listeners) { - listener.storedArtifact(artifact, this); - } - } - - - public User createUser( - final String name, - final String account, - final Document role, - final UserFactory factory, - final Object context - ) { - final User [] user = new User[1]; - - final byte [] roleData = XMLUtils.toByteArray(role, true); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - prepareStatement(SQL_USERS_NEXT_ID); - result = stmnt.executeQuery(); - - if (!result.next()) { - return false; - } - - int id = result.getInt(1); - - reset(); - - String identifier = newIdentifier(); - - prepareStatement(SQL_USERS_INSERT); - - stmnt.setInt(1, id); - stmnt.setString(2, identifier); - stmnt.setString(3, name); - stmnt.setString(4, account); - - if (roleData == null) { - stmnt.setNull(5, Types.BIGINT); - } - else { - stmnt.setBytes(5, roleData); - } - - stmnt.execute(); - conn.commit(); - - user[0] = factory.createUser( - identifier, name, account, role, context); - return true; - } - }; - - boolean success = exec.runWrite(); - - if (success) { - fireCreatedUser(user[0]); - return user[0]; - } - - return null; - } - - protected void fireCreatedUser(User user) { - for (BackendListener listener: listeners) { - listener.createdUser(user, this); - } - } - - public boolean deleteUser(final String identifier) { - - if (!isValidIdentifier(identifier)) { - return false; - } - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_USERS_SELECT_ID_BY_GID); - - stmnt.setString(1, identifier); - result = stmnt.executeQuery(); - - if (!result.next()) { // No such user - return false; - } - - int id = result.getInt(1); - - reset(); - - // outdate the artifacts exclusively used by the user - - prepareStatement(SQL_OUTDATE_ARTIFACTS_USER); - stmnt.setInt(1, id); - stmnt.setInt(2, id); - stmnt.execute(); - - reset(); - - // delete the collection items of the user - - prepareStatement(SQL_DELETE_USER_COLLECTION_ITEMS); - stmnt.setInt(1, id); - stmnt.execute(); - - reset(); - - // delete the collections of the user - - prepareStatement(SQL_USERS_DELETE_COLLECTIONS); - stmnt.setInt(1, id); - stmnt.execute(); - - reset(); - - // delete the user - - prepareStatement(SQL_USERS_DELETE_ID); - stmnt.setInt(1, id); - stmnt.execute(); - - conn.commit(); - return true; - } - }; - - boolean success = exec.runWrite(); - - if (success) { - fireDeletedUser(identifier); - } - - return success; - } - - protected void fireDeletedUser(String identifier) { - for (BackendListener listener: listeners) { - listener.deletedUser(identifier, this); - } - } - - public User getUser( - final String identifier, - final UserFactory factory, - final Object context - ) { - if (!isValidIdentifier(identifier)) { - logger.debug("Invalid UUID: '" + identifier + "'"); - return null; - } - - final User [] user = new User[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_USERS_SELECT_GID); - stmnt.setString(1, identifier); - result = stmnt.executeQuery(); - if (!result.next()) { // no such user - return false; - } - // omit id - String name = result.getString(2); - String account = result.getString(3); - byte [] roleData = result.getBytes(4); - - Document role = null; - if (roleData != null) { - role = XMLUtils.fromByteArray(roleData, true); - } - - user[0] = factory.createUser( - identifier, name, account, role, context); - return true; - } - }; - - return exec.runRead() ? user[0] : null; - } - - /** - * Find/Get user by account. - */ - public User findUser( - final String account, - final UserFactory factory, - final Object context - ) { - - final User [] user = new User[1]; - logger.debug("Trying to find user by account " + account); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_USERS_SELECT_ACCOUNT); - stmnt.setString(1, account); - result = stmnt.executeQuery(); - if (!result.next()) { // no such user - logger.debug("No user found."); - return false; - } - String identifier = result.getString(1); - String name = result.getString(2); - String account = result.getString(3); - byte [] roleData = result.getBytes(4); - - Document role = null; - if (roleData != null) { - role = XMLUtils.fromByteArray(roleData, true); - } - - user[0] = factory.createUser( - identifier, name, account, role, context); - return true; - } - }; - - return exec.runRead() ? user[0] : null; - } - - public User [] getUsers( - final UserFactory factory, - final Object context - ) { - final ArrayList<User> users = new ArrayList<User>(); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_USERS_SELECT_ALL); - result = stmnt.executeQuery(); - - while (result.next()) { - // omit id - String identifier = result.getString(2); - String name = result.getString(3); - String account = result.getString(4); - byte [] roleData = result.getBytes(5); - - Document role = XMLUtils.fromByteArray(roleData, true); - User user = factory.createUser( - identifier, name, account, role, context); - users.add(user); - } - return true; - } - }; - - return exec.runRead() - ? users.toArray(new User[users.size()]) - : null; - } - - public ArtifactCollection createCollection( - final String ownerIdentifier, - final String name, - final ArtifactCollectionFactory factory, - final Document attribute, - final Object context - ) { - if (name == null) { - logger.debug("Name is null"); - return null; - } - - if (!isValidIdentifier(ownerIdentifier)) { - logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); - return null; - } - - final ArtifactCollection [] collection = new ArtifactCollection[1]; - - final byte [] data = XMLUtils.toByteArray(attribute, true); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - // fetch owner id - prepareStatement(SQL_USERS_SELECT_ID_BY_GID); - stmnt.setString(1, ownerIdentifier); - result = stmnt.executeQuery(); - - if (!result.next()) { // no such user - return false; - } - - int ownerId = result.getInt(1); - reset(); - - // fetch new collection seq number. - prepareStatement(SQL_COLLECTIONS_NEXT_ID); - result = stmnt.executeQuery(); - - if (!result.next()) { // no identifier generated - return false; - } - - int id = result.getInt(1); - reset(); - - String identifier = newIdentifier(); - - prepareStatement(SQL_COLLECTIONS_INSERT); - - stmnt.setInt(1, id); - stmnt.setString(2, identifier); - stmnt.setString(3, name); - stmnt.setInt(4, ownerId); - - // XXX: A bit odd: we don't have a collection, yet. - Long ttl = factory.timeToLiveUntouched(null, context); - - if (ttl == null) { - stmnt.setNull(5, Types.BIGINT); - } - else { - stmnt.setLong(5, ttl); - } - - if (data == null) { - stmnt.setNull(6, Types.BINARY); - } - else { - stmnt.setBytes(6, data); - } - - stmnt.execute(); - conn.commit(); - - reset(); - - // fetch creation time from database - // done this way to use the time system - // of the database. - - prepareStatement(SQL_COLLECTIONS_CREATION_TIME); - stmnt.setInt(1, id); - - result = stmnt.executeQuery(); - - Date creationTime = null; - - if (result.next()) { - Timestamp timestamp = result.getTimestamp(1); - creationTime = new Date(timestamp.getTime()); - } - - collection[0] = factory.createCollection( - identifier, name, creationTime, ttl, attribute, context); - - if (collection[0] != null) { - // XXX: Little hack to make the listeners happy - collection[0].setUser(new DefaultUser(ownerIdentifier)); - } - - return true; - } - }; - - boolean success = exec.runWrite(); - - if (success) { - fireCreatedCollection(collection[0]); - return collection[0]; - } - return null; - } - - protected void fireCreatedCollection(ArtifactCollection collection) { - for (BackendListener listener: listeners) { - listener.createdCollection(collection, this); - } - } - - public ArtifactCollection getCollection( - final String collectionId, - final ArtifactCollectionFactory collectionFactory, - final UserFactory userFactory, - final Object context - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("collection id is not valid: " + collectionId); - return null; - } - - final ArtifactCollection[] ac = new ArtifactCollection[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - prepareStatement(SQL_COLLECTIONS_SELECT_GID); - stmnt.setString(1, collectionId); - - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection"); - return false; - } - - String collectionName = result.getString(2); - String ownerId = result.getString(3); - Date creationTime = - new Date(result.getTimestamp(4).getTime()); - Date lastAccess = - new Date(result.getTimestamp(5).getTime()); - Document attr = - XMLUtils.fromByteArray(result.getBytes(6), true); - long ttl = result.getLong(7); - - ArtifactCollection collection = - collectionFactory.createCollection( - collectionId, - collectionName, - creationTime, - ttl, - attr, - context); - - if (ownerId != null) { - collection.setUser(new LazyBackendUser( - ownerId, userFactory, Backend.this, context)); - } - - ac[0] = collection; - - return true; - } - }; - - return exec.runRead() ? ac[0] : null; - } - - public ArtifactCollection [] listCollections( - final String ownerIdentifier, - final Document data, - final ArtifactCollectionFactory collectionFactory, - final UserFactory userFactory, - final Object context - ) { - if (ownerIdentifier != null - && !isValidIdentifier(ownerIdentifier)) { - logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); - return null; - } - - final ArrayList<ArtifactCollection> collections = - new ArrayList<ArtifactCollection>(); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - - public boolean doIt() throws SQLException { - - if (ownerIdentifier != null) { - prepareStatement(SQL_COLLECTIONS_SELECT_USER); - stmnt.setString(1, ownerIdentifier); - } - else { - prepareStatement(SQL_COLLECTIONS_SELECT_ALL); - } - - result = stmnt.executeQuery(); - - HashMap<String, LazyBackendUser> users = - new HashMap<String, LazyBackendUser>(); - - while (result.next()) { - String collectionIdentifier = result.getString(1); - String collectionName = result.getString(2); - Date creationTime = - new Date(result.getTimestamp(3).getTime()); - String userIdentifier = result.getString(4); - long ttl = result.getLong(5); - - ArtifactCollection collection = - collectionFactory.createCollection( - collectionIdentifier, - collectionName, - creationTime, - ttl, - data, - context); - - if (userIdentifier != null) { - LazyBackendUser user = users.get(userIdentifier); - if (user == null) { - user = new LazyBackendUser( - userIdentifier, userFactory, - Backend.this, context); - users.put(userIdentifier, user); - } - collection.setUser(user); - } - - collections.add(collection); - } - return true; - } - }; - - return exec.runRead() - ? collections.toArray(new ArtifactCollection[collections.size()]) - : null; - } - - - public String getMasterArtifact(final String collectionId) { - if (!isValidIdentifier(collectionId)) { - logger.debug("Invalid collection id: '" + collectionId + "'"); - return null; - } - final String [] uuid = new String[1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - // Fetch masters (oldest artifact) id. - prepareStatement(SQL_COLLECTIONS_OLDEST_ARTIFACT); - stmnt.setString(1, collectionId); - stmnt.setMaxRows(1); // - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection: " + collectionId); - return false; - } - uuid[0] = result.getString(1); - if (logger.isDebugEnabled()) { - logger.debug("getMasterArtifact result.getString " + - uuid[0]); - } - return true; - } - }; - return exec.runRead() ? uuid[0] : null; - } - - public boolean deleteCollection(final String collectionId) { - if (!isValidIdentifier(collectionId)) { - logger.debug("Invalid collection id: '" + collectionId + "'"); - return false; - } - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - // fetch collection id - prepareStatement(SQL_COLLECTIONS_ID_BY_GID); - stmnt.setString(1, collectionId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection: " + collectionId); - return false; - } - int id = result.getInt(1); - reset(); - - // outdate artifacts that are only in this collection - logger.info("Outdate Artifacts that belong to collection: " + id); - - prepareStatement(SQL_OUTDATE_ARTIFACTS_COLLECTION); - stmnt.setInt(1, id); - stmnt.setInt(2, id); - stmnt.execute(); - reset(); - - // delete the collection items - prepareStatement(SQL_DELETE_COLLECTION_ITEMS); - stmnt.setInt(1, id); - stmnt.execute(); - reset(); - - // delete the collection - prepareStatement(SQL_DELETE_COLLECTION); - stmnt.setInt(1, id); - stmnt.execute(); - conn.commit(); - return true; - } - }; - boolean success = exec.runWrite(); - - if (success) { - fireDeletedCollection(collectionId); - } - - return success; - } - - protected void fireDeletedCollection(String identifier) { - for (BackendListener listener: listeners) { - listener.deletedCollection(identifier, this); - } - } - - public Document getCollectionAttribute(final String collectionId) { - if (!isValidIdentifier(collectionId)) { - logger.debug("collection id is not valid: " + collectionId); - } - - final byte[][] data = new byte[1][1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_COLLECTION_GET_ATTRIBUTE); - stmnt.setString(1, collectionId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection."); - return false; - } - - data[0] = result.getBytes(1); - return true; - } - }; - - return exec.runRead() - ? XMLUtils.fromByteArray(data[0], true) - : null; - } - - public boolean setCollectionAttribute( - final String collectionId, - Document attribute - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("collection id is not valid: " + collectionId); - return false; - } - - final byte [] data = XMLUtils.toByteArray(attribute, true); - - boolean success = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - // set the column in collection items - prepareStatement(SQL_COLLECTION_SET_ATTRIBUTE); - if (data == null) { - stmnt.setNull(1, Types.BINARY); - } - else { - stmnt.setBytes(1, data); - } - stmnt.setString(2, collectionId); - stmnt.execute(); - reset(); - - // touch the collection - prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); - stmnt.setString(1, collectionId); - stmnt.execute(); - - conn.commit(); - return true; - } - }.runWrite(); - - if (success) { - fireChangedCollectionAttribute(collectionId, attribute); - } - - return success; - } - - protected void fireChangedCollectionAttribute( - String collectionId, - Document document - ) { - for (BackendListener listener: listeners) { - listener.changedCollectionAttribute(collectionId, document, this); - } - } - - public Document getCollectionItemAttribute( - final String collectionId, - final String artifactId - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("collection id is not valid: " + collectionId); - return null; - } - if (!isValidIdentifier(artifactId)) { - logger.debug("artifact id is not valid: " + artifactId); - return null; - } - - final byte [][] data = new byte[1][1]; - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_COLLECTION_ITEM_GET_ATTRIBUTE); - stmnt.setString(1, collectionId); - stmnt.setString(2, artifactId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection item"); - return false; - } - data[0] = result.getBytes(1); - return true; - } - }; - - return exec.runRead() - ? XMLUtils.fromByteArray(data[0], true) - : null; - } - - public boolean setCollectionItemAttribute( - final String collectionId, - final String artifactId, - Document attribute - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("collection id is not valid: " + collectionId); - return false; - } - if (!isValidIdentifier(artifactId)) { - logger.debug("artifact id is not valid: " + artifactId); - return false; - } - - final byte [] data = XMLUtils.toByteArray(attribute, true); - - boolean success = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - // set the column in collection items - prepareStatement(SQL_COLLECTION_ITEM_SET_ATTRIBUTE); - if (data == null) { - stmnt.setNull(1, Types.BINARY); - } - else { - stmnt.setBytes(1, data); - } - stmnt.setString(2, collectionId); - stmnt.setString(3, artifactId); - stmnt.execute(); - reset(); - - // touch the collection - prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); - stmnt.setString(1, collectionId); - stmnt.execute(); - - conn.commit(); - return true; - } - }.runWrite(); - - if (success) { - fireChangedCollectionItemAttribute( - collectionId, artifactId, attribute); - } - - return success; - } - - protected void fireChangedCollectionItemAttribute( - String collectionId, - String artifactId, - Document document - ) { - for (BackendListener listener: listeners) { - listener.changedCollectionItemAttribute( - collectionId, artifactId, document, this); - } - } - - public boolean addCollectionArtifact( - final String collectionId, - final String artifactId, - final Document attribute - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("Invalid collection id: '" + collectionId + "'"); - return false; - } - - if (!isValidIdentifier(artifactId)) { - logger.debug("Invalid artifact id: '" + artifactId + "'"); - return false; - } - - final byte [] data = XMLUtils.toByteArray(attribute, true); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - // fetch artifact id - prepareStatement(SQL_GET_ID); - stmnt.setString(1, artifactId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such artifact: " + artifactId); - return false; - } - int aid = result.getInt(1); - reset(); - - // fetch collection id - prepareStatement(SQL_COLLECTIONS_ID_BY_GID); - stmnt.setString(1, collectionId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection: " + collectionId); - } - int cid = result.getInt(1); - reset(); - - // check if artifact is already in collection - prepareStatement(SQL_COLLECTION_CHECK_ARTIFACT); - stmnt.setInt(1, aid); - stmnt.setInt(2, cid); - result = stmnt.executeQuery(); - if (result.next()) { - logger.debug("artifact already in collection"); - return false; - } - reset(); - - // fetch fresh id for new collection item - prepareStatement(SQL_COLLECTION_ITEMS_ID_NEXTVAL); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("no collection item id generated"); - return false; - } - int ci_id = result.getInt(1); - reset(); - - // insert new collection item - prepareStatement(SQL_COLLECTION_ITEMS_INSERT); - stmnt.setInt(1, ci_id); - stmnt.setInt(2, cid); - stmnt.setInt(3, aid); - - if (data == null) { - stmnt.setNull(4, Types.BINARY); - } - else { - stmnt.setBytes(4, data); - } - stmnt.execute(); - conn.commit(); - - return true; - } - }; - boolean success = exec.runWrite(); - - if (success) { - fireAddedArtifactToCollection(artifactId, collectionId); - } - - return success; - } - - protected void fireAddedArtifactToCollection( - String artifactId, - String collectionId - ) { - for (BackendListener listener: listeners) { - listener.addedArtifactToCollection( - artifactId, collectionId, this); - } - } - - public boolean removeCollectionArtifact( - final String collectionId, - final String artifactId - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("Invalid collection id: '" + collectionId + "'"); - return false; - } - - boolean success = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - - // fetch id, collection id and artitfact id - prepareStatement(SQL_COLLECTION_ITEM_ID_CID_AID); - stmnt.setString(1, collectionId); - stmnt.setString(2, artifactId); - result = stmnt.executeQuery(); - if (!result.next()) { - logger.debug("No such collection item"); - return false; - } - int id = result.getInt(1); - int cid = result.getInt(2); - int aid = result.getInt(3); - reset(); - - // outdate artifact iff it is only in this collection - prepareStatement(SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT); - stmnt.setInt(1, aid); - stmnt.setInt(2, cid); - stmnt.setInt(3, aid); - stmnt.execute(); - reset(); - - // delete collection item - prepareStatement(SQL_COLLECTION_ITEM_DELETE); - stmnt.setInt(1, id); - stmnt.execute(); - reset(); - - // touch collection - prepareStatement(SQL_COLLECTIONS_TOUCH_BY_ID); - stmnt.setInt(1, cid); - stmnt.execute(); - - conn.commit(); - return true; - } - }.runWrite(); - - if (success) { - fireRemovedArtifactFromCollection(artifactId, collectionId); - } - - return success; - } - - protected void fireRemovedArtifactFromCollection( - String artifactId, - String collectionId - ) { - for (BackendListener listener: listeners) { - listener.removedArtifactFromCollection( - artifactId, collectionId, this); - } - } - - public CollectionItem [] listCollectionArtifacts( - final String collectionId - ) { - if (!isValidIdentifier(collectionId)) { - logger.debug("Invalid collection id: '" + collectionId + "'"); - return null; - } - - final ArrayList<CollectionItem> collectionItems = - new ArrayList<CollectionItem>(); - - SQLExecutor.Instance exec = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_COLLECTION_ITEMS_LIST_GID); - stmnt.setString(1, collectionId); - result = stmnt.executeQuery(); - while (result.next()) { - CollectionItem item = new DefaultCollectionItem( - result.getString(1), - result.getBytes(2)); - collectionItems.add(item); - } - return true; - } - }; - - return exec.runRead() - ? collectionItems.toArray( - new CollectionItem[collectionItems.size()]) - : null; - } - - - public boolean setCollectionTTL(final String uuid, final Long ttl) { - if (!isValidIdentifier(uuid)) { - logger.debug("Invalid collection id: '" + uuid + "'"); - return false; - } - - return sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_UPDATE_COLLECTION_TTL); - if (ttl == null) { - stmnt.setNull(1, Types.BIGINT); - } - else { - stmnt.setLong(1, ttl); - } - stmnt.setString(2, uuid); - stmnt.execute(); - conn.commit(); - - return true; - } - }.runWrite(); - } - - - public boolean setCollectionName(final String uuid, final String name) { - if (!isValidIdentifier(uuid)) { - logger.debug("Invalid collection id: '" + uuid + "'"); - return false; - } - - boolean success = sqlExecutor.new Instance() { - public boolean doIt() throws SQLException { - prepareStatement(SQL_UPDATE_COLLECTION_NAME); - stmnt.setString(1, name); - stmnt.setString(2, uuid); - stmnt.execute(); - conn.commit(); - - return true; - } - }.runWrite(); - - if (success) { - fireSetCollectionName(uuid, name); - } - - return success; - } - - protected void fireSetCollectionName(String identifier, String name) { - for (BackendListener listener: listeners) { - listener.setCollectionName(identifier, name); - } - } - - public boolean loadAllArtifacts(final ArtifactLoadedCallback alc) { - - logger.debug("loadAllArtifacts"); - - if (factoryLookup == null) { - logger.error("factory lookup == null"); - return false; - } - - boolean success = sqlExecutor.new Instance() { - @Override - public boolean doIt() throws SQLException { - // a little cache to avoid too much deserializations. - LRUCache<String, Artifact> alreadyLoaded = - new LRUCache<String, Artifact>(200); - - prepareStatement(SQL_ALL_ARTIFACTS); - result = stmnt.executeQuery(); - while (result.next()) { - String userId = result.getString("u_gid"); - String collectionId = result.getString("c_gid"); - String collectionName = result.getString("c_name"); - String artifactId = result.getString("a_gid"); - String factoryName = result.getString("factory"); - Date collectionCreated = - new Date(result.getTimestamp("c_creation").getTime()); - Date artifactCreated = - new Date(result.getTimestamp("a_creation").getTime()); - - Artifact artifact = alreadyLoaded.get(artifactId); - - if (artifact != null) { - alc.artifactLoaded( - userId, - collectionId, collectionName, - collectionCreated, - artifactId, artifactCreated, artifact); - continue; - } - - ArtifactFactory factory = factoryLookup - .getArtifactFactory(factoryName); - - if (factory == null) { - logger.error("factory '" + factoryName + "' not found"); - continue; - } - - byte [] bytes = result.getBytes("data"); - - artifact = factory.getSerializer().fromBytes(bytes); - - if (artifact != null) { - alc.artifactLoaded( - userId, - collectionId, collectionName, collectionCreated, - artifactId, artifactCreated, artifact); - } - - alreadyLoaded.put(artifactId, artifact); - } - return true; - } - }.runRead(); - - if (logger.isDebugEnabled()) { - logger.debug("loadAllArtifacts success: " + success); - } - - return success; - } - - @Override - public void killedArtifacts(List<String> identifiers) { - logger.debug("killedArtifacts"); - for (BackendListener listener: listeners) { - listener.killedArtifacts(identifiers, this); - } - } - - @Override - public void killedCollections(List<String> identifiers) { - logger.debug("killedCollections"); - for (BackendListener listener: listeners) { - listener.killedCollections(identifiers, this); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -package de.intevation.artifactdatabase; - -import java.util.List; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.User; - -import org.w3c.dom.Document; - -public interface BackendListener -{ - void setup(GlobalContext globalContext); - - void createdArtifact(Artifact artifact, Backend backend); - - void storedArtifact(Artifact artifact, Backend backend); - - void createdUser(User user, Backend backend); - - void deletedUser(String identifier, Backend backend); - - void createdCollection(ArtifactCollection collection, Backend backend); - - void deletedCollection(String identifier, Backend backend); - - void changedCollectionAttribute( - String identifier, - Document document, - Backend backend); - - void changedCollectionItemAttribute( - String collectionId, - String artifactId, - Document document, - Backend backend); - - void addedArtifactToCollection( - String artifactId, - String collectionId, - Backend backend); - - void removedArtifactFromCollection( - String artifactId, - String collectionId, - Backend backend); - - void setCollectionName( - String collectionId, - String name); - - void killedCollections( - List<String> identifiers, - Backend backend); - - void killedArtifacts( - List<String> identifiers, - Backend backend); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import java.util.LinkedList; - -import org.apache.log4j.Logger; - -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.Message; - - -/** - * Class that implements the call context handed to ArtifactCollection specific - * operations. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class CollectionCallContext extends AbstractCallContext { - - private static Logger log = Logger.getLogger(CollectionCallContext.class); - - /** - * The ArtifactCollection. - */ - protected ArtifactCollection collection; - - - public CollectionCallContext( - ArtifactDatabaseImpl artifactDatabase, - int action, - CallMeta callMeta, - ArtifactCollection collection) - { - super(artifactDatabase, action, callMeta); - - this.collection = collection; - } - - - public void afterCall(int action) { - log.debug("CollectionCallContext.afterCall - NOT IMPLEMENTED"); - } - - - public void afterBackground(int action) { - log.debug("CollectionCallContext.afterBackground - NOT IMPLEMENTED"); - } - - - public boolean isInBackground() { - log.debug("CollectionCallContext.isInBackground - NOT IMPLEMENTED"); - return false; - } - - - public void addBackgroundMessage(Message msg) { - log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); - } - - - public LinkedList<Message> getBackgroundMessages() { - log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); - return null; - } - - - public Long getTimeToLive() { - log.debug("CollectionCallContext.getTimeToLive - NOT IMPLEMENTED"); - return null; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConfig.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import de.intevation.artifactdatabase.db.SQL; -import de.intevation.artifactdatabase.db.DBConnection; - -public class DBConfig -{ - /** - * XPath to access the database driver within the global configuration. - */ - public static final String DB_DRIVER = - "/artifact-database/database/driver/text()"; - /** - * XPath to access the database URL within the global configuration. - */ - public static final String DB_URL = - "/artifact-database/database/url/text()"; - /** - * XPath to access the database use within the global configuration. - */ - public static final String DB_USER = - "/artifact-database/database/user/text()"; - /** - * XPath to access the database password within the global configuration. - */ - public static final String DB_PASSWORD = - "/artifact-database/database/password/text()"; - - private static DBConfig instance; - - private DBConnection dbConnection; - private SQL sql; - - private DBConfig() { - } - - private DBConfig(DBConnection dbConnection, SQL sql) { - this.dbConnection = dbConnection; - this.sql = sql; - } - - public static synchronized DBConfig getInstance() { - if (instance == null) { - instance = createInstance(); - } - return instance; - } - - public SQL getSQL() { - return sql; - } - - public DBConnection getDBConnection() { - return dbConnection; - } - - private static DBConfig createInstance() { - - String driver = Config.getStringXPath( - DB_DRIVER, DBConnection.DEFAULT_DRIVER); - - String url = Config.getStringXPath( - DB_URL, DBConnection.DEFAULT_URL); - - url = Config.replaceConfigDir(url); - - String user = Config.getStringXPath( - DB_USER, DBConnection.DEFAULT_USER); - - String password = Config.getStringXPath( - DB_PASSWORD, DBConnection.DEFAULT_PASSWORD); - - DBConnection dbConnection = new DBConnection( - driver, url, user, password); - - SQL sql = new SQL(driver); - - return new DBConfig(dbConnection, sql); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,446 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; -import de.intevation.artifacts.common.utils.StringUtils; - -import de.intevation.artifacts.Artifact; - -import de.intevation.artifactdatabase.db.SQL; -import de.intevation.artifactdatabase.db.DBConnection; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.Collections; - -import javax.sql.DataSource; - -import org.apache.log4j.Logger; - -/** - * The database cleaner runs in background. It sleep for a configurable - * while and when it wakes up it removes outdated artifacts from the - * database. Outdated means that the the last access to the artifact - * is longer aga then the time to live of this artifact.<br> - * Before the artifact is finally removed from the system it is - * revived one last time an the #endOfLife() method of the artifact - * is called.<br> - * The artifact implementations may e.g. use this to remove some extrenal - * resources form the system. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DatabaseCleaner -extends Thread -{ - /** - * Implementors of this interface are able to create a - * living artifact from a given byte array. - */ - public interface ArtifactReviver { - - /** - * Called to revive an artifact from a given byte array. - * @param factoryName The name of the factory which - * created this artifact. - * @param bytes The bytes of the serialized artifact. - * @return The revived artfiact. - */ - Artifact reviveArtifact(String factoryName, byte [] bytes); - - void killedArtifacts(List<String> identifiers); - void killedCollections(List<String> identifiers); - - } // interface ArtifactReviver - - public interface LockedIdsProvider { - Set<Integer> getLockedIds(); - } // interface LockedIdsProvider - - private static Logger logger = Logger.getLogger(DatabaseCleaner.class); - - /** - * Number of artifacts to be loaded at once. Used to - * mitigate the problem of a massive denial of service - * if too many artifacts have died since last cleanup. - */ - public static final int MAX_ROWS = 50; - - public static final Set<Integer> EMPTY_IDS = Collections.emptySet(); - - /** - * The SQL statement to select the outdated artifacts. - */ - public String SQL_OUTDATED; - - public String SQL_OUTDATED_COLLECTIONS; - public String SQL_DELETE_COLLECTION_ITEMS; - public String SQL_DELETE_COLLECTION; - - /** - * The SQL statement to delete some artifacts from the database. - */ - public String SQL_DELETE_ARTIFACT; - - /** - * XPath to figure out how long the cleaner should sleep between - * cleanups. This is stored in the global configuration. - */ - public static final String SLEEP_XPATH = - "/artifact-database/cleaner/sleep-time/text()"; - - /** - * Default nap time between cleanups: 5 minutes. - */ - public static final long SLEEP_DEFAULT = - 5 * 60 * 1000L; // 5 minutes - - /** - * The configured nap time. - */ - protected long sleepTime; - - /** - * Internal locking mechanism to prevent some race conditions. - */ - protected Object sleepLock = new Object(); - - /** - * A reference to the global context. - */ - protected Object context; - - /** - * A specialized Id filter which only delete some artifacts. - * This is used to prevent deletion of living artifacts. - */ - protected LockedIdsProvider lockedIdsProvider; - - /** - * The reviver used to bring the dead artifact on last - * time back to live to call endOfLife() on them. - */ - protected ArtifactReviver reviver; - - protected DBConnection dbConnection; - - /** - * Default constructor. - */ - public DatabaseCleaner() { - } - - /** - * Constructor to create a cleaner with a given global context - * and a given reviver. - * @param context The global context of the artifact database - * @param reviver The reviver to awake artifact one last time. - */ - public DatabaseCleaner(Object context, ArtifactReviver reviver, DBConfig config) { - setDaemon(true); - sleepTime = getSleepTime(); - this.context = context; - this.reviver = reviver; - this.dbConnection = config.getDBConnection(); - setupSQL(config.getSQL()); - } - - protected void setupSQL(SQL sql) { - SQL_OUTDATED = sql.get("artifacts.outdated"); - SQL_OUTDATED_COLLECTIONS = sql.get("collections.outdated"); - SQL_DELETE_COLLECTION_ITEMS = sql.get("delete.collection.items"); - SQL_DELETE_COLLECTION = sql.get("delete.collection"); - SQL_DELETE_ARTIFACT = sql.get("artifacts.delete"); - } - - /** - * Sets the filter that prevents deletion of living artifacts. - * Living artifacts are artifacts which are currently active - * inside the artifact database. Deleting them in this state - * would create severe internal problems. - */ - public void setLockedIdsProvider(LockedIdsProvider lockedIdsProvider) { - this.lockedIdsProvider = lockedIdsProvider; - } - - /** - * External hook to tell the cleaner to wake up before its - * regular nap time is over. This is the case when the artifact - * database finds an artifact which is already outdated. - */ - public void wakeup() { - synchronized (sleepLock) { - sleepLock.notify(); - } - } - - /** - * Fetches the sleep time from the global configuration. - * @return the time to sleep between database cleanups in ms. - */ - protected static long getSleepTime() { - String sleepTimeString = Config.getStringXPath(SLEEP_XPATH); - - if (sleepTimeString == null) { - return SLEEP_DEFAULT; - } - try { - // sleep at least one second - return Math.max(Long.parseLong(sleepTimeString), 1000L); - } - catch (NumberFormatException nfe) { - logger.warn("Cleaner sleep time defaults to " + SLEEP_DEFAULT); - } - return SLEEP_DEFAULT; - } - - private static class IdIdentifier { - - int id; - String identifier; - - private IdIdentifier(int id, String identifier) { - this.id = id; - this.identifier = identifier; - } - } // class IdIdentifier - - private static final class IdData - extends IdIdentifier - { - byte [] data; - String factoryName; - - public IdData( - int id, - String factoryName, - byte [] data, - String identifier - ) { - super(id, identifier); - this.factoryName = factoryName; - this.data = data; - } - } // class IdData - - /** - * Cleaning is done in two phases. First we fetch a list of ids - * of artifacts. If there are artifacts the cleaning is done. - * Second we load the artifacts one by one one and call there - * endOfLife() method. In this loop we remove them from database, too. - * Each deletion is commited to ensure that a sudden failure - * of the artifact database server does delete artifacts twice - * or does not delete them at all. After this the first step - * is repeated. - */ - protected void cleanup() { - logger.info("database cleanup"); - - Connection connection = null; - PreparedStatement fetchIds = null; - PreparedStatement stmnt = null; - ResultSet result = null; - - DataSource dataSource = dbConnection.getDataSource(); - - Set<Integer> lockedIds = lockedIdsProvider != null - ? lockedIdsProvider.getLockedIds() - : EMPTY_IDS; - - String questionMarks = lockedIds.isEmpty() - ? "-666" // XXX: A bit hackish. - : StringUtils.repeat('?', lockedIds.size(), ','); - - List<String> deletedCollections = new ArrayList<String>(); - List<String> deletedArtifacts = new ArrayList<String>(); - - try { - connection = dataSource.getConnection(); - connection.setAutoCommit(false); - - fetchIds = connection.prepareStatement( - SQL_OUTDATED.replace("$LOCKED_IDS$", questionMarks)); - - // some dbms like derby do not support LIMIT - // in SQL statements. - fetchIds.setMaxRows(MAX_ROWS); - - // Fetch ids of outdated collections - stmnt = connection.prepareStatement( - SQL_OUTDATED_COLLECTIONS.replace( - "$LOCKED_IDS$", questionMarks)); - - // fill in the locked ids - int idx = 1; - for (Integer id: lockedIds) { - fetchIds.setInt(idx, id); - stmnt .setInt(idx, id); - ++idx; - } - - ArrayList<IdIdentifier> cs = new ArrayList<IdIdentifier>(); - result = stmnt.executeQuery(); - while (result.next()) { - cs.add(new IdIdentifier( - result.getInt(1), - result.getString(2))); - } - - result.close(); result = null; - stmnt.close(); stmnt = null; - - // delete collection items - stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION_ITEMS); - - for (IdIdentifier id: cs) { - logger.debug("Mark collection for deletion: " + id.id); - stmnt.setInt(1, id.id); - stmnt.execute(); - } - - stmnt.close(); stmnt = null; - - // delete collections - stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION); - - for (IdIdentifier id: cs) { - stmnt.setInt(1, id.id); - stmnt.execute(); - deletedCollections.add(id.identifier); - } - - stmnt.close(); stmnt = null; - connection.commit(); - - cs = null; - - // remove artifacts - stmnt = connection.prepareStatement(SQL_DELETE_ARTIFACT); - - for (;;) { - List<IdData> ids = new ArrayList<IdData>(); - - result = fetchIds.executeQuery(); - - while (result.next()) { - ids.add(new IdData( - result.getInt(1), - result.getString(2), - result.getBytes(3), - result.getString(4))); - } - - result.close(); result = null; - - if (ids.isEmpty()) { - break; - } - - for (int i = ids.size()-1; i >= 0; --i) { - IdData idData = ids.get(i); - Artifact artifact = reviver.reviveArtifact( - idData.factoryName, idData.data); - idData.data = null; - - logger.debug("Prepare Artifact (id=" - + idData.id + ") for deletion."); - - stmnt.setInt(1, idData.id); - stmnt.execute(); - connection.commit(); - - try { - if (artifact != null) { - logger.debug("Call endOfLife for Artifact: " - + artifact.identifier()); - - artifact.endOfLife(context); - } - } - catch (Exception e) { - logger.error(e.getMessage(), e); - } - - deletedArtifacts.add(idData.identifier); - } // for all fetched data - } - } - catch (SQLException sqle) { - logger.error(sqle.getLocalizedMessage(), sqle); - } - finally { - if (result != null) { - try { result.close(); } - catch (SQLException sqle) {} - } - if (stmnt != null) { - try { stmnt.close(); } - catch (SQLException sqle) {} - } - if (fetchIds != null) { - try { fetchIds.close(); } - catch (SQLException sqle) {} - } - if (connection != null) { - try { connection.close(); } - catch (SQLException sqle) {} - } - } - - if (!deletedCollections.isEmpty()) { - reviver.killedCollections(deletedCollections); - } - - if (!deletedArtifacts.isEmpty()) { - reviver.killedArtifacts(deletedArtifacts); - } - - if (logger.isDebugEnabled()) { - logger.debug( - "collections removed: " + deletedCollections.size()); - logger.debug( - "artifacts removed: " + deletedArtifacts.size()); - } - } - - /** - * The main code of the cleaner. It sleeps for the configured - * nap time, cleans up the database, sleeps again and so on. - */ - @Override - public void run() { - logger.info("sleep time: " + sleepTime + "ms"); - for (;;) { - cleanup(); - long startTime = System.currentTimeMillis(); - - try { - synchronized (sleepLock) { - sleepLock.wait(sleepTime); - } - } - catch (InterruptedException ie) { - } - - long stopTime = System.currentTimeMillis(); - - if (logger.isDebugEnabled()) { - logger.debug("Cleaner slept " + (stopTime - startTime) + "ms"); - } - } // for (;;) - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactFactory; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.CallMeta; - -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -/** - * Trivial implementation of an artifact. Useful to be subclassed. - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultArtifact -implements Artifact -{ - private static Logger logger = Logger.getLogger(DefaultArtifact.class); - - /** - * The identifier of the artifact. - */ - protected String identifier; - - - /** - * Default constructor. - */ - public DefaultArtifact() { - } - - - public void setIdentifier(String identifier) { - if (logger.isDebugEnabled()) { - logger.debug("Change identifier: " - + this.identifier + " -> " + identifier); - } - this.identifier = identifier; - } - - public String identifier() { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.identifier: " + identifier); - } - return this.identifier; - } - - - public String hash() { - String hash = String.valueOf(hashCode()); - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.hashCode: " - + identifier + " (" + hash + ")"); - } - return hash; - } - - public Document describe(Document data, CallContext context) { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.describe: " + identifier); - } - return XMLUtils.newDocument(); - } - - public Document advance(Document target, CallContext context) { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.advance: " + identifier); - } - return XMLUtils.newDocument(); - } - - public Document feed(Document target, CallContext context) { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.feed: " + identifier); - } - return XMLUtils.newDocument(); - } - - public void out( - Document format, - OutputStream out, - CallContext context - ) - throws IOException - { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.out: " + identifier); - } - } - - public void out( - String type, - Document format, - OutputStream out, - CallContext context - ) - throws IOException - { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.out: " + identifier); - } - } - - public void setup(String identifier, - ArtifactFactory factory, - Object context, - CallMeta callMeta, - Document data) - { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.setup: " + identifier); - } - this.identifier = identifier; - } - - public void endOfLife(Object context) { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.endOfLife: " + identifier); - } - } - - public void cleanup(Object context) { - if (logger.isDebugEnabled()) { - logger.debug("DefaultArtifact.cleanup: " + identifier); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,353 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import java.io.IOException; -import java.io.OutputStream; - -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.ArtifactCollectionFactory; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.User; - -import de.intevation.artifacts.common.utils.XMLUtils; - - -/** - * Trivial implementation of an artifact collection. Useful to be subclassed. - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultArtifactCollection -implements ArtifactCollection -{ - /** The logger used in this class. */ - private static Logger logger = - Logger.getLogger(DefaultArtifactCollection.class); - - /** The identifier of the collection. */ - protected String identifier; - - /** The identifier of the collection. */ - protected String name; - - /** The owner of this collection. */ - protected User user; - - /** The attribute of this collection. */ - protected Document attribute; - - /** The artifacts stored in this collection. */ - protected List<Artifact> artifacts; - - /** - * The attributes used for the artifacts stored in this collection. The key - * of this map represents the identifier of the artifact which the attribute - * belong to. - */ - protected Map<String, Document> attributes; - - /** The creation time of this collection.*/ - protected Date creationTime; - - protected long ttl; - - - /** - * Default constructor. - */ - public DefaultArtifactCollection() { - } - - - /** - * When created by a factory this method is called to - * initialize the collection. - * @param identifier The identifier from collection database - * @param factory The factory which created this collection. - * @param context The global context of the runtime system. - * @param data The data which can be use to setup a collection with - * more details. - */ - public void setup( - String identifier, - String name, - Date creationTime, - long ttl, - ArtifactCollectionFactory factory, - Object context, - Document data) - { - logger.debug("DefaultArtifactCollection.setup: " + identifier); - - artifacts = new ArrayList<Artifact>(); - attributes = new HashMap<String, Document>(); - - setIdentifier(identifier); - setName(name); - setCreationTime(creationTime); - setTTL(ttl); - setAttribute(data); - } - - - public Document describe(CallContext context) { - logger.debug("DefaultArtifactCollection.describe: " + identifier); - - return XMLUtils.newDocument(); - } - - - /** - * Set a new identifier for this collection. - * @param identifier New identifier for this collection. - */ - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - - /** - * Identify this collection. - * @return Returns unique string to identify this collection globally. - */ - public String identifier() { - return identifier; - } - - - /** - * Name of this collection. - * @return Returns the name of this collection - */ - public String getName() { - return name; - } - - /** - * Name of this collection. - * @param name the name of this collection - */ - public void setName(String name) { - this.name = name; - } - - - /** - * Set a new owner of this collection. - * @param user New owner for this collection. - */ - public void setUser(User user) { - this.user = user; - } - - - /** - * Identify the owner of the collection. - * @return Returns owner of the collection. - */ - public User getUser() { - return user; - } - - - /** - * Returns the creation time of the collection. - * - * @return the creation time of the collection. - */ - public Date getCreationTime() { - return creationTime; - } - - - /** - * Sets the creation time of the collection. - * - * @param creationTime The new creation time. - */ - public void setCreationTime(Date creationTime) { - this.creationTime = creationTime; - } - - - public long getTTL() { - return ttl; - } - - - public void setTTL(long ttl) { - this.ttl = ttl; - } - - - /** - * Returns the attribute of the collection. - * - * @return the attribute of the collection. - */ - public Document getAttribute() { - return attribute; - } - - - /** - * Sets the attribute of the collection. - * - * @param attribute The attribute of this collection. - */ - public void setAttribute(Document attribute) { - this.attribute = attribute; - } - - - /** - * Called from artifact database when an artifact is - * going to be removed from system. - * @param context The global context of the runtime system. - */ - public void endOfLife(Object context) { - logger.debug("DefaultArtifactCollection.endOfLife"); - } - - - /** - * Internal hash of this collection. - * @return Returns hash that should stay the same if the internal - * value has not changed. Useful for caching - */ - public String hash() { - logger.debug("DefaultArtifactCollection.hash"); - - return String.valueOf(hashCode()); - } - - - /** - * Called from artifact database before an artifact is - * going to be exported as xml document. - * @param context The global context of the runtime system. - */ - public void cleanup(Object context) { - logger.debug("DefaultArtifactCollection.cleanup"); - } - - - /** - * Adds a new artifact to this collection. - * - * @param artifact The new artifact. - * @param attribute The attributes used for this artifact. - * @param context The CallContext. - */ - public void addArtifact( - Artifact artifact, - Document attribute, - CallContext context) - { - logger.debug("DefaultArtifactCollection.addArtifact"); - - artifacts.add(artifact); - attributes.put(artifact.identifier(), attribute); - } - - - /** - * Removes the given artifact from this collection. - * - * @param artifact The artifact that should be removed. - * @param context The CallContext. - */ - public void removeArtifact(Artifact artifact, CallContext context) { - logger.debug("DefaultArtifactCollection.removeArtifact"); - - if (artifact == null) { - return; - } - - artifacts.remove(artifact); - attributes.remove(artifact.identifier()); - } - - - /** - * Returns a list of artifacts that are stored in this collection. - * - * @param context The CallContext. - * - * @return the list of artifacts stored in this collection. - */ - public Artifact[] getArtifacts(CallContext context) { - logger.debug("DefaultArtifactCollection.getArtifacts"); - - return (Artifact[]) artifacts.toArray(); - } - - - /** - * Returns the attribute document for the given artifact. - * - * @param artifact The artifact. - * @param context The CallContext. - * - * @return a document that contains the attributes of the artifact. - */ - public Document getAttribute(Artifact artifact, CallContext context) { - logger.debug("DefaultArtifactCollection.getAttribute"); - - return attributes.get(artifact.identifier()); - } - - - /** - * Set the attribute for the given artifact. - * - * @param artifact The artifact of the attribute. - * @param document The new attribute of the artifact. - * @param context The CallContext. - */ - public void setAttribute( - Artifact artifact, - Document document, - CallContext context) - { - logger.debug("DefaultArtifactCollection.setAttribute"); - - attributes.put(artifact.identifier(), document); - } - - - /** - * Produce output for this collection. - * @param type Specifies the output type. - * @param format Specifies the format of the output. - * @param out Stream to write the result data to. - * @param context The global context of the runtime system. - * @throws IOException Thrown if an I/O occurs. - */ - public void out( - String type, - Document format, - OutputStream out, - CallContext context) - throws IOException - { - logger.debug("DefaultArtifactCollection.out"); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,194 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.ArtifactCollectionFactory; - -import java.util.Date; - - -/** - * The default implementation of a ArtifactCollectionFactory. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultArtifactCollectionFactory -implements ArtifactCollectionFactory -{ - /** The logger that is used in this factory.*/ - private static Logger logger = - Logger.getLogger(DefaultArtifactCollectionFactory.class); - - /** XPath to access the TTL of this artifact.*/ - public static final String XPATH_TTL = "@ttl"; - - /** XPath to access the name of this factory.*/ - public static final String XPATH_NAME = "@name"; - - /** XPath to access the description of this artifact factory.*/ - public static final String XPATH_DESCRIPTION = "@description"; - - /** - * XPath to access the class name of the artifacts to be build - * by this factory. - */ - public static final String XPATH_ARTIFACTCOLLECTION = "@artifact-collection"; - - /** - * Default description of this factory if none is given by the - * configuration. - */ - public static final String DEFAULT_DESCRIPTION = - "No description available"; - - /** - * Class to load if no artifact class is given in the configuration. - */ - public static final String DEFAULT_ARTIFACTCOLLECTION = - "de.intevation.artifactdatabase.DefaultArtifact"; - - - /** The name of the factory.*/ - protected String name; - - /** The description of the factory.*/ - protected String description; - - /** The class that is used to instantiate new ArtifactCollection.*/ - protected Class clazz; - - /** The time to live of the artifact collection build by this factory.*/ - protected Long ttl; - - - /** - * The default constructor. - */ - public DefaultArtifactCollectionFactory() { - } - - - /** - * The short name of this factory. - * - * @return the name of this factory. - */ - public String getName() { - return name; - } - - - /** - * Description of this factory. - * - * @return description of the factory. - */ - public String getDescription() { - return description; - } - - - /** - * Returns the time to live of the given artifact. - */ - public Long timeToLiveUntouched( - ArtifactCollection collection, - Object context) - { - return ttl; - } - - - /** - * Create a new artifact of certain type, given a general purpose context and - * an identifier. - * @param context a context from the ArtifactDatabase. - * @param identifier unique identifer for the new artifact - * @param data the data containing more details for the setup of an Artifact. - * @return a new {@linkplain de.intevation.artifacts.ArtifactCollection ArtifactCollection} - */ - public ArtifactCollection createCollection( - String identifier, - String name, - Date creationTime, - long ttl, - Document data, - Object context - ) { - try { - ArtifactCollection collection = - (ArtifactCollection) clazz.newInstance(); - - collection.setup(identifier, - name, - creationTime, - ttl, - this, - context, - data); - - return collection; - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - - return null; - } - - /** - * Setup the factory with a given configuration - * @param config the configuration - * @param factoryNode the ConfigurationNode of this Factory - */ - public void setup(Document config, Node factoryNode) { - String ttlString = Config.getStringXPath(factoryNode, XPATH_TTL); - if (ttlString != null) { - try { - ttl = Long.valueOf(ttlString); - } - catch (NumberFormatException nfe) { - logger.warn("'" + ttlString + "' is not an integer."); - } - } - - description = Config.getStringXPath( - factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); - - name = Config.getStringXPath(factoryNode, XPATH_NAME, toString()); - - String artifactCollection = Config.getStringXPath( - factoryNode, XPATH_ARTIFACTCOLLECTION, DEFAULT_ARTIFACTCOLLECTION); - - try { - clazz = Class.forName(artifactCollection); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - - if (clazz == null) { - clazz = DefaultArtifactCollection.class; - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import java.util.HashMap; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.GlobalContext; - -/** - * Default implementation of the context. - * Besides of the configuration it hosts a map to store key/value pairs. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultArtifactContext implements GlobalContext -{ - /** - * The global configuration document of the artifact database. - */ - protected Document config; - - /** - * Custom key/value pairs to be used globally in the whole server. - */ - protected HashMap map; - - /** - * Default constructor - */ - public DefaultArtifactContext() { - this(null); - } - - /** - * Constructor to create a context with a given global - * configuration document and an empty map of custom - * key/value pairs. - * @param config - */ - public DefaultArtifactContext(Document config) { - this.config = config; - map = new HashMap(); - } - - /** - * Fetch a custom value from the global key/value map using - * a given key. - * @param key The key. - * @return The stored value or null if no value was found under - * this key. - */ - public synchronized Object get(Object key) { - return map.get(key); - } - - /** - * Store a custom key/value pair in the global map. - * @param key The key to store - * @param value The value to store - * @return The old value registered under the key or null - * if none wa there before. - */ - public synchronized Object put(Object key, Object value) { - return map.put(key, value); - } - - /** - * Returns a reference to the global configuration document. - * @return The global configuration document. - */ - public Document getConfig() { - return config; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.ArtifactContextFactory; -import de.intevation.artifacts.GlobalContext; - -import org.w3c.dom.Document; - -/** - * Default implementation of the context factory. - * Creates a new @see DefaultArtifactContext. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultArtifactContextFactory -implements ArtifactContextFactory -{ - /** - * Default constructor. - */ - public DefaultArtifactContextFactory() { - } - - public GlobalContext createArtifactContext(Document config) { - return new DefaultArtifactContext(config); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactFactory; -import de.intevation.artifacts.ArtifactSerializer; -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.GlobalContext; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * Trivial implementation of the ArtifactFactory interface. - * Time to live (ttl), name and description are configured - * via the Node given to #setup(Document, Node) with attributes - * of same name. The class name of the artifacts to be build by this - * factory is configures with the attribute 'artifact'. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultArtifactFactory -implements ArtifactFactory -{ - private static Logger logger = - Logger.getLogger(DefaultArtifactFactory.class); - - /** - * XPath to access the TTL of this artifact. - */ - public static final String XPATH_TTL = "@ttl"; - /** - * XPath to access the name of this factory. - */ - public static final String XPATH_NAME = "@name"; - /** - * XPath to access the description of this artifact factory. - */ - public static final String XPATH_DESCRIPTION = "@description"; - /** - * XPath to access the class name of the artifacts to be build - * by this factory. - */ - public static final String XPATH_ARTIFACT = "@artifact"; - - /** - * Default description of this factory if none is given by the - * configuration. - */ - public static final String DEFAULT_DESCRIPTION = - "No description available"; - - /** - * Class to load if no artifact class is given in the configuration. - */ - public static final String DEFAULT_ARTIFACT = - "de.intevation.artifactdatabase.DefaultArtifact"; - - /** - * The Time to live of the artifacts build by this factory. - */ - protected Long ttl; - - /** - * The name of this factory. - */ - protected String name; - - /** - * The description of this factory. - */ - protected String description; - - /** - * The class of the artifacts to be build by this factory. - */ - protected Class artifactClass; - - /** - * Default constructor. - */ - public DefaultArtifactFactory() { - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; - } - - public Artifact createArtifact( - String identifier, - GlobalContext context, - CallMeta callMeta, - Document data - ) { - try { - Artifact artifact = - (Artifact)artifactClass.newInstance(); - - artifact.setup(identifier, this, context, callMeta, data); - - return artifact; - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - - return null; - } - - public void setup(Document document, Node factoryNode) { - - String ttlString = Config.getStringXPath(factoryNode, XPATH_TTL); - if (ttlString != null) { - try { - ttl = Long.valueOf(ttlString); - } - catch (NumberFormatException nfe) { - logger.warn("'" + ttlString + "' is not an integer."); - } - } - - description = Config.getStringXPath( - factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); - - name = Config.getStringXPath( - factoryNode, XPATH_NAME, toString()); - - String artifact = Config.getStringXPath( - factoryNode, XPATH_ARTIFACT, DEFAULT_ARTIFACT); - - try { - artifactClass = Class.forName(artifact); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - - if (artifactClass == null) { - artifactClass = DefaultArtifact.class; - } - } - - public Long timeToLiveUntouched(Artifact artifact, Object context) { - return ttl; - } - - public ArtifactSerializer getSerializer() { - return DefaultArtifactSerializer.INSTANCE; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactSerializer.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactSerializer; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; - -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; - -import org.apache.log4j.Logger; - -/** - * Default implementation of the ArtifactSerializer interface. - * It uses serialized Java objects which are gzipped and - * turned into bytes. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultArtifactSerializer -implements ArtifactSerializer -{ - private static Logger logger = - Logger.getLogger(DefaultArtifactSerializer.class); - - /** - * Static instance to avoid repeated creation of Serializers. - */ - public static final ArtifactSerializer INSTANCE = - new DefaultArtifactSerializer(); - - /** - * Default constructor. - */ - public DefaultArtifactSerializer() { - } - - public Artifact fromBytes(byte [] bytes) { - - if (bytes == null) { - return null; - } - - ObjectInputStream ois = null; - - try { - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - GZIPInputStream gis = new GZIPInputStream(bis); - ois = getObjectInputStream(gis); - - return (Artifact)ois.readObject(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - finally { - if (ois != null) { - try { ois.close(); } - catch (IOException ioe) { } - } - } - - return null; - } - - public byte [] toBytes(Artifact artifact) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - GZIPOutputStream gos = new GZIPOutputStream(bos); - ObjectOutputStream oos = getObjectOutputStream(gos); - - oos.writeObject(artifact); - oos.flush(); - oos.close(); - - return bos.toByteArray(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - throw new RuntimeException(ioe); - } - } - - /** - * Wraps an input stream into an object input stream. You may - * overwrite this to get a more specialized deserializer. - * @param is The raw input stream - * @return An instance of a subclass of ObjectInputStream. - * @throws IOException Thrown if something went wrong during - * creation of the object input stream. - */ - protected ObjectInputStream getObjectInputStream(InputStream is) - throws IOException - { - return new ObjectInputStream(is); - } - - /** - * Wraps an output stream into an object output stream. You may - * overwrite this to get a more specialized serializer. - * @param os the raw output stream. - * @return An instance of a subclass of ObjectOutputStream. - * @throws IOException Thrown if something went wrong during - * creation of the object output stream. - */ - protected ObjectOutputStream getObjectOutputStream(OutputStream os) - throws IOException - { - return new ObjectOutputStream(os); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -package de.intevation.artifactdatabase; - -import java.util.List; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactCollection; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.User; - -import org.w3c.dom.Document; - -import org.apache.log4j.Logger; - -public class DefaultBackendListener -implements BackendListener -{ - private static Logger log = Logger.getLogger(DefaultBackendListener.class); - - public DefaultBackendListener() { - } - - @Override - public void setup(GlobalContext globalContext) { - log.debug("setup"); - } - - @Override - public void createdArtifact(Artifact artifact, Backend backend) { - log.debug("createdArtifact"); - } - - @Override - public void storedArtifact(Artifact artifact, Backend backend) { - log.debug("storedArtifact"); - } - - @Override - public void createdUser(User user, Backend backend) { - log.debug("createdUser"); - } - - @Override - public void deletedUser(String identifier, Backend backend) { - log.debug("deletedUser"); - } - - @Override - public void createdCollection( - ArtifactCollection collection, - Backend backend - ) { - log.debug("createdCollection"); - } - - @Override - public void deletedCollection(String identifier, Backend backend) { - log.debug("deletedCollection"); - } - - @Override - public void changedCollectionAttribute( - String identifier, - Document document, - Backend backend - ) { - log.debug("changedCollectionAttribute"); - } - - @Override - public void changedCollectionItemAttribute( - String collectionId, - String artifactId, - Document document, - Backend backend - ) { - log.debug("changedCollectionItemAttribute"); - } - - @Override - public void addedArtifactToCollection( - String artifactId, - String collectionId, - Backend backend - ) { - log.debug("addedArtifactToCollection"); - } - - @Override - public void removedArtifactFromCollection( - String artifactId, - String collectionId, - Backend backend - ) { - log.debug("removedArtifactFromCollection"); - } - - @Override - public void setCollectionName( - String collectionId, - String name - ) { - log.debug("setCollectionName"); - } - - @Override - public void killedCollections(List<String> identifiers, Backend backend) { - log.debug("killedCollections"); - } - - @Override - public void killedArtifacts(List<String> identifiers, Backend backend) { - log.debug("killedArtifacts"); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : -
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultCallMeta.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.PreferredLocale; - -import java.util.Locale; - -/** - * Default implementation of CallMeta. It provides a list of - * preferred langauages and implements an intersection mechanism - * to figure out the best matching language given a list of server - * provided languages. - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultCallMeta -implements CallMeta -{ - /** - * The list of preferred languages. - */ - protected PreferredLocale [] languages; - - /** - * Default constructor. - */ - public DefaultCallMeta() { - } - - /** - * Creates new DefaultCallMeta with a given list of languages. - * @param languages The list of preferred languages. - */ - public DefaultCallMeta(PreferredLocale [] languages) { - this.languages = languages; - } - - public PreferredLocale [] getLanguages() { - return languages; - } - - public Locale getPreferredLocale(Locale [] locales) { - if (locales == null || locales.length == 0) { - return null; - } - - Locale best = null; - float quality = -Float.MAX_VALUE; - - for (int i = 0; i < locales.length; ++i) { - Locale wish = locales[i]; - String wishLanguage = wish.getLanguage(); - - for (int j = 0; j < languages.length; ++j) { - PreferredLocale have = languages[j]; - Locale haveLocale = have.getLocale(); - if (haveLocale.getLanguage().equals(wishLanguage)) { - float haveQuality = have.getQuality(); - if (haveQuality > quality) { - quality = haveQuality; - best = wish; - } - break; // Languages should not contain - // same locale twice. - } - } - } - - return best == null - ? locales[0] - : best; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultCollectionItem.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.CollectionItem; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import org.w3c.dom.Document; - -public class DefaultCollectionItem -implements CollectionItem -{ - protected String artifactIdentifier; - - protected byte [] data; - - protected Document document; - - public DefaultCollectionItem() { - } - - public DefaultCollectionItem(String artifactIdentifier, byte [] attribute) { - this.artifactIdentifier = artifactIdentifier; - this.data = attribute; - } - - public String getArtifactIdentifier() { - return artifactIdentifier; - } - - public synchronized Document getAttribute() { - if (document == null) { - if (data != null) { - document = XMLUtils.fromByteArray(data, true); - } - } - return document; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultPreferredLocale.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.PreferredLocale; - -import java.util.Locale; - -/** - * Models a pair of Locale and quality (0.0-1.0) to be used to - * find best matching locale between server offerings and clients requests. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultPreferredLocale -implements PreferredLocale -{ - /** - * The locale of this tuple pair. - */ - protected Locale locale; - /** - * The quality of this tuple pair between 0.0 and 1.0. - */ - protected float quality; - - /** - * Default constructor - */ - public DefaultPreferredLocale() { - } - - /** - * Constructor to build a pair of given a locale speficied by - * string 'lang' and an given 'quality'. - * @param lang The name of the locale. - * @param quality The quality of the locale. - */ - public DefaultPreferredLocale(String lang, float quality) { - locale = new Locale(lang); - this.quality = quality; - } - - public Locale getLocale() { - return locale; - } - - public float getQuality() { - return quality; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.Service; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.ServiceFactory; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -/** - * Trivial implementation of an artifact database service. Useful to - * be subclassed. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultService -implements Service -{ - private static Logger logger = Logger.getLogger(DefaultService.class); - - public static class Output implements Service.Output { - - protected Object data; - protected String mimeType; - - public Output() { - } - - public Output(Object data, String mimeType) { - this.data = data; - this.mimeType = mimeType; - } - - @Override - public Object getData() { - return data; - } - - @Override - public String getMIMEType() { - return mimeType; - } - } // class Output - - @Override - public Service.Output process( - Document data, - GlobalContext globalContext, - CallMeta callMeta - ) { - logger.debug("Service.process"); - return new Output(new byte[0], "application/octet-stream"); - } - - @Override - public void setup(ServiceFactory factory, GlobalContext globalContext) { - logger.debug("Service.setup"); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultServiceFactory.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import de.intevation.artifacts.Service; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.ServiceFactory; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -/** - * Trivial implementation of the ServiceFactory interface. - * Name and an description are configured by the given Node given to - * #setup(Document, Node) via the 'name' and 'description' attributes. - * The name of the class that provides the concrete serice is configured - * by the 'service' attribute. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class DefaultServiceFactory -implements ServiceFactory -{ - private static Logger logger = - Logger.getLogger(DefaultServiceFactory.class); - - /** - * XPath to access the name of the service. - */ - public static final String XPATH_NAME = "@name"; - /** - * XPath to access the description of the service. - */ - public static final String XPATH_DESCRIPTION = "@description"; - /** - * XPath to access the class name of the service to be build by - * this factory. - */ - public static final String XPATH_SERVICE = "@service"; - - /** - * Default description if no description is given in configuration. - */ - public static final String DEFAULT_DESCRIPTION = - "No description available"; - - /** - * Loaded service class if no class name is given in the configuration. - */ - public static final String DEFAULT_SERVICE = - "de.intevation.artifactdatabase.DefaultService"; - - /** - * The name of the service factory. - */ - protected String name; - - /** - * The description of the service factory. - */ - protected String description; - - /** - * The loaded class used to build the concrete service. - */ - protected Class serviceClass; - - /** - * Default constructor. - */ - public DefaultServiceFactory() { - } - - @Override - public String getName() { - return name; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public Service createService(GlobalContext globalContext) { - try { - Service service = (Service)serviceClass.newInstance(); - - service.setup(this, globalContext); - - return service; - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - - return null; - } - - @Override - public void setup(Document config, Node factoryNode) { - - description = Config.getStringXPath( - factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); - - name = Config.getStringXPath( - factoryNode, XPATH_NAME, toString()); - - String service = Config.getStringXPath( - factoryNode, XPATH_SERVICE, DEFAULT_SERVICE); - - try { - serviceClass = Class.forName(service); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - - if (serviceClass == null) { - serviceClass = DefaultService.class; - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.User; - - -/** - * Trivial implementation of a user. Useful to be subclassed. - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultUser -implements User -{ - /** The identifier of the user.*/ - protected String identifier; - - /** The name of the user.*/ - protected String name; - - /** The account name of the user.*/ - protected String account; - - /** The role of the user.*/ - protected Document role; - - - /** - * The default constructor. - */ - public DefaultUser() { - } - - public DefaultUser(String identifier) { - this.identifier = identifier; - } - - /** - * A constructor that creates a new user. - * - * @param identifier The uuid of the user. - * @param name The name of the user. - * @param account The account name of the user. - * @param role The role of the user. - */ - public DefaultUser(String identifier, String name, String account, - Document role) { - this.identifier = identifier; - this.name = name; - this.role = role; - this.account = account; - } - - - /** - * Returns the identifier of this user. - * - * @return the identifier of this user. - */ - @Override - public String identifier() { - return identifier; - } - - - /** - * Returns the name of the user. - * - * @return the name of the user. - */ - @Override - public String getName() { - return name; - } - - - /** - * Set the name of the user. - * - * @param name The name for this user. - */ - @Override - public void setName(String name) { - this.name = name; - } - - - /** - * Set the identifier of the user. - * - * @param identifier The new identifier. - */ - @Override - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - - /** - * Set the role of the user. - * - * @param role The new role of the user. - */ - @Override - public void setRole(Document role) { - this.role = role; - } - - - /** - * Returns the role of the user. - * - * @return the role of the user. - */ - @Override - public Document getRole() { - return role; - } - - /** - * Returns the account of the user. - * - * @return the account name of the user. - */ - @Override - public String getAccount() { - return account; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import de.intevation.artifacts.User; -import de.intevation.artifacts.UserFactory; - - -/** - * Default implementation of a UserFactory. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultUserFactory -implements UserFactory -{ - /** The logger that is used in this factory.*/ - private static Logger logger = Logger.getLogger(DefaultUserFactory.class); - - - /** - * Default constructor. - */ - public DefaultUserFactory() { - } - - - public void setup(Document config, Node factoryNode) { - logger.debug("DefaultUserFactory.setup"); - } - - - /** - * This method creates a new DefaultUser with the given identifier, name and - * role. - * - * @param identifier The identifier for the new user. - * @param name The name for the new user. - * @param account The name of the new users account. - * @param role The role for the new user. - * @param context The CallContext. - */ - public User createUser( - String identifier, - String name, - String account, - Document role, - Object context) - { - logger.debug("DefaultUserFactory.createUser: " + name); - return new DefaultUser(identifier, name, account, role); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,733 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.common.utils.Config; - -import de.intevation.artifacts.ArtifactCollectionFactory; -import de.intevation.artifacts.ArtifactContextFactory; -import de.intevation.artifacts.ArtifactFactory; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.GlobalContext; -import de.intevation.artifacts.Hook; -import de.intevation.artifacts.ServiceFactory; -import de.intevation.artifacts.UserFactory; - -import de.intevation.artifacts.common.utils.StringUtils; - -import de.intevation.artifactdatabase.rest.HTTPServer; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -/** - * Bootstrap facility for the global context and the artifact factories. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class FactoryBootstrap -{ - private static Logger logger = Logger.getLogger(FactoryBootstrap.class); - - /** - * XPath to figure out the class name of the context factory from - * the global configuration. - */ - public static final String CONTEXT_FACTORY = - "/artifact-database/factories/context-factory/text()"; - - /** - * The name of the default context factory. - */ - public static final String DEFAULT_CONTEXT_FACTORY = - "de.intevation.artifactdatabase.DefaultArtifactContextFactory"; - - /** - * XPath to figure out the names of the artifact factories from - * the global configuration to be exposed by the artifact database. - */ - public static final String ARTIFACT_FACTORIES = - "/artifact-database/factories/artifact-factories/artifact-factory"; - - /** - * XPath to figure out the names of the service factories from - * the global configuration to build the services offered by the - * artifact database. - */ - public static final String SERVICE_FACTORIES = - "/artifact-database/factories/service-factories/service-factory"; - - /** - * XPath to figure out the class name of the user factory from global - * configuration. - */ - public static final String USER_FACTORY = - "/artifact-database/factories/user-factory"; - - /** - * The name of the default user factory. - */ - public static final String DEFAULT_USER_FACTORY = - "de.intevation.artifactdatabase.DefaultUserFactory"; - - /** - * XPath to figure out the class name of the collection factory from global - * configuration. - */ - public static final String COLLECTION_FACTORY = - "/artifact-database/factories/collection-factory"; - - /** - * The name of the default user factory. - */ - public static final String DEFAULT_COLLECTION_FACTORY = - "de.intevation.artifactdatabase.DefaultArtifactCollectionFactory"; - - /** - * XPath to figure out the secret used to sign the artifact exports - * made by the artfifact database server. - */ - public static final String EXPORT_SECRET = - "/artifact-database/export-secret/text()"; - - /** - * XPAth that points to a configuration node for a CallContext.Listener. - */ - public static final String CALLCONTEXT_LISTENER = - "/artifact-database/callcontext-listener"; - - /** - * XPath that points to configuration nodes for hooks. - */ - public static final String HOOKS = - "/artifact-database/hooks/hook"; - - public static final String HTTP_SERVER = - "/artifact-database/rest-server/http-server/text()"; - - public static final String DEFAULT_HTTP_SERVER = - "de.intevation.artifactdatabase.rest.Standalone"; - - public static final String LIFETIME_LISTENERS = - "/artifact-database/lifetime-listeners/listener"; - - public static final String BACKEND_LISTENERS = - "/artifact-database/backend-listeners/listener"; - - /** - * Default export signing secret. - * <strong>PLEASE CHANGE THE SECRET VIA THE XPATH EXPORT_SECRET - * IN THE CONFIGURATION.</strong>. - */ - public static final String DEFAULT_EXPORT_SECRET = - "!!!CHANGE ME! I'M NO SECRET!!!"; - - /** - * Reference to the global context build by the global context factory. - */ - protected GlobalContext context; - - /** - * List of the artifact factories to be exposed by the - * artifact database. - */ - protected ArtifactFactory [] artifactFactories; - - /** - * List of service factories which creates services that are - * exposed by the artifact database. - */ - protected ServiceFactory [] serviceFactories; - - /** - * The factory that is used to create and list users. - */ - protected UserFactory userFactory; - - /** - * The factory that is used to create new artifact collections. - */ - protected ArtifactCollectionFactory collectionFactory; - - /** - * The CallContext.Listener. - */ - protected CallContext.Listener callContextListener; - - protected List<Hook> postFeedHooks; - - protected List<Hook> postAdvanceHooks; - - protected List<Hook> postDescribeHooks; - - protected List<LifetimeListener> lifetimeListeners; - - protected List<BackendListener> backendListeners; - - /** - * byte array holding the export signing secret. - */ - protected byte [] exportSecret; - - protected HTTPServer httpServer; - - - /** - * Default constructor - */ - public FactoryBootstrap() { - } - - void buildContext() { - String className = Config.getStringXPath( - CONTEXT_FACTORY, DEFAULT_CONTEXT_FACTORY); - - ArtifactContextFactory factory = null; - - try { - Class clazz = Class.forName(className); - factory = (ArtifactContextFactory)clazz.newInstance(); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - - if (factory == null) { - factory = new DefaultArtifactContextFactory(); - } - - logger.info("Using class '" + factory.getClass().getName() - + "' for context creation."); - - context = factory.createArtifactContext(Config.getConfig()); - } - - - /** - * Scans the global configuration to load the configured collection factory - * and sets it up. - */ - protected void loadCollectionFactory() { - - logger.info("loading collection factory."); - - Node factory = Config.getNodeXPath(COLLECTION_FACTORY); - - String className = Config.getStringXPath( - factory, "text()", DEFAULT_COLLECTION_FACTORY); - - try { - Class clazz = Class.forName(className); - collectionFactory = (ArtifactCollectionFactory) clazz.newInstance(); - - collectionFactory.setup(Config.getConfig(), factory); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - /** - * Scans the global configuration to load the configured - * artifact factories and sets them up. - */ - protected void loadArtifactFactories() { - - logger.info("loading artifact factories"); - - ArrayList loadedFactories = new ArrayList(); - - NodeList nodes = Config.getNodeSetXPath(ARTIFACT_FACTORIES); - - if (nodes == null) { - logger.warn("No factories found"); - } - - Document config = Config.getConfig(); - - for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) { - String className = nodes.item(i).getTextContent().trim(); - - ArtifactFactory factory = null; - - try { - Class clazz = Class.forName(className); - factory = (ArtifactFactory)clazz.newInstance(); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - - if (factory != null) { - factory.setup(config, nodes.item(i)); - loadedFactories.add(factory); - logger.info("Registering '" - + factory.getName() + "' as artifact factory."); - } - } - - artifactFactories = (ArtifactFactory [])loadedFactories.toArray( - new ArtifactFactory[loadedFactories.size()]); - } - - /** - * Scans the global configuration for the configured service factories - * and sets them up. - */ - protected void loadServiceFactories() { - - logger.info("loading service factories"); - - ArrayList loadedFactories = new ArrayList(); - - NodeList nodes = Config.getNodeSetXPath(SERVICE_FACTORIES); - - if (nodes == null) { - logger.warn("No factories found"); - } - - Document config = Config.getConfig(); - - for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) { - String className = nodes.item(i).getTextContent().trim(); - - ServiceFactory factory = null; - - try { - Class clazz = Class.forName(className); - factory = (ServiceFactory)clazz.newInstance(); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - - if (factory != null) { - factory.setup(config, nodes.item(i)); - loadedFactories.add(factory); - logger.info( "Registering '" + factory.getName() - + "' as service factory."); - } - } - - serviceFactories = (ServiceFactory [])loadedFactories.toArray( - new ServiceFactory[loadedFactories.size()]); - } - - - /** - * Scans the global configuration for the configured user factory. - */ - protected void loadUserFactory() { - logger.info("loading user factory"); - - Node factory = Config.getNodeXPath(USER_FACTORY); - - String className = Config.getStringXPath( - factory, "text()", DEFAULT_USER_FACTORY); - - try { - Class clazz = Class.forName(className); - userFactory = (UserFactory) clazz.newInstance(); - - userFactory.setup(Config.getConfig(), factory); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - - protected void loadCallContextListener() { - logger.info("loading CallContext.Listener"); - - Node listener = Config.getNodeXPath(CALLCONTEXT_LISTENER); - - if (listener == null) { - return; - } - - String className = Config.getStringXPath(listener, "text()"); - - try { - Class clazz = Class.forName(className); - callContextListener = (CallContext.Listener) clazz.newInstance(); - - callContextListener.setup(Config.getConfig(), listener); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - protected void loadHTTPServer() { - logger.info("loading HTTPServer"); - - String className = Config.getStringXPath( - HTTP_SERVER, DEFAULT_HTTP_SERVER); - - logger.info("using HTTP server: " + className); - - try { - Class clazz = Class.forName(className); - httpServer = (HTTPServer)clazz.newInstance(); - - httpServer.setup(Config.getConfig()); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - protected void loadLifetimeListeners() { - logger.info("loading lifetime listeners"); - - NodeList nodes = Config.getNodeSetXPath(LIFETIME_LISTENERS); - - if (nodes == null) { - logger.debug("no lifetime listeners configure"); - return; - } - - List<LifetimeListener> ltls = new ArrayList<LifetimeListener>(); - - for (int i = 0, N = nodes.getLength(); i < N; ++i) { - Node node = nodes.item(i); - String className = node.getTextContent(); - if (className == null - || (className = className.trim()).length() == 0) { - continue; - } - try { - Class clazz = Class.forName(className); - LifetimeListener listener = - (LifetimeListener)clazz.newInstance(); - - listener.setup(Config.getConfig()); - - ltls.add(listener); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - lifetimeListeners = ltls; - } - - protected void loadBackendListeners() { - logger.info("loading backend listeners"); - - NodeList nodes = Config.getNodeSetXPath(BACKEND_LISTENERS); - - if (nodes == null) { - logger.debug("no backend listeners configure"); - return; - } - - List<BackendListener> bls = new ArrayList<BackendListener>(); - - for (int i = 0, N = nodes.getLength(); i < N; ++i) { - Node node = nodes.item(i); - String className = node.getTextContent(); - if (className == null - || (className = className.trim()).length() == 0) { - continue; - } - try { - Class clazz = Class.forName(className); - BackendListener listener = - (BackendListener)clazz.newInstance(); - - bls.add(listener); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - } - - backendListeners = bls; - } - - protected void loadHooks() { - logger.info("loading hooks"); - - postFeedHooks = new ArrayList<Hook>(); - postAdvanceHooks = new ArrayList<Hook>(); - postDescribeHooks = new ArrayList<Hook>(); - - NodeList nodes = Config.getNodeSetXPath(HOOKS); - - if (nodes == null) { - logger.info("No hooks found"); - return; - } - - for (int i = 0, len = nodes.getLength(); i < len; i++) { - Node cfg = nodes.item(i); - String applies = Config.getStringXPath(cfg, "@applies"); - - if (applies == null || applies.length() == 0) { - continue; - } - - Hook hook = loadHook(cfg); - String[] apply = applies.split(","); - - for (String a: apply) { - a = a.trim().toLowerCase(); - - if (a.equals("post-feed")) { - postFeedHooks.add(hook); - } - else if (a.equals("post-advance")) { - postAdvanceHooks.add(hook); - } - else if (a.equals("post-describe")) { - postDescribeHooks.add(hook); - } - } - } - } - - protected Hook loadHook(Node hookCfg) { - if (hookCfg == null) { - return null; - } - - Hook hook = null; - - String className = Config.getStringXPath(hookCfg, "@class"); - - try { - Class clazz = Class.forName(className); - hook = (Hook) clazz.newInstance(); - - hook.setup(hookCfg); - } - catch (ClassNotFoundException cnfe) { - logger.error(cnfe.getLocalizedMessage(), cnfe); - } - catch (InstantiationException ie) { - logger.error(ie.getLocalizedMessage(), ie); - } - catch (ClassCastException cce) { - logger.error(cce.getLocalizedMessage(), cce); - } - catch (IllegalAccessException iae) { - logger.error(iae.getLocalizedMessage(), iae); - } - - return hook; - } - - /** - * Fetches the export signing secret from the global configuration. - * If none is found if defaults to the DEFAULT_EXORT_SECRET which - * is insecure. - */ - protected void setupExportSecret() { - String secret = Config.getStringXPath(EXPORT_SECRET); - - if (secret == null) { - logger.warn("NO EXPORT SECRET SET! USING INSECURE DEFAULT!"); - secret = DEFAULT_EXPORT_SECRET; - } - - exportSecret = StringUtils.getUTF8Bytes(secret); - } - - /** - * Loads all the dynamic classes configured by the global configuration. - */ - public void boot() { - setupExportSecret(); - buildContext(); - loadCollectionFactory(); - loadArtifactFactories(); - loadServiceFactories(); - loadUserFactory(); - loadCallContextListener(); - loadHTTPServer(); - loadHooks(); - loadLifetimeListeners(); - loadBackendListeners(); - } - - /** - * Returns the artifact collection factory. - * - * @return the artifact collection factory. - */ - public ArtifactCollectionFactory getArtifactCollectionFactory() { - return collectionFactory; - } - - /** - * Returns the list of ready to use artifact factories. - * @return The list of artifact factories. - */ - public ArtifactFactory [] getArtifactFactories() { - return artifactFactories; - } - - /** - * Returns the ready to use service factories. - * @return The list of service factories. - */ - public ServiceFactory [] getServiceFactories() { - return serviceFactories; - } - - /** - * Returns the user factory. - * - * @return the user factory. - */ - public UserFactory getUserFactory() { - return userFactory; - } - - /** - * Returns the global context created by the global context factory. - * @return The global context. - */ - public GlobalContext getContext() { - return context; - } - - /** - * Returns the signing secret to be used when ex- and importing - * artifacts from and into the artifact database. - * @return the byte array containg the signing secret. - */ - public byte [] getExportSecret() { - return exportSecret; - } - - /** - * Returns a CallContext.Listener if configured or null. - * - * @return a CallContext.Listener. - */ - public CallContext.Listener getCallContextListener() { - return callContextListener; - } - - public List<Hook> getPostFeedHooks() { - return postFeedHooks; - } - - public List<Hook> getPostAdvanceHooks() { - return postAdvanceHooks; - } - - public List<Hook> getPostDescribeHooks() { - return postDescribeHooks; - } - - public HTTPServer getHTTPServer() { - return httpServer; - } - - public List<LifetimeListener> getLifetimeListeners() { - return lifetimeListeners; - } - - public List<BackendListener> getBackendListeners() { - return backendListeners; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.User; -import de.intevation.artifacts.UserFactory; - -import org.w3c.dom.Document; - -public class LazyBackendUser -implements User -{ - protected UserFactory factory; - protected Backend backend; - protected String identifier; - protected User user; - protected Object context; - - public LazyBackendUser( - String identifier, - UserFactory factory, - Backend backend, - Object context - ) { - this.identifier = identifier; - this.factory = factory; - this.backend = backend; - this.context = context; - } - - protected User getUser() { - if (user == null) { - user = backend.getUser(identifier, factory, context); - if (user == null) { - throw new IllegalStateException("loading user failed"); - } - } - return user; - } - - @Override - public String identifier() { - return getUser().identifier(); - } - - @Override - public String getName() { - return getUser().getName(); - } - - @Override - public void setName(String name) { - getUser().setName(name); - } - - @Override - public void setIdentifier(String identifier) { - getUser().setIdentifier(identifier); - } - - @Override - public Document getRole() { - return getUser().getRole(); - } - - @Override - public void setRole(Document document) { - getUser().setRole(document); - } - - @Override - public String getAccount() { - return getUser().getAccount(); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/LifetimeListener.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.GlobalContext; - -import org.w3c.dom.Document; - -public interface LifetimeListener -{ - void setup(Document document); - - void systemUp(GlobalContext globalContext); - - void systemDown(GlobalContext globalContext); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase; - -import java.util.List; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import de.intevation.artifacts.ArtifactNamespaceContext; - -import de.intevation.artifacts.common.utils.XMLUtils; -import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; - -import de.intevation.artifactdatabase.state.Facet; -import de.intevation.artifactdatabase.state.Output; -import de.intevation.artifactdatabase.state.State; - - -/** - * This class provides methods that help creating the artifact protocol - * documents describe, feed, advance and out. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class ProtocolUtils { - - /** - * It should not be necessary to create instances of this class. - */ - private ProtocolUtils() {} - - - /** - * This method creates a node that might be used for the artifact protocol. - * - * @param creator The ElementCreator that is used to create the node. - * @param nodeName The node name. - * @param attrName The names of optional attributes. - * @param value The values for the optional attributes. - * - * @return the created node. - */ - public static Element createArtNode( - XMLUtils.ElementCreator creator, - String nodeName, String[] attrName, String[] value) - { - Element typeNode = creator.create(nodeName); - - if (attrName != null && value != null) { - for (int i = 0; i < attrName.length; i++) { - if (i < value.length) { - creator.addAttr(typeNode, attrName[i], value[i], true); - } - else { - break; - } - } - } - - return typeNode; - } - - - /** - * This method creates the root node for all artifact protocol documents. - * - * @param creator The ElementCreator used to create new elements. - * - * @return the root node for the artifact protocol document. - */ - public static Element createRootNode(XMLUtils.ElementCreator creator) { - return createArtNode(creator, "result", null, null); - } - - - /** - * This method appends the three necessary nodes <i>type</i>, <i>uuid</i> - * and <i>hash</i> of the describe document to <i>root</i> node. - * - * @param creator The ElementCreator that is used to create new nodes. - * @param root The root node of the describe document. - * @param uuid The UUID of the artifact. - * @param hash The hash if the artifact. - */ - public static void appendDescribeHeader( - XMLUtils.ElementCreator creator, Element root, String uuid, String hash) - { - root.appendChild(createArtNode( - creator, - "type", - new String[] {"name"}, - new String[] {"describe"})); - - root.appendChild(createArtNode( - creator, - "uuid", - new String[] {"value"}, - new String[] {uuid})); - - root.appendChild(createArtNode( - creator, - "hash", - new String[] {"value"}, - new String[] {hash})); - } - - - /** - * This method appends a node that describes the current state to - * <i>root</i>. - * - * @param creator The ElementCreator used to create new elements. - * @param root The parent node for new elements. - * @param state The state to be appended. - */ - public static void appendState( - XMLUtils.ElementCreator creator, Element root, State state) - { - root.appendChild(createArtNode( - creator, "state", - new String[] { "description", "name" }, - new String[] { state.getDescription(), state.getID() })); - } - - - /** - * This method appends a node with reachable states to <i>root</i>. - * - * @param creator The ElementCreator used to create new elements. - * @param root The parent node for new elements. - * @param states The reachable states to be appended. - */ - public static void appendReachableStates( - XMLUtils.ElementCreator creator, - Element root, - List<State> states) - { - Element reachable = createArtNode( - creator, "reachable-states", null, null); - - for (State s: states) { - appendState(creator, reachable, s); - } - - root.appendChild(reachable); - } - - - /** - * This method appends a node for each Output in the <i>outputs</i> list to - * <i>out</i>. Note: an output node includes its provided facets! - * - * @param doc The document to which to add new elements. - * @param out The parent node for new elements. - * @param outputs The list of reachable outputs. - */ - public static void appendOutputModes( - Document doc, - Element out, - List<Output> outputs) - { - ElementCreator creator = new ElementCreator( - doc, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - for (Output o: outputs) { - Element newOut = createArtNode( - creator, - "output", - new String[] {"name", "description", "mime-type", "type"}, - new String[] { - o.getName(), - o.getDescription(), - o.getMimeType(), - o.getType() }); - - Element facets = createArtNode(creator, "facets", null, null); - appendFacets(doc, facets, o.getFacets()); - - newOut.appendChild(facets); - out.appendChild(newOut); - } - } - - - /** - * This method appends a node for each Facet in the <i>facets</i> list to - * <i>facet</i>. - * - * @param doc The document to wich to add new elements. - * @param facet The root node for new elements. - * @param facets The list of facets. - */ - public static void appendFacets( - Document doc, - Element facet, - List<Facet> facets) - { - if (facets == null || facets.size() == 0) { - return; - } - - ElementCreator creator = new ElementCreator( - doc, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - for (Facet f: facets) { - Node node = f.toXML(doc); - - if (node != null) { - facet.appendChild(node); - } - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ProxyArtifact.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,186 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.CallContext; - -import java.io.IOException; -import java.io.OutputStream; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -/** - * The proxy artifact is a wrapper around another artifact. It simply forwards - * the interface calls to this underlaying artifact. - * The reason for using proxy artifacts is enable the workflow to exchange - * artifacts at any time by something else without losing the concrete - * artifact. From the outside it always looks like there is only one - * distinct artifact.<br> - * - * An inner artifact is able to replace itself by indirectly hand over - * the replacement via the call context to the proxy artifact.<br> - * To do so the proxied artifact has to call - * <code>callContext.getContextValue(EPLACE_PROXY, replacement);</code>. - * After the current call (describe, feed, advance and out) of the proxied - * artifact is finished the proxy artifact replaces the former proxied artifact - * with the replacement. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class ProxyArtifact -extends DefaultArtifact -{ - /** - * Key to signal that the proxied artifact should be replaced. - */ - public static final Object REPLACE_PROXY = new Object(); - - private static Logger logger = Logger.getLogger(ProxyArtifact.class); - - /** - * The proxied artifact. - */ - protected Artifact proxied; - - /** - * Default constructor. - */ - public ProxyArtifact() { - } - - /** - * Constructor to create a new proxy artifact around a given artifact. - * @param proxied The artifact to be proxied. - */ - public ProxyArtifact(Artifact proxied) { - this.proxied = proxied; - } - - /** - * The currently proxied artifact. - * @return The proxied artifact. - */ - public Artifact getProxied() { - return proxied; - } - - /** - * Explicitly set the proxied artifacts. - * @param proxied - */ - public void setProxied(Artifact proxied) { - this.proxied = proxied; - } - - @Override - public void setIdentifier(String identifier) { - this.identifier = identifier; - - if (proxied != null) - proxied.setIdentifier(identifier); - } - - /** - * Method to check if the current proxied artifact should be replaced - * by a new one coming from the call context. - * @param callContext - */ - protected void checkReplacement(CallContext callContext) { - Object replacement = callContext.getContextValue(REPLACE_PROXY); - if (replacement instanceof Artifact) { - setProxied((Artifact)replacement); - } - } - - @Override - public String hash() { - return proxied != null - ? proxied.hash() - : super.hash(); - } - - @Override - public Document describe(Document data, CallContext context) { - try { - return proxied != null - ? proxied.describe(data, context) - : super.describe(data, context); - } - finally { - checkReplacement(context); - } - } - - @Override - public Document advance(Document target, CallContext context) { - try { - return proxied != null - ? proxied.advance(target, context) - : super.advance(target, context); - } - finally { - checkReplacement(context); - } - } - - @Override - public Document feed(Document target, CallContext context) { - try { - return proxied != null - ? proxied.feed(target, context) - : super.feed(target, context); - } - finally { - checkReplacement(context); - } - } - - @Override - public void out( - Document format, - OutputStream out, - CallContext context - ) - throws IOException - { - try { - if (proxied != null) { - proxied.out(format, out, context); - } - else { - super.out(format, out, context); - } - } - finally { - checkReplacement(context); - } - } - - @Override - public void endOfLife(Object context) { - if (proxied != null) { - proxied.endOfLife(context); - } - else { - super.endOfLife(context); - } - } - - @Override - public void cleanup(Object context) { - if (proxied != null) - proxied.cleanup(context); - else - super.cleanup(context); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/XMLService.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase; - -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.Service; -import de.intevation.artifacts.GlobalContext; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; - -/** - * Trivial implementation of an artifact database service. Useful to - * be subclassed. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class XMLService -extends DefaultService -{ - private static Logger logger = Logger.getLogger(XMLService.class); - - @Override - public Service.Output process( - Document data, - GlobalContext globalContext, - CallMeta callMeta - ) { - return new Output( - processXML(data, globalContext, callMeta), - "application/xml"); - } - - public Document processXML( - Document data, - GlobalContext globalContext, - CallMeta callMeta - ) { - return XMLUtils.newDocument(); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.data; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultStateData implements StateData { - - /** The name of the data. */ - protected String name; - - /** The description of the data. */ - protected String description; - - /** The type of the data. */ - protected String type; - - /** The value. */ - protected Object value; - - public DefaultStateData() { - } - - /** - * The default constructor. It creates empty StateData objects with no - * value. - * - * @param name The name. - * @param description The description. - * @param type The type. - */ - public DefaultStateData(String name, String description, String type) { - this.name = name; - this.description = description; - this.type = type; - } - - public void set(StateData other) { - name = other.getName(); - description = other.getDescription(); - type = other.getType(); - value = other.getValue(); - } - - - /** - * A constructor that takes the name of the data, its value and the - * describing parameters description and type. - * - * @param name The name of the data item. - * @param description The description. - * @param type The type. - * @param value The value of the data item. - */ - public DefaultStateData( - String name, - String description, - String type, - String value) - { - this.name = name; - this.description = description; - this.type = type; - this.value = value; - } - - - /** - * Returns the name of the data object. - * - * @return the name. - */ - public String getName() { - return name; - } - - - /** - * Returns the description of the data object. - * - * @return the description of the data object. - */ - public String getDescription() { - return description; - } - - - /** - * Returns the type of the data object as string. - * - * @return the type as string. - */ - public String getType() { - return type; - } - - - /** - * Returns the value of the data object. - * - * @return the value. - */ - public Object getValue() { - return value; - } - - - /** - * Set the value of this data object. - * - * @param value The new value for this data object. - */ - public void setValue(Object value) { - this.value = value; - } - - @Override - public StateData deepCopy() { - DefaultStateData copy = new DefaultStateData(); - copy.set(this); - return copy; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/data/StateData.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.data; - -import java.io.Serializable; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface StateData extends Serializable { - - /** - * Returns the name of the data object. - * - * @return the name. - */ - public String getName(); - - - /** - * Returns the description of the data object. - * - * @return the description of the data object. - */ - public String getDescription(); - - - /** - * Returns the type of the data object as string. - * - * @return the type as string. - */ - public String getType(); - - - /** - * Returns the value of the data object. - * - * @return the value. - */ - public Object getValue(); - - - /** - * Set the value of this data object. - * - * @param value The new value for this data object. - */ - public void setValue(Object value); - - public StateData deepCopy(); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -package de.intevation.artifactdatabase.db; - -import javax.sql.DataSource; - -import java.io.File; - -import org.apache.commons.pool.ObjectPool; - -import org.apache.commons.pool.impl.GenericObjectPool; - -import org.apache.commons.dbcp.DriverManagerConnectionFactory; -import org.apache.commons.dbcp.PoolableConnectionFactory; -import org.apache.commons.dbcp.PoolingDataSource; - -import de.intevation.artifacts.common.utils.Config; - -import org.apache.log4j.Logger; - -public class DBConnection -{ - private static Logger log = Logger.getLogger(DBConnection.class); - - public static final String DEFAULT_DRIVER = "org.h2.Driver"; - public static final String DEFAULT_USER = ""; - public static final String DEFAULT_PASSWORD = ""; - public static final String DEFAULT_DATABASE_FILE = "artifacts.db"; - public static final String DEFAULT_URL = getDefaultURL(); - - public static final String getDefaultURL() { - File configDir = Config.getConfigDirectory(); - File databaseFile = new File(configDir, DEFAULT_DATABASE_FILE); - return "jdbc:h2:" + databaseFile; - } - - protected DataSource dataSource; - - protected String driver; - protected String url; - protected String user; - protected String password; - - public DBConnection() { - } - - public DBConnection( - String driver, - String url, - String user, - String password - ) { - this.driver = driver; - this.url = url; - this.user = user; - this.password = password; - } - - public String getUser() { - return user; - } - - public void setUser(String user) { - this.user = user; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getDriver() { - return driver; - } - - public void setDriver(String driver) { - this.driver = driver; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public synchronized DataSource getDataSource() { - if (dataSource == null) { - if (log.isDebugEnabled()) { - log.debug("create new datasource:"); - log.debug(" driver: " + driver); - log.debug(" url : " + url); - log.debug(" user : " + user); - } - - try { - synchronized (DBConnection.class) { - Class.forName(driver); - } - } - catch (ClassNotFoundException cnfe) { - log.error("cannot load driver", cnfe); - return null; - } - - DriverManagerConnectionFactory dmcf = - new DriverManagerConnectionFactory(url, user, password); - - ObjectPool cp = new GenericObjectPool(); - - PoolableConnectionFactory pcf = new PoolableConnectionFactory( - dmcf, cp, null, null, false, false); - - dataSource = new PoolingDataSource(cp); - } - return dataSource; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -package de.intevation.artifactdatabase.db; - -import java.util.Properties; - -import java.io.IOException; -import java.io.InputStream; - -import org.apache.log4j.Logger; - -public class SQL { - - private static Logger logger = Logger.getLogger(SQL.class); - - protected Properties statements; - - public SQL() { - } - - public SQL(String driver) { - this(SQL.class, driver); - } - - public SQL(Class clazz, String driver) { - this(clazz, "/sql", driver); - } - - public SQL(Class clazz, String resourcePath, String driver) { - statements = loadStatements(clazz, resourcePath, driver); - } - - public static final String driverToProperties(String driver) { - return driver.replace('.', '-').toLowerCase() + ".properties"; - } - - /** - * Returns key/value pairs of SQL statements for the used database - * backend. - * The concrete set of SQL statements is determined by the - * used JDBC database driver which is configured in conf.xml. - * The class name of the driver is transformed by replacing - * all '.' with '_' and lower case the resulting string. - * The transformed string is used to load a properties file - * in '/sql/' which should contain the statements. - * Example:<br> - * <code>org.postgresql.Driver</code> results in loading of - * <code>/sql/org-postgresql-driver.properties</code>. - * @return The key/value pairs of SQL statements. - */ - protected Properties loadStatements( - Class clazz, - String resourcePath, - String driver - ) { - logger.debug("loadStatements"); - - Properties properties = new Properties(); - - String resDriver = driverToProperties(driver); - - InputStream in = null; - try { - String res = resourcePath + "/" + resDriver; - - in = clazz.getResourceAsStream(res); - - if (in == null) { - logger.warn("No SQL file for driver '" + driver + "' found."); - resDriver = driverToProperties(DBConnection.DEFAULT_DRIVER); - res = resourcePath + "/" + resDriver; - - in = clazz.getResourceAsStream(res); - if (in == null) { - logger.error("No SQL file for driver '" + - DBConnection.DEFAULT_DRIVER + "' found."); - } - } - else { - if (logger.isDebugEnabled()) { - logger.debug("found resource: " + res); - } - } - - if (in != null) { - properties.load(in); - } - } - catch (IOException ioe) { - logger.error(ioe); - } - - return properties; - } - - public String get(String key) { - boolean debug = logger.isDebugEnabled(); - if (debug) { - logger.debug("looking for SQL " + key); - logger.debug("statements != null: " + (statements != null)); - } - - String sql = statements.getProperty(key); - - if (sql == null) { - logger.error("cannot find SQL for key '" + key + "'"); - } - - if (debug) { - logger.debug("-> '" + sql + "'"); - } - - return sql; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQLExecutor.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -package de.intevation.artifactdatabase.db; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; - -import javax.sql.DataSource; - -import org.apache.log4j.Logger; - -public class SQLExecutor -{ - private static Logger logger = Logger.getLogger(SQLExecutor.class); - - public class Instance { - - public Connection conn; - public PreparedStatement stmnt; - public ResultSet result; - - public Instance() { - } - - public void reset() throws SQLException { - if (result != null) { - result.close(); - result = null; - } - if (stmnt != null) { - result = null; - stmnt.close(); - } - } - - public PreparedStatement prepareStatement(String query) - throws SQLException { - return stmnt = conn.prepareStatement(query); - } - - public void close() { - if (result != null) { - try { result.close(); } - catch (SQLException sqle) {} - } - if (stmnt != null) { - try { stmnt.close(); } - catch (SQLException sqle) {} - } - if (conn != null) { - try { conn.close(); } - catch (SQLException sqle) {} - } - } - - public boolean runWrite() { - DataSource dataSource = dbConnection.getDataSource(); - try { - conn = dataSource.getConnection(); - try { - conn.setAutoCommit(false); - return doIt(); - } - catch (SQLException sqle) { - conn.rollback(); - throw sqle; - } - } - catch (SQLException sqle) { - logger.error(sqle.getLocalizedMessage(), sqle); - } - finally { - close(); - } - return false; - } - - public boolean runRead() { - DataSource dataSource = dbConnection.getDataSource(); - try { - conn = dataSource.getConnection(); - return doIt(); - } - catch (SQLException sqle) { - logger.error(sqle.getLocalizedMessage(), sqle); - } - finally { - close(); - } - return false; - } - - public boolean doIt() throws SQLException { - return true; - } - } // class Instance - - protected DBConnection dbConnection; - - public SQLExecutor() { - } - - public SQLExecutor(DBConnection dbConnection) { - this.dbConnection = dbConnection; - } - - public DBConnection getDBConnection() { - return dbConnection; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -package de.intevation.artifactdatabase.h2; - -import org.h2.api.Trigger; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.PreparedStatement; - -import de.intevation.artifactdatabase.DBConfig; - -import de.intevation.artifactdatabase.db.SQL; - -import org.apache.log4j.Logger; - -public class CollectionAccessUpdateTrigger -implements Trigger -{ - private static Logger logger = - Logger.getLogger(CollectionAccessUpdateTrigger.class); - - public String COLLECTIONS_TOUCH_TRIGGER_FUNCTION; - - public void init( - Connection conn, - String schemaName, - String triggerName, - String tableName, - boolean before, - int type - ) - throws SQLException { - logger.debug("CollectionAccessUpdateTrigger.init"); - setupSQL(DBConfig.getInstance().getSQL()); - } - - protected void setupSQL(SQL sql) { - COLLECTIONS_TOUCH_TRIGGER_FUNCTION = - sql.get("collections.touch.trigger.function"); - } - - public void fire( - Connection conn, - Object [] oldRow, - Object [] newRow - ) - throws SQLException { - logger.debug("CollectionAccessUpdateTrigger.fire"); - PreparedStatement stmnt = conn.prepareStatement( - COLLECTIONS_TOUCH_TRIGGER_FUNCTION); - stmnt.setObject(1, newRow[0]); - stmnt.execute(); - stmnt.close(); - } - - public void close() throws SQLException { - logger.debug("CollectionAccessUpdateTrigger.close"); - } - - public void remove() throws SQLException { - logger.debug("CollectionAccessUpdateTrigger.remove"); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/package.html Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> -<html> -<head> -</head> -<body> -The reference implementation of an artifact database. It starts -an HTTP server and publishes the interface of the artifact database -via REST. -</body> -</html>
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2010, 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.CallMeta; - -import org.apache.log4j.Logger; - -import org.restlet.Request; - -import org.w3c.dom.Document; - -/** - * Resource to serve the out()-outputs of artifacts. - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class ArtifactOutResource -extends BaseOutResource -{ - /** - * server URL where to find the resource. - */ - public static final String PATH = "/artifact/{uuid}/{type}"; - - private static Logger logger = Logger.getLogger(ArtifactOutResource.class); - - - /** - * Returns the identifier of the collection. - * - * @return the identifier of the collection. - */ - protected String getIdentifier() { - Request request = getRequest(); - - return (String) request.getAttributes().get("uuid"); - } - - - protected String getType() { - Request request = getRequest(); - - return (String) request.getAttributes().get("type"); - } - - - /** - * Call the ArtifactDatabase.out method. - */ - protected ArtifactDatabase.DeferredOutput doOut( - String identifier, - String type, - Document input, - ArtifactDatabase db, - CallMeta meta) - throws ArtifactDatabaseException - { - logger.debug("ArtifactOutResource.doOut"); - - return db.out(identifier, type, input, meta); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.ArtifactNamespaceContext; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Request; -import org.restlet.Response; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; - -/** - * Resource to expose the core artifact methods - * (describe, feed and advance) via REST. - * - * <ul> - * <li>describe() is modelled via GET.</li> - * <li>advance() and feed() are modelled via POST.</li> - * </ul> - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class ArtifactResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(ArtifactResource.class); - - /** - * XPath to figure out the type of action (feed, advance) via the - * incoming POST request. - */ - public static final String XPATH_ACTION = "/art:action/art:type/@name"; - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/artifact/{uuid}"; - - /** - * Error message if no action was given. - */ - public static final String NO_ACTION_MESSAGE = "no action given"; - - /** - * Error message if a unknown action was given. - */ - public static final String NO_SUCH_ACTION_MESSAGE = "no such action"; - - /** - * Error message if the requested artifact was not found in - * the artifact database. - */ - public static final String NO_ARTIFACT_FOUND = "Artifact not found"; - - /** - * Action name 'advance'. - */ - public static final String ADVANCE = "advance"; - /** - * Action name 'feed'. - */ - public static final String FEED = "feed"; - /** - * Action name 'describe'. - */ - public static final String DESCRIBE = "describe"; - - @Override - protected Representation innerGet() - throws ResourceException - { - Request request = getRequest(); - - String identifier = (String)request.getAttributes().get("uuid"); - - if (logger.isDebugEnabled()) { - logger.debug("looking for artifact id '" + identifier + "'"); - } - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.describe(identifier, null, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } - - /** - * Method to figure out which POST action (feed or advance) was - * triggered and perform this operation on the artifact specified - * by 'identifier' and found in the artifact database 'db'. - * - * @param identifier The identifier of the artifact. - * @param action The action to be performed. - * @param source The input document to further parameterize the - * operation. - * @param db The artifact database where to find the artifact. - * @return The representation produced by the performed action. - */ - protected Representation dispatch( - String identifier, - String action, - Document source, - ArtifactDatabase db - ) { - Document out = null; - - try { - if (action.equals(FEED)) { - out = db.feed(identifier, source, getCallMeta()); - } - else if (action.equals(ADVANCE)) { - out = db.advance(identifier, source, getCallMeta()); - } - else if (action.equals(DESCRIBE)) { - out = db.describe(identifier, source, getCallMeta()); - } - else { - throw new ArtifactDatabaseException(NO_SUCH_ACTION_MESSAGE); - } - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); - return new EmptyRepresentation(); - } - - return new DomRepresentation(MediaType.APPLICATION_XML, out); - } - - @Override - protected Representation innerPost(Representation requestRepr) { - - Document inputDocument = null; - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - String action = XMLUtils.xpathString( - inputDocument, - XPATH_ACTION, - ArtifactNamespaceContext.INSTANCE); - - if (action == null || action.length() == 0) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MESSAGE); - return new EmptyRepresentation(); - } - - Request request = getRequest(); - - String identifier = (String)request.getAttributes().get("uuid"); - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - return dispatch(identifier, action, inputDocument, db); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseOutResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.ArtifactNamespaceContext; -import de.intevation.artifacts.CallMeta; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Request; -import org.restlet.Response; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; - - -/** - * Base Resource to serve the out()-outputs of collections and artifacts. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public abstract class BaseOutResource -extends BaseResource -{ - /** The logger used in this class.*/ - private static Logger logger = Logger.getLogger(BaseOutResource.class); - - /** XPath to figure out the MIME type of the requested result.*/ - public static final String XPATH_MIME_TYPE = - "/art:action/art:out/art:mime-type/@value"; - - /** Default result MIME type: octet stream.*/ - public static final MediaType DEFAULT_MIME_TYPE = - MediaType.APPLICATION_OCTET_STREAM; - - - @Override - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - Document inputDocument = null; - - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - ArtifactDatabase db = getArtifactDatabase(); - - Request request = getRequest(); - - String identifier = getIdentifier(); - String outType = getType(); - - if (logger.isDebugEnabled()) { - logger.debug("looking for artifact id '" + identifier + "'"); - } - - String mimeTypeString = XMLUtils.xpathString( - inputDocument, - XPATH_MIME_TYPE, - ArtifactNamespaceContext.INSTANCE); - - MediaType mimeType = DEFAULT_MIME_TYPE; - - if (mimeTypeString != null && mimeTypeString.length() != 0) { - try { - mimeType = MediaType.valueOf(mimeTypeString); - } - catch (Exception e) { - logger.error(e.getLocalizedMessage()); - } - } - - try { - return new OutRepresentation( - mimeType, - doOut(identifier, outType, inputDocument, db, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } - - /** - * Returns the identifier of the artifact or collection. - * - * @return the identifier. - */ - protected abstract String getIdentifier(); - - - /** - * Returns the concrete output type of the artifact or collection. - * - * @return the output type. - */ - protected abstract String getType(); - - /** - * This method is called to process the operation on artifacts or - * collections. - * - * @param identifier The identifier of the artifact or collection. - * @param type The output type. - * @param input The input document of the request. - * @param db The artifact database. - * @param meta The CallMeta object. - * - * @return the result of the operation. - */ - protected abstract ArtifactDatabase.DeferredOutput doOut( - String identifier, - String type, - Document input, - ArtifactDatabase db, - CallMeta meta) - throws ArtifactDatabaseException; -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifactdatabase.DefaultCallMeta; -import de.intevation.artifactdatabase.DefaultPreferredLocale; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.CallMeta; -import de.intevation.artifacts.PreferredLocale; - -import java.util.List; - -import org.apache.log4j.Logger; - -import org.restlet.data.ClientInfo; -import org.restlet.data.Language; -import org.restlet.data.Preference; - -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; -import org.restlet.resource.ServerResource; - -/** - * Base class for the resources of REST interface of the artifact database. - * Primarily used to unify the logging. - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class BaseResource -extends ServerResource -{ - private static Logger logger = Logger.getLogger(BaseResource.class); - - /** - * Default constructor. - */ - public BaseResource() { - } - - /** - * Overrides the post method of ServerResource to handle some - * exceptions and to the required logging. - * The call bridges to #innerPost(Representation) which - * should be overwitten by the subclasses to do the real - * request processing. - * @param requestRepr The incoming represention of the HTTP request. - * @return The representation produced by #innerPost(Representation). - * @throws ResourceException Thrown if something went wrong during - * request processing. - */ - @Override - protected Representation post(Representation requestRepr) - throws ResourceException - { - try { - return innerPost(requestRepr); - } - catch (ResourceException re) { - throw re; - } - catch (RuntimeException re) { - logger.error(re.getLocalizedMessage(), re); - throw re; - } - } - - /** - * Trivial implementation of innerPost() which is called by - * #post(Representation) which simply calls super.post(Representation). - * This should be overwritten by subclasses which need POST support. - * @param requestRepr The incoming representation of the request. - * @return The representation produced by super.post(Representation). - * @throws ResourceException Thrown if something went wrong during - * request processing. - */ - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - return super.post(requestRepr); - } - - /** - * Wrapper around get() of the super class to handle some exceptions - * and do the corresponing logging. The call is bridged to #innerGet() - * which should be overwritten by subclasses. - * @return The representation produced by #innerGet() - * @throws ResourceException Thrown if something went wrong during - * request processing. - */ - @Override - protected Representation get() - throws ResourceException - { - try { - return innerGet(); - } - catch (ResourceException re) { - throw re; - } - catch (RuntimeException re) { - logger.error(re.getLocalizedMessage(), re); - throw re; - } - } - - /** - * Trivial implementaion of innerGet() which simply calls - * super.get() to produce some output representation. This method - * should be overwritten by subclasses which need GET support. - * @return The representation produced by super.get(). - * @throws ResourceException Thrown if something went wrong during - * request processing. - */ - protected Representation innerGet() - throws ResourceException - { - return super.get(); - } - - /** - * Returns meta information (preferred languages et. al.) - * of the current HTTP request. - * @return the meta information - */ - protected CallMeta getCallMeta() { - ClientInfo clientInfo = getClientInfo(); - - List<Preference<Language>> pl = clientInfo.getAcceptedLanguages(); - - PreferredLocale [] languages = new PreferredLocale[pl.size()]; - - int index = 0; - - for (Preference<Language> p: pl) { - String lang = p.getMetadata().getName(); - float quality = p.getQuality(); - languages[index++] = new DefaultPreferredLocale(lang, quality); - } - - return new DefaultCallMeta(languages); - } - - - /** - * Returns the artifact database stored in the context of the REST - * application. - * - * @return the artifact database. - */ - protected ArtifactDatabase getArtifactDatabase() { - return (ArtifactDatabase) getContext().getAttributes().get("database"); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ByteArrayRepresentation.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -package de.intevation.artifactdatabase.rest; - -import org.restlet.representation.Representation; - -import java.io.Reader; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.Writer; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.ByteArrayInputStream; - -import java.nio.ByteBuffer; - -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; - -import org.restlet.data.MediaType; - -public class ByteArrayRepresentation -extends Representation -{ - protected byte [] data; - - public ByteArrayRepresentation(MediaType mediaType, byte [] data) { - super(mediaType); - this.data = data; - } - - @Override - public long getSize() { - return data.length; - } - - @Override - public ReadableByteChannel getChannel() throws IOException { - return null; - } - - @Override - public Reader getReader() throws IOException { - return new InputStreamReader(getStream()); - } - - @Override - public InputStream getStream() throws IOException { - return new ByteArrayInputStream(data); - } - - @Override - public void write(Writer writer) throws IOException { - writer.append(ByteBuffer.wrap(data).asCharBuffer()); - } - - @Override - public void write(WritableByteChannel writableChannel) throws IOException { - writableChannel.write(ByteBuffer.wrap(data)); - } - - @Override - public void write(OutputStream outputStream) throws IOException { - outputStream.write(data); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionOutResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.CallMeta; - -import org.apache.log4j.Logger; - -import org.restlet.Request; - -import org.w3c.dom.Document; - - -/** - * Resource to serve the out()-outputs of collections. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class CollectionOutResource -extends BaseOutResource -{ - /** The logger used in this class.*/ - private static Logger logger = Logger.getLogger(CollectionOutResource.class); - - /** server URL where to find the resource.*/ - public static final String PATH = "/collection/{uuid}/{type}"; - - - /** - * Returns the identifier of the collection. - * - * @return the identifier of the collection. - */ - protected String getIdentifier() { - Request request = getRequest(); - - return (String) request.getAttributes().get("uuid"); - } - - - protected String getType() { - Request request = getRequest(); - - return (String) request.getAttributes().get("type"); - } - - - /** - * Call the ArtifactDatabase.outCollection method. - */ - protected ArtifactDatabase.DeferredOutput doOut( - String identifier, - String type, - Document input, - ArtifactDatabase db, - CallMeta meta) - throws ArtifactDatabaseException - { - logger.debug("CollectionOutResource.doOut"); - - return db.outCollection(identifier, type, input, meta); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,276 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.CallMeta; - -import de.intevation.artifacts.common.ArtifactNamespaceContext; -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifactdatabase.ArtifactDatabaseImpl; - -import java.io.IOException; - -import javax.xml.xpath.XPathConstants; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.Request; -import org.restlet.Response; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class CollectionResource -extends BaseResource -{ - /** The logger that is used in this class.*/ - private static Logger logger = Logger.getLogger(CollectionResource.class); - - /** server URL where to reach the resource.*/ - public static final String PATH = "/collection/{uuid}"; - - /** - * XPath to figure out the type of action (feed, advance) via the - * incoming POST request. - */ - public static final String XPATH_ACTION = "/art:action/art:type/@name"; - - /** - * XPath to figure out the identifier of the artifact described in the - * action. - */ - public static final String XPATH_ARTIFACT = - "/art:action/art:type/art:artifact/@uuid"; - - /** Error message if no action was given.*/ - public static final String NO_ACTION_MSG = "no action given"; - - /** Error message if a unknown action was given.*/ - public static final String NO_SUCH_ACTION_MSG = "no such action"; - - /** Action name for deleting a collection.*/ - public static final String ACTION_DELETE = "delete"; - - /** Action name for describing the collection.*/ - public static final String ACTION_DESCRIBE = "describe"; - - /** Action name for retrieving the attribute of a collection.*/ - public static final String ACTION_GET_ATTRIBUTE = "getattribute"; - - /** Action name for retrieving the attributes of an artifact stored in the - * collection.*/ - public static final String ACTION_GET_ITEM_ATTRIBUTE = "getitemattribute"; - - /** Action name for setting the attribute of a collection.*/ - public static final String ACTION_SET_ATTRIBUTE = "setattribute"; - - /** Action name for setting the attribute for an artifact stored in the - * collection.*/ - public static final String ACTION_SET_ITEM_ATTRIBUTE = "setitemattribute"; - - /** Action name for adding a new artifact to the collection.*/ - public static final String ACTION_ADD_ARTIFACT = "addartifact"; - - /** Action name for removing an artifact from the collection.*/ - public static final String ACTION_REMOVE_ARTIFACT = "removeartifact"; - - /** Action name for listing the artifacts of the collection.*/ - public static final String ACTION_LIST_ARTIFACTS = "listartifacts"; - - /** Action name for setting the ttl of a collection.*/ - public static final String ACTION_SET_TTL = "settimetolive"; - - /** Action name for setting the name of a collection.*/ - public static final String ACTION_SET_NAME = "setname"; - - - /** - * Method to figure out which POST action was triggered and perform this - * operation on the collection specified by 'identifier' and found in the - * artifact database 'db'. - * - * @param identifier The identifier of the collection. - * @param action The action to be performed. - * @param source The input document to further parameterize the operation. - * @param db The artifact database where to find the collection. - * - * @return The representation produced by the performed action. - */ - protected Representation dispatch( - String identifier, - String action, - Document source, - ArtifactDatabase db - ) { - Document out = null; - - try { - CallMeta meta = getCallMeta(); - - if (action.equals(ACTION_DELETE)) { - logger.info("Delete collection '" + identifier + "'"); - out = db.deleteCollection(identifier, getCallMeta()); - } - else if (action.equals(ACTION_DESCRIBE)) { - logger.info("Describe collection '" + identifier + "'"); - - out = db.describeCollection(identifier, meta); - } - else if (action.equals(ACTION_ADD_ARTIFACT)) { - String art = getArtifactIdentifier(source); - - logger.info("Add artifact '" + art + "' to collection."); - out = db.addCollectionArtifact(identifier, art, source, meta); - } - else if (action.equals(ACTION_REMOVE_ARTIFACT)) { - String art = getArtifactIdentifier(source); - - logger.info("Remove artifact '" + art + "' from collection."); - out = db.removeCollectionArtifact(identifier, art, meta); - } - else if (action.equals(ACTION_LIST_ARTIFACTS)) { - logger.info("List artifacts of collection '" + identifier +"'"); - out = db.listCollectionArtifacts(identifier, meta); - } - else if (action.equals(ACTION_SET_ATTRIBUTE)) { - String art = getArtifactIdentifier(source); - - logger.info("Set attribute for collection '" + identifier + "'"); - - Document attr = getCollectionAttribute(source); - - out = db.setCollectionAttribute(identifier, meta, attr); - } - else if (action.equals(ACTION_SET_ITEM_ATTRIBUTE)) { - String art = getArtifactIdentifier(source); - - logger.info("Set attribute for artifact '" + art + "'"); - out = db.setCollectionItemAttribute(identifier, art, source, meta); - } - else if (action.equals(ACTION_GET_ATTRIBUTE)) { - String art = getArtifactIdentifier(source); - - logger.info("Retrieve attribute of collection '" + identifier + "'"); - out = db.getCollectionAttribute(identifier, meta); - } - else if (action.equals(ACTION_GET_ITEM_ATTRIBUTE)) { - String art = getArtifactIdentifier(source); - - logger.info("Retrieve attribute of artifact '" + art + "'"); - out = db.getCollectionItemAttribute(identifier, art, meta); - } - else if (action.equals(ACTION_SET_TTL)) { - out = db.setCollectionTTL(identifier, source, meta); - } - else if (action.equals(ACTION_SET_NAME)) { - out = db.setCollectionName(identifier, source, meta); - } - else { - throw new ArtifactDatabaseException(NO_SUCH_ACTION_MSG); - } - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); - return new EmptyRepresentation(); - } - - return new DomRepresentation(MediaType.APPLICATION_XML, out); - } - - - @Override - protected Representation innerPost(Representation requestRepr) { - Document input = null; - try { - DomRepresentation in = new DomRepresentation(requestRepr); - in.setNamespaceAware(true); - input = in.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - String action = XMLUtils.xpathString( - input, XPATH_ACTION, ArtifactNamespaceContext.INSTANCE); - - if (action == null || action.length() == 0) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MSG); - return new EmptyRepresentation(); - } - - Request request = getRequest(); - - String identifier = (String) request.getAttributes().get("uuid"); - - ArtifactDatabase db = getArtifactDatabase(); - - return dispatch(identifier, action, input, db); - } - - - /** - * Retrieves the identifier of the artifact used in the action. - * - * @param source The incoming document that describes the operation. - * - * @return the uuid of the artifact described in the document. - */ - protected String getArtifactIdentifier(Document source) { - return XMLUtils.xpathString( - source, XPATH_ARTIFACT, ArtifactNamespaceContext.INSTANCE); - } - - - /** - * Returns the attribute for a collection of the incoming request document. - * - * @param request The request document. - * - * @return the contained attribute as document. - */ - protected Document getCollectionAttribute(Document request) { - Node attr = (Node) XMLUtils.xpath( - request, - ArtifactDatabaseImpl.XPATH_COLLECTION_ATTRIBUTE, - XPathConstants.NODE, - ArtifactNamespaceContext.INSTANCE); - - Document newAttr = XMLUtils.newDocument(); - - if (attr == null) { - logger.error("Collection attribute document not found!"); - return newAttr; - } - - newAttr.appendChild(newAttr.importNode(attr, true)); - - return newAttr; - } -}
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateCollectionResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ResourceException; -import org.restlet.Response; -import org.restlet.Request; - -import org.w3c.dom.Document; - -/** - * Resource to create a new collections within the artifact database. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class CreateCollectionResource -extends BaseResource -{ - /** The logger used in this class.*/ - private static Logger logger = - Logger.getLogger(CreateCollectionResource.class); - - /** The URL part for this resource.*/ - public static final String PATH = "/create-collection/{ownerid}"; - - - @Override - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - Document input = null; - - try { - DomRepresentation in = new DomRepresentation(requestRepr); - in.setNamespaceAware(true); - input = in.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - ArtifactDatabase db = getArtifactDatabase(); - - Request request = getRequest(); - - String ownerId = (String) request.getAttributes().get("ownerid"); - - logger.info("Create new collection owned by: " + ownerId); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.createCollection(ownerId, input, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; -import de.intevation.artifacts.ArtifactNamespaceContext; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Response; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; - -/** - * Resource to create a new artifact within artifact database. - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class CreateResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(CreateResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/create"; - - /** - * XPATH to figure out the name of the factory which should be used - * to create the new artifact. - */ - public static final String XPATH_FACTORY = "/art:action/art:factory/@name"; - - /** - * Error message if no factory was given. - */ - public static final String NO_FACTORY_MESSAGE = "No factory given"; - - /** - * Error message if no artifact was created. - */ - public static final String NO_ARTIFACT_CREATED = "No artifact created"; - - @Override - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - Document inputDocument = null; - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - String factory = XMLUtils.xpathString( - inputDocument, - XPATH_FACTORY, - ArtifactNamespaceContext.INSTANCE); - - if (factory == null || factory.length() == 0) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, NO_FACTORY_MESSAGE); - return new EmptyRepresentation(); - } - - if (logger.isDebugEnabled()) { - logger.debug("Create artifact with factory '" + factory + "'"); - } - - ArtifactDatabase db = getArtifactDatabase(); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.createArtifactWithFactory(factory, - getCallMeta(), - inputDocument)); - } - catch (ArtifactDatabaseException adbe) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CreateUserResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Response; -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - - -/** - * Resource to create a new users within the artifact database. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class CreateUserResource -extends BaseResource -{ - /** The logger used in this class.*/ - private static Logger logger = Logger.getLogger(CreateUserResource.class); - - /** The URL part for this resource.*/ - public static final String PATH = "/create-user"; - - - @Override - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - Document input = null; - - try { - DomRepresentation in = new DomRepresentation(requestRepr); - in.setNamespaceAware(true); - input = in.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - logger.debug("Create user"); - - ArtifactDatabase db = getArtifactDatabase(); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.createUser(input, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ExportResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import org.apache.log4j.Logger; - -import org.restlet.Request; -import org.restlet.Response; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -/** - * Resource to produce an external XML representation of a given - * artifact to be import by ImportResource later on. - * - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class ExportResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(ExportResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/export/{uuid}"; - - @Override - protected Representation innerGet() - throws ResourceException - { - Request request = getRequest(); - - String identifier = (String)request.getAttributes().get("uuid"); - - if (logger.isDebugEnabled()) { - logger.debug("looking for artifact id '" + identifier + "'"); - } - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.exportArtifact(identifier, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FactoriesResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; -import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactNamespaceContext; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/** - * Resource to list the available factories. - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class FactoriesResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(FactoriesResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/factories"; - - @Override - protected Representation innerGet() - throws ResourceException - { - Document document = XMLUtils.newDocument(); - - ElementCreator ec = new ElementCreator( - document, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - Element root = ec.create("result"); - document.appendChild(root); - - Element type = ec.create("type"); - ec.addAttr(type, "name", "factory-list"); - root.appendChild(type); - - Element factories = ec.create("factories"); - root.appendChild(factories); - - String [][] factoryNames = db.artifactFactoryNamesAndDescriptions(); - - for (int i = 0; i < factoryNames.length; ++i) { - String [] nd = factoryNames[i]; - Element factoryElement = ec.create("factory"); - ec.addAttr(factoryElement, "name", nd[0]); - ec.addAttr(factoryElement, "description", nd[1]); - factories.appendChild(factoryElement); - } - - document.normalizeDocument(); - - return new DomRepresentation( - MediaType.APPLICATION_XML, document); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FindUserResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ResourceException; -import org.restlet.Response; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -/** - * A Rest resource that finds the user provided by the artifact database. - * - */ -public class FindUserResource -extends BaseResource -{ - /** The logger that is used in this class.*/ - private static Logger logger = Logger.getLogger(FindUserResource.class); - - /** server URL where to reach the resource.*/ - public static final String PATH = "/find-user"; - - - @Override - protected Representation innerPost(Representation requestRepr) - throws ResourceException - { - Document input = null; - - try { - DomRepresentation in = new DomRepresentation(requestRepr); - in.setNamespaceAware(true); - input = in.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getLocalizedMessage(), ioe); - - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - ArtifactDatabase db = getArtifactDatabase(); - - try { - logger.info(PATH); - - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.findUser(input, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/HTTPServer.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -package de.intevation.artifactdatabase.rest; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.ArtifactDatabase; - -public interface HTTPServer -{ - void setup(Document document); - - void startAsServer(ArtifactDatabase database); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ImportResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Request; -import org.restlet.Response; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.w3c.dom.Document; - -/** - * Resource to import an XML document containg an artifact produced by - * the ExportResource. - * - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class ImportResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(ImportResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/import"; - - @Override - protected Representation innerPost(Representation requestRepr) { - - Document inputDocument = null; - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - Request request = getRequest(); - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - try { - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.importArtifact(inputDocument, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/JettyServer.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; - -import org.restlet.Component; -import org.restlet.Server; - -import org.restlet.ext.jetty.HttpServerHelper; - -import org.apache.log4j.Logger; - -public class JettyServer -extends Standalone -{ - private static Logger logger = Logger.getLogger(JettyServer.class); - - @Override - public void startAsServer(ArtifactDatabase db) { - - Component component = new Component(); - - RestApp app = new RestApp(db); - - Server server = createServer(); - - // TODO: Do more sophisticated Jetty server configuration here. - - component.getServers().add(server); - - component.getDefaultHost().attach(app); - - logServerStart(); - - HttpServerHelper serverHelper = new HttpServerHelper(server); - - try { - serverHelper.start(); - } - catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ListCollectionsResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.Request; -import org.restlet.resource.ResourceException; -import org.restlet.Response; - - -/** - * A Rest resource that lists the collections of a specific user provided by - * the artifact database. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class ListCollectionsResource -extends BaseResource -{ - /** The logger that is used in this class.*/ - private static Logger logger = - Logger.getLogger(ListCollectionsResource.class); - - /** server URL where to reach the resource.*/ - public static final String PATH = "/list-collections/{ownerid}"; - - - @Override - protected Representation innerGet() - throws ResourceException - { - Request request = getRequest(); - - String ownerId = (String) request.getAttributes().get("ownerid"); - - ArtifactDatabase db = getArtifactDatabase(); - - try { - logger.info("List collections owned by " + ownerId); - - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.listCollections(ownerId, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ListUsersResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.resource.ResourceException; -import org.restlet.Response; - -/** - * A Rest resource that lists the users provided by the artifact database. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class ListUsersResource -extends BaseResource -{ - /** The logger that is used in this class.*/ - private static Logger logger = Logger.getLogger(ListUsersResource.class); - - /** server URL where to reach the resource.*/ - public static final String PATH = "/list-users"; - - - @Override - protected Representation innerGet() - throws ResourceException - { - ArtifactDatabase db = getArtifactDatabase(); - - try { - logger.info(PATH); - - return new DomRepresentation( - MediaType.APPLICATION_XML, - db.listUsers(getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); - return new EmptyRepresentation(); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/OutRepresentation.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase.DeferredOutput; - -import java.io.IOException; -import java.io.OutputStream; - -import org.restlet.data.MediaType; - -import org.restlet.representation.OutputRepresentation; - -/** - * Special representation to serve the out()-outputs - * via DeferredOutput efficently . - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class OutRepresentation -extends OutputRepresentation -{ - /** - * The deferred output fetched from ArtifactDatabase.out(). - */ - protected DeferredOutput out; - - /** - * Constructor to create representation with a given MIME type and - * a deferred output. - * @param mediaType The MIME type of this representation. - * @param out The deferred output from the ArtifactDatabase.out() call. - */ - public OutRepresentation(MediaType mediaType, DeferredOutput out) { - super(mediaType); - this.out = out; - } - - /** - * Overwrites the write(OutputStream) of OutRepresentation to serve - * the data from the deferred output. - * @param output the stream where to write the data into. - * @throws IOException Thrown if an exception occurred while writing - * the data to the output stream. - */ - public void write(OutputStream output) throws IOException { - out.write(output); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; - -import java.util.concurrent.ConcurrentMap; - -import org.restlet.Application; -import org.restlet.Context; -import org.restlet.Restlet; - -import org.restlet.routing.Router; - -/** - * This is the core REST application that binds the serveral resources - * used to manage the artifact database to the HTTP server provided - * by the Restlet framework. - * - * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> - */ -public class RestApp -extends Application -{ - /** - * The central artifact database instance to work with. - */ - protected ArtifactDatabase database; - - /** - * Default constructor - */ - public RestApp() { - } - - public RestApp(Context context, ArtifactDatabase database) { - super(context); - this.database = database; - } - - /** - * Constructor to create REST appliction bound to a specific - * artifact database. - * - * @param database The artifact database to be used. - */ - public RestApp(ArtifactDatabase database) { - this.database = database; - } - - /** - * Overwrites the createRoot() method of Application to - * build the resource tree to form the exposed server URLs. - * - * @return The root of the URL tree exposed by the HTTP server. - */ - @Override - public Restlet createRoot() { - - Context context = getContext(); - - ConcurrentMap map = context.getAttributes(); - map.put("database", database); - - Router router = new Router(context); - - router.attach(ServicesResource.PATH, ServicesResource.class); - router.attach(ServiceResource.PATH, ServiceResource.class); - router.attach(FactoriesResource.PATH, FactoriesResource.class); - router.attach(CreateResource.PATH, CreateResource.class); - router.attach(ArtifactResource.PATH, ArtifactResource.class); - router.attach(ArtifactOutResource.PATH, ArtifactOutResource.class); - router.attach(ExportResource.PATH, ExportResource.class); - router.attach(ImportResource.PATH, ImportResource.class); - router.attach(CreateUserResource.PATH, CreateUserResource.class); - router.attach(ListUsersResource.PATH, ListUsersResource.class); - router.attach(UserResource.PATH, UserResource.class); - router.attach(FindUserResource.PATH, FindUserResource.class); - router.attach( - CreateCollectionResource.PATH, CreateCollectionResource.class); - router.attach( - ListCollectionsResource.PATH, ListCollectionsResource.class); - router.attach( - CollectionResource.PATH, CollectionResource.class); - router.attach( - CollectionOutResource.PATH, CollectionOutResource.class); - - return router; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.Request; -import org.restlet.Response; -import org.restlet.data.MediaType; -import org.restlet.data.Status; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; - -import org.w3c.dom.Document; - -import de.intevation.artifacts.Service; - -/** - * Resource to process incoming XML documents with a given service. - * - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class ServiceResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(ServiceResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/service/{service}"; - - /** - * Error message if no corresponing service is provided by - * the artifact database. - */ - public static final String NO_SUCH_ACTION_MESSAGE = "no such service"; - - @Override - protected Representation innerPost(Representation requestRepr) { - - Document inputDocument = null; - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - Request request = getRequest(); - - String service = (String)request.getAttributes().get("service"); - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - try { - return guessRepresentation( - db.process(service, inputDocument, getCallMeta())); - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); - return new EmptyRepresentation(); - } - } - - protected static Representation guessRepresentation(Service.Output output) { - - MediaType mediaType = new MediaType(output.getMIMEType()); - Object data = output.getData(); - - if (data instanceof Document) { - return new DomRepresentation(mediaType, (Document)data); - } - - if (data instanceof byte []) { - return new ByteArrayRepresentation(mediaType, (byte [])data); - } - - return new EmptyRepresentation(); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServicesResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; -import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactNamespaceContext; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; - -import org.restlet.ext.xml.DomRepresentation; - -import org.restlet.representation.Representation; - -import org.restlet.resource.ResourceException; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/** - * Resource to list the available service offered by the artifact database. - * - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class ServicesResource -extends BaseResource -{ - private static Logger logger = Logger.getLogger(ServicesResource.class); - - /** - * server URL where to reach the resource. - */ - public static final String PATH = "/services"; - - @Override - protected Representation innerGet() - throws ResourceException - { - Document document = XMLUtils.newDocument(); - - ElementCreator ec = new ElementCreator( - document, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - ArtifactDatabase db = (ArtifactDatabase)getContext() - .getAttributes().get("database"); - - Element root = ec.create("result"); - document.appendChild(root); - - Element type = ec.create("type"); - ec.addAttr(type, "name", "service-list"); - root.appendChild(type); - - Element factories = ec.create("services"); - root.appendChild(factories); - - String [][] factoryNames = db.serviceNamesAndDescriptions(); - - for (int i = 0; i < factoryNames.length; ++i) { - String [] nd = factoryNames[i]; - Element factoryElement = ec.create("service"); - ec.addAttr(factoryElement, "name", nd[0]); - ec.addAttr(factoryElement, "description", nd[1]); - factories.appendChild(factoryElement); - } - - document.normalizeDocument(); - - return new DomRepresentation( - MediaType.APPLICATION_XML, document); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2010 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ - -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifacts.ArtifactDatabase; - -import org.apache.log4j.Logger; - -import org.restlet.Component; -import org.restlet.Server; - -import org.restlet.data.Protocol; - -import org.w3c.dom.Document; - -/** - * Starts an HTTP server bound to a RestApp. - * The server (binding interface and port) is configure via the - * global configuration. - * - * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> - */ -public class Standalone -implements HTTPServer -{ - private static Logger logger = Logger.getLogger(Standalone.class); - - /** - * XPath to figure out the port where to listen from the - * global configuration. - */ - public static final String REST_PORT = - "/artifact-database/rest-server/port/text()"; - - /** - * XPath to figure out from global configuration - * which network interface to use to bind the HTTP server. - */ - public static final String LISTEN_INTERFACE = - "/artifact-database/rest-server/listen/text()"; - - /** - * The default port of the HTTP server: 8181 - */ - public static final int DEFAULT_PORT = 8181; - - public static final String MAX_THREADS = - "/artifact-database/rest-server/max-threads/text()"; - - public static final String MAX_THREADS_DEFAULT = - "1024"; - - protected int port; - - protected String listen; - - protected String maxThreads; - - public Standalone() { - } - - @Override - public void setup(Document document) { - String portString = XMLUtils.xpathString(document, REST_PORT, null); - - port = DEFAULT_PORT; - - if (portString != null) { - try { - port = Integer.parseInt(portString); - if (port < 0) { - throw new NumberFormatException(); - } - } - catch (NumberFormatException nfe) { - logger.error("rest port is not a positive integer value.", nfe); - return; - } - } - - listen = XMLUtils.xpathString(document, LISTEN_INTERFACE, null); - maxThreads = XMLUtils.xpathString(document, MAX_THREADS, null); - } - - protected Server createServer() { - return listen != null && listen.length() > 0 - ? new Server(Protocol.HTTP, listen, port) - : new Server(Protocol.HTTP, port); - } - - protected void logServerStart() { - logger.info("Starting " + getClass().getName() + " HTTP server on " - + (listen != null ? listen : "*") - + ":" + port); - } - - /** - * Builds a RestApp wrapped around the given artifact database, - * and bind this application to HTTP server. The HTTP server - * is configured by the global configuration. If no port is - * given by the configuration the default port is used. If - * no interface is given the HTTP server is reachable from - * all interfaces. - * @param db The artifact database to be exposed via the - * REST application. - */ - @Override - public void startAsServer(ArtifactDatabase db) { - - RestApp app = new RestApp(db); - - Component component = new Component(); - - Server server = createServer(); - - component.getServers().add(server); - - server.getContext().getParameters().add( - "maxThreads", maxThreads != null && maxThreads.length() > 0 - ? maxThreads - : MAX_THREADS_DEFAULT); - - component.getDefaultHost().attach(app); - - logServerStart(); - - try { - component.start(); - } - catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.rest; - -import de.intevation.artifacts.ArtifactDatabase; -import de.intevation.artifacts.ArtifactDatabaseException; - -import de.intevation.artifacts.common.ArtifactNamespaceContext; -import de.intevation.artifacts.common.utils.XMLUtils; - -import java.io.IOException; - -import org.apache.log4j.Logger; - -import org.restlet.data.MediaType; -import org.restlet.data.Status; -import org.restlet.ext.xml.DomRepresentation; -import org.restlet.representation.EmptyRepresentation; -import org.restlet.representation.Representation; -import org.restlet.Request; -import org.restlet.Response; - -import org.w3c.dom.Document; - -/** - * A resource that handles actions to a specific user. - * - * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> - */ -public class UserResource -extends BaseResource -{ - /** The logger that is used in this class. */ - private static Logger logger = Logger.getLogger(UserResource.class); - - /** server URL where to reach the resource. */ - public static final String PATH = "/user/{uuid}"; - - /** - * XPath to figure out the type of action (feed, advance) via the - * incoming POST request. - */ - public static final String XPATH_ACTION = "/art:action/art:type/@name"; - - /** Error message if no action was given. */ - public static final String NO_ACTION_MSG = "no action given"; - - /** Error message if a unknown action was given. */ - public static final String NO_SUCH_ACTION_MSG = "no such action"; - - /** Action name for deleting users. */ - public static final String ACTION_DELETE = "delete"; - - - @Override - protected Representation innerPost(Representation requestRepr) { - Document inputDocument = null; - - try { - DomRepresentation input = new DomRepresentation(requestRepr); - input.setNamespaceAware(true); - inputDocument = input.getDocument(); - } - catch (IOException ioe) { - logger.error(ioe.getMessage()); - Response response = getResponse(); - response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); - return new EmptyRepresentation(); - } - - String action = XMLUtils.xpathString( - inputDocument, - XPATH_ACTION, - ArtifactNamespaceContext.INSTANCE); - - if (action == null || action.length() == 0) { - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MSG); - return new EmptyRepresentation(); - } - - Request request = getRequest(); - - String identifier = (String)request.getAttributes().get("uuid"); - - ArtifactDatabase db = getArtifactDatabase(); - - return dispatch(identifier, action, inputDocument, db); - } - - /** - * Method to figure out which POST action (feed or advance) was - * triggered and perform this operation on the artifact specified - * by 'identifier' and found in the artifact database 'db' - * @param identifier The identifier of the artifact. - * @param action The action to be performed. - * @param source The input document to further parameterize the - * operation. - * @param db The artifact database where to find the artifact. - * @return The representation produced by the performed action. - */ - protected Representation dispatch( - String identifier, - String action, - Document source, - ArtifactDatabase db) - { - Document out = null; - - logger.info("Action: " + action + " | User: " + identifier); - - try { - if (action.equals(ACTION_DELETE)) { - out = db.deleteUser(identifier, getCallMeta()); - } - else { - throw new ArtifactDatabaseException(NO_SUCH_ACTION_MSG); - } - } - catch (ArtifactDatabaseException adbe) { - logger.warn(adbe.getLocalizedMessage(), adbe); - Response response = getResponse(); - response.setStatus( - Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); - return new EmptyRepresentation(); - } - - return new DomRepresentation(MediaType.APPLICATION_XML, out); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/package.html Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -</head> -<body> -The REST interface of the artfifact database. This package contains classes -that offer the URL resources to manage the artifacts via REST. -</body> -</html>
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.state; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.xpath.XPathConstants; - -import org.apache.log4j.Logger; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactNamespaceContext; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.CallMeta; - -import de.intevation.artifacts.common.utils.Config; -import de.intevation.artifacts.common.utils.XMLUtils; - -import de.intevation.artifactdatabase.data.StateData; - - -/** - * An abstract implementation of a {@link State}. It implements some basic - * methods that return the id, description and data. The methods - * <code>describe()</code> and <code>setup()</code> depend on the concrete class - * and need to be implemented by those. - */ -public abstract class AbstractState implements State { - - /** The XPath to the ID of the state relative to the state node in the - * configuration. */ - public static final String XPATH_ID = "@id"; - - /** The XPath to the description of the state relative to the state node in - * the configuration. */ - public static final String XPATH_DESCRIPTION = "@description"; - - /** The XPath that points to the help text.*/ - public static final String XPATH_HELP_TEXT = "@helpText"; - - /** The XPath to the output nodes of the state configuration. */ - public static final String XPATH_OUTPUT_MODES = "outputmodes/outputmode"; - - /** The XPath to the list of facets relative to the output mode it belongs - * to. */ - public static final String XPATH_FACETS = "facets/facet"; - - public static final String XPATH_HELP_URL = "/artifact-database/help-url/text()"; - - public static final String HELP_URL = "${help.url}"; - - - /** The logger that is used in this class. */ - private static Logger logger = Logger.getLogger(AbstractState.class); - - - /** The ID of the state. */ - protected String id; - - /** The description of the state. */ - protected String description; - - /** The help text for this state.*/ - protected String helpText; - - /** The data provided by this state. */ - protected Map<String, StateData> data; - - /** A list of output modes which are available for this state. */ - protected List<Output> outputs; - - private static String helpUrl; - - - public AbstractState() { - outputs = new ArrayList<Output>(); - } - - public static synchronized final String getHelpUrl() { - if (helpUrl == null) { - helpUrl = Config.getStringXPath(XPATH_HELP_URL, HELP_URL); - } - return helpUrl; - } - - public static String replaceHelpUrl(String string) { - return string.replace(HELP_URL, getHelpUrl()); - } - - - /** - * The default constructor. - * - * @param id The ID of the state. - * @param description The description of the state. - */ - public AbstractState(String id, String description) { - super(); - - this.id = id; - this.description = description; - } - - - public AbstractState(String id, String description, String helpText) { - this(id, description); - this.helpText = replaceHelpUrl(helpText); - } - - - /** - * Returns the ID of the state. - * - * @return the ID of the state. - */ - public String getID() { - return id; - } - - - /** - * Set the ID of the state. - * - * @param id The ID of the state. - */ - public void setID(String id) { - this.id = id; - } - - - /** - * Returns the description of the state. - * - * @return the description of the state. - */ - public String getDescription() { - return description; - } - - - /** - * Set the description of the state. - * - * @param description The description of the state. - */ - public void setDescription(String description) { - this.description = description; - } - - - /** - * Returns the help text of this state. - * - * @return the help text. - */ - public String getHelpText() { - return helpText; - } - - - /** - * Set the help text for this state. - * - * @param helpText The help text. - */ - public void setHelpText(String helpText) { - this.helpText = replaceHelpUrl(helpText); - } - - - /** - * Returns the data of the state. - * - * @return the data of the state. - */ - public Map<String, StateData> getData() { - return data; - } - - - /** - * Returns a specific data object of the state. - * - * @param name The name of the data object. - * - * @return a data object of the state or null if no such data object exists. - */ - public StateData getData(String name) { - if (data != null) { - return data.get(name); - } - - return null; - } - - - /** - * Add new data to the state. NOTE: If there is already an object existing - * with the key <i>name</i>, this object is overwritten by the new value. - * - * @param name The name of the data object. - * @param data The data object. - */ - public void addData(String name, StateData data) { - if (this.data == null) { - this.data = new HashMap<String, StateData>(); - } - - this.data.put(name, data); - } - - - /** - * Returns the list of possible outputs of this state. The list is empty - * if no output is available for this state. - * - * @return a list of possible outputs of this state. - */ - public List<Output> getOutputs() { - return outputs; - } - - - /** - * Initialize the state based on the state node in the configuration. - * - * @param config The state configuration node. - */ - public void setup(Node config) { - logger.info("AbstractState.setup"); - - id = (String) XMLUtils.xpath(config, XPATH_ID, XPathConstants.STRING); - - description = (String) XMLUtils.xpath( - config, XPATH_DESCRIPTION, XPathConstants.STRING); - - helpText = (String) XMLUtils.xpath( - config, XPATH_HELP_TEXT, XPathConstants.STRING); - - if (helpUrl != null) { - helpUrl = replaceHelpUrl(helpUrl); - } - - setupOutputs(config); - } - - - /** - * This default implementation does nothing at all. - * - * @param orig - * @param owner - * @param context - * @param callMeta - */ - public void initialize( - Artifact orig, - Artifact owner, - Object context, - CallMeta callMeta - ) { - // do nothing. - } - - - /** - * This method tries reading the available output nodes configured in the - * state configuration and adds possible Outputs to the outputs list. - * - * @param config The state configuration node. - */ - protected void setupOutputs(Node config) { - NodeList outs = (NodeList) XMLUtils.xpath( - config, - XPATH_OUTPUT_MODES, - XPathConstants.NODESET, - ArtifactNamespaceContext.INSTANCE); - - if (outs == null || outs.getLength() == 0) { - return; - } - - int size = outs.getLength(); - - for (int i = 0; i < size; i++) { - addOutput(buildOutput(outs.item(i))); - } - } - - /** - * This methods allows subclasses to manually add outputs - * - * @param out The output to add - */ - protected void addOutput(Output out) { - outputs.add(out); - } - - /** - * A helper method that creates an Output object based on the <i>out</i> - * node. - * - * @param out The output node configuration. - * - * @return an Output object. - */ - protected Output buildOutput(Node out) { - String name = XMLUtils.xpathString( - out, "@name", ArtifactNamespaceContext.INSTANCE); - - String desc = XMLUtils.xpathString( - out, "@description", ArtifactNamespaceContext.INSTANCE); - - String mimetype = XMLUtils.xpathString( - out, "@mime-type", ArtifactNamespaceContext.INSTANCE); - - String type = XMLUtils.xpathString( - out, "@type", ArtifactNamespaceContext.INSTANCE); - - if (name == null) { - return null; - } - - NodeList facets = (NodeList) XMLUtils.xpath( - out, - XPATH_FACETS, - XPathConstants.NODESET, - ArtifactNamespaceContext.INSTANCE); - - if (facets == null || facets.getLength() == 0) { - return new DefaultOutput(name, desc, mimetype, type); - } - - int num = facets.getLength(); - - List<Facet> facetList = new ArrayList<Facet>(num); - - for (int i = 0; i < num; i++) { - Facet facet = buildFacet(facets.item(i)); - - if (facet != null) { - facetList.add(facet); - } - } - - return new DefaultOutput(name, desc, mimetype, facetList, type); - } - - - /** - * A helper method that creates a Facet object based on the <i>facet</i> - * node. - * - * @param facet The facet node. - * - * @return a Facet object or null if no valid Facet was found. - */ - protected Facet buildFacet(Node facet) { - String name = XMLUtils.xpathString( - facet, "@name", ArtifactNamespaceContext.INSTANCE); - - String desc = XMLUtils.xpathString( - facet, "@description", ArtifactNamespaceContext.INSTANCE); - - return name != null ? new DefaultFacet(name, desc) : null; - } - - - /** - * Describes the UI of the state. This method needs to be implemented by - * concrete subclasses. - * - * @param artifact A reference to the artifact this state belongs to. - * @param document Describe doucment. - * @param rootNode Parent node for all new elements. - * @param context The CallContext. - * @param uuid The uuid of an artifact. - */ - public abstract Element describe( - Artifact artifact, - Document document, - Node rootNode, - CallContext context, - String uuid - ); - - - @Override - public void endOfLife(Artifact artifact, Object context) { - // nothing to do here - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.List; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.DataProvider; - - -/** - * A bundle of a "native" Facet and its Artifact. - */ -public class ArtifactAndFacet implements DataProvider { - /** The Artifact. */ - protected Artifact artifact; - - /** The (native) facet. */ - protected Facet facet; - - /** An alternative facet description that might be set from outside. */ - protected String facetDescription; - - - /** Trivial constructor. */ - public ArtifactAndFacet( - Artifact a, - Facet f - ) { - this.artifact = a; - this.facet = f; - } - - - /** Get data (to plot). */ - public Object getData(CallContext context) { - return facet.getData(artifact, context); - } - - - /** Get data (for other facet). */ - @Override - public Object provideData(Object key, Object param, CallContext context) { - return facet.provideBlackboardData(artifact, key, param, context); - } - - - /** (Maybe) Register on blackboard (depending on facet). */ - @Override - public void register(CallContext context) { - List keys = facet.getDataProviderKeys(this.artifact, context); - if (keys == null) { - return; - } - for (Object key: keys) { - context.registerDataProvider(key, this); - } - } - - - /** Access the artifact. */ - public Artifact getArtifact() { - return artifact; - } - - - /** Access the (native) facet. */ - public Facet getFacet() { - return facet; - } - - - /** Shortcut to facets name. */ - public String getFacetName() { - return facet.getName(); - } - - - /** - * Returns the description for a facet. The return value depends on the - * internal <i>facetDescription</i> instance variable. If this has been set - * by setFacetDescription, this value is returned, otherwise the return - * value of facet.getDescription(). - */ - public String getFacetDescription() { - if (facetDescription == null) { - return facet.getDescription(); - } - - return facetDescription; - } - - - public void setFacetDescription(String facetDescription) { - this.facetDescription = facetDescription; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Attribute.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.io.Serializable; - -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Attribute extends Serializable { - - /** - * Returns the name of this Attribute. - * - * @return the name of this Attribute. - */ - String getName(); - - /** - * Returns the value of this Attribute. - * - * @return the value of this Attribute. - */ - Object getValue(); - - /** - * Sets the value of this Attribute. - * - * @param value The new value. - */ - void setValue(Object value); - - /** - * Transforms this Attribute into XML. - * - * @param parent The parent node. - * - * @return the Node that represents this Attribute. - */ - Node toXML(Node parent); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultAttribute.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultAttribute implements Attribute { - - protected String name; - - protected Object value; - - - public DefaultAttribute(String name, Object value) { - this.name = name; - this.value = value; - } - - - @Override - public String getName() { - return name; - } - - - @Override - public Object getValue() { - return value; - } - - - @Override - public void setValue(Object value) { - this.value = value; - } - - - @Override - public Node toXML(Node parent) { - Document owner = parent.getOwnerDocument(); - Element attr = owner.createElement(getName()); - - parent.appendChild(attr); - - attr.setTextContent(String.valueOf(getValue())); - - return attr; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.List; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.ArtifactNamespaceContext; -import de.intevation.artifacts.CallContext; - -import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; - - -/** - * The default implementation of a Facet. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultFacet implements Facet { - - /** The index of this facet. */ - protected int index; - - /** The name of this facet. */ - protected String name; - - /** The description of this facet. */ - protected String description; - - - /** Trivial, empty constructor. */ - public DefaultFacet() { - } - - - /** - * The default constructor to create new Facet objects. - * - * @param name The name of this new facet. - * @param description The description of this new facet. - */ - public DefaultFacet(String name, String description) { - this(0, name, description); - } - - - /** - * The default constructor to create new Facet objects. - * - * @param index The index of this new facet. - * @param name The name of this new facet. - * @param description The description of this new facet. - */ - public DefaultFacet(int index, String name, String description) { - this.index = index; - this.name = name; - this.description = description; - } - - - /** Get index. */ - public int getIndex() { - return index; - } - - - /** Returns the name ('type'). */ - public String getName() { - return name; - } - - - /** Returns the description (e.g. displayed in gui). */ - public String getDescription() { - return description; - } - - - /** - * @return null - */ - public Object getData(Artifact artifact, CallContext context) { - return null; - } - - - /** - * (Do not) provide data. - * Override to allow other facets to access your data. - * @return always null. - */ - public Object provideBlackboardData( - Artifact artifact, - Object key, - Object param, - CallContext context - ) { - return null; - } - - - /* - * Return list of keys (objects) for which this facet can provide data - * ("external parameterization"), for other facets, via blackboard. - * These are the keys that are independent from the current call (thus - * 'static'). - * @param artifact that this facet belongs to. - */ - public List getStaticDataProviderKeys(Artifact artifact) { - return null; - } - - /** - * Return list of keys (objects) for which this facet can provide data - * ("external parameterization"), for other facets, via blackboard. - * @param artifact that this facet belongs to. - */ - public List getDataProviderKeys(Artifact artifact, CallContext context) { - return getStaticDataProviderKeys(artifact); - } - - - /** Create a xml represantation. */ - public Node toXML(Document doc) { - ElementCreator ec = new ElementCreator( - doc, - ArtifactNamespaceContext.NAMESPACE_URI, - ArtifactNamespaceContext.NAMESPACE_PREFIX); - - Element facet = ec.create("facet"); - ec.addAttr(facet, "description", description, true); - ec.addAttr(facet, "name", name, true); - ec.addAttr(facet, "index", String.valueOf(index), true); - - return facet; - } - - - /** Create a string representation. */ - public String toString() { - return new StringBuilder("name = '") - .append(name).append("', index = ") - .append(index).append(", description = '") - .append(description).append("'") - .toString(); - } - - - /** - * Copies name, index and description of other facet. - */ - public void set(Facet other) { - index = other.getIndex(); - name = other.getName(); - description = other.getDescription(); - } - - - /** Create a deep copy of this facet. */ - public Facet deepCopy() { - DefaultFacet copy = new DefaultFacet(); - copy.set(this); - return copy; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.ArrayList; -import java.util.List; - -/** - * The default implementation of an Output. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultOutput implements Output { - - protected String name; - - protected String description; - - protected String mimeType; - - protected String type; - - protected List<Facet> facets; - - protected Settings settings; - - - /** - * The default constructor that instantiates a new DefaultOutput object. - * - * @param name The name of this output. - * @param description The description of this output. - * @param mimeType The mimetype of this output. - */ - public DefaultOutput(String name, String description, String mimeType) { - this.name = name; - this.description = description; - this.mimeType = mimeType; - this.type = ""; - this.facets = new ArrayList<Facet>(); - } - - - public DefaultOutput( - String name, - String description, - String mimeType, - String type) - { - this(name, description, mimeType); - - this.facets = new ArrayList<Facet>(); - this.type = type; - } - - - public DefaultOutput( - String name, - String description, - String mimeType, - List<Facet> facets) - { - this(name, description, mimeType); - - this.type = ""; - this.facets = facets; - } - - - /** - * This constructor builds a new Output object that contains facets as well. - * - * @param name The name of this output. - * @param description The description of this output. - * @param mimeType The mimetype of this output. - * @param facets The list of facets supported by this output. - * @param type The type of the Output e.g. chart - */ - public DefaultOutput( - String name, - String description, - String mimeType, - List<Facet> facets, - String type) - { - this(name, description, mimeType, facets); - - this.type = type; - } - - - /** - * Returns the name of this output. - * - * @return the name of this output. - */ - public String getName() { - return name; - } - - - /** - * Returns the description of this output. - * - * @return the description of this output. - */ - public String getDescription() { - return description; - } - - - /** - * Returns the mimetype of this output. - * - * @return the mimetype of this output. - */ - public String getMimeType() { - return mimeType; - } - - - public String getType() { - return type; - } - - - /** - * Returns the list of facets supported by this output. - * - * @return the list of facets supported by this output. - */ - public List<Facet> getFacets() { - return facets; - } - - - public void addFacet(Facet facet) { - if (facet != null && !facets.contains(facet)) { - facets.add(facet); - } - } - - - public void addFacets(List<Facet> facets) { - this.facets.addAll(facets); - } - - - @Override - public void setFacets(List<Facet> facets) { - this.facets = facets; - } - - - @Override - public void setSettings(Settings settings) { - this.settings = settings; - } - - - @Override - public Settings getSettings() { - return settings; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -/** - * Attributes keep the order in which they were inserted. - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultSection implements Section { - - protected String id; - - protected List<Section> subsections; - - /** Attribute-map. */ - protected Map<String, Attribute> attributes; - - - /** - * Creates a new DefaultSection instance. <b>Note, that the <i>id</i> is used - * as Node name of the new Element that is created in toXML().</b> - */ - public DefaultSection(String id) { - this.id = id; - // Use LinkedHashMap to keep insertion order. - this.attributes = new LinkedHashMap<String, Attribute>(); - this.subsections = new ArrayList<Section>(); - } - - - @Override - public String getId() { - return id; - } - - - @Override - public void addSubsection(Section subsection) { - if (subsection != null) { - subsections.add(subsection); - } - } - - - @Override - public int getSubsectionCount() { - return subsections.size(); - } - - - @Override - public Section getSubsection(int pos) { - if (pos >= 0 && pos < getSubsectionCount()) { - return subsections.get(pos); - } - - return null; - } - - - /** Adding attribute to end of list. */ - @Override - public void addAttribute(String key, Attribute attribute) { - if (key != null && key.length() > 0 && attribute != null) { - attributes.put(key, attribute); - } - } - - - @Override - public Attribute getAttribute(String key) { - if (key == null || key.length() == 0) { - return null; - } - - return attributes.get(key); - } - - - @Override - public Set<String> getKeys() { - return attributes.keySet(); - } - - - @Override - public void toXML(Node parent) { - Document owner = parent.getOwnerDocument(); - Element sectionEl = owner.createElement(getId()); - - parent.appendChild(sectionEl); - - for (String key: getKeys()) { - Attribute attr = getAttribute(key); - attr.toXML(sectionEl); - } - - for (int i = 0, n = getSubsectionCount(); i < n; i++) { - Section subsection = getSubsection(i); - subsection.toXML(sectionEl); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSettings.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class DefaultSettings implements Settings { - - protected List<Section> sections; - - public DefaultSettings() { - sections = new ArrayList<Section>(); - } - - @Override - public void addSection(Section section) { - if (section != null) { - sections.add(section); - } - } - - @Override - public int getSectionCount() { - return sections.size(); - } - - @Override - public Section getSection(int pos) { - if (pos >= 0 && pos < getSectionCount()) { - return sections.get(pos); - } - - return null; - } - - @Override - public void removeSection(Section section) { - if (section != null) { - sections.remove(section); - } - } - - @Override - public void toXML(Node parent) { - Document owner = parent.getOwnerDocument(); - Element settings = owner.createElement("settings"); - - parent.appendChild(settings); - - for (Section section: sections) { - section.toXML(settings); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.List; - -import java.io.Serializable; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.CallContext; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Facet extends Serializable { - - /** - * Returns the index of this facet. - * - * @return the index of this facet. - */ - int getIndex(); - - /** - * Returns the name of this facet. - * - * @return the name of this facet. - */ - String getName(); - - - /** - * Returns the description of this facet. - * - * @return the description of this facet. - */ - String getDescription(); - - - /** - * Returns the data this facet requires. - * - * @param artifact The owner artifact. - * @param context The CallContext. - * - * @return the data. - */ - Object getData(Artifact artifact, CallContext context); - - - /** - * Get keys for which this Facet can provide data (for other facets, not - * for plot). - * @param artifact Artifact that this facet belongs to. - * @return list of keys - */ - List getDataProviderKeys(Artifact artifact, CallContext context); - - - /** - * Provide data to other facet. - * - * @param art The artifact that this facet belongs to. - * @param key the key of the requested service. - * @param prm optional parameters. - * @param ctxt the callcontext. - * - * @return the data - */ - Object provideBlackboardData( - Artifact art, - Object key, - Object prm, - CallContext ctxt); - - - /** - * Write the internal representation of a facet to a node. - * - * @param doc A Document. - * - * @return the representation as Node. - */ - Node toXML(Document doc); - - Facet deepCopy(); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import de.intevation.artifacts.Artifact; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.log4j.Logger; - - -/** - * System used in practice used by AttributeWriter in flys-artifacts to decide - * whether a facet is initially active. - * Provides a singleton Registry into which FacetActivities can be registered - * under a key (in practice the artifacts name. This Registry is queried for - * new Facets in order to find whether they are active or inactive. - */ -public interface FacetActivity -{ - /** Static 'activity' that lets all facets be active. */ - public static final FacetActivity ACTIVE = new FacetActivity() { - @Override - public Boolean isInitialActive( - Artifact artifact, - Facet facet, - String output - ) { - return Boolean.TRUE; - } - }; - - /** Static 'activity' that lets all facets be inactive. */ - public static final FacetActivity INACTIVE = new FacetActivity() { - @Override - public Boolean isInitialActive( - Artifact artifact, - Facet facet, - String output - ) { - return Boolean.FALSE; - } - }; - - Boolean isInitialActive(Artifact artifact, Facet facet, String output); - - /** Singleton registry, that maps artifact names to the activities, which - * decide whether or not a facet should be (initially) active. */ - public static final class Registry { - - /** The logger for this class. */ - private static Logger logger = Logger.getLogger(Registry.class); - - /** Singleton instance. */ - private static final Registry INSTANCE = new Registry(); - - /** Map of keys (artifact names) to the activities. */ - private Map<String, List<FacetActivity>> activities; - - /** Private singleton constructor for the Facet-Activity-Registry. */ - private Registry() { - activities = new HashMap<String, List<FacetActivity>>(); - } - - /** Access Singleton instance. */ - public static Registry getInstance() { - return INSTANCE; - } - - /** Queries whether a given facet should be active or not. */ - public synchronized boolean isInitialActive( - String key, - Artifact artifact, - Facet facet, - String output - ) { - List<FacetActivity> activityList = activities.get(key); - if (activityList == null) { - logger.debug("FacetActivity.Registry: No activity " + - "registered for " + key); - return true; - } - if (activityList.size() != 1) { - logger.warn("FacetActivity.Registry: More than one " + - "FacetActivity registered for " + key); - } - for (FacetActivity activity: activityList) { - Boolean isActive = - activity.isInitialActive(artifact, facet, output); - // Nice. Only, in practice they never return NULL. - if (isActive != null) { - return isActive; - } - } - return true; - } - - - /** Add a FacetActivity under given key (usually artifacts name). */ - public synchronized void register(String key, FacetActivity activity) { - List<FacetActivity> activityList = activities.get(key); - if (activityList == null) { - activityList = new ArrayList<FacetActivity>(3); - activities.put(key, activityList); - } - activityList.add(activity); - } - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.List; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Output { - - /** - * Retrieve the name of this output mode. - * - * @return the name of this output mode. - */ - public String getName(); - - /** - * Retrieve the description of an output. - * - * @return the description. - */ - public String getDescription(); - - /** - * Retrieve the mimetype used for the output. - * - * @return the mimetype. - */ - public String getMimeType(); - - - /** - * Returns the type of this output. - * - * @return the type. - */ - public String getType(); - - /** - * Retrieve the facets of this output. - * - * @return the facets of this output. - */ - public List<Facet> getFacets(); - - /** - * Add a new facet to this output. - * - * @param facet The new facet. - */ - public void addFacet(Facet facet); - - /** - * Add a list of facet to this output. - * - * @param facets A list of facets. - */ - public void addFacets(List<Facet> facets); - - /** - * Replaces the old list of facets with a new one. - * - * @param facets A list of new facets. - */ - public void setFacets(List<Facet> facets); - - /** - * Returns a Settings object for this Output. - */ - public Settings getSettings(); - - /** - * Sets the Settings for this Output. - * - * @param settings the Settings for this Output. - */ - public void setSettings(Settings settings); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Section.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.io.Serializable; -import java.util.Set; - -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Section extends Serializable { - - /** - * Returns an ID for this Section. - * - * @return an ID for this Section. - */ - String getId(); - - /** - * Adds a new subsection to this Section object. - * - * @param subsection the new Section. - */ - void addSubsection(Section subsection); - - /** - * Returns the number of subsections in this Section. - * - * @return the number of subsections. - */ - int getSubsectionCount(); - - /** - * Returns a subsection at position <i>pos</i>. - * - * @param pos The position of the target subsection. - * - * @return the subsection at position <i>pos</i>. - */ - Section getSubsection(int pos); - - /** - * Adds a new Attribute to this Section. - * - * @param key The key that is used to store/retrieve the Attribute. - * @param attribute The new Attribute. - */ - void addAttribute(String key, Attribute attribute); - - /** - * Returns an Attribute for the specified <i>key</i>. - * - * @param key The key that is used to retrieve the target Attribute. - * - * @return the Attribute specified by <i>key</i>. - */ - Attribute getAttribute(String key); - - /** - * Returns all keys of all Attributes currently stored in this Section. - * - * @return all keys of all Attributes. - */ - Set<String> getKeys(); - - /** - * Transforms this Section into XML using Attribute.toXML() for each - * Attribute and Section.toXML() for each subsection stored in this Section. - * - * @param parent The parent node. - */ - void toXML(Node parent); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Settings.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.io.Serializable; - -import org.w3c.dom.Node; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Settings extends Serializable { - - /** - * Adds a new Section to this Settings object. - * - * @param section the new Section. - */ - void addSection(Section section); - - /** - * Returns the number of Sections in this Settings object. - * - * @return the number of sections. - */ - int getSectionCount(); - - /** - * Returns the section at position <i>pos</i>. - * - * @param pos the position of the target Section. - * - * @return the Section at position <i>pos</i> or null if no Section is - * existing at <i>pos</i>. - */ - Section getSection(int pos); - - /** - * Removes a Section if it is existing in this Settings. - * - * @param section The section that should be removed. - */ - void removeSection(Section section); - - /** - * Transforms this Settings object into a XML representation. Therefore, - * each Section object's <i>toXML</i> method is called to append its XML - * representation to the final document. - * - * @param parent The parent node. - */ - void toXML(Node parent); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2011 by Intevation GmbH - * - * This program is free software under the LGPL (>=v2.1) - * Read the file LGPL.txt coming with the software for details - * or visit http://www.gnu.org/licenses/ if it does not exist. - */ -package de.intevation.artifactdatabase.state; - -import java.io.Serializable; -import java.util.List; -import java.util.Map; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import de.intevation.artifacts.Artifact; -import de.intevation.artifacts.CallContext; -import de.intevation.artifacts.CallMeta; - -import de.intevation.artifactdatabase.data.StateData; - - -/** - * This interface describes the basic methods a concrete state class needs to - * implement. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface State extends Serializable { - - /** - * Return the id of the state. - * - * @return the id. - */ - public String getID(); - - - /** - * Return the description of the state. - * - * @return the description of the state. - */ - public String getDescription(); - - - /** - * Returns the help text configured for the state. - * - * @return the help text configured for the state. - */ - public String getHelpText(); - - - /** - * Returns the data provided by this state. - * - * @return the data stored in this state. - */ - public Map<String, StateData> getData(); - - - /** - * Returns a single desired StateData object based on its name. - * - * @param name The name of the desired StateData object. - * - * @return the desired StateData object. - */ - public StateData getData(String name); - - - /** - * This method should be used to add a new {@link StateData} object to the - * data pool of the state. - * - * @param name The name of the data object. - * @param data The data object. - */ - public void addData(String name, StateData data); - - - /** - * Returns the list of possible outputs of this state. The list is empty - * if no output is available for this state. - * - * @return a list of possible outputs of this state. - */ - public List<Output> getOutputs(); - - - /** - * Initialize the state based on the state node in the configuration. - * - * @param config The state configuration node. - */ - public void setup(Node config); - - - /** - * Initializes the internal state of this State based on an other State. - * - * @param orig The owner Artifact or the original State. - * @param owner The owner Artifact of this State. - * @param context The context object. - * @param callMeta The CallMeta of the current call. - */ - public void initialize( - Artifact orig, - Artifact owner, - Object context, - CallMeta callMeta); - - - /** - * This method is called when an artifacts retrieves a describe request. It - * creates the user interface description of the current state. - * - * @param artifact A reference to the artifact this state belongs to. - * @param document Describe doucment. - * @param rootNode Parent node for all new elements. - * @param context The CallContext. - * @param uuid The uuid of an artifact. - */ - public Element describe( - Artifact artifact, - Document document, - Node rootNode, - CallContext context, - String uuid - ); - - - /** - * This method should be called by an Artifact that removes this State - * (current State and previous States). E.g. this might be interesting to - * remove generated files or stuff like that. - * - * @param artifact A parent Artifact. - * @param context The CallContext. - */ - public void endOfLife(Artifact artifact, Object context); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -package de.intevation.artifactdatabase.state; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.log4j.Logger; - -import de.intevation.artifactdatabase.data.StateData; - - -/** - * The StateEngine stores all states and associated information about - * outputs and facets for each Artifact. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class StateEngine { - - /** The logger used in this class. */ - private static Logger logger = Logger.getLogger(StateEngine.class); - - /** - * A map that contains the states of the artifacts. The key of this map is - * the name of an artifact, its value is a list of all states the artifact - * can reach. - */ - protected Map<String, List<State>> states; - - - /** - * A map that contains all existing states. The key of this map is the ID of - * the state, its value is the state itself. - */ - protected Map<String, State> allStates; - - - /** - * The default constructor. - */ - public StateEngine() { - states = new HashMap<String, List<State>>(); - allStates = new HashMap<String, State>(); - } - - - /** - * This method adds a state into the map <i>allStates</i>. - * - * @param state The state to add. - */ - protected void addState(State state) { - allStates.put(state.getID(), state); - } - - - /** - * Returns the state based on its ID. - * - * @param stateId The ID of the desired state. - * - * @return the state. - */ - public State getState(String stateId) { - return allStates.get(stateId); - } - - - public StateData getStateData(String artifact, String dataName) { - List<State> artifactStates = getStates(artifact); - - if (artifactStates == null || artifactStates.size() == 0) { - logger.warn("No States for Artifact '" + artifact + "' existing."); - return null; - } - - for (State state: artifactStates) { - StateData sd = state.getData(dataName); - - if (sd != null) { - return sd; - } - } - - logger.warn( - "No StateData for Artifact '" + artifact + - "' with name '" + dataName + "' existing."); - - return null; - } - - - /** - * Add new states for a specific artifact. - * - * @param artifact The name of the artifact. - * @param states A list of states that the artifact can reach. - * - * @return true, if the states were added, otherwise false. - */ - public boolean addStates(String artifact, List<State> states) { - List tmp = this.states.get(artifact); - - if (tmp != null) { - logger.info( - "States for the artifact '" + artifact + "' already stored."); - - return false; - } - - // add the state to the map with all existing states - for (State s: states) { - addState(s); - } - - logger.debug("Add new states for the artifact '" + artifact + "'"); - return this.states.put(artifact, states) != null; - } - - - /** - * Returns the state list of an artifact specified by its name. - * - * @param artifact The name of the artifact (e.g. "winfo"). - * - * @return the list of states of this artifact or <i>null</i> if no states - * are existing for this <i>artifact</i>. - */ - public List<State> getStates(String artifact) { - return states.get(artifact); - } - - - /** - * Return mapping of output to facets for an artifact in its states. - */ - public Map<String, List<String>> getCompatibleFacets(List<String> aStates) { - Map<String, List<String>> compatibilityMatrix = - new HashMap<String, List<String>>(); - - // For all states that the artifact had seen, add outputs facets. - logger.debug("Searching in " + aStates); - for (String stateId: aStates) { - - State state = allStates.get(stateId); - if (state == null) { - logger.debug("No state found for id " + stateId); - continue; - } - - for (Output output: state.getOutputs()) { - List<Facet> outFacets = output.getFacets(); - logger.debug("Facets for output " + output.getName() + " :" + outFacets); - - List<String> oldFacets = compatibilityMatrix.get(output.getName()); - - if (oldFacets == null) { - oldFacets = new ArrayList<String>(); - } - - for (Facet facet: outFacets) { - oldFacets.add(facet.getName()); - } - - compatibilityMatrix.put(output.getName(), oldFacets); - } - } - return compatibilityMatrix; - } -} -// vim:set ts=4 sw=4 et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/Transition.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -package de.intevation.artifactdatabase.transition; - -import org.w3c.dom.Node; - -import de.intevation.artifacts.Artifact; - -import de.intevation.artifactdatabase.state.State; - - -/** - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public interface Transition { - - /** - * Initializes the transition. - * - * @param config The configuration node for the transition. - */ - public void init(Node config); - - /** - * Return the ID of the start State. - */ - public String getFrom(); - - /** - * Return the ID of the target State. - */ - public String getTo(); - - /** - * Set the ID of the current State. - * - * @param from The ID of the current state. - */ - public void setFrom(String from); - - /** - * Set the ID of the target State. - * - * @param to The ID of the target state. - */ - public void setTo(String to); - - /** - * Determines if its valid to step from state <i>a</i> of an artifact - * <i>artifact</i> to state <i>b</i>. - * - * @param artifact The owner artifact of state a and b. - * @param a The current state. - * @param b The target state. - * - * @return true, if it is valid to step from a to b, otherwise false. - */ - public boolean isValid(Artifact artifact, State a, State b); -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java Thu Apr 25 10:53:15 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -package de.intevation.artifactdatabase.transition; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.log4j.Logger; - -import de.intevation.artifacts.Artifact; - -import de.intevation.artifactdatabase.state.State; -import de.intevation.artifactdatabase.state.StateEngine; - - -/** - * The TransitionEngine stores all transitions for each Artifact and should be - * used to determine, if an Artifact is able to advance from one to another - * state. - * - * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> - */ -public class TransitionEngine { - - /** The logger used in this class. */ - private static Logger logger = Logger.getLogger(TransitionEngine.class); - - /** - * A map that contains the transitions of the artifacts. The key is the name - * of the artifact, its value is a list of all transitions of this artifact. - */ - protected Map<String, List> transitions; - - - /** - * The default constructor. - */ - public TransitionEngine() { - transitions = new HashMap<String, List>(); - } - - - /** - * Add new transitions for a specific artifact. - * - * @param stateId the name of the Artifact. - * @param transition the list of transition of the artifact. - * - * @return true, if the transitions were added, otherwise false. - */ - public boolean addTransition(String stateId, Transition transition) { - List tmp = transitions.get(stateId); - - if (tmp == null) { - tmp = new ArrayList<Transition>(); - } - - tmp.add(transition); - - logger.debug("Add new transitions for state '" + stateId + "'"); - - return transitions.put(stateId, tmp) != null; - } - - - /** - * This method returns all existing transitions of a state. - * - * @param state The state - * - * @return the existing transition of <i>state</i>. - */ - public List<Transition> getTransitions(State state) { - return transitions.get(state.getID()); - } - - - /** - * This method returns the reachable states of <i>state</i>. - * - * @param state The current state. - * @param engine The state engine. - * - * @return a list of reachable states. - */ - public List<State> getReachableStates( - Artifact artifact, - State state, - StateEngine engine) { - List<Transition> transitions = getTransitions(state); - List<State> reachable = new ArrayList<State>(); - - if (transitions == null) { - return reachable; - } - - for (Transition t: transitions) { - State target = engine.getState(t.getTo()); - - if (t.isValid(artifact, state, target)) { - reachable.add(target); - } - } - - return reachable; - } - - - /** - * Determines if a state with a given identifier is reachable from a current - * state. - * - * @param artifact The owner artifact of state <i>state</i>. - * @param targetId The identifier of the target state. - * @param state The start state. - * @param stateEngine The StateEngine. - * - * @return true, if the target state is reachable, otherwise false. - */ - public boolean isStateReachable( - Artifact artifact, - String targetId, - State state, - StateEngine stateEngine) - { - List<State> reachable = getReachableStates(artifact, state,stateEngine); - - if (reachable == null || reachable.size() == 0) { - return false; - } - - for (State s: reachable) { - if (targetId.equals(s.getID())) { - return true; - } - } - - return false; - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/AbstractCallContext.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2010, 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.DataProvider; + + +/** + * Abstract class that implements some basic methods of a CallContext. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public abstract class AbstractCallContext implements CallContext { + + Logger logger = Logger.getLogger(AbstractCallContext.class); + + /** + * The ArtifactDatabase instance. + */ + protected ArtifactDatabaseImpl database; + + /** + * The action to be performed after the artifacts or collections calls. + */ + protected int action; + + /** + * The meta information of the concrete call (preferred languages et. al.) + */ + protected CallMeta callMeta; + + /** + * Map to act like a clipboard when nesting calls like a proxy artifact. + */ + protected Map customValues; + + /** + * Map to act like a clipboard when nesting calls like a proxy artifact. + */ + protected Map<Object, List<DataProvider>> dataProviders; + + + /** + * The default constructor of this abstract CallContext. + * + * @param artifactDatabase The artifact database. + * @param action The action. + * @param callMeta The CallMeta object. + */ + public AbstractCallContext( + ArtifactDatabaseImpl artifactDatabase, + int action, + CallMeta callMeta + ) { + this.database = artifactDatabase; + this.action = action; + this.callMeta = callMeta; + + database.initCallContext(this); + } + + + public void postCall() { + database.closeCallContext(this); + } + + public abstract void afterCall(int action); + + public abstract Long getTimeToLive(); + + public abstract void afterBackground(int action); + + + public Object globalContext() { + return database.context; + } + + + public ArtifactDatabase getDatabase() { + return database; + } + + + public CallMeta getMeta() { + return callMeta; + } + + + public Object getContextValue(Object key) { + return customValues != null + ? customValues.get(key) + : null; + } + + public Object putContextValue(Object key, Object value) { + if (customValues == null) { + customValues = new HashMap(); + } + return customValues.put(key, value); + } + + /** + * Get list of DataProviders that registered for given key. + * @return list (empty list if none found, never null). + */ + public List<DataProvider> getDataProvider(Object key) { + if (dataProviders != null) { + List<DataProvider> list = dataProviders.get(key); + return list != null + ? list + : java.util.Collections.<DataProvider>emptyList(); + } + return java.util.Collections.<DataProvider>emptyList(); + } + + + /** + * Let a DataProvider register itself with given key. + * Multiple DataProvider can register under the same key. + */ + public Object registerDataProvider(Object key, DataProvider value) { + List<DataProvider> providers = null; + if (dataProviders == null) { + dataProviders = new HashMap(); + providers = new ArrayList<DataProvider>(); + providers.add(value); + return dataProviders.put(key, providers); + } + providers = dataProviders.get(key); + + if (providers == null) { + providers = new ArrayList<DataProvider>(); + } + providers.add(value); + return dataProviders.put(key, providers); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/App.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifactdatabase.rest.HTTPServer; + +import java.io.File; + +import java.net.MalformedURLException; + +import org.apache.log4j.PropertyConfigurator; + +import org.slf4j.bridge.SLF4JBridgeHandler; + +/** + * Starting point of the artifact database. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class App +{ + /** + * The logging is done via Log4j. To configure the logging + * a file 'log4j.properties' is search in the configuration directory. + */ + public static final String LOG4J_PROPERTIES = + "log4j.properties"; + + /** + * Trys to load the Log4j configuration from ${config.dir}/log4j.properties. + */ + public static final void configureLogging() { + File configDir = Config.getConfigDirectory(); + File propFile = new File(configDir, LOG4J_PROPERTIES); + + if (propFile.isFile() && propFile.canRead()) { + try { + PropertyConfigurator.configure(propFile.toURI().toURL()); + SLF4JBridgeHandler.install(); + } + catch (MalformedURLException mue) { + mue.printStackTrace(System.err); + } + } + } + + /** + * Starts the artifact database. + * @param args The commandline arguments. Unused. + */ + public static void main(String[] args) { + + configureLogging(); + + FactoryBootstrap bootstrap = new FactoryBootstrap(); + + bootstrap.boot(); + + Backend backend = Backend.getInstance(); + + ArtifactDatabaseImpl db = new ArtifactDatabaseImpl( + bootstrap, backend); + + DatabaseCleaner cleaner = new DatabaseCleaner( + bootstrap.getContext(), backend, backend.getConfig()); + + HTTPServer httpServer = bootstrap.getHTTPServer(); + + bootstrap = null; + + backend.setCleaner(cleaner); + + cleaner.setLockedIdsProvider(db); + + cleaner.start(); + + db.start(); + + httpServer.startAsServer(db); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/ArtifactCallContext.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010, 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import java.util.LinkedList; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.Message; + +import de.intevation.artifactdatabase.Backend.PersistentArtifact; + + +/** + * Class that implements the call context handed to the methods calls + * describe(), feed(), etc. of the artifact. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class ArtifactCallContext extends AbstractCallContext { + + private static Logger logger = Logger.getLogger(ArtifactCallContext.class); + + + /** + * Error message issued if an artifact wants to translate itself + * into a none valid persistent state. + */ + public static final String INVALID_CALL_STATE = "Invalid after call state"; + + /** + * Error message issued if one tries to remove a requested artifact + * from the list of artifacts running in background which is + * not in this list. + */ + public static final String NOT_IN_BACKGROUND = "Not in background"; + + + /** + * The persistence wrapper around the living artifact + */ + protected PersistentArtifact artifact; + + + public ArtifactCallContext( + ArtifactDatabaseImpl artifactDatabase, + int action, + CallMeta callMeta, + PersistentArtifact artifact) + { + super(artifactDatabase, action, callMeta); + + this.artifact = artifact; + } + + + public void afterCall(int action) { + this.action = action; + if (action == BACKGROUND) { + database.addIdToBackground(artifact.getId()); + } + } + + + public void afterBackground(int action) { + if (this.action != BACKGROUND) { + throw new IllegalStateException(NOT_IN_BACKGROUND); + } + database.fromBackground(artifact, action); + } + + + public boolean isInBackground() { + return database.getLockedIds().contains(artifact.getId()); + } + + + public void addBackgroundMessage(Message msg) { + database.addBackgroundMessage(artifact.getArtifact().identifier(), msg); + } + + + public LinkedList<Message> getBackgroundMessages() { + return database.getBackgroundMessages( + artifact.getArtifact().identifier()); + } + + + public Long getTimeToLive() { + return artifact.getTTL(); + } + + + /** + * Dispatches and executes the persistence action after + * the return of the concrete artifact call. + */ + public void postCall() { + try { + switch (action) { + case NOTHING: + break; + case TOUCH: + artifact.touch(); + break; + case STORE: + artifact.store(); + break; + case BACKGROUND: + logger.warn( + "BACKGROUND processing is not fully implemented, yet!"); + artifact.store(); + break; + default: + logger.error(INVALID_CALL_STATE + ": " + action); + throw new IllegalStateException(INVALID_CALL_STATE); + } + } + finally { + super.postCall(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/ArtifactDatabaseImpl.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,1976 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.artifactdatabase.Backend.PersistentArtifact; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.ArtifactSerializer; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.CollectionItem; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.Hook; +import de.intevation.artifacts.Message; +import de.intevation.artifacts.Service; +import de.intevation.artifacts.ServiceFactory; +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + +import java.io.IOException; +import java.io.OutputStream; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.xml.xpath.XPathConstants; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * The core implementation of artifact database. This layer exposes + * the needed methods to the artifact runtime system which e.g. may + * expose them via REST. The concrete persistent representation of the + * artifacts is handled by the {@link Backend backend}. + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class ArtifactDatabaseImpl +implements ArtifactDatabase, + DatabaseCleaner.LockedIdsProvider, + Backend.FactoryLookup +{ + private static Logger logger = + Logger.getLogger(ArtifactDatabaseImpl.class); + + /** The key under which the artifact database is stored in the global + * context.*/ + public static final String GLOBAL_CONTEXT_KEY = "global.artifact.database"; + + /** Message that is returned if an operation was successful.*/ + public static final String OPERATION_SUCCESSFUL = + "SUCCESS"; + + /** Message that is returned if an operation failed.*/ + public static final String OPERATION_FAILURE = + "FAILURE"; + + /** + * Error message issued if a requested artifact factory + * is not registered to this database. + */ + public static final String NO_SUCH_FACTORY = + "No such factory"; + + /** + * Error message issued if a requested artifact is not found + * in this database. + */ + public static final String NO_SUCH_ARTIFACT = + "No such artifact"; + + /** + * Error message issued if a requested artifact is not found + * in this database. + */ + public static final String NO_SUCH_COLLECTION = + "No such collection"; + + /** + * Error message issued if the creation of an artifact failed. + */ + public static final String CREATION_FAILED = + "Creation of artifact failed"; + + /** + * Error message if an severe internal error occurred. + */ + public static final String INTERNAL_ERROR = + "Creation of artifact failed"; + + /** + * Error message issued if a requested service is not + * offered by this database. + */ + public static final String NO_SUCH_SERVICE = + "No such service"; + + /** + * Default digest hash to be used while im-/exporting artifacts. + */ + public static final String DIGEST_ALGORITHM = + "SHA-1"; + + /** + * XPath to get the checksum from an XML representation of + * an exported artifact. + */ + public static final String XPATH_IMPORT_CHECKSUM = + "/art:action/art:data/@checksum"; + + /** + * XPath to get the name of the factory which should be + * used to revive an antrifact that is going to be imported. + */ + public static final String XPATH_IMPORT_FACTORY = + "/art:action/art:data/@factory"; + + /** + * XPath to get the base64 encoded data of an artifact + * that is going to be imported. + */ + public static final String XPATH_IMPORT_DATA = + "/art:action/art:data/text()"; + + /** + * Error message issued if the checksum of an + * artifact to be imported has an invalid syntax. + */ + public static final String INVALID_CHECKSUM = + "Invalid checksum"; + + /** + * Error message issued the checksum validation + * of an artifact to be imported fails. + */ + public static final String CHECKSUM_MISMATCH = + "Mismatching checksum"; + + /** + * Error message issued if an artifact to be imported + * does not have any data. + */ + public static final String NO_DATA = + "No data"; + + /** + * Error message issued if the deserialization of + * an artifact to be imported fails. + */ + public static final String INVALID_ARTIFACT = + "Invalid artifact"; + + + // User constants + + /** + * Error message issued if the creation of a user failed. + */ + public static final String USER_CREATION_FAILED = + "Creation of user failed."; + + /** XPath to figure out the name of a new user.*/ + public static final String XPATH_USERNAME = + "/art:action/art:user/@name"; + + /** XPath to figure out the role of a new user.*/ + public static final String XPATH_USERROLE = + "/art:action/art:user/art:role"; + + /** XPath to figure out the account of a new user.*/ + public static final String XPATH_USERACCOUNT = + "/art:action/art:user/art:account/@name"; + + /** XPath to figure out the account of when searching for a user .*/ + public static final String XPATH_USERACCOUNT_FIND = + "/art:action/art:account/@name"; + + /** Error message if a specified user does not exist.*/ + public static final String NO_SUCH_USER = + "No such user"; + + /** Error message if no username is given for user creation.*/ + public static final String NO_USERNAME = + "Invalid username"; + + /** Error message if no user account is given for user creation.*/ + public static final String NO_USERACCOUNT = + "Invalid user account name"; + + // Collection constants + + /** + * Error message issued if the creation of a collection failed. + */ + public static final String COLLECTION_CREATION_FAILED = + "Creation of collection failed"; + + /** + * XPath to figure out the name of a collection described in the incoming + * document. + */ + public static final String XPATH_COLLECTION_NAME = + "/art:action/art:type/art:collection/@name"; + + /** + * XPath to figure out the attributes for a collection. + */ + public static final String XPATH_COLLECTION_ATTRIBUTE = + "/art:action/art:type/art:collection/art:attribute"; + + /** + * XPath to figure out the attributes for an artifact that is put into a + * collection. + */ + public static final String XPATH_COLLECTION_ITEM_ATTRIBUTE = + "/art:action/art:type/art:artifact/art:attribute"; + + /** + * XPath to figure out the time to live value for setting a new TTL. + */ + public static final String XPATH_COLLECTION_TTL = + "/art:action/art:type/art:ttl/@value"; + + + /** + * This inner class allows the deferral of writing the output + * of the artifact's out() call. + */ + public class DeferredOutputImpl + implements DeferredOutput + { + /** + * The persistence wrapper around a living artifact. + */ + protected PersistentArtifact artifact; + /** + * The output type. + */ + protected String type; + /** + * The input document for the artifact's out() call. + */ + protected Document format; + /** + * The meta information of the artifact's out() call. + */ + protected CallMeta callMeta; + + /** + * Default constructor. + */ + public DeferredOutputImpl() { + } + + /** + * Constructor to create a deferred execution unit for + * the artifact's out() call given an artifact, an input document + * an the meta information. + * @param artifact The persistence wrapper around a living artifact. + * @param format The input document for the artifact's out() call. + * @param callMeta The meta information of the artifact's out() call. + */ + public DeferredOutputImpl( + PersistentArtifact artifact, + String type, + Document format, + CallMeta callMeta + ) { + this.artifact = artifact; + this.type = type; + this.format = format; + this.callMeta = callMeta; + } + + public void write(OutputStream output) throws IOException { + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + artifact); + + try { + artifact.getArtifact().out(type, format, output, cc); + } + finally { + cc.postCall(); + } + } + } // class DeferredOutputImpl + + + /** + * This inner class allows the deferral of writing the output + * of the artifact's out() call. + */ + public class DeferredCollectionOutputImpl + implements DeferredOutput + { + /** + * The persistence wrapper around a living collection. + */ + protected ArtifactCollection collection; + /** + * The output type. + */ + protected String type; + /** + * The input document for the collection's out() call. + */ + protected Document format; + /** + * The meta information of the collection's out() call. + */ + protected CallMeta callMeta; + + /** + * Default constructor. + */ + public DeferredCollectionOutputImpl() { + } + + /** + * Constructor to create a deferred execution unit for + * the collection's out() call given a collection, an input document + * an the meta information. + * @param collection The collection. + * @param format The input document for the collection's out() call. + * @param callMeta The meta information of the collection's out() call. + */ + public DeferredCollectionOutputImpl( + ArtifactCollection collection, + String type, + Document format, + CallMeta callMeta + ) { + this.collection = collection; + this.type = type; + this.format = format; + this.callMeta = callMeta; + } + + public void write(OutputStream output) throws IOException { + + CollectionCallContext cc = new CollectionCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + collection); + + try { + collection.out(type, format, output, cc); + } + finally { + cc.postCall(); + } + } + } // class DeferredCollectionOutputImpl + + /** + * List of name/description pairs needed for + * {@link #artifactFactoryNamesAndDescriptions() }. + */ + protected String [][] factoryNamesAndDescription; + /** + * Map to access artifact factories by there name. + */ + protected HashMap name2factory; + + /** + * List of name/description pairs needed for + * {@link #serviceNamesAndDescriptions() }. + */ + protected String [][] serviceNamesAndDescription; + /** + * Map to access services by there name. + */ + protected HashMap name2service; + + /** + * The factory that is used to create new artifact collections. + */ + protected ArtifactCollectionFactory collectionFactory; + + /** + * The factory that is used to create and list users. + */ + protected UserFactory userFactory; + + /** + * Reference to the storage backend. + */ + protected Backend backend; + /** + * Reference of the global context of the artifact runtime system. + */ + protected GlobalContext context; + + /** + * The signing secret to be used for ex-/importing artifacts. + */ + protected byte [] exportSecret; + + /** + * A set of ids of artifact which currently running in background. + * This artifacts should not be removed from the database by the + * database cleaner. + */ + protected HashSet<Integer> backgroundIds; + + /** + * A list of background messages for Artifacts and Collections. + */ + protected Map<String, LinkedList<Message>> backgroundMsgs; + + + protected CallContext.Listener callContextListener; + + /** + * Hooks that are executed after an artifact has been fed. + */ + protected List<Hook> postFeedHooks; + + /** + * Hooks that are executed after an artifact has advanced. + */ + protected List<Hook> postAdvanceHooks; + + /** + * Hooks that are executed after an artifact's describe() operation was + * called. + */ + protected List<Hook> postDescribeHooks; + + protected List<LifetimeListener> lifetimeListeners; + + /** + * Default constructor. + */ + public ArtifactDatabaseImpl() { + } + + /** + * Constructor to create a artifact database with the given + * bootstrap parameters like artifact- and service factories et. al. + * Created this way the artifact database has no backend. + * @param bootstrap The parameters to start this artifact database. + */ + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { + this(bootstrap, null); + } + + /** + * Constructor to create a artifact database with the a given + * backend and + * bootstrap parameters like artifact- and service factories et. al. + * @param bootstrap The parameters to start this artifact database. + * @param backend The storage backend. + */ + public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { + + logger.debug("new ArtifactDatabaseImpl"); + + backgroundIds = new HashSet<Integer>(); + backgroundMsgs = new HashMap<String, LinkedList<Message>>(); + + setupArtifactCollectionFactory(bootstrap); + setupArtifactFactories(bootstrap); + setupServices(bootstrap); + setupUserFactory(bootstrap); + setupCallContextListener(bootstrap); + setupHooks(bootstrap); + setupLifetimeListeners(bootstrap); + + context = bootstrap.getContext(); + context.put(GLOBAL_CONTEXT_KEY, this); + + exportSecret = bootstrap.getExportSecret(); + + wireWithBackend(backend, bootstrap); + } + + public CallContext.Listener getCallContextListener() { + return callContextListener; + } + + public void setCallContextListener( + CallContext.Listener callContextListener + ) { + this.callContextListener = callContextListener; + } + + + public void setPostFeedHook(List<Hook> postFeedHooks) { + this.postFeedHooks = postFeedHooks; + } + + public void setPostAdvanceHook(List<Hook> postAdvanceHooks) { + this.postAdvanceHooks = postAdvanceHooks; + } + + public void setPostDescribeHook(List<Hook> postDescribeHooks) { + this.postDescribeHooks = postDescribeHooks; + } + + /** + * Used to extract the artifact collection factory from bootstrap. + * + * @param bootstrap The bootstrap parameters. + */ + protected void setupArtifactCollectionFactory(FactoryBootstrap bootstrap) { + collectionFactory = bootstrap.getArtifactCollectionFactory(); + } + + /** + * Used to extract the artifact factories from the bootstrap + * parameters and building the internal lookup tables. + * @param bootstrap The bootstrap parameters. + */ + protected void setupArtifactFactories(FactoryBootstrap bootstrap) { + name2factory = new HashMap(); + + ArtifactFactory [] factories = bootstrap.getArtifactFactories(); + factoryNamesAndDescription = new String[factories.length][]; + + for (int i = 0; i < factories.length; ++i) { + + ArtifactFactory factory = factories[i]; + + String name = factory.getName(); + String description = factory.getDescription(); + + factoryNamesAndDescription[i] = + new String [] { name, description }; + + name2factory.put(name, factory); + } + } + + /** + * Used to extract the callContextListener from the bootstrap. + * + * @param bootstrap The bootstrap parameters. + */ + protected void setupCallContextListener(FactoryBootstrap bootstrap) { + setCallContextListener(bootstrap.getCallContextListener()); + } + + + protected void setupHooks(FactoryBootstrap bootstrap) { + setPostFeedHook(bootstrap.getPostFeedHooks()); + setPostAdvanceHook(bootstrap.getPostAdvanceHooks()); + setPostDescribeHook(bootstrap.getPostDescribeHooks()); + } + + protected void setupBackendListeners(FactoryBootstrap bootstrap) { + logger.debug("setupBackendListeners"); + List<BackendListener> bls = bootstrap.getBackendListeners(); + if (bls != null && !bls.isEmpty()) { + for (BackendListener listener: bls) { + listener.setup(context); + } + backend.addAllListeners(bls); + } + } + + protected void setupLifetimeListeners(FactoryBootstrap bootstrap) { + this.lifetimeListeners = bootstrap.getLifetimeListeners(); + } + + /** + * Used to extract the user factory from the bootstrap. + */ + protected void setupUserFactory(FactoryBootstrap bootstrap) { + userFactory = bootstrap.getUserFactory(); + } + + /** + * Used to extract the service factories from the bootstrap + * parameters, setting up the services and building the internal + * lookup tables. + * @param bootstrap The bootstrap parameters. + */ + protected void setupServices(FactoryBootstrap bootstrap) { + + name2service = new HashMap(); + + ServiceFactory [] serviceFactories = + bootstrap.getServiceFactories(); + + serviceNamesAndDescription = + new String[serviceFactories.length][]; + + for (int i = 0; i < serviceFactories.length; ++i) { + ServiceFactory factory = serviceFactories[i]; + + String name = factory.getName(); + String description = factory.getDescription(); + + serviceNamesAndDescription[i] = + new String [] { name, description }; + + name2service.put( + name, + factory.createService(bootstrap.getContext())); + } + + } + + /** + * Wires a storage backend to this artifact database and + * establishes a callback to be able to revive artifacts + * via the serializers of this artifact factories. + * @param backend The backend to be wired with this artifact database. + */ + public void wireWithBackend(Backend backend, FactoryBootstrap bootstrap) { + logger.debug("wireWithBackend"); + if (backend != null) { + this.backend = backend; + backend.setFactoryLookup(this); + setupBackendListeners(bootstrap); + } + } + + /** + * Called after an backgrounded artifact signals its + * will to be written back to the backend. + * @param artifact The persistence wrapper around + * the backgrounded artifact. + * @param action The action to be performed. + */ + protected void fromBackground(PersistentArtifact artifact, int action) { + logger.warn("BACKGROUND processing is not fully implemented, yet!"); + switch (action) { + case CallContext.NOTHING: + break; + case CallContext.TOUCH: + artifact.touch(); + break; + case CallContext.STORE: + artifact.store(); + break; + default: + logger.warn("operation not allowed in fromBackground"); + } + removeIdFromBackground(artifact.getId()); + removeBackgroundMessages(artifact.getArtifact().identifier()); + } + + /** + * Removes an artifact's database id from the set of backgrounded + * artifacts. The database cleaner is now able to remove it safely + * from the database again. + * @param id The database id of the artifact. + */ + protected void removeIdFromBackground(int id) { + synchronized (backgroundIds) { + backgroundIds.remove(id); + } + } + + + /** + * Removes all messages that have been added to the <i>backgroundMsgs</i> + * list. + * + * @param uuid The UUID of an artifact or collection. + */ + protected void removeBackgroundMessages(String uuid) { + logger.debug("Remove background messages for: " + uuid); + + synchronized (backgroundMsgs) { + backgroundMsgs.remove(uuid); + } + } + + /** + * Adds an artifact's database id to the set of artifacts + * running in backgroound. To be in this set prevents the + * artifact to be removed from the database by the database cleaner. + * @param id The database id of the artifact to be protected + * from being removed from the database. + */ + protected void addIdToBackground(int id) { + synchronized (backgroundIds) { + backgroundIds.add(Integer.valueOf(id)); + } + } + + /** + * Adds a <i>Message</i> to the background messages list of the Artifact or + * Collection. + * + * @param uuid The UUID of the Artifact or Collection. + * @param msg The message that should be added to the background messages + * list. + */ + public void addBackgroundMessage(String uuid, Message msg) { + logger.debug("Add new background messsage for: " + uuid); + + synchronized (backgroundMsgs) { + LinkedList<Message> messages = backgroundMsgs.get(uuid); + + if (messages == null) { + messages = new LinkedList<Message>(); + backgroundMsgs.put(uuid, messages); + } + + messages.addLast(msg); + } + } + + public Set<Integer> getLockedIds() { + synchronized (backgroundIds) { + return new HashSet<Integer>(backgroundIds); + } + } + + /** + * Returns the background <i>Message</i>s for a specific Artifact or + * Collection. + * + * @param uuid The Artifact's or Collection's UUID. + * + * @return a <i>List</i> of <i>Message</i>s or null if no messages are + * existing. + */ + public LinkedList<Message> getBackgroundMessages(String uuid) { + logger.debug("Retrieve background message for: " + uuid); + + synchronized (backgroundMsgs) { + return backgroundMsgs.get(uuid); + } + } + + public String [][] artifactFactoryNamesAndDescriptions() { + return factoryNamesAndDescription; + } + + public ArtifactFactory getInternalArtifactFactory(String factoryName) { + return getArtifactFactory(factoryName); + } + + public ArtifactFactory getArtifactFactory(String factoryName) { + return (ArtifactFactory)name2factory.get(factoryName); + } + + public UserFactory getUserFactory() { + return userFactory; + } + + public ArtifactCollectionFactory getArtifactCollectionFactory() { + return collectionFactory; + } + + public Document createArtifactWithFactory( + String factoryName, + CallMeta callMeta, + Document data + ) + throws ArtifactDatabaseException + { + logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory " + + factoryName); + ArtifactFactory factory = getArtifactFactory(factoryName); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + Artifact artifact = factory.createArtifact( + backend.newIdentifier(), + context, + callMeta, + data); + + if (artifact == null) { + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + PersistentArtifact persistentArtifact; + + try { + persistentArtifact = backend.storeInitially( + artifact, + factory, + factory.timeToLiveUntouched(artifact, context)); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + persistentArtifact); + + try { + return artifact.describe(null, cc); + } + finally { + cc.postCall(); + } + } + + + public Artifact getRawArtifact(String identifier) + throws ArtifactDatabaseException + { + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return artifact.getArtifact(); + } + + + public Document describe( + String identifier, + Document data, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.TOUCH, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.describe(data, cc); + + if (postDescribeHooks != null) { + for (Hook hook: postDescribeHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public Document advance( + String identifier, + Document target, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.STORE, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.advance(target, cc); + + if (postAdvanceHooks != null) { + for (Hook hook: postAdvanceHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public Document feed(String identifier, Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.STORE, + callMeta, + artifact); + + try { + Artifact art = artifact.getArtifact(); + Document res = art.feed(data, cc); + + if (postFeedHooks != null) { + for (Hook hook: postFeedHooks) { + hook.execute(art, cc, res); + } + } + + return res; + } + finally { + cc.postCall(); + } + } + + public DeferredOutput out( + String identifier, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + return out(identifier, null, format, callMeta); + } + + public DeferredOutput out( + String identifier, + String type, + Document format, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + // TODO: Handle background tasks + PersistentArtifact artifact = backend.getArtifact(identifier); + + if (artifact == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return new DeferredOutputImpl(artifact, type, format, callMeta); + } + + public Document exportArtifact(String artifact, CallMeta callMeta) + throws ArtifactDatabaseException + { + final String [] factoryName = new String[1]; + + byte [] bytes = (byte [])backend.loadArtifact( + artifact, + new Backend.ArtifactLoader() { + public Object load( + ArtifactFactory factory, + Long ttl, + byte [] bytes, + int id + ) { + factoryName[0] = factory.getName(); + + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(bytes); + artifact.cleanup(context); + + return serializer.toBytes(artifact); + } + }); + + if (bytes == null) { + throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); + } + + return createExportDocument( + factoryName[0], + bytes, + exportSecret); + } + + /** + * Creates an exteral XML representation of an artifact. + * @param factoryName The name of the factory which is responsible + * for the serialized artifact. + * @param artifact The byte data of the artifact itself. + * @param secret The signing secret. + * @return An XML document containing the external representation + * of the artifact. + */ + protected static Document createExportDocument( + String factoryName, + byte [] artifact, + byte [] secret + ) { + Document document = XMLUtils.newDocument(); + + MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } + catch (NoSuchAlgorithmException nsae) { + logger.error(nsae.getLocalizedMessage(), nsae); + return document; + } + + md.update(artifact); + md.update(secret); + + String checksum = Hex.encodeHexString(md.digest()); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("action"); + document.appendChild(root); + + Element type = ec.create("type"); + ec.addAttr(type, "name", "export", true); + root.appendChild(type); + + Element data = ec.create("data"); + ec.addAttr(data, "checksum", checksum, true); + ec.addAttr(data, "factory", factoryName, true); + data.setTextContent(Base64.encodeBase64String(artifact)); + + root.appendChild(data); + + return document; + } + + public Document importArtifact(Document input, CallMeta callMeta) + throws ArtifactDatabaseException + { + String factoryName = XMLUtils.xpathString( + input, + XPATH_IMPORT_FACTORY, + ArtifactNamespaceContext.INSTANCE); + + ArtifactFactory factory; + + if (factoryName == null + || (factoryName = factoryName.trim()).length() == 0 + || (factory = getArtifactFactory(factoryName)) == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String checksumString = XMLUtils.xpathString( + input, + XPATH_IMPORT_CHECKSUM, + ArtifactNamespaceContext.INSTANCE); + + byte [] checksum; + + if (checksumString == null + || (checksumString = checksumString.trim()).length() == 0 + || (checksum = StringUtils.decodeHex(checksumString)) == null + ) { + throw new ArtifactDatabaseException(INVALID_CHECKSUM); + } + + checksumString = null; + + String dataString = XMLUtils.xpathString( + input, + XPATH_IMPORT_DATA, + ArtifactNamespaceContext.INSTANCE); + + if (dataString == null + || (dataString = dataString.trim()).length() == 0) { + throw new ArtifactDatabaseException(NO_DATA); + } + + byte [] data = Base64.decodeBase64(dataString); + + dataString = null; + + MessageDigest md; + try { + md = MessageDigest.getInstance(DIGEST_ALGORITHM); + } + catch (NoSuchAlgorithmException nsae) { + logger.error(nsae.getLocalizedMessage(), nsae); + return XMLUtils.newDocument(); + } + + md.update(data); + md.update(exportSecret); + + byte [] digest = md.digest(); + + if (!Arrays.equals(checksum, digest)) { + throw new ArtifactDatabaseException(CHECKSUM_MISMATCH); + } + + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(data); data = null; + + if (artifact == null) { + throw new ArtifactDatabaseException(INVALID_ARTIFACT); + } + + artifact.setIdentifier(backend.newIdentifier()); + PersistentArtifact persistentArtifact; + + try { + persistentArtifact = backend.storeOrReplace( + artifact, + factory, + factory.timeToLiveUntouched(artifact, context)); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + throw new ArtifactDatabaseException(CREATION_FAILED); + } + + ArtifactCallContext cc = new ArtifactCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + persistentArtifact); + + try { + return artifact.describe(input, cc); + } + finally { + cc.postCall(); + } + } + + public String [][] serviceNamesAndDescriptions() { + return serviceNamesAndDescription; + } + + public Service.Output process( + String serviceName, + Document input, + CallMeta callMeta + ) + throws ArtifactDatabaseException + { + Service service = (Service)name2service.get(serviceName); + + if (service == null) { + throw new ArtifactDatabaseException(NO_SUCH_SERVICE); + } + + return service.process(input, context, callMeta); + } + + // User API + + /** Returns user(s) elements. */ + public Document listUsers(CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + User [] users = backend.getUsers(factory, context); + + if (users != null) { + logger.debug(users.length + " users found in the backend."); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("users"); + result.appendChild(root); + + if(users != null) { + for (User user: users) { + Element ue = ec.create("user"); + ec.addAttr(ue, "uuid", user.identifier(), true); + ec.addAttr(ue, "name", user.getName(), true); + Element ua = ec.create("account"); + ec.addAttr(ua, "name", user.getAccount(), true); + ue.appendChild(ua); + + Document role = user.getRole(); + + if (role != null) { + ue.appendChild(result.importNode(role.getFirstChild(), true)); + } + + root.appendChild(ue); + } + } + + return result; + } + + /** Search for a user. */ + public Document findUser(Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String account = XMLUtils.xpathString( + data, XPATH_USERACCOUNT_FIND, ArtifactNamespaceContext.INSTANCE); + + if (account == null || account.length() == 0) { + logger.warn("Can't find user without account!"); + throw new ArtifactDatabaseException(NO_USERACCOUNT); + } + + User user = backend.findUser(account, factory, context); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element ue = ec.create("user"); + + if (user != null) { + logger.debug(user + " user found in the backend."); + + ec.addAttr(ue, "uuid", user.identifier(), true); + ec.addAttr(ue, "name", user.getName(), true); + Element ua = ec.create("account"); + ec.addAttr(ua, "name", user.getAccount(), true); + ue.appendChild(ua); + + Document role = user.getRole(); + + if (role != null) { + ue.appendChild(result.importNode(role.getFirstChild(), true)); + } + } + + result.appendChild(ue); + + return result; + } + + public Document createUser(Document data, CallMeta callMeta) + throws ArtifactDatabaseException + { + UserFactory factory = getUserFactory(); + + if (factory == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String name = XMLUtils.xpathString( + data, XPATH_USERNAME, ArtifactNamespaceContext.INSTANCE); + + if (name == null || name.length() == 0) { + logger.warn("User without username not accepted!"); + throw new ArtifactDatabaseException(NO_USERNAME); + } + + String account = XMLUtils.xpathString( + data, XPATH_USERACCOUNT, ArtifactNamespaceContext.INSTANCE); + + if (account == null || account.length() == 0) { + logger.warn("User without account not accepted!"); + throw new ArtifactDatabaseException(NO_USERACCOUNT); + } + + Node tmp = (Node) XMLUtils.xpath( + data, + XPATH_USERROLE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + Document role = XMLUtils.newDocument(); + + if (tmp != null) { + Node clone = role.importNode(tmp, true); + role.appendChild(clone); + } + + User newUser = null; + + try { + newUser = backend.createUser(name, account, role, userFactory, context); + } + catch (Exception e) { + logger.error(e.getMessage(), e); + throw new ArtifactDatabaseException(USER_CREATION_FAILED); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + + if (newUser != null) { + root.setTextContent(OPERATION_SUCCESSFUL); + } + else { + root.setTextContent(OPERATION_FAILURE); + } + + result.appendChild(root); + + return result; + } + + public Document deleteUser(String userId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Delete user: " + userId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.deleteUser(userId); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + // Collection API + + public Document getCollectionsMasterArtifact( + String collectionId, + CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + String masterUUID = backend.getMasterArtifact(collectionId); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + Element root = ec.create("artifact-collection"); + ec.addAttr(root, "name", c.getName(), true); + ec.addAttr(root, "uuid", c.identifier(), true); + ec.addAttr(root, "ttl", String.valueOf(c.getTTL()), true); + + Date creationTime = c.getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + ec.addAttr(root, "creation", creation, true); + result.appendChild(root); + + if (masterUUID == null || masterUUID.length() == 0) { + logger.debug("No master for the collection existing."); + return result; + } + + Element master = ec.create("artifact"); + ec.addAttr(master, "uuid", masterUUID, true); + + root.appendChild(master); + + return result; + } + + public Document listCollections(String userId, CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + UserFactory uf = getUserFactory(); + + if (acf == null || uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + logger.debug("Fetch the list of collection for user: " + userId); + + ArtifactCollection [] ac = backend.listCollections( + userId, + null, // XXX: fetch from REST + acf, uf, + context); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("artifact-collections"); + result.appendChild(root); + + if (ac == null || ac.length == 0) { + logger.debug("No collections for the user existing."); + + return result; + } + + logger.debug("Found " + ac.length + " collections of the user."); + + for (ArtifactCollection c: ac) { + Element collection = ec.create("artifact-collection"); + ec.addAttr(collection, "name", c.getName(), true); + ec.addAttr(collection, "uuid", c.identifier(), true); + ec.addAttr(collection, "ttl", String.valueOf(c.getTTL()), true); + + Date creationTime = c.getCreationTime(); + String creation = creationTime != null + ? Long.toString(creationTime.getTime()) + : ""; + + ec.addAttr(collection, "creation", creation, true); + + root.appendChild(collection); + } + + return result; + } + + public Document createCollection(String ownerId, Document data, + CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + String name = XMLUtils.xpathString( + data, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); + + logger.debug("Create new collection with name: " + name); + + Document attr = null; + + Node attrNode = (Node) XMLUtils.xpath( + data, + XPATH_COLLECTION_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attrNode != null) { + attr = XMLUtils.newDocument(); + attr.appendChild(attr.importNode(attrNode, true)); + } + + ArtifactCollection ac = backend.createCollection( + ownerId, name, acf, attr, context); + + if (ac == null) { + throw new ArtifactDatabaseException(COLLECTION_CREATION_FAILED); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + Element acElement = ec.create("artifact-collection"); + ec.addAttr(acElement, "uuid", ac.identifier(), true); + ec.addAttr(acElement, "ttl", String.valueOf(ac.getTTL()), true); + + root.appendChild(acElement); + + return result; + } + + public Document deleteCollection(String collectionId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Delete collection: " + collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.deleteCollection(collectionId); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document describeCollection(String collectionId, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Describe collection: " + collectionId); + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + CollectionCallContext cc = new CollectionCallContext( + ArtifactDatabaseImpl.this, + CallContext.NOTHING, + callMeta, + c); + + try { + return c.describe(cc); + } + finally { + cc.postCall(); + } + } + + + public Document getCollectionAttribute(String collectionId, CallMeta meta) + throws ArtifactDatabaseException + { + logger.debug("Fetch collection attribute for: " + collectionId); + + return backend.getCollectionAttribute(collectionId); + } + + + public Document setCollectionAttribute( + String collectionId, + CallMeta meta, + Document attribute) + throws ArtifactDatabaseException + { + logger.debug("Set new attribute for the collection: " + collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.setCollectionAttribute( + collectionId, attribute); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document getCollectionItemAttribute(String collectionId, String artifactId, + CallMeta callMeta) throws ArtifactDatabaseException + { + logger.debug("Fetch the attribute for the artifact: " + artifactId); + + return backend.getCollectionItemAttribute(collectionId, artifactId); + } + + public Document setCollectionItemAttribute(String collectionId, String artifactId, + Document source, CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug("Set the attribute for the artifact: " + artifactId); + + Document attribute = null; + + Node attr = (Node) XMLUtils.xpath( + source, + XPATH_COLLECTION_ITEM_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attr != null) { + attribute = XMLUtils.newDocument(); + attribute.appendChild(attribute.importNode(attr, true)); + } + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + boolean success = backend.setCollectionItemAttribute( + collectionId, artifactId, attribute); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document addCollectionArtifact( + String collectionId, + String artifactId, + Document input, + CallMeta callMeta) + throws ArtifactDatabaseException + { + logger.debug( + "Add artifact '" + artifactId + "' collection '" +collectionId+"'"); + + Document attr = XMLUtils.newDocument(); + + Node attrNode = (Node) XMLUtils.xpath( + input, + XPATH_COLLECTION_ITEM_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + if (attrNode != null) { + attr.appendChild(attr.importNode(attrNode, true)); + } + + boolean success = backend.addCollectionArtifact( + collectionId, + artifactId, + attr); + + if (!success) { + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + root.setTextContent(OPERATION_FAILURE); + + return result; + } + + return describeCollection(collectionId, callMeta); + } + + public Document removeCollectionArtifact(String collectionId, String artifactId, + CallMeta callMeta) throws ArtifactDatabaseException + { + logger.debug( + "Remove artifact '" + artifactId + "' from collection '" + + collectionId + "'"); + + Document attr = XMLUtils.newDocument(); + + boolean success = backend.removeCollectionArtifact( + collectionId, + artifactId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + public Document listCollectionArtifacts(String collectionId, + CallMeta callMeta) throws ArtifactDatabaseException + { + CollectionItem[] items = backend.listCollectionArtifacts(collectionId); + + Document result = XMLUtils.newDocument(); + + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + Element ac = ec.create("artifact-collection"); + ec.addAttr(ac, "uuid", collectionId, true); + + for (CollectionItem item: items) { + Element i = ec.create("collection-item"); + Element attr = ec.create("attribute"); + ec.addAttr(i, "uuid", item.getArtifactIdentifier(), true); + + Document attribute = item.getAttribute(); + if (attribute != null) { + Node firstChild = attribute.getFirstChild(); + attr.appendChild(result.importNode(firstChild, true)); + } + else { + logger.debug("No attributes for the collection item!"); + } + + i.appendChild(attr); + ac.appendChild(i); + } + + root.appendChild(ac); + result.appendChild(root); + + return result; + } + + public Document setCollectionTTL(String uuid, Document doc, CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + String tmp = XMLUtils.xpathString( + doc, XPATH_COLLECTION_TTL, ArtifactNamespaceContext.INSTANCE); + + logger.info("Set TTL of artifact collection '" + uuid + "' to: " + tmp); + + if (tmp == null || tmp.length() == 0) { + logger.warn("No ttl for this collection specified."); + root.setTextContent(OPERATION_FAILURE); + + return result; + } + + Long ttl = null; + if ((tmp = tmp.toUpperCase()).equals("INF")) { + ttl = null; + } + else if (tmp.equals("DEFAULT")) { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + ttl = acf.timeToLiveUntouched(null, context); + } + else { + try { + ttl = Long.valueOf(tmp); + + if (ttl < 0) { + throw new NumberFormatException("Negative value."); + } + } + catch (NumberFormatException nfe) { + logger.error("Could not determine TTL", nfe); + root.setTextContent(OPERATION_FAILURE); + return result; + } + } + + boolean success = backend.setCollectionTTL(uuid, ttl); + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + public Document setCollectionName(String uuid, Document doc, CallMeta meta) + throws ArtifactDatabaseException + { + Document result = XMLUtils.newDocument(); + XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( + result, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element root = ec.create("result"); + result.appendChild(root); + + String name = XMLUtils.xpathString( + doc, XPATH_COLLECTION_NAME, ArtifactNamespaceContext.INSTANCE); + + logger.info("Set name of collection '" + uuid + "' to: " + name); + + if (name == null || name.length() == 0) { + logger.warn("The new name is emtpy. No new name set!"); + root.setTextContent(OPERATION_FAILURE); + return result; + } + + boolean success = backend.setCollectionName(uuid, name); + root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE); + + return result; + } + + + public DeferredOutput outCollection( + String collectionId, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + return outCollection(collectionId, null, format, callMeta); + } + + public DeferredOutput outCollection( + String collectionId, + String type, + Document format, + CallMeta callMeta) + throws ArtifactDatabaseException + { + ArtifactCollectionFactory acf = getArtifactCollectionFactory(); + + if (acf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + UserFactory uf = getUserFactory(); + if (uf == null) { + throw new ArtifactDatabaseException(NO_SUCH_FACTORY); + } + + ArtifactCollection c = backend.getCollection( + collectionId, acf, uf, context); + + if (c == null) { + logger.warn("No collection found with identifier: " + collectionId); + throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); + } + + return new DeferredCollectionOutputImpl(c, type, format, callMeta); + } + + protected void initCallContext(CallContext cc) { + logger.debug("initCallContext"); + if (callContextListener != null) { + callContextListener.init(cc); + } + } + + protected void closeCallContext(CallContext cc) { + logger.debug("closeCallContext"); + if (callContextListener != null) { + callContextListener.close(cc); + } + } + + @Override + public void loadAllArtifacts(ArtifactLoadedCallback callback) + throws ArtifactDatabaseException + { + logger.debug("loadAllArtifacts"); + boolean success = backend.loadAllArtifacts(callback); + if (!success) { + throw new ArtifactDatabaseException(INTERNAL_ERROR); + } + } + + public void start() { + if (lifetimeListeners == null || lifetimeListeners.isEmpty()) { + return; + } + + for (LifetimeListener ltl: lifetimeListeners) { + ltl.systemUp(context); + } + + logger.debug("all lifetime listeners started"); + + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + for (LifetimeListener ltl: lifetimeListeners) { + ltl.systemDown(context); + } + } + }); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/Backend.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,1914 @@ +/* + * Copyright (c) 2010, 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactDatabase.ArtifactLoadedCallback; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactSerializer; +import de.intevation.artifacts.CollectionItem; +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + +import de.intevation.artifacts.common.utils.StringUtils; +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.LRUCache; + +import de.intevation.artifactdatabase.db.SQLExecutor; +import de.intevation.artifactdatabase.db.SQL; + +import java.sql.SQLException; +import java.sql.Timestamp; +import java.sql.Types; + +import java.util.List; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * The backend implements the low level layer used to store artifacts + * in a SQL database. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class Backend +implements DatabaseCleaner.ArtifactReviver +{ + private static Logger logger = Logger.getLogger(Backend.class); + + /** + * The SQL statement to create new artifact id inside the database. + */ + public String SQL_NEXT_ID; + + /** + * The SQL statement to insert an artifact into the database. + */ + public String SQL_INSERT; + + /** + * The SQL statement to update some columns of an existing + * artifact in the database. + */ + public String SQL_UPDATE; + + /** + * The SQL statement to touch the access time of an + * artifact inside the database. + */ + public String SQL_TOUCH; + + /** + * The SQL statement to load an artifact by a given + * identifier from the database. + */ + public String SQL_LOAD_BY_GID; + + /** + * The SQL statement to get the database id of an artifact + * identified by the identifier. + */ + public String SQL_GET_ID; + + /** + * The SQL statement to replace the content of an + * existing artifact inside the database. + */ + public String SQL_REPLACE; + + // USER SQL + + public String SQL_USERS_NEXT_ID; + public String SQL_USERS_INSERT; + public String SQL_USERS_SELECT_ID_BY_GID; + public String SQL_USERS_SELECT_GID; + public String SQL_USERS_SELECT_ACCOUNT; + public String SQL_USERS_DELETE_ID; + public String SQL_USERS_DELETE_COLLECTIONS; + public String SQL_USERS_SELECT_ALL; + public String SQL_USERS_COLLECTIONS; + public String SQL_USERS_COLLECTION_IDS; + public String SQL_USERS_DELETE_ALL_COLLECTIONS; + public String SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY; + public String SQL_OUTDATE_ARTIFACTS_COLLECTION; + public String SQL_UPDATE_COLLECTION_TTL; + public String SQL_UPDATE_COLLECTION_NAME; + public String SQL_OUTDATE_ARTIFACTS_USER; + public String SQL_DELETE_USER_COLLECTION_ITEMS; + public String SQL_COLLECTIONS_NEXT_ID; + public String SQL_COLLECTIONS_INSERT; + public String SQL_COLLECTIONS_SELECT_USER; + public String SQL_COLLECTIONS_SELECT_ALL; + public String SQL_COLLECTIONS_SELECT_GID; + public String SQL_COLLECTIONS_CREATION_TIME; + public String SQL_COLLECTIONS_ID_BY_GID; + public String SQL_COLLECTIONS_OLDEST_ARTIFACT; + public String SQL_DELETE_COLLECTION_ITEMS; + public String SQL_DELETE_COLLECTION; + public String SQL_COLLECTION_CHECK_ARTIFACT; + public String SQL_COLLECTION_ITEMS_ID_NEXTVAL; + public String SQL_COLLECTION_ITEMS_INSERT; + public String SQL_COLLECTION_GET_ATTRIBUTE; + public String SQL_COLLECTION_SET_ATTRIBUTE; + public String SQL_COLLECTION_ITEM_GET_ATTRIBUTE; + public String SQL_COLLECTION_ITEM_SET_ATTRIBUTE; + public String SQL_COLLECTIONS_TOUCH_BY_GID; + public String SQL_COLLECTION_ITEM_ID_CID_AID; + public String SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT; + public String SQL_COLLECTION_ITEM_DELETE; + public String SQL_COLLECTIONS_TOUCH_BY_ID; + public String SQL_COLLECTION_ITEMS_LIST_GID; + public String SQL_ALL_ARTIFACTS; + + /** The singleton.*/ + protected static Backend instance; + + protected SQLExecutor sqlExecutor; + + protected List<BackendListener> listeners; + + protected DBConfig config; + + /** + * The database cleaner. Reference is stored here because + * the cleaner is woken up if the backend finds an outdated + * artifact. This artifact should be removed as soon as + * possible. + */ + protected DatabaseCleaner cleaner; + + /** + * To revive an artifact from the bytes coming from the database + * we need the artifact factory which references the artifact + * serializer which is able to do the reviving job. + */ + protected FactoryLookup factoryLookup; + + /** + * Little helper interface to decouple the ArtifactDatabase + * from the Backend. A ArtifactDatabase should depend on a + * Backend but a Backend not from an ArtifactDatabase. + */ + public interface FactoryLookup { + + /** + * Returns an ArtifactFactory which is bound to a given name. + * @param factoryName The name of the artifact factory. + * @return The ArtifactFactory bound to the factory name or + * null if not matching factory is found. + */ + ArtifactFactory getArtifactFactory(String factoryName); + + } // interface FactoryLookup + + /** + * Inner class that brigdes between the persisten form of the + * artifact and the living one inside the artifact database. + * After the describe(), feed(), advance() and out() operations + * of the artifact it must be possible to write to modified artifact + * back into the database. + */ + public final class PersistentArtifact + { + private int id; + private Artifact artifact; + private ArtifactSerializer serializer; + private Long ttl; + + /** + * Cronstructor to create a persistent artifact. + * @param artifact The living artifact. + * @param serializer The serializer to store the artifact + * after the operations. + * @param ttl The time to life of the artifact. + * @param id The database id of the artifact. + */ + public PersistentArtifact( + Artifact artifact, + ArtifactSerializer serializer, + Long ttl, + int id + ) { + this.id = id; + this.artifact = artifact; + this.serializer = serializer; + this.ttl = ttl; + } + + public int getId() { + return id; + } + + /** + * Returns the wrapped living artifact. + * @return the living artifact. + */ + public Artifact getArtifact() { + return artifact; + } + + /** + * Returns the serialized which is able to write a + * modified artifact back into the database. + * @return The serializer. + */ + public ArtifactSerializer getSerializer() { + return serializer; + } + + /** + * The time to life of the artifact. + * @return The time to live. + */ + public Long getTTL() { + return ttl; + } + + /** + * Stores the living artifact back into the database. + */ + public void store() { + if (logger.isDebugEnabled()) { + logger.debug("storing artifact id = " + getId()); + } + Backend.this.store(this); + } + + /** + * Only touches the access time of the artifact. + */ + public void touch() { + if (logger.isDebugEnabled()) { + logger.debug("touching artifact id = " + getId()); + } + Backend.this.touch(this); + } + } // class ArtifactWithId + + /** + * Default constructor + */ + public Backend() { + listeners = new CopyOnWriteArrayList<BackendListener>(); + } + + public Backend(DBConfig config) { + this(); + this.config = config; + sqlExecutor = new SQLExecutor(config.getDBConnection()); + setupSQL(config.getSQL()); + } + + /** + * Constructor to create a backend with a link to the database cleaner. + * @param cleaner The clean which periodically removes outdated + * artifacts from the database. + */ + public Backend(DBConfig config, DatabaseCleaner cleaner) { + this(config); + this.cleaner = cleaner; + } + + public DBConfig getConfig() { + return config; + } + + /** + * Returns the singleton of this Backend. + * + * @return the backend. + */ + public static synchronized Backend getInstance() { + if (instance == null) { + instance = new Backend(DBConfig.getInstance()); + } + + return instance; + } + + protected void setupSQL(SQL sql) { + SQL_NEXT_ID = sql.get("artifacts.id.nextval"); + SQL_INSERT = sql.get("artifacts.insert"); + SQL_UPDATE = sql.get("artifacts.update"); + SQL_TOUCH = sql.get("artifacts.touch"); + SQL_LOAD_BY_GID = sql.get("artifacts.select.gid"); + SQL_GET_ID = sql.get("artifacts.get.id"); + SQL_REPLACE = sql.get("artifacts.replace"); + SQL_USERS_NEXT_ID = sql.get("users.id.nextval"); + SQL_USERS_INSERT = sql.get("users.insert"); + SQL_USERS_SELECT_ID_BY_GID = sql.get("users.select.id.by.gid"); + SQL_USERS_SELECT_GID = sql.get("users.select.gid"); + SQL_USERS_SELECT_ACCOUNT = sql.get("users.select.account"); + SQL_USERS_DELETE_ID = sql.get("users.delete.id"); + SQL_USERS_DELETE_COLLECTIONS = sql.get("users.delete.collections"); + SQL_USERS_SELECT_ALL = sql.get("users.select.all"); + SQL_USERS_COLLECTIONS = sql.get("users.collections"); + SQL_USERS_COLLECTION_IDS = sql.get("users.collection.ids"); + SQL_USERS_DELETE_ALL_COLLECTIONS = + sql.get("users.delete.collections"); + SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY = + sql.get("artifacts.in.one.collection.only"); + SQL_OUTDATE_ARTIFACTS_COLLECTION = + sql.get("outdate.artifacts.collection"); + SQL_UPDATE_COLLECTION_TTL = sql.get("collections.update.ttl"); + SQL_UPDATE_COLLECTION_NAME = sql.get("collections.update.name"); + SQL_OUTDATE_ARTIFACTS_USER = sql.get("outdate.artifacts.user"); + SQL_DELETE_USER_COLLECTION_ITEMS = + sql.get("delete.user.collection.items"); + SQL_COLLECTIONS_NEXT_ID = sql.get("collections.id.nextval"); + SQL_COLLECTIONS_INSERT = sql.get("collections.insert"); + SQL_COLLECTIONS_SELECT_USER = sql.get("collections.select.user"); + SQL_COLLECTIONS_SELECT_ALL = sql.get("collections.select.all"); + SQL_COLLECTIONS_SELECT_GID = sql.get("collections.select.by.gid"); + SQL_COLLECTIONS_CREATION_TIME = sql.get("collection.creation.time"); + SQL_COLLECTIONS_OLDEST_ARTIFACT = sql.get("collections.artifacts.oldest"); + SQL_COLLECTIONS_ID_BY_GID = sql.get("collections.id.by.gid"); + SQL_DELETE_COLLECTION_ITEMS = sql.get("delete.collection.items"); + SQL_DELETE_COLLECTION = sql.get("delete.collection"); + SQL_COLLECTION_CHECK_ARTIFACT = sql.get("collection.check.artifact"); + SQL_COLLECTION_ITEMS_ID_NEXTVAL = + sql.get("collection.items.id.nextval"); + SQL_COLLECTION_ITEMS_INSERT = sql.get("collection.items.insert"); + SQL_COLLECTION_GET_ATTRIBUTE = sql.get("collection.get.attribute"); + SQL_COLLECTION_SET_ATTRIBUTE = sql.get("collection.set.attribute"); + SQL_COLLECTION_ITEM_GET_ATTRIBUTE = + sql.get("collection.item.get.attribute"); + SQL_COLLECTION_ITEM_SET_ATTRIBUTE = + sql.get("collection.item.set.attribute"); + SQL_COLLECTIONS_TOUCH_BY_GID = sql.get("collections.touch.by.gid"); + SQL_COLLECTION_ITEM_ID_CID_AID = sql.get("collection.item.id.cid.aid"); + SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT = + sql.get("collection.item.outdate.artifact"); + SQL_COLLECTION_ITEM_DELETE = sql.get("collection.item.delete"); + SQL_COLLECTIONS_TOUCH_BY_ID = sql.get("collections.touch.by.id"); + SQL_COLLECTION_ITEMS_LIST_GID = sql.get("collection.items.list.gid"); + SQL_ALL_ARTIFACTS = sql.get("all.artifacts"); + } + + public void addListener(BackendListener listener) { + listeners.add(listener); + logger.debug("# listeners: " + listeners.size()); + } + + public void addAllListeners(List<BackendListener> others) { + listeners.addAll(others); + logger.debug("# listeners: " + listeners.size()); + } + + /** + * Sets the factory lookup mechanism to decouple ArtifactDatabase + * and Backend. + * @param factoryLookup + */ + public void setFactoryLookup(FactoryLookup factoryLookup) { + this.factoryLookup = factoryLookup; + } + + /** + * Sets the database cleaner explicitly. + * @param cleaner The database cleaner + */ + public void setCleaner(DatabaseCleaner cleaner) { + this.cleaner = cleaner; + } + + /** + * Returns a new unique identifier to external identify + * the artifact across the system. This implementation + * uses random UUIDs v4 to achieve this target. + * @return the new identifier + */ + public String newIdentifier() { + // TODO: check database for collisions. + return StringUtils.newUUID(); + } + + public boolean isValidIdentifier(String identifier) { + return StringUtils.checkUUID(identifier); + } + + /** + * Stores a new artifact into the database. + * @param artifact The artifact to be stored + * @param factory The factory which build the artifact + * @param ttl The initial time to life of the artifact. + * @return A persistent wrapper around the living + * artifact to be able to write modification later. + * @throws Exception Thrown if something went wrong with the + * storage process. + */ + public PersistentArtifact storeInitially( + Artifact artifact, + ArtifactFactory factory, + Long ttl + ) + throws Exception + { + return new PersistentArtifact( + artifact, + factory.getSerializer(), + ttl, + insertDatabase(artifact, factory, ttl)); + } + + /** + * Stores an artifact into database if it does not exist there. + * If it exists there it is only updated. + * @param artifact The artifact to store/update. + * @param factory The factory which created the artifact. + * @param ttl The initial time to live of the artifact. + * @return A persistent version of the artifact to be able + * to store a modification later. + * @throws Exception Thrown if something went wrong during + * storing/updating. + */ + public PersistentArtifact storeOrReplace( + Artifact artifact, + ArtifactFactory factory, + Long ttl + ) + throws Exception + { + return new PersistentArtifact( + artifact, + factory.getSerializer(), + ttl, + storeOrReplaceDatabase(artifact, factory, ttl)); + } + + /** + * Implementors of this interface are able to process the raw + * artifact data from the database for loading. + */ + public interface ArtifactLoader { + + /** + * Creates a custom object from the raw artifact database data. + * @param factory The factory that created this artifact. + * @param ttl The current time to life of the artifact. + * @param bytes The raw artifact bytes from the database. + * @param id The database id of the artifact. + * @return The custom object created by the implementation. + */ + Object load(ArtifactFactory factory, Long ttl, byte [] bytes, int id); + + } // interface ArtifactLoader + + /** + * Fetches an artifact from the database identified by the + * given identifier. + * @param identifer The identifier of the artifact. + * @return A persistent wrapper around the found artifact + * to be able to write back a modifaction later or null + * if no artifact is found for this identifier. + */ + public PersistentArtifact getArtifact(String identifer) { + + return (PersistentArtifact)loadArtifact( + identifer, + new ArtifactLoader() { + + public Object load( + ArtifactFactory factory, + Long ttl, + byte [] bytes, + int id + ) { + ArtifactSerializer serializer = factory.getSerializer(); + + Artifact artifact = serializer.fromBytes(bytes); + + return artifact == null + ? null + : new PersistentArtifact(artifact, serializer, ttl, id); + } + }); + } + + /** + * More general loading mechanism for artifacts. The concrete + * load processing is delegated to the given loader. + * @param identifer The identifier of the artifact. + * @param loader The loader which processes the raw database data. + * @return The object created by the loader. + */ + public Object loadArtifact( + final String identifer, + final ArtifactLoader loader + ) { + if (!isValidIdentifier(identifer)) { + return null; + } + + if (factoryLookup == null) { + logger.error("factory lookup == null"); + return false; + } + + final Object [] loaded = new Object[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_LOAD_BY_GID); + stmnt.setString(1, identifer); + + result = stmnt.executeQuery(); + + if (!result.next()) { + return false; + } + + int id = result.getInt(1); + long ttlX = result.getLong(2); + Long ttl = result.wasNull() ? null : ttlX; + + String factoryName = result.getString(3); + + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error("factory '" + factoryName + "' not found"); + return false; + } + + byte [] bytes = result.getBytes(4); + + loaded[0] = loader.load(factory, ttl, bytes, id); + return true; + } + }; + + return exec.runRead() ? loaded[0] : null; + } + + /** + * Called if the load mechanism found an outdated artifact. + * It wakes up the database cleaner. + * @param id The id of the outdated artifact. + */ + protected void artifactOutdated(int id) { + if (logger.isDebugEnabled()) { + logger.info("artifactOutdated: id = " + id); + } + if (cleaner != null) { + cleaner.wakeup(); + } + } + + public Artifact reviveArtifact(String factoryName, byte [] bytes) { + if (factoryLookup == null) { + logger.error("reviveArtifact: factory lookup == null"); + return null; + } + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error( + "reviveArtifact: no factory '" + factoryName + "' found"); + return null; + } + + ArtifactSerializer serializer = factory.getSerializer(); + + return serializer.fromBytes(bytes); + } + + /** + * Internal method to store/replace an artifact inside the database. + * If an artifact with the given identifier does not exists it is + * created else only the content data is updated. + * @param artifact The artifact to be store/update inside the database. + * @param factory The factory that created the artifact. + * @param ttl The initial time to life of the artifact. + * @return The database id of the stored/updated artifact. + */ + protected int storeOrReplaceDatabase( + final Artifact artifact, + final ArtifactFactory factory, + final Long ttl + ) { + final String uuid = artifact.identifier(); + + if (!isValidIdentifier(uuid)) { + throw new RuntimeException("No valid UUID"); + } + + final int [] id = new int[1]; + final boolean [] stored = new boolean[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_GET_ID); + stmnt.setString(1, uuid); + result = stmnt.executeQuery(); + + Integer ID = result.next() + ? Integer.valueOf(result.getInt(1)) + : null; + + reset(); + + if (stored[0] = ID != null) { // already in database + prepareStatement(SQL_REPLACE); + + if (ttl == null) { + stmnt.setNull(1, Types.BIGINT); + } + else { + stmnt.setLong(1, ttl.longValue()); + } + + stmnt.setString(2, factory.getName()); + stmnt.setBytes( + 3, + factory.getSerializer().toBytes(artifact)); + id[0] = ID.intValue(); + stmnt.setInt(4, id[0]); + } + else { // new artifact + prepareStatement(SQL_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + logger.error("No id generated"); + return false; + } + + reset(); + + prepareStatement(SQL_INSERT); + + id[0] = result.getInt(1); + stmnt.setInt(1, id[0]); + stmnt.setString(2, uuid); + if (ttl == null) { + stmnt.setNull(3, Types.BIGINT); + } + else { + stmnt.setLong(3, ttl.longValue()); + } + + stmnt.setString(4, factory.getName()); + + stmnt.setBytes( + 5, + factory.getSerializer().toBytes(artifact)); + } + stmnt.execute(); + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + throw new RuntimeException("failed insert artifact into database"); + } + + if (stored[0]) { + fireStoredArtifact(artifact); + } + else { + fireCreatedArtifact(artifact); + } + + return id[0]; + } + + /** + * Internal method to store an artifact inside the database. + * @param artifact The artifact to be stored. + * @param factory The factory which created the artifact. + * @param ttl The initial time to live of the artifact. + * @return The database id of the stored artifact. + */ + protected int insertDatabase( + final Artifact artifact, + final ArtifactFactory factory, + final Long ttl + ) { + final int [] id = new int[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + logger.error("No id generated"); + return false; + } + + id[0] = result.getInt(1); + + reset(); + prepareStatement(SQL_INSERT); + + String uuid = artifact.identifier(); + stmnt.setInt(1, id[0]); + stmnt.setString(2, uuid); + if (ttl == null) { + stmnt.setNull(3, Types.BIGINT); + } + else { + stmnt.setLong(3, ttl.longValue()); + } + + stmnt.setString(4, factory.getName()); + + stmnt.setBytes( + 5, + factory.getSerializer().toBytes(artifact)); + + stmnt.execute(); + + conn.commit(); + return true; + } + }; + + if (!exec.runWrite()) { + throw new RuntimeException("failed insert artifact into database"); + } + + fireCreatedArtifact(artifact); + + return id[0]; + } + + protected void fireCreatedArtifact(Artifact artifact) { + for (BackendListener listener: listeners) { + listener.createdArtifact(artifact, this); + } + } + + /** + * Touches the access timestamp of a given artifact to prevent + * that it will be removed from the database by the database cleaner. + * @param artifact The persistent wrapper around the living artifact. + */ + public void touch(final PersistentArtifact artifact) { + sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_TOUCH); + stmnt.setInt(1, artifact.getId()); + stmnt.execute(); + conn.commit(); + return true; + } + }.runWrite(); + } + + /** + * Writes modification of an artifact back to the database. + * @param artifact The persistent wrapper around a living + * artifact. + */ + public void store(final PersistentArtifact artifact) { + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE); + stmnt.setInt(2, artifact.getId()); + + byte [] bytes = artifact + .getSerializer() + .toBytes(artifact.getArtifact()); + + stmnt.setBytes(1, bytes); + stmnt.execute(); + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireStoredArtifact(artifact.getArtifact()); + } + } + + protected void fireStoredArtifact(Artifact artifact) { + for (BackendListener listener: listeners) { + listener.storedArtifact(artifact, this); + } + } + + + public User createUser( + final String name, + final String account, + final Document role, + final UserFactory factory, + final Object context + ) { + final User [] user = new User[1]; + + final byte [] roleData = XMLUtils.toByteArray(role, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_USERS_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { + return false; + } + + int id = result.getInt(1); + + reset(); + + String identifier = newIdentifier(); + + prepareStatement(SQL_USERS_INSERT); + + stmnt.setInt(1, id); + stmnt.setString(2, identifier); + stmnt.setString(3, name); + stmnt.setString(4, account); + + if (roleData == null) { + stmnt.setNull(5, Types.BIGINT); + } + else { + stmnt.setBytes(5, roleData); + } + + stmnt.execute(); + conn.commit(); + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireCreatedUser(user[0]); + return user[0]; + } + + return null; + } + + protected void fireCreatedUser(User user) { + for (BackendListener listener: listeners) { + listener.createdUser(user, this); + } + } + + public boolean deleteUser(final String identifier) { + + if (!isValidIdentifier(identifier)) { + return false; + } + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ID_BY_GID); + + stmnt.setString(1, identifier); + result = stmnt.executeQuery(); + + if (!result.next()) { // No such user + return false; + } + + int id = result.getInt(1); + + reset(); + + // outdate the artifacts exclusively used by the user + + prepareStatement(SQL_OUTDATE_ARTIFACTS_USER); + stmnt.setInt(1, id); + stmnt.setInt(2, id); + stmnt.execute(); + + reset(); + + // delete the collection items of the user + + prepareStatement(SQL_DELETE_USER_COLLECTION_ITEMS); + stmnt.setInt(1, id); + stmnt.execute(); + + reset(); + + // delete the collections of the user + + prepareStatement(SQL_USERS_DELETE_COLLECTIONS); + stmnt.setInt(1, id); + stmnt.execute(); + + reset(); + + // delete the user + + prepareStatement(SQL_USERS_DELETE_ID); + stmnt.setInt(1, id); + stmnt.execute(); + + conn.commit(); + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireDeletedUser(identifier); + } + + return success; + } + + protected void fireDeletedUser(String identifier) { + for (BackendListener listener: listeners) { + listener.deletedUser(identifier, this); + } + } + + public User getUser( + final String identifier, + final UserFactory factory, + final Object context + ) { + if (!isValidIdentifier(identifier)) { + logger.debug("Invalid UUID: '" + identifier + "'"); + return null; + } + + final User [] user = new User[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_GID); + stmnt.setString(1, identifier); + result = stmnt.executeQuery(); + if (!result.next()) { // no such user + return false; + } + // omit id + String name = result.getString(2); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); + + Document role = null; + if (roleData != null) { + role = XMLUtils.fromByteArray(roleData, true); + } + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + return exec.runRead() ? user[0] : null; + } + + /** + * Find/Get user by account. + */ + public User findUser( + final String account, + final UserFactory factory, + final Object context + ) { + + final User [] user = new User[1]; + logger.debug("Trying to find user by account " + account); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ACCOUNT); + stmnt.setString(1, account); + result = stmnt.executeQuery(); + if (!result.next()) { // no such user + logger.debug("No user found."); + return false; + } + String identifier = result.getString(1); + String name = result.getString(2); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); + + Document role = null; + if (roleData != null) { + role = XMLUtils.fromByteArray(roleData, true); + } + + user[0] = factory.createUser( + identifier, name, account, role, context); + return true; + } + }; + + return exec.runRead() ? user[0] : null; + } + + public User [] getUsers( + final UserFactory factory, + final Object context + ) { + final ArrayList<User> users = new ArrayList<User>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ALL); + result = stmnt.executeQuery(); + + while (result.next()) { + // omit id + String identifier = result.getString(2); + String name = result.getString(3); + String account = result.getString(4); + byte [] roleData = result.getBytes(5); + + Document role = XMLUtils.fromByteArray(roleData, true); + User user = factory.createUser( + identifier, name, account, role, context); + users.add(user); + } + return true; + } + }; + + return exec.runRead() + ? users.toArray(new User[users.size()]) + : null; + } + + public ArtifactCollection createCollection( + final String ownerIdentifier, + final String name, + final ArtifactCollectionFactory factory, + final Document attribute, + final Object context + ) { + if (name == null) { + logger.debug("Name is null"); + return null; + } + + if (!isValidIdentifier(ownerIdentifier)) { + logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); + return null; + } + + final ArtifactCollection [] collection = new ArtifactCollection[1]; + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch owner id + prepareStatement(SQL_USERS_SELECT_ID_BY_GID); + stmnt.setString(1, ownerIdentifier); + result = stmnt.executeQuery(); + + if (!result.next()) { // no such user + return false; + } + + int ownerId = result.getInt(1); + reset(); + + // fetch new collection seq number. + prepareStatement(SQL_COLLECTIONS_NEXT_ID); + result = stmnt.executeQuery(); + + if (!result.next()) { // no identifier generated + return false; + } + + int id = result.getInt(1); + reset(); + + String identifier = newIdentifier(); + + prepareStatement(SQL_COLLECTIONS_INSERT); + + stmnt.setInt(1, id); + stmnt.setString(2, identifier); + stmnt.setString(3, name); + stmnt.setInt(4, ownerId); + + // XXX: A bit odd: we don't have a collection, yet. + Long ttl = factory.timeToLiveUntouched(null, context); + + if (ttl == null) { + stmnt.setNull(5, Types.BIGINT); + } + else { + stmnt.setLong(5, ttl); + } + + if (data == null) { + stmnt.setNull(6, Types.BINARY); + } + else { + stmnt.setBytes(6, data); + } + + stmnt.execute(); + conn.commit(); + + reset(); + + // fetch creation time from database + // done this way to use the time system + // of the database. + + prepareStatement(SQL_COLLECTIONS_CREATION_TIME); + stmnt.setInt(1, id); + + result = stmnt.executeQuery(); + + Date creationTime = null; + + if (result.next()) { + Timestamp timestamp = result.getTimestamp(1); + creationTime = new Date(timestamp.getTime()); + } + + collection[0] = factory.createCollection( + identifier, name, creationTime, ttl, attribute, context); + + if (collection[0] != null) { + // XXX: Little hack to make the listeners happy + collection[0].setUser(new DefaultUser(ownerIdentifier)); + } + + return true; + } + }; + + boolean success = exec.runWrite(); + + if (success) { + fireCreatedCollection(collection[0]); + return collection[0]; + } + return null; + } + + protected void fireCreatedCollection(ArtifactCollection collection) { + for (BackendListener listener: listeners) { + listener.createdCollection(collection, this); + } + } + + public ArtifactCollection getCollection( + final String collectionId, + final ArtifactCollectionFactory collectionFactory, + final UserFactory userFactory, + final Object context + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return null; + } + + final ArtifactCollection[] ac = new ArtifactCollection[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + prepareStatement(SQL_COLLECTIONS_SELECT_GID); + stmnt.setString(1, collectionId); + + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection"); + return false; + } + + String collectionName = result.getString(2); + String ownerId = result.getString(3); + Date creationTime = + new Date(result.getTimestamp(4).getTime()); + Date lastAccess = + new Date(result.getTimestamp(5).getTime()); + Document attr = + XMLUtils.fromByteArray(result.getBytes(6), true); + long ttl = result.getLong(7); + + ArtifactCollection collection = + collectionFactory.createCollection( + collectionId, + collectionName, + creationTime, + ttl, + attr, + context); + + if (ownerId != null) { + collection.setUser(new LazyBackendUser( + ownerId, userFactory, Backend.this, context)); + } + + ac[0] = collection; + + return true; + } + }; + + return exec.runRead() ? ac[0] : null; + } + + public ArtifactCollection [] listCollections( + final String ownerIdentifier, + final Document data, + final ArtifactCollectionFactory collectionFactory, + final UserFactory userFactory, + final Object context + ) { + if (ownerIdentifier != null + && !isValidIdentifier(ownerIdentifier)) { + logger.debug("Invalid owner id: '" + ownerIdentifier + "'"); + return null; + } + + final ArrayList<ArtifactCollection> collections = + new ArrayList<ArtifactCollection>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + + public boolean doIt() throws SQLException { + + if (ownerIdentifier != null) { + prepareStatement(SQL_COLLECTIONS_SELECT_USER); + stmnt.setString(1, ownerIdentifier); + } + else { + prepareStatement(SQL_COLLECTIONS_SELECT_ALL); + } + + result = stmnt.executeQuery(); + + HashMap<String, LazyBackendUser> users = + new HashMap<String, LazyBackendUser>(); + + while (result.next()) { + String collectionIdentifier = result.getString(1); + String collectionName = result.getString(2); + Date creationTime = + new Date(result.getTimestamp(3).getTime()); + String userIdentifier = result.getString(4); + long ttl = result.getLong(5); + + ArtifactCollection collection = + collectionFactory.createCollection( + collectionIdentifier, + collectionName, + creationTime, + ttl, + data, + context); + + if (userIdentifier != null) { + LazyBackendUser user = users.get(userIdentifier); + if (user == null) { + user = new LazyBackendUser( + userIdentifier, userFactory, + Backend.this, context); + users.put(userIdentifier, user); + } + collection.setUser(user); + } + + collections.add(collection); + } + return true; + } + }; + + return exec.runRead() + ? collections.toArray(new ArtifactCollection[collections.size()]) + : null; + } + + + public String getMasterArtifact(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return null; + } + final String [] uuid = new String[1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // Fetch masters (oldest artifact) id. + prepareStatement(SQL_COLLECTIONS_OLDEST_ARTIFACT); + stmnt.setString(1, collectionId); + stmnt.setMaxRows(1); // + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + return false; + } + uuid[0] = result.getString(1); + if (logger.isDebugEnabled()) { + logger.debug("getMasterArtifact result.getString " + + uuid[0]); + } + return true; + } + }; + return exec.runRead() ? uuid[0] : null; + } + + public boolean deleteCollection(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch collection id + prepareStatement(SQL_COLLECTIONS_ID_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + return false; + } + int id = result.getInt(1); + reset(); + + // outdate artifacts that are only in this collection + logger.info("Outdate Artifacts that belong to collection: " + id); + + prepareStatement(SQL_OUTDATE_ARTIFACTS_COLLECTION); + stmnt.setInt(1, id); + stmnt.setInt(2, id); + stmnt.execute(); + reset(); + + // delete the collection items + prepareStatement(SQL_DELETE_COLLECTION_ITEMS); + stmnt.setInt(1, id); + stmnt.execute(); + reset(); + + // delete the collection + prepareStatement(SQL_DELETE_COLLECTION); + stmnt.setInt(1, id); + stmnt.execute(); + conn.commit(); + return true; + } + }; + boolean success = exec.runWrite(); + + if (success) { + fireDeletedCollection(collectionId); + } + + return success; + } + + protected void fireDeletedCollection(String identifier) { + for (BackendListener listener: listeners) { + listener.deletedCollection(identifier, this); + } + } + + public Document getCollectionAttribute(final String collectionId) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + } + + final byte[][] data = new byte[1][1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_GET_ATTRIBUTE); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection."); + return false; + } + + data[0] = result.getBytes(1); + return true; + } + }; + + return exec.runRead() + ? XMLUtils.fromByteArray(data[0], true) + : null; + } + + public boolean setCollectionAttribute( + final String collectionId, + Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // set the column in collection items + prepareStatement(SQL_COLLECTION_SET_ATTRIBUTE); + if (data == null) { + stmnt.setNull(1, Types.BINARY); + } + else { + stmnt.setBytes(1, data); + } + stmnt.setString(2, collectionId); + stmnt.execute(); + reset(); + + // touch the collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); + stmnt.setString(1, collectionId); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireChangedCollectionAttribute(collectionId, attribute); + } + + return success; + } + + protected void fireChangedCollectionAttribute( + String collectionId, + Document document + ) { + for (BackendListener listener: listeners) { + listener.changedCollectionAttribute(collectionId, document, this); + } + } + + public Document getCollectionItemAttribute( + final String collectionId, + final String artifactId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return null; + } + if (!isValidIdentifier(artifactId)) { + logger.debug("artifact id is not valid: " + artifactId); + return null; + } + + final byte [][] data = new byte[1][1]; + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ITEM_GET_ATTRIBUTE); + stmnt.setString(1, collectionId); + stmnt.setString(2, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection item"); + return false; + } + data[0] = result.getBytes(1); + return true; + } + }; + + return exec.runRead() + ? XMLUtils.fromByteArray(data[0], true) + : null; + } + + public boolean setCollectionItemAttribute( + final String collectionId, + final String artifactId, + Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("collection id is not valid: " + collectionId); + return false; + } + if (!isValidIdentifier(artifactId)) { + logger.debug("artifact id is not valid: " + artifactId); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // set the column in collection items + prepareStatement(SQL_COLLECTION_ITEM_SET_ATTRIBUTE); + if (data == null) { + stmnt.setNull(1, Types.BINARY); + } + else { + stmnt.setBytes(1, data); + } + stmnt.setString(2, collectionId); + stmnt.setString(3, artifactId); + stmnt.execute(); + reset(); + + // touch the collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_GID); + stmnt.setString(1, collectionId); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireChangedCollectionItemAttribute( + collectionId, artifactId, attribute); + } + + return success; + } + + protected void fireChangedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document + ) { + for (BackendListener listener: listeners) { + listener.changedCollectionItemAttribute( + collectionId, artifactId, document, this); + } + } + + public boolean addCollectionArtifact( + final String collectionId, + final String artifactId, + final Document attribute + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + + if (!isValidIdentifier(artifactId)) { + logger.debug("Invalid artifact id: '" + artifactId + "'"); + return false; + } + + final byte [] data = XMLUtils.toByteArray(attribute, true); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + // fetch artifact id + prepareStatement(SQL_GET_ID); + stmnt.setString(1, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such artifact: " + artifactId); + return false; + } + int aid = result.getInt(1); + reset(); + + // fetch collection id + prepareStatement(SQL_COLLECTIONS_ID_BY_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection: " + collectionId); + } + int cid = result.getInt(1); + reset(); + + // check if artifact is already in collection + prepareStatement(SQL_COLLECTION_CHECK_ARTIFACT); + stmnt.setInt(1, aid); + stmnt.setInt(2, cid); + result = stmnt.executeQuery(); + if (result.next()) { + logger.debug("artifact already in collection"); + return false; + } + reset(); + + // fetch fresh id for new collection item + prepareStatement(SQL_COLLECTION_ITEMS_ID_NEXTVAL); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("no collection item id generated"); + return false; + } + int ci_id = result.getInt(1); + reset(); + + // insert new collection item + prepareStatement(SQL_COLLECTION_ITEMS_INSERT); + stmnt.setInt(1, ci_id); + stmnt.setInt(2, cid); + stmnt.setInt(3, aid); + + if (data == null) { + stmnt.setNull(4, Types.BINARY); + } + else { + stmnt.setBytes(4, data); + } + stmnt.execute(); + conn.commit(); + + return true; + } + }; + boolean success = exec.runWrite(); + + if (success) { + fireAddedArtifactToCollection(artifactId, collectionId); + } + + return success; + } + + protected void fireAddedArtifactToCollection( + String artifactId, + String collectionId + ) { + for (BackendListener listener: listeners) { + listener.addedArtifactToCollection( + artifactId, collectionId, this); + } + } + + public boolean removeCollectionArtifact( + final String collectionId, + final String artifactId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + + // fetch id, collection id and artitfact id + prepareStatement(SQL_COLLECTION_ITEM_ID_CID_AID); + stmnt.setString(1, collectionId); + stmnt.setString(2, artifactId); + result = stmnt.executeQuery(); + if (!result.next()) { + logger.debug("No such collection item"); + return false; + } + int id = result.getInt(1); + int cid = result.getInt(2); + int aid = result.getInt(3); + reset(); + + // outdate artifact iff it is only in this collection + prepareStatement(SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT); + stmnt.setInt(1, aid); + stmnt.setInt(2, cid); + stmnt.setInt(3, aid); + stmnt.execute(); + reset(); + + // delete collection item + prepareStatement(SQL_COLLECTION_ITEM_DELETE); + stmnt.setInt(1, id); + stmnt.execute(); + reset(); + + // touch collection + prepareStatement(SQL_COLLECTIONS_TOUCH_BY_ID); + stmnt.setInt(1, cid); + stmnt.execute(); + + conn.commit(); + return true; + } + }.runWrite(); + + if (success) { + fireRemovedArtifactFromCollection(artifactId, collectionId); + } + + return success; + } + + protected void fireRemovedArtifactFromCollection( + String artifactId, + String collectionId + ) { + for (BackendListener listener: listeners) { + listener.removedArtifactFromCollection( + artifactId, collectionId, this); + } + } + + public CollectionItem [] listCollectionArtifacts( + final String collectionId + ) { + if (!isValidIdentifier(collectionId)) { + logger.debug("Invalid collection id: '" + collectionId + "'"); + return null; + } + + final ArrayList<CollectionItem> collectionItems = + new ArrayList<CollectionItem>(); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_COLLECTION_ITEMS_LIST_GID); + stmnt.setString(1, collectionId); + result = stmnt.executeQuery(); + while (result.next()) { + CollectionItem item = new DefaultCollectionItem( + result.getString(1), + result.getBytes(2)); + collectionItems.add(item); + } + return true; + } + }; + + return exec.runRead() + ? collectionItems.toArray( + new CollectionItem[collectionItems.size()]) + : null; + } + + + public boolean setCollectionTTL(final String uuid, final Long ttl) { + if (!isValidIdentifier(uuid)) { + logger.debug("Invalid collection id: '" + uuid + "'"); + return false; + } + + return sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE_COLLECTION_TTL); + if (ttl == null) { + stmnt.setNull(1, Types.BIGINT); + } + else { + stmnt.setLong(1, ttl); + } + stmnt.setString(2, uuid); + stmnt.execute(); + conn.commit(); + + return true; + } + }.runWrite(); + } + + + public boolean setCollectionName(final String uuid, final String name) { + if (!isValidIdentifier(uuid)) { + logger.debug("Invalid collection id: '" + uuid + "'"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_UPDATE_COLLECTION_NAME); + stmnt.setString(1, name); + stmnt.setString(2, uuid); + stmnt.execute(); + conn.commit(); + + return true; + } + }.runWrite(); + + if (success) { + fireSetCollectionName(uuid, name); + } + + return success; + } + + protected void fireSetCollectionName(String identifier, String name) { + for (BackendListener listener: listeners) { + listener.setCollectionName(identifier, name); + } + } + + public boolean loadAllArtifacts(final ArtifactLoadedCallback alc) { + + logger.debug("loadAllArtifacts"); + + if (factoryLookup == null) { + logger.error("factory lookup == null"); + return false; + } + + boolean success = sqlExecutor.new Instance() { + @Override + public boolean doIt() throws SQLException { + // a little cache to avoid too much deserializations. + LRUCache<String, Artifact> alreadyLoaded = + new LRUCache<String, Artifact>(200); + + prepareStatement(SQL_ALL_ARTIFACTS); + result = stmnt.executeQuery(); + while (result.next()) { + String userId = result.getString("u_gid"); + String collectionId = result.getString("c_gid"); + String collectionName = result.getString("c_name"); + String artifactId = result.getString("a_gid"); + String factoryName = result.getString("factory"); + Date collectionCreated = + new Date(result.getTimestamp("c_creation").getTime()); + Date artifactCreated = + new Date(result.getTimestamp("a_creation").getTime()); + + Artifact artifact = alreadyLoaded.get(artifactId); + + if (artifact != null) { + alc.artifactLoaded( + userId, + collectionId, collectionName, + collectionCreated, + artifactId, artifactCreated, artifact); + continue; + } + + ArtifactFactory factory = factoryLookup + .getArtifactFactory(factoryName); + + if (factory == null) { + logger.error("factory '" + factoryName + "' not found"); + continue; + } + + byte [] bytes = result.getBytes("data"); + + artifact = factory.getSerializer().fromBytes(bytes); + + if (artifact != null) { + alc.artifactLoaded( + userId, + collectionId, collectionName, collectionCreated, + artifactId, artifactCreated, artifact); + } + + alreadyLoaded.put(artifactId, artifact); + } + return true; + } + }.runRead(); + + if (logger.isDebugEnabled()) { + logger.debug("loadAllArtifacts success: " + success); + } + + return success; + } + + @Override + public void killedArtifacts(List<String> identifiers) { + logger.debug("killedArtifacts"); + for (BackendListener listener: listeners) { + listener.killedArtifacts(identifiers, this); + } + } + + @Override + public void killedCollections(List<String> identifiers) { + logger.debug("killedCollections"); + for (BackendListener listener: listeners) { + listener.killedCollections(identifiers, this); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/BackendListener.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,61 @@ +package de.intevation.artifactdatabase; + +import java.util.List; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.User; + +import org.w3c.dom.Document; + +public interface BackendListener +{ + void setup(GlobalContext globalContext); + + void createdArtifact(Artifact artifact, Backend backend); + + void storedArtifact(Artifact artifact, Backend backend); + + void createdUser(User user, Backend backend); + + void deletedUser(String identifier, Backend backend); + + void createdCollection(ArtifactCollection collection, Backend backend); + + void deletedCollection(String identifier, Backend backend); + + void changedCollectionAttribute( + String identifier, + Document document, + Backend backend); + + void changedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document, + Backend backend); + + void addedArtifactToCollection( + String artifactId, + String collectionId, + Backend backend); + + void removedArtifactFromCollection( + String artifactId, + String collectionId, + Backend backend); + + void setCollectionName( + String collectionId, + String name); + + void killedCollections( + List<String> identifiers, + Backend backend); + + void killedArtifacts( + List<String> identifiers, + Backend backend); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/CollectionCallContext.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import java.util.LinkedList; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.Message; + + +/** + * Class that implements the call context handed to ArtifactCollection specific + * operations. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class CollectionCallContext extends AbstractCallContext { + + private static Logger log = Logger.getLogger(CollectionCallContext.class); + + /** + * The ArtifactCollection. + */ + protected ArtifactCollection collection; + + + public CollectionCallContext( + ArtifactDatabaseImpl artifactDatabase, + int action, + CallMeta callMeta, + ArtifactCollection collection) + { + super(artifactDatabase, action, callMeta); + + this.collection = collection; + } + + + public void afterCall(int action) { + log.debug("CollectionCallContext.afterCall - NOT IMPLEMENTED"); + } + + + public void afterBackground(int action) { + log.debug("CollectionCallContext.afterBackground - NOT IMPLEMENTED"); + } + + + public boolean isInBackground() { + log.debug("CollectionCallContext.isInBackground - NOT IMPLEMENTED"); + return false; + } + + + public void addBackgroundMessage(Message msg) { + log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); + } + + + public LinkedList<Message> getBackgroundMessages() { + log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); + return null; + } + + + public Long getTimeToLive() { + log.debug("CollectionCallContext.getTimeToLive - NOT IMPLEMENTED"); + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DBConfig.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,83 @@ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifactdatabase.db.SQL; +import de.intevation.artifactdatabase.db.DBConnection; + +public class DBConfig +{ + /** + * XPath to access the database driver within the global configuration. + */ + public static final String DB_DRIVER = + "/artifact-database/database/driver/text()"; + /** + * XPath to access the database URL within the global configuration. + */ + public static final String DB_URL = + "/artifact-database/database/url/text()"; + /** + * XPath to access the database use within the global configuration. + */ + public static final String DB_USER = + "/artifact-database/database/user/text()"; + /** + * XPath to access the database password within the global configuration. + */ + public static final String DB_PASSWORD = + "/artifact-database/database/password/text()"; + + private static DBConfig instance; + + private DBConnection dbConnection; + private SQL sql; + + private DBConfig() { + } + + private DBConfig(DBConnection dbConnection, SQL sql) { + this.dbConnection = dbConnection; + this.sql = sql; + } + + public static synchronized DBConfig getInstance() { + if (instance == null) { + instance = createInstance(); + } + return instance; + } + + public SQL getSQL() { + return sql; + } + + public DBConnection getDBConnection() { + return dbConnection; + } + + private static DBConfig createInstance() { + + String driver = Config.getStringXPath( + DB_DRIVER, DBConnection.DEFAULT_DRIVER); + + String url = Config.getStringXPath( + DB_URL, DBConnection.DEFAULT_URL); + + url = Config.replaceConfigDir(url); + + String user = Config.getStringXPath( + DB_USER, DBConnection.DEFAULT_USER); + + String password = Config.getStringXPath( + DB_PASSWORD, DBConnection.DEFAULT_PASSWORD); + + DBConnection dbConnection = new DBConnection( + driver, url, user, password); + + SQL sql = new SQL(driver); + + return new DBConfig(dbConnection, sql); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DatabaseCleaner.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.db.SQL; +import de.intevation.artifactdatabase.db.DBConnection; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.Collections; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; + +/** + * The database cleaner runs in background. It sleep for a configurable + * while and when it wakes up it removes outdated artifacts from the + * database. Outdated means that the the last access to the artifact + * is longer aga then the time to live of this artifact.<br> + * Before the artifact is finally removed from the system it is + * revived one last time an the #endOfLife() method of the artifact + * is called.<br> + * The artifact implementations may e.g. use this to remove some extrenal + * resources form the system. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DatabaseCleaner +extends Thread +{ + /** + * Implementors of this interface are able to create a + * living artifact from a given byte array. + */ + public interface ArtifactReviver { + + /** + * Called to revive an artifact from a given byte array. + * @param factoryName The name of the factory which + * created this artifact. + * @param bytes The bytes of the serialized artifact. + * @return The revived artfiact. + */ + Artifact reviveArtifact(String factoryName, byte [] bytes); + + void killedArtifacts(List<String> identifiers); + void killedCollections(List<String> identifiers); + + } // interface ArtifactReviver + + public interface LockedIdsProvider { + Set<Integer> getLockedIds(); + } // interface LockedIdsProvider + + private static Logger logger = Logger.getLogger(DatabaseCleaner.class); + + /** + * Number of artifacts to be loaded at once. Used to + * mitigate the problem of a massive denial of service + * if too many artifacts have died since last cleanup. + */ + public static final int MAX_ROWS = 50; + + public static final Set<Integer> EMPTY_IDS = Collections.emptySet(); + + /** + * The SQL statement to select the outdated artifacts. + */ + public String SQL_OUTDATED; + + public String SQL_OUTDATED_COLLECTIONS; + public String SQL_DELETE_COLLECTION_ITEMS; + public String SQL_DELETE_COLLECTION; + + /** + * The SQL statement to delete some artifacts from the database. + */ + public String SQL_DELETE_ARTIFACT; + + /** + * XPath to figure out how long the cleaner should sleep between + * cleanups. This is stored in the global configuration. + */ + public static final String SLEEP_XPATH = + "/artifact-database/cleaner/sleep-time/text()"; + + /** + * Default nap time between cleanups: 5 minutes. + */ + public static final long SLEEP_DEFAULT = + 5 * 60 * 1000L; // 5 minutes + + /** + * The configured nap time. + */ + protected long sleepTime; + + /** + * Internal locking mechanism to prevent some race conditions. + */ + protected Object sleepLock = new Object(); + + /** + * A reference to the global context. + */ + protected Object context; + + /** + * A specialized Id filter which only delete some artifacts. + * This is used to prevent deletion of living artifacts. + */ + protected LockedIdsProvider lockedIdsProvider; + + /** + * The reviver used to bring the dead artifact on last + * time back to live to call endOfLife() on them. + */ + protected ArtifactReviver reviver; + + protected DBConnection dbConnection; + + /** + * Default constructor. + */ + public DatabaseCleaner() { + } + + /** + * Constructor to create a cleaner with a given global context + * and a given reviver. + * @param context The global context of the artifact database + * @param reviver The reviver to awake artifact one last time. + */ + public DatabaseCleaner(Object context, ArtifactReviver reviver, DBConfig config) { + setDaemon(true); + sleepTime = getSleepTime(); + this.context = context; + this.reviver = reviver; + this.dbConnection = config.getDBConnection(); + setupSQL(config.getSQL()); + } + + protected void setupSQL(SQL sql) { + SQL_OUTDATED = sql.get("artifacts.outdated"); + SQL_OUTDATED_COLLECTIONS = sql.get("collections.outdated"); + SQL_DELETE_COLLECTION_ITEMS = sql.get("delete.collection.items"); + SQL_DELETE_COLLECTION = sql.get("delete.collection"); + SQL_DELETE_ARTIFACT = sql.get("artifacts.delete"); + } + + /** + * Sets the filter that prevents deletion of living artifacts. + * Living artifacts are artifacts which are currently active + * inside the artifact database. Deleting them in this state + * would create severe internal problems. + */ + public void setLockedIdsProvider(LockedIdsProvider lockedIdsProvider) { + this.lockedIdsProvider = lockedIdsProvider; + } + + /** + * External hook to tell the cleaner to wake up before its + * regular nap time is over. This is the case when the artifact + * database finds an artifact which is already outdated. + */ + public void wakeup() { + synchronized (sleepLock) { + sleepLock.notify(); + } + } + + /** + * Fetches the sleep time from the global configuration. + * @return the time to sleep between database cleanups in ms. + */ + protected static long getSleepTime() { + String sleepTimeString = Config.getStringXPath(SLEEP_XPATH); + + if (sleepTimeString == null) { + return SLEEP_DEFAULT; + } + try { + // sleep at least one second + return Math.max(Long.parseLong(sleepTimeString), 1000L); + } + catch (NumberFormatException nfe) { + logger.warn("Cleaner sleep time defaults to " + SLEEP_DEFAULT); + } + return SLEEP_DEFAULT; + } + + private static class IdIdentifier { + + int id; + String identifier; + + private IdIdentifier(int id, String identifier) { + this.id = id; + this.identifier = identifier; + } + } // class IdIdentifier + + private static final class IdData + extends IdIdentifier + { + byte [] data; + String factoryName; + + public IdData( + int id, + String factoryName, + byte [] data, + String identifier + ) { + super(id, identifier); + this.factoryName = factoryName; + this.data = data; + } + } // class IdData + + /** + * Cleaning is done in two phases. First we fetch a list of ids + * of artifacts. If there are artifacts the cleaning is done. + * Second we load the artifacts one by one one and call there + * endOfLife() method. In this loop we remove them from database, too. + * Each deletion is commited to ensure that a sudden failure + * of the artifact database server does delete artifacts twice + * or does not delete them at all. After this the first step + * is repeated. + */ + protected void cleanup() { + logger.info("database cleanup"); + + Connection connection = null; + PreparedStatement fetchIds = null; + PreparedStatement stmnt = null; + ResultSet result = null; + + DataSource dataSource = dbConnection.getDataSource(); + + Set<Integer> lockedIds = lockedIdsProvider != null + ? lockedIdsProvider.getLockedIds() + : EMPTY_IDS; + + String questionMarks = lockedIds.isEmpty() + ? "-666" // XXX: A bit hackish. + : StringUtils.repeat('?', lockedIds.size(), ','); + + List<String> deletedCollections = new ArrayList<String>(); + List<String> deletedArtifacts = new ArrayList<String>(); + + try { + connection = dataSource.getConnection(); + connection.setAutoCommit(false); + + fetchIds = connection.prepareStatement( + SQL_OUTDATED.replace("$LOCKED_IDS$", questionMarks)); + + // some dbms like derby do not support LIMIT + // in SQL statements. + fetchIds.setMaxRows(MAX_ROWS); + + // Fetch ids of outdated collections + stmnt = connection.prepareStatement( + SQL_OUTDATED_COLLECTIONS.replace( + "$LOCKED_IDS$", questionMarks)); + + // fill in the locked ids + int idx = 1; + for (Integer id: lockedIds) { + fetchIds.setInt(idx, id); + stmnt .setInt(idx, id); + ++idx; + } + + ArrayList<IdIdentifier> cs = new ArrayList<IdIdentifier>(); + result = stmnt.executeQuery(); + while (result.next()) { + cs.add(new IdIdentifier( + result.getInt(1), + result.getString(2))); + } + + result.close(); result = null; + stmnt.close(); stmnt = null; + + // delete collection items + stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION_ITEMS); + + for (IdIdentifier id: cs) { + logger.debug("Mark collection for deletion: " + id.id); + stmnt.setInt(1, id.id); + stmnt.execute(); + } + + stmnt.close(); stmnt = null; + + // delete collections + stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION); + + for (IdIdentifier id: cs) { + stmnt.setInt(1, id.id); + stmnt.execute(); + deletedCollections.add(id.identifier); + } + + stmnt.close(); stmnt = null; + connection.commit(); + + cs = null; + + // remove artifacts + stmnt = connection.prepareStatement(SQL_DELETE_ARTIFACT); + + for (;;) { + List<IdData> ids = new ArrayList<IdData>(); + + result = fetchIds.executeQuery(); + + while (result.next()) { + ids.add(new IdData( + result.getInt(1), + result.getString(2), + result.getBytes(3), + result.getString(4))); + } + + result.close(); result = null; + + if (ids.isEmpty()) { + break; + } + + for (int i = ids.size()-1; i >= 0; --i) { + IdData idData = ids.get(i); + Artifact artifact = reviver.reviveArtifact( + idData.factoryName, idData.data); + idData.data = null; + + logger.debug("Prepare Artifact (id=" + + idData.id + ") for deletion."); + + stmnt.setInt(1, idData.id); + stmnt.execute(); + connection.commit(); + + try { + if (artifact != null) { + logger.debug("Call endOfLife for Artifact: " + + artifact.identifier()); + + artifact.endOfLife(context); + } + } + catch (Exception e) { + logger.error(e.getMessage(), e); + } + + deletedArtifacts.add(idData.identifier); + } // for all fetched data + } + } + catch (SQLException sqle) { + logger.error(sqle.getLocalizedMessage(), sqle); + } + finally { + if (result != null) { + try { result.close(); } + catch (SQLException sqle) {} + } + if (stmnt != null) { + try { stmnt.close(); } + catch (SQLException sqle) {} + } + if (fetchIds != null) { + try { fetchIds.close(); } + catch (SQLException sqle) {} + } + if (connection != null) { + try { connection.close(); } + catch (SQLException sqle) {} + } + } + + if (!deletedCollections.isEmpty()) { + reviver.killedCollections(deletedCollections); + } + + if (!deletedArtifacts.isEmpty()) { + reviver.killedArtifacts(deletedArtifacts); + } + + if (logger.isDebugEnabled()) { + logger.debug( + "collections removed: " + deletedCollections.size()); + logger.debug( + "artifacts removed: " + deletedArtifacts.size()); + } + } + + /** + * The main code of the cleaner. It sleeps for the configured + * nap time, cleans up the database, sleeps again and so on. + */ + @Override + public void run() { + logger.info("sleep time: " + sleepTime + "ms"); + for (;;) { + cleanup(); + long startTime = System.currentTimeMillis(); + + try { + synchronized (sleepLock) { + sleepLock.wait(sleepTime); + } + } + catch (InterruptedException ie) { + } + + long stopTime = System.currentTimeMillis(); + + if (logger.isDebugEnabled()) { + logger.debug("Cleaner slept " + (stopTime - startTime) + "ms"); + } + } // for (;;) + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifact.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * Trivial implementation of an artifact. Useful to be subclassed. + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultArtifact +implements Artifact +{ + private static Logger logger = Logger.getLogger(DefaultArtifact.class); + + /** + * The identifier of the artifact. + */ + protected String identifier; + + + /** + * Default constructor. + */ + public DefaultArtifact() { + } + + + public void setIdentifier(String identifier) { + if (logger.isDebugEnabled()) { + logger.debug("Change identifier: " + + this.identifier + " -> " + identifier); + } + this.identifier = identifier; + } + + public String identifier() { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.identifier: " + identifier); + } + return this.identifier; + } + + + public String hash() { + String hash = String.valueOf(hashCode()); + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.hashCode: " + + identifier + " (" + hash + ")"); + } + return hash; + } + + public Document describe(Document data, CallContext context) { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.describe: " + identifier); + } + return XMLUtils.newDocument(); + } + + public Document advance(Document target, CallContext context) { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.advance: " + identifier); + } + return XMLUtils.newDocument(); + } + + public Document feed(Document target, CallContext context) { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.feed: " + identifier); + } + return XMLUtils.newDocument(); + } + + public void out( + Document format, + OutputStream out, + CallContext context + ) + throws IOException + { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.out: " + identifier); + } + } + + public void out( + String type, + Document format, + OutputStream out, + CallContext context + ) + throws IOException + { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.out: " + identifier); + } + } + + public void setup(String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.setup: " + identifier); + } + this.identifier = identifier; + } + + public void endOfLife(Object context) { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.endOfLife: " + identifier); + } + } + + public void cleanup(Object context) { + if (logger.isDebugEnabled()) { + logger.debug("DefaultArtifact.cleanup: " + identifier); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactCollection.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import java.io.IOException; +import java.io.OutputStream; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.User; + +import de.intevation.artifacts.common.utils.XMLUtils; + + +/** + * Trivial implementation of an artifact collection. Useful to be subclassed. + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultArtifactCollection +implements ArtifactCollection +{ + /** The logger used in this class. */ + private static Logger logger = + Logger.getLogger(DefaultArtifactCollection.class); + + /** The identifier of the collection. */ + protected String identifier; + + /** The identifier of the collection. */ + protected String name; + + /** The owner of this collection. */ + protected User user; + + /** The attribute of this collection. */ + protected Document attribute; + + /** The artifacts stored in this collection. */ + protected List<Artifact> artifacts; + + /** + * The attributes used for the artifacts stored in this collection. The key + * of this map represents the identifier of the artifact which the attribute + * belong to. + */ + protected Map<String, Document> attributes; + + /** The creation time of this collection.*/ + protected Date creationTime; + + protected long ttl; + + + /** + * Default constructor. + */ + public DefaultArtifactCollection() { + } + + + /** + * When created by a factory this method is called to + * initialize the collection. + * @param identifier The identifier from collection database + * @param factory The factory which created this collection. + * @param context The global context of the runtime system. + * @param data The data which can be use to setup a collection with + * more details. + */ + public void setup( + String identifier, + String name, + Date creationTime, + long ttl, + ArtifactCollectionFactory factory, + Object context, + Document data) + { + logger.debug("DefaultArtifactCollection.setup: " + identifier); + + artifacts = new ArrayList<Artifact>(); + attributes = new HashMap<String, Document>(); + + setIdentifier(identifier); + setName(name); + setCreationTime(creationTime); + setTTL(ttl); + setAttribute(data); + } + + + public Document describe(CallContext context) { + logger.debug("DefaultArtifactCollection.describe: " + identifier); + + return XMLUtils.newDocument(); + } + + + /** + * Set a new identifier for this collection. + * @param identifier New identifier for this collection. + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + + /** + * Identify this collection. + * @return Returns unique string to identify this collection globally. + */ + public String identifier() { + return identifier; + } + + + /** + * Name of this collection. + * @return Returns the name of this collection + */ + public String getName() { + return name; + } + + /** + * Name of this collection. + * @param name the name of this collection + */ + public void setName(String name) { + this.name = name; + } + + + /** + * Set a new owner of this collection. + * @param user New owner for this collection. + */ + public void setUser(User user) { + this.user = user; + } + + + /** + * Identify the owner of the collection. + * @return Returns owner of the collection. + */ + public User getUser() { + return user; + } + + + /** + * Returns the creation time of the collection. + * + * @return the creation time of the collection. + */ + public Date getCreationTime() { + return creationTime; + } + + + /** + * Sets the creation time of the collection. + * + * @param creationTime The new creation time. + */ + public void setCreationTime(Date creationTime) { + this.creationTime = creationTime; + } + + + public long getTTL() { + return ttl; + } + + + public void setTTL(long ttl) { + this.ttl = ttl; + } + + + /** + * Returns the attribute of the collection. + * + * @return the attribute of the collection. + */ + public Document getAttribute() { + return attribute; + } + + + /** + * Sets the attribute of the collection. + * + * @param attribute The attribute of this collection. + */ + public void setAttribute(Document attribute) { + this.attribute = attribute; + } + + + /** + * Called from artifact database when an artifact is + * going to be removed from system. + * @param context The global context of the runtime system. + */ + public void endOfLife(Object context) { + logger.debug("DefaultArtifactCollection.endOfLife"); + } + + + /** + * Internal hash of this collection. + * @return Returns hash that should stay the same if the internal + * value has not changed. Useful for caching + */ + public String hash() { + logger.debug("DefaultArtifactCollection.hash"); + + return String.valueOf(hashCode()); + } + + + /** + * Called from artifact database before an artifact is + * going to be exported as xml document. + * @param context The global context of the runtime system. + */ + public void cleanup(Object context) { + logger.debug("DefaultArtifactCollection.cleanup"); + } + + + /** + * Adds a new artifact to this collection. + * + * @param artifact The new artifact. + * @param attribute The attributes used for this artifact. + * @param context The CallContext. + */ + public void addArtifact( + Artifact artifact, + Document attribute, + CallContext context) + { + logger.debug("DefaultArtifactCollection.addArtifact"); + + artifacts.add(artifact); + attributes.put(artifact.identifier(), attribute); + } + + + /** + * Removes the given artifact from this collection. + * + * @param artifact The artifact that should be removed. + * @param context The CallContext. + */ + public void removeArtifact(Artifact artifact, CallContext context) { + logger.debug("DefaultArtifactCollection.removeArtifact"); + + if (artifact == null) { + return; + } + + artifacts.remove(artifact); + attributes.remove(artifact.identifier()); + } + + + /** + * Returns a list of artifacts that are stored in this collection. + * + * @param context The CallContext. + * + * @return the list of artifacts stored in this collection. + */ + public Artifact[] getArtifacts(CallContext context) { + logger.debug("DefaultArtifactCollection.getArtifacts"); + + return (Artifact[]) artifacts.toArray(); + } + + + /** + * Returns the attribute document for the given artifact. + * + * @param artifact The artifact. + * @param context The CallContext. + * + * @return a document that contains the attributes of the artifact. + */ + public Document getAttribute(Artifact artifact, CallContext context) { + logger.debug("DefaultArtifactCollection.getAttribute"); + + return attributes.get(artifact.identifier()); + } + + + /** + * Set the attribute for the given artifact. + * + * @param artifact The artifact of the attribute. + * @param document The new attribute of the artifact. + * @param context The CallContext. + */ + public void setAttribute( + Artifact artifact, + Document document, + CallContext context) + { + logger.debug("DefaultArtifactCollection.setAttribute"); + + attributes.put(artifact.identifier(), document); + } + + + /** + * Produce output for this collection. + * @param type Specifies the output type. + * @param format Specifies the format of the output. + * @param out Stream to write the result data to. + * @param context The global context of the runtime system. + * @throws IOException Thrown if an I/O occurs. + */ + public void out( + String type, + Document format, + OutputStream out, + CallContext context) + throws IOException + { + logger.debug("DefaultArtifactCollection.out"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactCollectionFactory.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.ArtifactCollectionFactory; + +import java.util.Date; + + +/** + * The default implementation of a ArtifactCollectionFactory. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultArtifactCollectionFactory +implements ArtifactCollectionFactory +{ + /** The logger that is used in this factory.*/ + private static Logger logger = + Logger.getLogger(DefaultArtifactCollectionFactory.class); + + /** XPath to access the TTL of this artifact.*/ + public static final String XPATH_TTL = "@ttl"; + + /** XPath to access the name of this factory.*/ + public static final String XPATH_NAME = "@name"; + + /** XPath to access the description of this artifact factory.*/ + public static final String XPATH_DESCRIPTION = "@description"; + + /** + * XPath to access the class name of the artifacts to be build + * by this factory. + */ + public static final String XPATH_ARTIFACTCOLLECTION = "@artifact-collection"; + + /** + * Default description of this factory if none is given by the + * configuration. + */ + public static final String DEFAULT_DESCRIPTION = + "No description available"; + + /** + * Class to load if no artifact class is given in the configuration. + */ + public static final String DEFAULT_ARTIFACTCOLLECTION = + "de.intevation.artifactdatabase.DefaultArtifact"; + + + /** The name of the factory.*/ + protected String name; + + /** The description of the factory.*/ + protected String description; + + /** The class that is used to instantiate new ArtifactCollection.*/ + protected Class clazz; + + /** The time to live of the artifact collection build by this factory.*/ + protected Long ttl; + + + /** + * The default constructor. + */ + public DefaultArtifactCollectionFactory() { + } + + + /** + * The short name of this factory. + * + * @return the name of this factory. + */ + public String getName() { + return name; + } + + + /** + * Description of this factory. + * + * @return description of the factory. + */ + public String getDescription() { + return description; + } + + + /** + * Returns the time to live of the given artifact. + */ + public Long timeToLiveUntouched( + ArtifactCollection collection, + Object context) + { + return ttl; + } + + + /** + * Create a new artifact of certain type, given a general purpose context and + * an identifier. + * @param context a context from the ArtifactDatabase. + * @param identifier unique identifer for the new artifact + * @param data the data containing more details for the setup of an Artifact. + * @return a new {@linkplain de.intevation.artifacts.ArtifactCollection ArtifactCollection} + */ + public ArtifactCollection createCollection( + String identifier, + String name, + Date creationTime, + long ttl, + Document data, + Object context + ) { + try { + ArtifactCollection collection = + (ArtifactCollection) clazz.newInstance(); + + collection.setup(identifier, + name, + creationTime, + ttl, + this, + context, + data); + + return collection; + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + + return null; + } + + /** + * Setup the factory with a given configuration + * @param config the configuration + * @param factoryNode the ConfigurationNode of this Factory + */ + public void setup(Document config, Node factoryNode) { + String ttlString = Config.getStringXPath(factoryNode, XPATH_TTL); + if (ttlString != null) { + try { + ttl = Long.valueOf(ttlString); + } + catch (NumberFormatException nfe) { + logger.warn("'" + ttlString + "' is not an integer."); + } + } + + description = Config.getStringXPath( + factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); + + name = Config.getStringXPath(factoryNode, XPATH_NAME, toString()); + + String artifactCollection = Config.getStringXPath( + factoryNode, XPATH_ARTIFACTCOLLECTION, DEFAULT_ARTIFACTCOLLECTION); + + try { + clazz = Class.forName(artifactCollection); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + + if (clazz == null) { + clazz = DefaultArtifactCollection.class; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactContext.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import java.util.HashMap; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.GlobalContext; + +/** + * Default implementation of the context. + * Besides of the configuration it hosts a map to store key/value pairs. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultArtifactContext implements GlobalContext +{ + /** + * The global configuration document of the artifact database. + */ + protected Document config; + + /** + * Custom key/value pairs to be used globally in the whole server. + */ + protected HashMap map; + + /** + * Default constructor + */ + public DefaultArtifactContext() { + this(null); + } + + /** + * Constructor to create a context with a given global + * configuration document and an empty map of custom + * key/value pairs. + * @param config + */ + public DefaultArtifactContext(Document config) { + this.config = config; + map = new HashMap(); + } + + /** + * Fetch a custom value from the global key/value map using + * a given key. + * @param key The key. + * @return The stored value or null if no value was found under + * this key. + */ + public synchronized Object get(Object key) { + return map.get(key); + } + + /** + * Store a custom key/value pair in the global map. + * @param key The key to store + * @param value The value to store + * @return The old value registered under the key or null + * if none wa there before. + */ + public synchronized Object put(Object key, Object value) { + return map.put(key, value); + } + + /** + * Returns a reference to the global configuration document. + * @return The global configuration document. + */ + public Document getConfig() { + return config; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactContextFactory.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.ArtifactContextFactory; +import de.intevation.artifacts.GlobalContext; + +import org.w3c.dom.Document; + +/** + * Default implementation of the context factory. + * Creates a new @see DefaultArtifactContext. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultArtifactContextFactory +implements ArtifactContextFactory +{ + /** + * Default constructor. + */ + public DefaultArtifactContextFactory() { + } + + public GlobalContext createArtifactContext(Document config) { + return new DefaultArtifactContext(config); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactFactory.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.ArtifactSerializer; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.GlobalContext; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Trivial implementation of the ArtifactFactory interface. + * Time to live (ttl), name and description are configured + * via the Node given to #setup(Document, Node) with attributes + * of same name. The class name of the artifacts to be build by this + * factory is configures with the attribute 'artifact'. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultArtifactFactory +implements ArtifactFactory +{ + private static Logger logger = + Logger.getLogger(DefaultArtifactFactory.class); + + /** + * XPath to access the TTL of this artifact. + */ + public static final String XPATH_TTL = "@ttl"; + /** + * XPath to access the name of this factory. + */ + public static final String XPATH_NAME = "@name"; + /** + * XPath to access the description of this artifact factory. + */ + public static final String XPATH_DESCRIPTION = "@description"; + /** + * XPath to access the class name of the artifacts to be build + * by this factory. + */ + public static final String XPATH_ARTIFACT = "@artifact"; + + /** + * Default description of this factory if none is given by the + * configuration. + */ + public static final String DEFAULT_DESCRIPTION = + "No description available"; + + /** + * Class to load if no artifact class is given in the configuration. + */ + public static final String DEFAULT_ARTIFACT = + "de.intevation.artifactdatabase.DefaultArtifact"; + + /** + * The Time to live of the artifacts build by this factory. + */ + protected Long ttl; + + /** + * The name of this factory. + */ + protected String name; + + /** + * The description of this factory. + */ + protected String description; + + /** + * The class of the artifacts to be build by this factory. + */ + protected Class artifactClass; + + /** + * Default constructor. + */ + public DefaultArtifactFactory() { + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public Artifact createArtifact( + String identifier, + GlobalContext context, + CallMeta callMeta, + Document data + ) { + try { + Artifact artifact = + (Artifact)artifactClass.newInstance(); + + artifact.setup(identifier, this, context, callMeta, data); + + return artifact; + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + + return null; + } + + public void setup(Document document, Node factoryNode) { + + String ttlString = Config.getStringXPath(factoryNode, XPATH_TTL); + if (ttlString != null) { + try { + ttl = Long.valueOf(ttlString); + } + catch (NumberFormatException nfe) { + logger.warn("'" + ttlString + "' is not an integer."); + } + } + + description = Config.getStringXPath( + factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); + + name = Config.getStringXPath( + factoryNode, XPATH_NAME, toString()); + + String artifact = Config.getStringXPath( + factoryNode, XPATH_ARTIFACT, DEFAULT_ARTIFACT); + + try { + artifactClass = Class.forName(artifact); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + + if (artifactClass == null) { + artifactClass = DefaultArtifact.class; + } + } + + public Long timeToLiveUntouched(Artifact artifact, Object context) { + return ttl; + } + + public ArtifactSerializer getSerializer() { + return DefaultArtifactSerializer.INSTANCE; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultArtifactSerializer.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactSerializer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +import org.apache.log4j.Logger; + +/** + * Default implementation of the ArtifactSerializer interface. + * It uses serialized Java objects which are gzipped and + * turned into bytes. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultArtifactSerializer +implements ArtifactSerializer +{ + private static Logger logger = + Logger.getLogger(DefaultArtifactSerializer.class); + + /** + * Static instance to avoid repeated creation of Serializers. + */ + public static final ArtifactSerializer INSTANCE = + new DefaultArtifactSerializer(); + + /** + * Default constructor. + */ + public DefaultArtifactSerializer() { + } + + public Artifact fromBytes(byte [] bytes) { + + if (bytes == null) { + return null; + } + + ObjectInputStream ois = null; + + try { + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + GZIPInputStream gis = new GZIPInputStream(bis); + ois = getObjectInputStream(gis); + + return (Artifact)ois.readObject(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + finally { + if (ois != null) { + try { ois.close(); } + catch (IOException ioe) { } + } + } + + return null; + } + + public byte [] toBytes(Artifact artifact) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + GZIPOutputStream gos = new GZIPOutputStream(bos); + ObjectOutputStream oos = getObjectOutputStream(gos); + + oos.writeObject(artifact); + oos.flush(); + oos.close(); + + return bos.toByteArray(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + throw new RuntimeException(ioe); + } + } + + /** + * Wraps an input stream into an object input stream. You may + * overwrite this to get a more specialized deserializer. + * @param is The raw input stream + * @return An instance of a subclass of ObjectInputStream. + * @throws IOException Thrown if something went wrong during + * creation of the object input stream. + */ + protected ObjectInputStream getObjectInputStream(InputStream is) + throws IOException + { + return new ObjectInputStream(is); + } + + /** + * Wraps an output stream into an object output stream. You may + * overwrite this to get a more specialized serializer. + * @param os the raw output stream. + * @return An instance of a subclass of ObjectOutputStream. + * @throws IOException Thrown if something went wrong during + * creation of the object output stream. + */ + protected ObjectOutputStream getObjectOutputStream(OutputStream os) + throws IOException + { + return new ObjectOutputStream(os); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultBackendListener.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,116 @@ +package de.intevation.artifactdatabase; + +import java.util.List; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactCollection; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.User; + +import org.w3c.dom.Document; + +import org.apache.log4j.Logger; + +public class DefaultBackendListener +implements BackendListener +{ + private static Logger log = Logger.getLogger(DefaultBackendListener.class); + + public DefaultBackendListener() { + } + + @Override + public void setup(GlobalContext globalContext) { + log.debug("setup"); + } + + @Override + public void createdArtifact(Artifact artifact, Backend backend) { + log.debug("createdArtifact"); + } + + @Override + public void storedArtifact(Artifact artifact, Backend backend) { + log.debug("storedArtifact"); + } + + @Override + public void createdUser(User user, Backend backend) { + log.debug("createdUser"); + } + + @Override + public void deletedUser(String identifier, Backend backend) { + log.debug("deletedUser"); + } + + @Override + public void createdCollection( + ArtifactCollection collection, + Backend backend + ) { + log.debug("createdCollection"); + } + + @Override + public void deletedCollection(String identifier, Backend backend) { + log.debug("deletedCollection"); + } + + @Override + public void changedCollectionAttribute( + String identifier, + Document document, + Backend backend + ) { + log.debug("changedCollectionAttribute"); + } + + @Override + public void changedCollectionItemAttribute( + String collectionId, + String artifactId, + Document document, + Backend backend + ) { + log.debug("changedCollectionItemAttribute"); + } + + @Override + public void addedArtifactToCollection( + String artifactId, + String collectionId, + Backend backend + ) { + log.debug("addedArtifactToCollection"); + } + + @Override + public void removedArtifactFromCollection( + String artifactId, + String collectionId, + Backend backend + ) { + log.debug("removedArtifactFromCollection"); + } + + @Override + public void setCollectionName( + String collectionId, + String name + ) { + log.debug("setCollectionName"); + } + + @Override + public void killedCollections(List<String> identifiers, Backend backend) { + log.debug("killedCollections"); + } + + @Override + public void killedArtifacts(List<String> identifiers, Backend backend) { + log.debug("killedArtifacts"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultCallMeta.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.PreferredLocale; + +import java.util.Locale; + +/** + * Default implementation of CallMeta. It provides a list of + * preferred langauages and implements an intersection mechanism + * to figure out the best matching language given a list of server + * provided languages. + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultCallMeta +implements CallMeta +{ + /** + * The list of preferred languages. + */ + protected PreferredLocale [] languages; + + /** + * Default constructor. + */ + public DefaultCallMeta() { + } + + /** + * Creates new DefaultCallMeta with a given list of languages. + * @param languages The list of preferred languages. + */ + public DefaultCallMeta(PreferredLocale [] languages) { + this.languages = languages; + } + + public PreferredLocale [] getLanguages() { + return languages; + } + + public Locale getPreferredLocale(Locale [] locales) { + if (locales == null || locales.length == 0) { + return null; + } + + Locale best = null; + float quality = -Float.MAX_VALUE; + + for (int i = 0; i < locales.length; ++i) { + Locale wish = locales[i]; + String wishLanguage = wish.getLanguage(); + + for (int j = 0; j < languages.length; ++j) { + PreferredLocale have = languages[j]; + Locale haveLocale = have.getLocale(); + if (haveLocale.getLanguage().equals(wishLanguage)) { + float haveQuality = have.getQuality(); + if (haveQuality > quality) { + quality = haveQuality; + best = wish; + } + break; // Languages should not contain + // same locale twice. + } + } + } + + return best == null + ? locales[0] + : best; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultCollectionItem.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.CollectionItem; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import org.w3c.dom.Document; + +public class DefaultCollectionItem +implements CollectionItem +{ + protected String artifactIdentifier; + + protected byte [] data; + + protected Document document; + + public DefaultCollectionItem() { + } + + public DefaultCollectionItem(String artifactIdentifier, byte [] attribute) { + this.artifactIdentifier = artifactIdentifier; + this.data = attribute; + } + + public String getArtifactIdentifier() { + return artifactIdentifier; + } + + public synchronized Document getAttribute() { + if (document == null) { + if (data != null) { + document = XMLUtils.fromByteArray(data, true); + } + } + return document; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultPreferredLocale.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.PreferredLocale; + +import java.util.Locale; + +/** + * Models a pair of Locale and quality (0.0-1.0) to be used to + * find best matching locale between server offerings and clients requests. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultPreferredLocale +implements PreferredLocale +{ + /** + * The locale of this tuple pair. + */ + protected Locale locale; + /** + * The quality of this tuple pair between 0.0 and 1.0. + */ + protected float quality; + + /** + * Default constructor + */ + public DefaultPreferredLocale() { + } + + /** + * Constructor to build a pair of given a locale speficied by + * string 'lang' and an given 'quality'. + * @param lang The name of the locale. + * @param quality The quality of the locale. + */ + public DefaultPreferredLocale(String lang, float quality) { + locale = new Locale(lang); + this.quality = quality; + } + + public Locale getLocale() { + return locale; + } + + public float getQuality() { + return quality; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultService.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.Service; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.ServiceFactory; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * Trivial implementation of an artifact database service. Useful to + * be subclassed. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultService +implements Service +{ + private static Logger logger = Logger.getLogger(DefaultService.class); + + public static class Output implements Service.Output { + + protected Object data; + protected String mimeType; + + public Output() { + } + + public Output(Object data, String mimeType) { + this.data = data; + this.mimeType = mimeType; + } + + @Override + public Object getData() { + return data; + } + + @Override + public String getMIMEType() { + return mimeType; + } + } // class Output + + @Override + public Service.Output process( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + logger.debug("Service.process"); + return new Output(new byte[0], "application/octet-stream"); + } + + @Override + public void setup(ServiceFactory factory, GlobalContext globalContext) { + logger.debug("Service.setup"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultServiceFactory.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifacts.Service; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.ServiceFactory; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * Trivial implementation of the ServiceFactory interface. + * Name and an description are configured by the given Node given to + * #setup(Document, Node) via the 'name' and 'description' attributes. + * The name of the class that provides the concrete serice is configured + * by the 'service' attribute. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class DefaultServiceFactory +implements ServiceFactory +{ + private static Logger logger = + Logger.getLogger(DefaultServiceFactory.class); + + /** + * XPath to access the name of the service. + */ + public static final String XPATH_NAME = "@name"; + /** + * XPath to access the description of the service. + */ + public static final String XPATH_DESCRIPTION = "@description"; + /** + * XPath to access the class name of the service to be build by + * this factory. + */ + public static final String XPATH_SERVICE = "@service"; + + /** + * Default description if no description is given in configuration. + */ + public static final String DEFAULT_DESCRIPTION = + "No description available"; + + /** + * Loaded service class if no class name is given in the configuration. + */ + public static final String DEFAULT_SERVICE = + "de.intevation.artifactdatabase.DefaultService"; + + /** + * The name of the service factory. + */ + protected String name; + + /** + * The description of the service factory. + */ + protected String description; + + /** + * The loaded class used to build the concrete service. + */ + protected Class serviceClass; + + /** + * Default constructor. + */ + public DefaultServiceFactory() { + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Service createService(GlobalContext globalContext) { + try { + Service service = (Service)serviceClass.newInstance(); + + service.setup(this, globalContext); + + return service; + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + + return null; + } + + @Override + public void setup(Document config, Node factoryNode) { + + description = Config.getStringXPath( + factoryNode, XPATH_DESCRIPTION, DEFAULT_DESCRIPTION); + + name = Config.getStringXPath( + factoryNode, XPATH_NAME, toString()); + + String service = Config.getStringXPath( + factoryNode, XPATH_SERVICE, DEFAULT_SERVICE); + + try { + serviceClass = Class.forName(service); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + + if (serviceClass == null) { + serviceClass = DefaultService.class; + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultUser.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.User; + + +/** + * Trivial implementation of a user. Useful to be subclassed. + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultUser +implements User +{ + /** The identifier of the user.*/ + protected String identifier; + + /** The name of the user.*/ + protected String name; + + /** The account name of the user.*/ + protected String account; + + /** The role of the user.*/ + protected Document role; + + + /** + * The default constructor. + */ + public DefaultUser() { + } + + public DefaultUser(String identifier) { + this.identifier = identifier; + } + + /** + * A constructor that creates a new user. + * + * @param identifier The uuid of the user. + * @param name The name of the user. + * @param account The account name of the user. + * @param role The role of the user. + */ + public DefaultUser(String identifier, String name, String account, + Document role) { + this.identifier = identifier; + this.name = name; + this.role = role; + this.account = account; + } + + + /** + * Returns the identifier of this user. + * + * @return the identifier of this user. + */ + @Override + public String identifier() { + return identifier; + } + + + /** + * Returns the name of the user. + * + * @return the name of the user. + */ + @Override + public String getName() { + return name; + } + + + /** + * Set the name of the user. + * + * @param name The name for this user. + */ + @Override + public void setName(String name) { + this.name = name; + } + + + /** + * Set the identifier of the user. + * + * @param identifier The new identifier. + */ + @Override + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + + /** + * Set the role of the user. + * + * @param role The new role of the user. + */ + @Override + public void setRole(Document role) { + this.role = role; + } + + + /** + * Returns the role of the user. + * + * @return the role of the user. + */ + @Override + public Document getRole() { + return role; + } + + /** + * Returns the account of the user. + * + * @return the account name of the user. + */ + @Override + public String getAccount() { + return account; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/DefaultUserFactory.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + + +/** + * Default implementation of a UserFactory. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultUserFactory +implements UserFactory +{ + /** The logger that is used in this factory.*/ + private static Logger logger = Logger.getLogger(DefaultUserFactory.class); + + + /** + * Default constructor. + */ + public DefaultUserFactory() { + } + + + public void setup(Document config, Node factoryNode) { + logger.debug("DefaultUserFactory.setup"); + } + + + /** + * This method creates a new DefaultUser with the given identifier, name and + * role. + * + * @param identifier The identifier for the new user. + * @param name The name for the new user. + * @param account The name of the new users account. + * @param role The role for the new user. + * @param context The CallContext. + */ + public User createUser( + String identifier, + String name, + String account, + Document role, + Object context) + { + logger.debug("DefaultUserFactory.createUser: " + name); + return new DefaultUser(identifier, name, account, role); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/FactoryBootstrap.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,733 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.common.utils.Config; + +import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactContextFactory; +import de.intevation.artifacts.ArtifactFactory; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.GlobalContext; +import de.intevation.artifacts.Hook; +import de.intevation.artifacts.ServiceFactory; +import de.intevation.artifacts.UserFactory; + +import de.intevation.artifacts.common.utils.StringUtils; + +import de.intevation.artifactdatabase.rest.HTTPServer; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Bootstrap facility for the global context and the artifact factories. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class FactoryBootstrap +{ + private static Logger logger = Logger.getLogger(FactoryBootstrap.class); + + /** + * XPath to figure out the class name of the context factory from + * the global configuration. + */ + public static final String CONTEXT_FACTORY = + "/artifact-database/factories/context-factory/text()"; + + /** + * The name of the default context factory. + */ + public static final String DEFAULT_CONTEXT_FACTORY = + "de.intevation.artifactdatabase.DefaultArtifactContextFactory"; + + /** + * XPath to figure out the names of the artifact factories from + * the global configuration to be exposed by the artifact database. + */ + public static final String ARTIFACT_FACTORIES = + "/artifact-database/factories/artifact-factories/artifact-factory"; + + /** + * XPath to figure out the names of the service factories from + * the global configuration to build the services offered by the + * artifact database. + */ + public static final String SERVICE_FACTORIES = + "/artifact-database/factories/service-factories/service-factory"; + + /** + * XPath to figure out the class name of the user factory from global + * configuration. + */ + public static final String USER_FACTORY = + "/artifact-database/factories/user-factory"; + + /** + * The name of the default user factory. + */ + public static final String DEFAULT_USER_FACTORY = + "de.intevation.artifactdatabase.DefaultUserFactory"; + + /** + * XPath to figure out the class name of the collection factory from global + * configuration. + */ + public static final String COLLECTION_FACTORY = + "/artifact-database/factories/collection-factory"; + + /** + * The name of the default user factory. + */ + public static final String DEFAULT_COLLECTION_FACTORY = + "de.intevation.artifactdatabase.DefaultArtifactCollectionFactory"; + + /** + * XPath to figure out the secret used to sign the artifact exports + * made by the artfifact database server. + */ + public static final String EXPORT_SECRET = + "/artifact-database/export-secret/text()"; + + /** + * XPAth that points to a configuration node for a CallContext.Listener. + */ + public static final String CALLCONTEXT_LISTENER = + "/artifact-database/callcontext-listener"; + + /** + * XPath that points to configuration nodes for hooks. + */ + public static final String HOOKS = + "/artifact-database/hooks/hook"; + + public static final String HTTP_SERVER = + "/artifact-database/rest-server/http-server/text()"; + + public static final String DEFAULT_HTTP_SERVER = + "de.intevation.artifactdatabase.rest.Standalone"; + + public static final String LIFETIME_LISTENERS = + "/artifact-database/lifetime-listeners/listener"; + + public static final String BACKEND_LISTENERS = + "/artifact-database/backend-listeners/listener"; + + /** + * Default export signing secret. + * <strong>PLEASE CHANGE THE SECRET VIA THE XPATH EXPORT_SECRET + * IN THE CONFIGURATION.</strong>. + */ + public static final String DEFAULT_EXPORT_SECRET = + "!!!CHANGE ME! I'M NO SECRET!!!"; + + /** + * Reference to the global context build by the global context factory. + */ + protected GlobalContext context; + + /** + * List of the artifact factories to be exposed by the + * artifact database. + */ + protected ArtifactFactory [] artifactFactories; + + /** + * List of service factories which creates services that are + * exposed by the artifact database. + */ + protected ServiceFactory [] serviceFactories; + + /** + * The factory that is used to create and list users. + */ + protected UserFactory userFactory; + + /** + * The factory that is used to create new artifact collections. + */ + protected ArtifactCollectionFactory collectionFactory; + + /** + * The CallContext.Listener. + */ + protected CallContext.Listener callContextListener; + + protected List<Hook> postFeedHooks; + + protected List<Hook> postAdvanceHooks; + + protected List<Hook> postDescribeHooks; + + protected List<LifetimeListener> lifetimeListeners; + + protected List<BackendListener> backendListeners; + + /** + * byte array holding the export signing secret. + */ + protected byte [] exportSecret; + + protected HTTPServer httpServer; + + + /** + * Default constructor + */ + public FactoryBootstrap() { + } + + void buildContext() { + String className = Config.getStringXPath( + CONTEXT_FACTORY, DEFAULT_CONTEXT_FACTORY); + + ArtifactContextFactory factory = null; + + try { + Class clazz = Class.forName(className); + factory = (ArtifactContextFactory)clazz.newInstance(); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + + if (factory == null) { + factory = new DefaultArtifactContextFactory(); + } + + logger.info("Using class '" + factory.getClass().getName() + + "' for context creation."); + + context = factory.createArtifactContext(Config.getConfig()); + } + + + /** + * Scans the global configuration to load the configured collection factory + * and sets it up. + */ + protected void loadCollectionFactory() { + + logger.info("loading collection factory."); + + Node factory = Config.getNodeXPath(COLLECTION_FACTORY); + + String className = Config.getStringXPath( + factory, "text()", DEFAULT_COLLECTION_FACTORY); + + try { + Class clazz = Class.forName(className); + collectionFactory = (ArtifactCollectionFactory) clazz.newInstance(); + + collectionFactory.setup(Config.getConfig(), factory); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + /** + * Scans the global configuration to load the configured + * artifact factories and sets them up. + */ + protected void loadArtifactFactories() { + + logger.info("loading artifact factories"); + + ArrayList loadedFactories = new ArrayList(); + + NodeList nodes = Config.getNodeSetXPath(ARTIFACT_FACTORIES); + + if (nodes == null) { + logger.warn("No factories found"); + } + + Document config = Config.getConfig(); + + for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) { + String className = nodes.item(i).getTextContent().trim(); + + ArtifactFactory factory = null; + + try { + Class clazz = Class.forName(className); + factory = (ArtifactFactory)clazz.newInstance(); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + + if (factory != null) { + factory.setup(config, nodes.item(i)); + loadedFactories.add(factory); + logger.info("Registering '" + + factory.getName() + "' as artifact factory."); + } + } + + artifactFactories = (ArtifactFactory [])loadedFactories.toArray( + new ArtifactFactory[loadedFactories.size()]); + } + + /** + * Scans the global configuration for the configured service factories + * and sets them up. + */ + protected void loadServiceFactories() { + + logger.info("loading service factories"); + + ArrayList loadedFactories = new ArrayList(); + + NodeList nodes = Config.getNodeSetXPath(SERVICE_FACTORIES); + + if (nodes == null) { + logger.warn("No factories found"); + } + + Document config = Config.getConfig(); + + for (int i = 0, N = nodes != null ? nodes.getLength() : 0; i < N; ++i) { + String className = nodes.item(i).getTextContent().trim(); + + ServiceFactory factory = null; + + try { + Class clazz = Class.forName(className); + factory = (ServiceFactory)clazz.newInstance(); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + + if (factory != null) { + factory.setup(config, nodes.item(i)); + loadedFactories.add(factory); + logger.info( "Registering '" + factory.getName() + + "' as service factory."); + } + } + + serviceFactories = (ServiceFactory [])loadedFactories.toArray( + new ServiceFactory[loadedFactories.size()]); + } + + + /** + * Scans the global configuration for the configured user factory. + */ + protected void loadUserFactory() { + logger.info("loading user factory"); + + Node factory = Config.getNodeXPath(USER_FACTORY); + + String className = Config.getStringXPath( + factory, "text()", DEFAULT_USER_FACTORY); + + try { + Class clazz = Class.forName(className); + userFactory = (UserFactory) clazz.newInstance(); + + userFactory.setup(Config.getConfig(), factory); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + + protected void loadCallContextListener() { + logger.info("loading CallContext.Listener"); + + Node listener = Config.getNodeXPath(CALLCONTEXT_LISTENER); + + if (listener == null) { + return; + } + + String className = Config.getStringXPath(listener, "text()"); + + try { + Class clazz = Class.forName(className); + callContextListener = (CallContext.Listener) clazz.newInstance(); + + callContextListener.setup(Config.getConfig(), listener); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + protected void loadHTTPServer() { + logger.info("loading HTTPServer"); + + String className = Config.getStringXPath( + HTTP_SERVER, DEFAULT_HTTP_SERVER); + + logger.info("using HTTP server: " + className); + + try { + Class clazz = Class.forName(className); + httpServer = (HTTPServer)clazz.newInstance(); + + httpServer.setup(Config.getConfig()); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + protected void loadLifetimeListeners() { + logger.info("loading lifetime listeners"); + + NodeList nodes = Config.getNodeSetXPath(LIFETIME_LISTENERS); + + if (nodes == null) { + logger.debug("no lifetime listeners configure"); + return; + } + + List<LifetimeListener> ltls = new ArrayList<LifetimeListener>(); + + for (int i = 0, N = nodes.getLength(); i < N; ++i) { + Node node = nodes.item(i); + String className = node.getTextContent(); + if (className == null + || (className = className.trim()).length() == 0) { + continue; + } + try { + Class clazz = Class.forName(className); + LifetimeListener listener = + (LifetimeListener)clazz.newInstance(); + + listener.setup(Config.getConfig()); + + ltls.add(listener); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + lifetimeListeners = ltls; + } + + protected void loadBackendListeners() { + logger.info("loading backend listeners"); + + NodeList nodes = Config.getNodeSetXPath(BACKEND_LISTENERS); + + if (nodes == null) { + logger.debug("no backend listeners configure"); + return; + } + + List<BackendListener> bls = new ArrayList<BackendListener>(); + + for (int i = 0, N = nodes.getLength(); i < N; ++i) { + Node node = nodes.item(i); + String className = node.getTextContent(); + if (className == null + || (className = className.trim()).length() == 0) { + continue; + } + try { + Class clazz = Class.forName(className); + BackendListener listener = + (BackendListener)clazz.newInstance(); + + bls.add(listener); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + } + + backendListeners = bls; + } + + protected void loadHooks() { + logger.info("loading hooks"); + + postFeedHooks = new ArrayList<Hook>(); + postAdvanceHooks = new ArrayList<Hook>(); + postDescribeHooks = new ArrayList<Hook>(); + + NodeList nodes = Config.getNodeSetXPath(HOOKS); + + if (nodes == null) { + logger.info("No hooks found"); + return; + } + + for (int i = 0, len = nodes.getLength(); i < len; i++) { + Node cfg = nodes.item(i); + String applies = Config.getStringXPath(cfg, "@applies"); + + if (applies == null || applies.length() == 0) { + continue; + } + + Hook hook = loadHook(cfg); + String[] apply = applies.split(","); + + for (String a: apply) { + a = a.trim().toLowerCase(); + + if (a.equals("post-feed")) { + postFeedHooks.add(hook); + } + else if (a.equals("post-advance")) { + postAdvanceHooks.add(hook); + } + else if (a.equals("post-describe")) { + postDescribeHooks.add(hook); + } + } + } + } + + protected Hook loadHook(Node hookCfg) { + if (hookCfg == null) { + return null; + } + + Hook hook = null; + + String className = Config.getStringXPath(hookCfg, "@class"); + + try { + Class clazz = Class.forName(className); + hook = (Hook) clazz.newInstance(); + + hook.setup(hookCfg); + } + catch (ClassNotFoundException cnfe) { + logger.error(cnfe.getLocalizedMessage(), cnfe); + } + catch (InstantiationException ie) { + logger.error(ie.getLocalizedMessage(), ie); + } + catch (ClassCastException cce) { + logger.error(cce.getLocalizedMessage(), cce); + } + catch (IllegalAccessException iae) { + logger.error(iae.getLocalizedMessage(), iae); + } + + return hook; + } + + /** + * Fetches the export signing secret from the global configuration. + * If none is found if defaults to the DEFAULT_EXORT_SECRET which + * is insecure. + */ + protected void setupExportSecret() { + String secret = Config.getStringXPath(EXPORT_SECRET); + + if (secret == null) { + logger.warn("NO EXPORT SECRET SET! USING INSECURE DEFAULT!"); + secret = DEFAULT_EXPORT_SECRET; + } + + exportSecret = StringUtils.getUTF8Bytes(secret); + } + + /** + * Loads all the dynamic classes configured by the global configuration. + */ + public void boot() { + setupExportSecret(); + buildContext(); + loadCollectionFactory(); + loadArtifactFactories(); + loadServiceFactories(); + loadUserFactory(); + loadCallContextListener(); + loadHTTPServer(); + loadHooks(); + loadLifetimeListeners(); + loadBackendListeners(); + } + + /** + * Returns the artifact collection factory. + * + * @return the artifact collection factory. + */ + public ArtifactCollectionFactory getArtifactCollectionFactory() { + return collectionFactory; + } + + /** + * Returns the list of ready to use artifact factories. + * @return The list of artifact factories. + */ + public ArtifactFactory [] getArtifactFactories() { + return artifactFactories; + } + + /** + * Returns the ready to use service factories. + * @return The list of service factories. + */ + public ServiceFactory [] getServiceFactories() { + return serviceFactories; + } + + /** + * Returns the user factory. + * + * @return the user factory. + */ + public UserFactory getUserFactory() { + return userFactory; + } + + /** + * Returns the global context created by the global context factory. + * @return The global context. + */ + public GlobalContext getContext() { + return context; + } + + /** + * Returns the signing secret to be used when ex- and importing + * artifacts from and into the artifact database. + * @return the byte array containg the signing secret. + */ + public byte [] getExportSecret() { + return exportSecret; + } + + /** + * Returns a CallContext.Listener if configured or null. + * + * @return a CallContext.Listener. + */ + public CallContext.Listener getCallContextListener() { + return callContextListener; + } + + public List<Hook> getPostFeedHooks() { + return postFeedHooks; + } + + public List<Hook> getPostAdvanceHooks() { + return postAdvanceHooks; + } + + public List<Hook> getPostDescribeHooks() { + return postDescribeHooks; + } + + public HTTPServer getHTTPServer() { + return httpServer; + } + + public List<LifetimeListener> getLifetimeListeners() { + return lifetimeListeners; + } + + public List<BackendListener> getBackendListeners() { + return backendListeners; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/LazyBackendUser.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.User; +import de.intevation.artifacts.UserFactory; + +import org.w3c.dom.Document; + +public class LazyBackendUser +implements User +{ + protected UserFactory factory; + protected Backend backend; + protected String identifier; + protected User user; + protected Object context; + + public LazyBackendUser( + String identifier, + UserFactory factory, + Backend backend, + Object context + ) { + this.identifier = identifier; + this.factory = factory; + this.backend = backend; + this.context = context; + } + + protected User getUser() { + if (user == null) { + user = backend.getUser(identifier, factory, context); + if (user == null) { + throw new IllegalStateException("loading user failed"); + } + } + return user; + } + + @Override + public String identifier() { + return getUser().identifier(); + } + + @Override + public String getName() { + return getUser().getName(); + } + + @Override + public void setName(String name) { + getUser().setName(name); + } + + @Override + public void setIdentifier(String identifier) { + getUser().setIdentifier(identifier); + } + + @Override + public Document getRole() { + return getUser().getRole(); + } + + @Override + public void setRole(Document document) { + getUser().setRole(document); + } + + @Override + public String getAccount() { + return getUser().getAccount(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/LifetimeListener.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,15 @@ +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.GlobalContext; + +import org.w3c.dom.Document; + +public interface LifetimeListener +{ + void setup(Document document); + + void systemUp(GlobalContext globalContext); + + void systemDown(GlobalContext globalContext); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/ProtocolUtils.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase; + +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.ArtifactNamespaceContext; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifactdatabase.state.Facet; +import de.intevation.artifactdatabase.state.Output; +import de.intevation.artifactdatabase.state.State; + + +/** + * This class provides methods that help creating the artifact protocol + * documents describe, feed, advance and out. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class ProtocolUtils { + + /** + * It should not be necessary to create instances of this class. + */ + private ProtocolUtils() {} + + + /** + * This method creates a node that might be used for the artifact protocol. + * + * @param creator The ElementCreator that is used to create the node. + * @param nodeName The node name. + * @param attrName The names of optional attributes. + * @param value The values for the optional attributes. + * + * @return the created node. + */ + public static Element createArtNode( + XMLUtils.ElementCreator creator, + String nodeName, String[] attrName, String[] value) + { + Element typeNode = creator.create(nodeName); + + if (attrName != null && value != null) { + for (int i = 0; i < attrName.length; i++) { + if (i < value.length) { + creator.addAttr(typeNode, attrName[i], value[i], true); + } + else { + break; + } + } + } + + return typeNode; + } + + + /** + * This method creates the root node for all artifact protocol documents. + * + * @param creator The ElementCreator used to create new elements. + * + * @return the root node for the artifact protocol document. + */ + public static Element createRootNode(XMLUtils.ElementCreator creator) { + return createArtNode(creator, "result", null, null); + } + + + /** + * This method appends the three necessary nodes <i>type</i>, <i>uuid</i> + * and <i>hash</i> of the describe document to <i>root</i> node. + * + * @param creator The ElementCreator that is used to create new nodes. + * @param root The root node of the describe document. + * @param uuid The UUID of the artifact. + * @param hash The hash if the artifact. + */ + public static void appendDescribeHeader( + XMLUtils.ElementCreator creator, Element root, String uuid, String hash) + { + root.appendChild(createArtNode( + creator, + "type", + new String[] {"name"}, + new String[] {"describe"})); + + root.appendChild(createArtNode( + creator, + "uuid", + new String[] {"value"}, + new String[] {uuid})); + + root.appendChild(createArtNode( + creator, + "hash", + new String[] {"value"}, + new String[] {hash})); + } + + + /** + * This method appends a node that describes the current state to + * <i>root</i>. + * + * @param creator The ElementCreator used to create new elements. + * @param root The parent node for new elements. + * @param state The state to be appended. + */ + public static void appendState( + XMLUtils.ElementCreator creator, Element root, State state) + { + root.appendChild(createArtNode( + creator, "state", + new String[] { "description", "name" }, + new String[] { state.getDescription(), state.getID() })); + } + + + /** + * This method appends a node with reachable states to <i>root</i>. + * + * @param creator The ElementCreator used to create new elements. + * @param root The parent node for new elements. + * @param states The reachable states to be appended. + */ + public static void appendReachableStates( + XMLUtils.ElementCreator creator, + Element root, + List<State> states) + { + Element reachable = createArtNode( + creator, "reachable-states", null, null); + + for (State s: states) { + appendState(creator, reachable, s); + } + + root.appendChild(reachable); + } + + + /** + * This method appends a node for each Output in the <i>outputs</i> list to + * <i>out</i>. Note: an output node includes its provided facets! + * + * @param doc The document to which to add new elements. + * @param out The parent node for new elements. + * @param outputs The list of reachable outputs. + */ + public static void appendOutputModes( + Document doc, + Element out, + List<Output> outputs) + { + ElementCreator creator = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + for (Output o: outputs) { + Element newOut = createArtNode( + creator, + "output", + new String[] {"name", "description", "mime-type", "type"}, + new String[] { + o.getName(), + o.getDescription(), + o.getMimeType(), + o.getType() }); + + Element facets = createArtNode(creator, "facets", null, null); + appendFacets(doc, facets, o.getFacets()); + + newOut.appendChild(facets); + out.appendChild(newOut); + } + } + + + /** + * This method appends a node for each Facet in the <i>facets</i> list to + * <i>facet</i>. + * + * @param doc The document to wich to add new elements. + * @param facet The root node for new elements. + * @param facets The list of facets. + */ + public static void appendFacets( + Document doc, + Element facet, + List<Facet> facets) + { + if (facets == null || facets.size() == 0) { + return; + } + + ElementCreator creator = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + for (Facet f: facets) { + Node node = f.toXML(doc); + + if (node != null) { + facet.appendChild(node); + } + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/ProxyArtifact.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * The proxy artifact is a wrapper around another artifact. It simply forwards + * the interface calls to this underlaying artifact. + * The reason for using proxy artifacts is enable the workflow to exchange + * artifacts at any time by something else without losing the concrete + * artifact. From the outside it always looks like there is only one + * distinct artifact.<br> + * + * An inner artifact is able to replace itself by indirectly hand over + * the replacement via the call context to the proxy artifact.<br> + * To do so the proxied artifact has to call + * <code>callContext.getContextValue(EPLACE_PROXY, replacement);</code>. + * After the current call (describe, feed, advance and out) of the proxied + * artifact is finished the proxy artifact replaces the former proxied artifact + * with the replacement. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class ProxyArtifact +extends DefaultArtifact +{ + /** + * Key to signal that the proxied artifact should be replaced. + */ + public static final Object REPLACE_PROXY = new Object(); + + private static Logger logger = Logger.getLogger(ProxyArtifact.class); + + /** + * The proxied artifact. + */ + protected Artifact proxied; + + /** + * Default constructor. + */ + public ProxyArtifact() { + } + + /** + * Constructor to create a new proxy artifact around a given artifact. + * @param proxied The artifact to be proxied. + */ + public ProxyArtifact(Artifact proxied) { + this.proxied = proxied; + } + + /** + * The currently proxied artifact. + * @return The proxied artifact. + */ + public Artifact getProxied() { + return proxied; + } + + /** + * Explicitly set the proxied artifacts. + * @param proxied + */ + public void setProxied(Artifact proxied) { + this.proxied = proxied; + } + + @Override + public void setIdentifier(String identifier) { + this.identifier = identifier; + + if (proxied != null) + proxied.setIdentifier(identifier); + } + + /** + * Method to check if the current proxied artifact should be replaced + * by a new one coming from the call context. + * @param callContext + */ + protected void checkReplacement(CallContext callContext) { + Object replacement = callContext.getContextValue(REPLACE_PROXY); + if (replacement instanceof Artifact) { + setProxied((Artifact)replacement); + } + } + + @Override + public String hash() { + return proxied != null + ? proxied.hash() + : super.hash(); + } + + @Override + public Document describe(Document data, CallContext context) { + try { + return proxied != null + ? proxied.describe(data, context) + : super.describe(data, context); + } + finally { + checkReplacement(context); + } + } + + @Override + public Document advance(Document target, CallContext context) { + try { + return proxied != null + ? proxied.advance(target, context) + : super.advance(target, context); + } + finally { + checkReplacement(context); + } + } + + @Override + public Document feed(Document target, CallContext context) { + try { + return proxied != null + ? proxied.feed(target, context) + : super.feed(target, context); + } + finally { + checkReplacement(context); + } + } + + @Override + public void out( + Document format, + OutputStream out, + CallContext context + ) + throws IOException + { + try { + if (proxied != null) { + proxied.out(format, out, context); + } + else { + super.out(format, out, context); + } + } + finally { + checkReplacement(context); + } + } + + @Override + public void endOfLife(Object context) { + if (proxied != null) { + proxied.endOfLife(context); + } + else { + super.endOfLife(context); + } + } + + @Override + public void cleanup(Object context) { + if (proxied != null) + proxied.cleanup(context); + else + super.cleanup(context); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/XMLService.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase; + +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.Service; +import de.intevation.artifacts.GlobalContext; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; + +/** + * Trivial implementation of an artifact database service. Useful to + * be subclassed. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class XMLService +extends DefaultService +{ + private static Logger logger = Logger.getLogger(XMLService.class); + + @Override + public Service.Output process( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + return new Output( + processXML(data, globalContext, callMeta), + "application/xml"); + } + + public Document processXML( + Document data, + GlobalContext globalContext, + CallMeta callMeta + ) { + return XMLUtils.newDocument(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/data/DefaultStateData.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.data; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultStateData implements StateData { + + /** The name of the data. */ + protected String name; + + /** The description of the data. */ + protected String description; + + /** The type of the data. */ + protected String type; + + /** The value. */ + protected Object value; + + public DefaultStateData() { + } + + /** + * The default constructor. It creates empty StateData objects with no + * value. + * + * @param name The name. + * @param description The description. + * @param type The type. + */ + public DefaultStateData(String name, String description, String type) { + this.name = name; + this.description = description; + this.type = type; + } + + public void set(StateData other) { + name = other.getName(); + description = other.getDescription(); + type = other.getType(); + value = other.getValue(); + } + + + /** + * A constructor that takes the name of the data, its value and the + * describing parameters description and type. + * + * @param name The name of the data item. + * @param description The description. + * @param type The type. + * @param value The value of the data item. + */ + public DefaultStateData( + String name, + String description, + String type, + String value) + { + this.name = name; + this.description = description; + this.type = type; + this.value = value; + } + + + /** + * Returns the name of the data object. + * + * @return the name. + */ + public String getName() { + return name; + } + + + /** + * Returns the description of the data object. + * + * @return the description of the data object. + */ + public String getDescription() { + return description; + } + + + /** + * Returns the type of the data object as string. + * + * @return the type as string. + */ + public String getType() { + return type; + } + + + /** + * Returns the value of the data object. + * + * @return the value. + */ + public Object getValue() { + return value; + } + + + /** + * Set the value of this data object. + * + * @param value The new value for this data object. + */ + public void setValue(Object value) { + this.value = value; + } + + @Override + public StateData deepCopy() { + DefaultStateData copy = new DefaultStateData(); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/data/StateData.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.data; + +import java.io.Serializable; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface StateData extends Serializable { + + /** + * Returns the name of the data object. + * + * @return the name. + */ + public String getName(); + + + /** + * Returns the description of the data object. + * + * @return the description of the data object. + */ + public String getDescription(); + + + /** + * Returns the type of the data object as string. + * + * @return the type as string. + */ + public String getType(); + + + /** + * Returns the value of the data object. + * + * @return the value. + */ + public Object getValue(); + + + /** + * Set the value of this data object. + * + * @param value The new value for this data object. + */ + public void setValue(Object value); + + public StateData deepCopy(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/db/DBConnection.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,121 @@ +package de.intevation.artifactdatabase.db; + +import javax.sql.DataSource; + +import java.io.File; + +import org.apache.commons.pool.ObjectPool; + +import org.apache.commons.pool.impl.GenericObjectPool; + +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDataSource; + +import de.intevation.artifacts.common.utils.Config; + +import org.apache.log4j.Logger; + +public class DBConnection +{ + private static Logger log = Logger.getLogger(DBConnection.class); + + public static final String DEFAULT_DRIVER = "org.h2.Driver"; + public static final String DEFAULT_USER = ""; + public static final String DEFAULT_PASSWORD = ""; + public static final String DEFAULT_DATABASE_FILE = "artifacts.db"; + public static final String DEFAULT_URL = getDefaultURL(); + + public static final String getDefaultURL() { + File configDir = Config.getConfigDirectory(); + File databaseFile = new File(configDir, DEFAULT_DATABASE_FILE); + return "jdbc:h2:" + databaseFile; + } + + protected DataSource dataSource; + + protected String driver; + protected String url; + protected String user; + protected String password; + + public DBConnection() { + } + + public DBConnection( + String driver, + String url, + String user, + String password + ) { + this.driver = driver; + this.url = url; + this.user = user; + this.password = password; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public synchronized DataSource getDataSource() { + if (dataSource == null) { + if (log.isDebugEnabled()) { + log.debug("create new datasource:"); + log.debug(" driver: " + driver); + log.debug(" url : " + url); + log.debug(" user : " + user); + } + + try { + synchronized (DBConnection.class) { + Class.forName(driver); + } + } + catch (ClassNotFoundException cnfe) { + log.error("cannot load driver", cnfe); + return null; + } + + DriverManagerConnectionFactory dmcf = + new DriverManagerConnectionFactory(url, user, password); + + ObjectPool cp = new GenericObjectPool(); + + PoolableConnectionFactory pcf = new PoolableConnectionFactory( + dmcf, cp, null, null, false, false); + + dataSource = new PoolingDataSource(cp); + } + return dataSource; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/db/SQL.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,114 @@ +package de.intevation.artifactdatabase.db; + +import java.util.Properties; + +import java.io.IOException; +import java.io.InputStream; + +import org.apache.log4j.Logger; + +public class SQL { + + private static Logger logger = Logger.getLogger(SQL.class); + + protected Properties statements; + + public SQL() { + } + + public SQL(String driver) { + this(SQL.class, driver); + } + + public SQL(Class clazz, String driver) { + this(clazz, "/sql", driver); + } + + public SQL(Class clazz, String resourcePath, String driver) { + statements = loadStatements(clazz, resourcePath, driver); + } + + public static final String driverToProperties(String driver) { + return driver.replace('.', '-').toLowerCase() + ".properties"; + } + + /** + * Returns key/value pairs of SQL statements for the used database + * backend. + * The concrete set of SQL statements is determined by the + * used JDBC database driver which is configured in conf.xml. + * The class name of the driver is transformed by replacing + * all '.' with '_' and lower case the resulting string. + * The transformed string is used to load a properties file + * in '/sql/' which should contain the statements. + * Example:<br> + * <code>org.postgresql.Driver</code> results in loading of + * <code>/sql/org-postgresql-driver.properties</code>. + * @return The key/value pairs of SQL statements. + */ + protected Properties loadStatements( + Class clazz, + String resourcePath, + String driver + ) { + logger.debug("loadStatements"); + + Properties properties = new Properties(); + + String resDriver = driverToProperties(driver); + + InputStream in = null; + try { + String res = resourcePath + "/" + resDriver; + + in = clazz.getResourceAsStream(res); + + if (in == null) { + logger.warn("No SQL file for driver '" + driver + "' found."); + resDriver = driverToProperties(DBConnection.DEFAULT_DRIVER); + res = resourcePath + "/" + resDriver; + + in = clazz.getResourceAsStream(res); + if (in == null) { + logger.error("No SQL file for driver '" + + DBConnection.DEFAULT_DRIVER + "' found."); + } + } + else { + if (logger.isDebugEnabled()) { + logger.debug("found resource: " + res); + } + } + + if (in != null) { + properties.load(in); + } + } + catch (IOException ioe) { + logger.error(ioe); + } + + return properties; + } + + public String get(String key) { + boolean debug = logger.isDebugEnabled(); + if (debug) { + logger.debug("looking for SQL " + key); + logger.debug("statements != null: " + (statements != null)); + } + + String sql = statements.getProperty(key); + + if (sql == null) { + logger.error("cannot find SQL for key '" + key + "'"); + } + + if (debug) { + logger.debug("-> '" + sql + "'"); + } + + return sql; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/db/SQLExecutor.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,111 @@ +package de.intevation.artifactdatabase.db; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; + +public class SQLExecutor +{ + private static Logger logger = Logger.getLogger(SQLExecutor.class); + + public class Instance { + + public Connection conn; + public PreparedStatement stmnt; + public ResultSet result; + + public Instance() { + } + + public void reset() throws SQLException { + if (result != null) { + result.close(); + result = null; + } + if (stmnt != null) { + result = null; + stmnt.close(); + } + } + + public PreparedStatement prepareStatement(String query) + throws SQLException { + return stmnt = conn.prepareStatement(query); + } + + public void close() { + if (result != null) { + try { result.close(); } + catch (SQLException sqle) {} + } + if (stmnt != null) { + try { stmnt.close(); } + catch (SQLException sqle) {} + } + if (conn != null) { + try { conn.close(); } + catch (SQLException sqle) {} + } + } + + public boolean runWrite() { + DataSource dataSource = dbConnection.getDataSource(); + try { + conn = dataSource.getConnection(); + try { + conn.setAutoCommit(false); + return doIt(); + } + catch (SQLException sqle) { + conn.rollback(); + throw sqle; + } + } + catch (SQLException sqle) { + logger.error(sqle.getLocalizedMessage(), sqle); + } + finally { + close(); + } + return false; + } + + public boolean runRead() { + DataSource dataSource = dbConnection.getDataSource(); + try { + conn = dataSource.getConnection(); + return doIt(); + } + catch (SQLException sqle) { + logger.error(sqle.getLocalizedMessage(), sqle); + } + finally { + close(); + } + return false; + } + + public boolean doIt() throws SQLException { + return true; + } + } // class Instance + + protected DBConnection dbConnection; + + public SQLExecutor() { + } + + public SQLExecutor(DBConnection dbConnection) { + this.dbConnection = dbConnection; + } + + public DBConnection getDBConnection() { + return dbConnection; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/h2/CollectionAccessUpdateTrigger.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,63 @@ +package de.intevation.artifactdatabase.h2; + +import org.h2.api.Trigger; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.PreparedStatement; + +import de.intevation.artifactdatabase.DBConfig; + +import de.intevation.artifactdatabase.db.SQL; + +import org.apache.log4j.Logger; + +public class CollectionAccessUpdateTrigger +implements Trigger +{ + private static Logger logger = + Logger.getLogger(CollectionAccessUpdateTrigger.class); + + public String COLLECTIONS_TOUCH_TRIGGER_FUNCTION; + + public void init( + Connection conn, + String schemaName, + String triggerName, + String tableName, + boolean before, + int type + ) + throws SQLException { + logger.debug("CollectionAccessUpdateTrigger.init"); + setupSQL(DBConfig.getInstance().getSQL()); + } + + protected void setupSQL(SQL sql) { + COLLECTIONS_TOUCH_TRIGGER_FUNCTION = + sql.get("collections.touch.trigger.function"); + } + + public void fire( + Connection conn, + Object [] oldRow, + Object [] newRow + ) + throws SQLException { + logger.debug("CollectionAccessUpdateTrigger.fire"); + PreparedStatement stmnt = conn.prepareStatement( + COLLECTIONS_TOUCH_TRIGGER_FUNCTION); + stmnt.setObject(1, newRow[0]); + stmnt.execute(); + stmnt.close(); + } + + public void close() throws SQLException { + logger.debug("CollectionAccessUpdateTrigger.close"); + } + + public void remove() throws SQLException { + logger.debug("CollectionAccessUpdateTrigger.remove"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/package.html Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,10 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<html> +<head> +</head> +<body> +The reference implementation of an artifact database. It starts +an HTTP server and publishes the interface of the artifact database +via REST. +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ArtifactOutResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2010, 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.CallMeta; + +import org.apache.log4j.Logger; + +import org.restlet.Request; + +import org.w3c.dom.Document; + +/** + * Resource to serve the out()-outputs of artifacts. + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class ArtifactOutResource +extends BaseOutResource +{ + /** + * server URL where to find the resource. + */ + public static final String PATH = "/artifact/{uuid}/{type}"; + + private static Logger logger = Logger.getLogger(ArtifactOutResource.class); + + + /** + * Returns the identifier of the collection. + * + * @return the identifier of the collection. + */ + protected String getIdentifier() { + Request request = getRequest(); + + return (String) request.getAttributes().get("uuid"); + } + + + protected String getType() { + Request request = getRequest(); + + return (String) request.getAttributes().get("type"); + } + + + /** + * Call the ArtifactDatabase.out method. + */ + protected ArtifactDatabase.DeferredOutput doOut( + String identifier, + String type, + Document input, + ArtifactDatabase db, + CallMeta meta) + throws ArtifactDatabaseException + { + logger.debug("ArtifactOutResource.doOut"); + + return db.out(identifier, type, input, meta); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ArtifactResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Request; +import org.restlet.Response; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; + +/** + * Resource to expose the core artifact methods + * (describe, feed and advance) via REST. + * + * <ul> + * <li>describe() is modelled via GET.</li> + * <li>advance() and feed() are modelled via POST.</li> + * </ul> + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class ArtifactResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(ArtifactResource.class); + + /** + * XPath to figure out the type of action (feed, advance) via the + * incoming POST request. + */ + public static final String XPATH_ACTION = "/art:action/art:type/@name"; + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/artifact/{uuid}"; + + /** + * Error message if no action was given. + */ + public static final String NO_ACTION_MESSAGE = "no action given"; + + /** + * Error message if a unknown action was given. + */ + public static final String NO_SUCH_ACTION_MESSAGE = "no such action"; + + /** + * Error message if the requested artifact was not found in + * the artifact database. + */ + public static final String NO_ARTIFACT_FOUND = "Artifact not found"; + + /** + * Action name 'advance'. + */ + public static final String ADVANCE = "advance"; + /** + * Action name 'feed'. + */ + public static final String FEED = "feed"; + /** + * Action name 'describe'. + */ + public static final String DESCRIBE = "describe"; + + @Override + protected Representation innerGet() + throws ResourceException + { + Request request = getRequest(); + + String identifier = (String)request.getAttributes().get("uuid"); + + if (logger.isDebugEnabled()) { + logger.debug("looking for artifact id '" + identifier + "'"); + } + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.describe(identifier, null, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } + + /** + * Method to figure out which POST action (feed or advance) was + * triggered and perform this operation on the artifact specified + * by 'identifier' and found in the artifact database 'db'. + * + * @param identifier The identifier of the artifact. + * @param action The action to be performed. + * @param source The input document to further parameterize the + * operation. + * @param db The artifact database where to find the artifact. + * @return The representation produced by the performed action. + */ + protected Representation dispatch( + String identifier, + String action, + Document source, + ArtifactDatabase db + ) { + Document out = null; + + try { + if (action.equals(FEED)) { + out = db.feed(identifier, source, getCallMeta()); + } + else if (action.equals(ADVANCE)) { + out = db.advance(identifier, source, getCallMeta()); + } + else if (action.equals(DESCRIBE)) { + out = db.describe(identifier, source, getCallMeta()); + } + else { + throw new ArtifactDatabaseException(NO_SUCH_ACTION_MESSAGE); + } + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); + return new EmptyRepresentation(); + } + + return new DomRepresentation(MediaType.APPLICATION_XML, out); + } + + @Override + protected Representation innerPost(Representation requestRepr) { + + Document inputDocument = null; + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + String action = XMLUtils.xpathString( + inputDocument, + XPATH_ACTION, + ArtifactNamespaceContext.INSTANCE); + + if (action == null || action.length() == 0) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MESSAGE); + return new EmptyRepresentation(); + } + + Request request = getRequest(); + + String identifier = (String)request.getAttributes().get("uuid"); + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + return dispatch(identifier, action, inputDocument, db); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/BaseOutResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallMeta; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Request; +import org.restlet.Response; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; + + +/** + * Base Resource to serve the out()-outputs of collections and artifacts. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public abstract class BaseOutResource +extends BaseResource +{ + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(BaseOutResource.class); + + /** XPath to figure out the MIME type of the requested result.*/ + public static final String XPATH_MIME_TYPE = + "/art:action/art:out/art:mime-type/@value"; + + /** Default result MIME type: octet stream.*/ + public static final MediaType DEFAULT_MIME_TYPE = + MediaType.APPLICATION_OCTET_STREAM; + + + @Override + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + Document inputDocument = null; + + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + ArtifactDatabase db = getArtifactDatabase(); + + Request request = getRequest(); + + String identifier = getIdentifier(); + String outType = getType(); + + if (logger.isDebugEnabled()) { + logger.debug("looking for artifact id '" + identifier + "'"); + } + + String mimeTypeString = XMLUtils.xpathString( + inputDocument, + XPATH_MIME_TYPE, + ArtifactNamespaceContext.INSTANCE); + + MediaType mimeType = DEFAULT_MIME_TYPE; + + if (mimeTypeString != null && mimeTypeString.length() != 0) { + try { + mimeType = MediaType.valueOf(mimeTypeString); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage()); + } + } + + try { + return new OutRepresentation( + mimeType, + doOut(identifier, outType, inputDocument, db, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } + + /** + * Returns the identifier of the artifact or collection. + * + * @return the identifier. + */ + protected abstract String getIdentifier(); + + + /** + * Returns the concrete output type of the artifact or collection. + * + * @return the output type. + */ + protected abstract String getType(); + + /** + * This method is called to process the operation on artifacts or + * collections. + * + * @param identifier The identifier of the artifact or collection. + * @param type The output type. + * @param input The input document of the request. + * @param db The artifact database. + * @param meta The CallMeta object. + * + * @return the result of the operation. + */ + protected abstract ArtifactDatabase.DeferredOutput doOut( + String identifier, + String type, + Document input, + ArtifactDatabase db, + CallMeta meta) + throws ArtifactDatabaseException; +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/BaseResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifactdatabase.DefaultCallMeta; +import de.intevation.artifactdatabase.DefaultPreferredLocale; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.PreferredLocale; + +import java.util.List; + +import org.apache.log4j.Logger; + +import org.restlet.data.ClientInfo; +import org.restlet.data.Language; +import org.restlet.data.Preference; + +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; +import org.restlet.resource.ServerResource; + +/** + * Base class for the resources of REST interface of the artifact database. + * Primarily used to unify the logging. + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class BaseResource +extends ServerResource +{ + private static Logger logger = Logger.getLogger(BaseResource.class); + + /** + * Default constructor. + */ + public BaseResource() { + } + + /** + * Overrides the post method of ServerResource to handle some + * exceptions and to the required logging. + * The call bridges to #innerPost(Representation) which + * should be overwitten by the subclasses to do the real + * request processing. + * @param requestRepr The incoming represention of the HTTP request. + * @return The representation produced by #innerPost(Representation). + * @throws ResourceException Thrown if something went wrong during + * request processing. + */ + @Override + protected Representation post(Representation requestRepr) + throws ResourceException + { + try { + return innerPost(requestRepr); + } + catch (ResourceException re) { + throw re; + } + catch (RuntimeException re) { + logger.error(re.getLocalizedMessage(), re); + throw re; + } + } + + /** + * Trivial implementation of innerPost() which is called by + * #post(Representation) which simply calls super.post(Representation). + * This should be overwritten by subclasses which need POST support. + * @param requestRepr The incoming representation of the request. + * @return The representation produced by super.post(Representation). + * @throws ResourceException Thrown if something went wrong during + * request processing. + */ + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + return super.post(requestRepr); + } + + /** + * Wrapper around get() of the super class to handle some exceptions + * and do the corresponing logging. The call is bridged to #innerGet() + * which should be overwritten by subclasses. + * @return The representation produced by #innerGet() + * @throws ResourceException Thrown if something went wrong during + * request processing. + */ + @Override + protected Representation get() + throws ResourceException + { + try { + return innerGet(); + } + catch (ResourceException re) { + throw re; + } + catch (RuntimeException re) { + logger.error(re.getLocalizedMessage(), re); + throw re; + } + } + + /** + * Trivial implementaion of innerGet() which simply calls + * super.get() to produce some output representation. This method + * should be overwritten by subclasses which need GET support. + * @return The representation produced by super.get(). + * @throws ResourceException Thrown if something went wrong during + * request processing. + */ + protected Representation innerGet() + throws ResourceException + { + return super.get(); + } + + /** + * Returns meta information (preferred languages et. al.) + * of the current HTTP request. + * @return the meta information + */ + protected CallMeta getCallMeta() { + ClientInfo clientInfo = getClientInfo(); + + List<Preference<Language>> pl = clientInfo.getAcceptedLanguages(); + + PreferredLocale [] languages = new PreferredLocale[pl.size()]; + + int index = 0; + + for (Preference<Language> p: pl) { + String lang = p.getMetadata().getName(); + float quality = p.getQuality(); + languages[index++] = new DefaultPreferredLocale(lang, quality); + } + + return new DefaultCallMeta(languages); + } + + + /** + * Returns the artifact database stored in the context of the REST + * application. + * + * @return the artifact database. + */ + protected ArtifactDatabase getArtifactDatabase() { + return (ArtifactDatabase) getContext().getAttributes().get("database"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ByteArrayRepresentation.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,65 @@ +package de.intevation.artifactdatabase.rest; + +import org.restlet.representation.Representation; + +import java.io.Reader; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.Writer; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.ByteArrayInputStream; + +import java.nio.ByteBuffer; + +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.WritableByteChannel; + +import org.restlet.data.MediaType; + +public class ByteArrayRepresentation +extends Representation +{ + protected byte [] data; + + public ByteArrayRepresentation(MediaType mediaType, byte [] data) { + super(mediaType); + this.data = data; + } + + @Override + public long getSize() { + return data.length; + } + + @Override + public ReadableByteChannel getChannel() throws IOException { + return null; + } + + @Override + public Reader getReader() throws IOException { + return new InputStreamReader(getStream()); + } + + @Override + public InputStream getStream() throws IOException { + return new ByteArrayInputStream(data); + } + + @Override + public void write(Writer writer) throws IOException { + writer.append(ByteBuffer.wrap(data).asCharBuffer()); + } + + @Override + public void write(WritableByteChannel writableChannel) throws IOException { + writableChannel.write(ByteBuffer.wrap(data)); + } + + @Override + public void write(OutputStream outputStream) throws IOException { + outputStream.write(data); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/CollectionOutResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.CallMeta; + +import org.apache.log4j.Logger; + +import org.restlet.Request; + +import org.w3c.dom.Document; + + +/** + * Resource to serve the out()-outputs of collections. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class CollectionOutResource +extends BaseOutResource +{ + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(CollectionOutResource.class); + + /** server URL where to find the resource.*/ + public static final String PATH = "/collection/{uuid}/{type}"; + + + /** + * Returns the identifier of the collection. + * + * @return the identifier of the collection. + */ + protected String getIdentifier() { + Request request = getRequest(); + + return (String) request.getAttributes().get("uuid"); + } + + + protected String getType() { + Request request = getRequest(); + + return (String) request.getAttributes().get("type"); + } + + + /** + * Call the ArtifactDatabase.outCollection method. + */ + protected ArtifactDatabase.DeferredOutput doOut( + String identifier, + String type, + Document input, + ArtifactDatabase db, + CallMeta meta) + throws ArtifactDatabaseException + { + logger.debug("CollectionOutResource.doOut"); + + return db.outCollection(identifier, type, input, meta); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/CollectionResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.ArtifactDatabaseImpl; + +import java.io.IOException; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.Request; +import org.restlet.Response; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class CollectionResource +extends BaseResource +{ + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(CollectionResource.class); + + /** server URL where to reach the resource.*/ + public static final String PATH = "/collection/{uuid}"; + + /** + * XPath to figure out the type of action (feed, advance) via the + * incoming POST request. + */ + public static final String XPATH_ACTION = "/art:action/art:type/@name"; + + /** + * XPath to figure out the identifier of the artifact described in the + * action. + */ + public static final String XPATH_ARTIFACT = + "/art:action/art:type/art:artifact/@uuid"; + + /** Error message if no action was given.*/ + public static final String NO_ACTION_MSG = "no action given"; + + /** Error message if a unknown action was given.*/ + public static final String NO_SUCH_ACTION_MSG = "no such action"; + + /** Action name for deleting a collection.*/ + public static final String ACTION_DELETE = "delete"; + + /** Action name for describing the collection.*/ + public static final String ACTION_DESCRIBE = "describe"; + + /** Action name for retrieving the attribute of a collection.*/ + public static final String ACTION_GET_ATTRIBUTE = "getattribute"; + + /** Action name for retrieving the attributes of an artifact stored in the + * collection.*/ + public static final String ACTION_GET_ITEM_ATTRIBUTE = "getitemattribute"; + + /** Action name for setting the attribute of a collection.*/ + public static final String ACTION_SET_ATTRIBUTE = "setattribute"; + + /** Action name for setting the attribute for an artifact stored in the + * collection.*/ + public static final String ACTION_SET_ITEM_ATTRIBUTE = "setitemattribute"; + + /** Action name for adding a new artifact to the collection.*/ + public static final String ACTION_ADD_ARTIFACT = "addartifact"; + + /** Action name for removing an artifact from the collection.*/ + public static final String ACTION_REMOVE_ARTIFACT = "removeartifact"; + + /** Action name for listing the artifacts of the collection.*/ + public static final String ACTION_LIST_ARTIFACTS = "listartifacts"; + + /** Action name for setting the ttl of a collection.*/ + public static final String ACTION_SET_TTL = "settimetolive"; + + /** Action name for setting the name of a collection.*/ + public static final String ACTION_SET_NAME = "setname"; + + + /** + * Method to figure out which POST action was triggered and perform this + * operation on the collection specified by 'identifier' and found in the + * artifact database 'db'. + * + * @param identifier The identifier of the collection. + * @param action The action to be performed. + * @param source The input document to further parameterize the operation. + * @param db The artifact database where to find the collection. + * + * @return The representation produced by the performed action. + */ + protected Representation dispatch( + String identifier, + String action, + Document source, + ArtifactDatabase db + ) { + Document out = null; + + try { + CallMeta meta = getCallMeta(); + + if (action.equals(ACTION_DELETE)) { + logger.info("Delete collection '" + identifier + "'"); + out = db.deleteCollection(identifier, getCallMeta()); + } + else if (action.equals(ACTION_DESCRIBE)) { + logger.info("Describe collection '" + identifier + "'"); + + out = db.describeCollection(identifier, meta); + } + else if (action.equals(ACTION_ADD_ARTIFACT)) { + String art = getArtifactIdentifier(source); + + logger.info("Add artifact '" + art + "' to collection."); + out = db.addCollectionArtifact(identifier, art, source, meta); + } + else if (action.equals(ACTION_REMOVE_ARTIFACT)) { + String art = getArtifactIdentifier(source); + + logger.info("Remove artifact '" + art + "' from collection."); + out = db.removeCollectionArtifact(identifier, art, meta); + } + else if (action.equals(ACTION_LIST_ARTIFACTS)) { + logger.info("List artifacts of collection '" + identifier +"'"); + out = db.listCollectionArtifacts(identifier, meta); + } + else if (action.equals(ACTION_SET_ATTRIBUTE)) { + String art = getArtifactIdentifier(source); + + logger.info("Set attribute for collection '" + identifier + "'"); + + Document attr = getCollectionAttribute(source); + + out = db.setCollectionAttribute(identifier, meta, attr); + } + else if (action.equals(ACTION_SET_ITEM_ATTRIBUTE)) { + String art = getArtifactIdentifier(source); + + logger.info("Set attribute for artifact '" + art + "'"); + out = db.setCollectionItemAttribute(identifier, art, source, meta); + } + else if (action.equals(ACTION_GET_ATTRIBUTE)) { + String art = getArtifactIdentifier(source); + + logger.info("Retrieve attribute of collection '" + identifier + "'"); + out = db.getCollectionAttribute(identifier, meta); + } + else if (action.equals(ACTION_GET_ITEM_ATTRIBUTE)) { + String art = getArtifactIdentifier(source); + + logger.info("Retrieve attribute of artifact '" + art + "'"); + out = db.getCollectionItemAttribute(identifier, art, meta); + } + else if (action.equals(ACTION_SET_TTL)) { + out = db.setCollectionTTL(identifier, source, meta); + } + else if (action.equals(ACTION_SET_NAME)) { + out = db.setCollectionName(identifier, source, meta); + } + else { + throw new ArtifactDatabaseException(NO_SUCH_ACTION_MSG); + } + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); + return new EmptyRepresentation(); + } + + return new DomRepresentation(MediaType.APPLICATION_XML, out); + } + + + @Override + protected Representation innerPost(Representation requestRepr) { + Document input = null; + try { + DomRepresentation in = new DomRepresentation(requestRepr); + in.setNamespaceAware(true); + input = in.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + String action = XMLUtils.xpathString( + input, XPATH_ACTION, ArtifactNamespaceContext.INSTANCE); + + if (action == null || action.length() == 0) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MSG); + return new EmptyRepresentation(); + } + + Request request = getRequest(); + + String identifier = (String) request.getAttributes().get("uuid"); + + ArtifactDatabase db = getArtifactDatabase(); + + return dispatch(identifier, action, input, db); + } + + + /** + * Retrieves the identifier of the artifact used in the action. + * + * @param source The incoming document that describes the operation. + * + * @return the uuid of the artifact described in the document. + */ + protected String getArtifactIdentifier(Document source) { + return XMLUtils.xpathString( + source, XPATH_ARTIFACT, ArtifactNamespaceContext.INSTANCE); + } + + + /** + * Returns the attribute for a collection of the incoming request document. + * + * @param request The request document. + * + * @return the contained attribute as document. + */ + protected Document getCollectionAttribute(Document request) { + Node attr = (Node) XMLUtils.xpath( + request, + ArtifactDatabaseImpl.XPATH_COLLECTION_ATTRIBUTE, + XPathConstants.NODE, + ArtifactNamespaceContext.INSTANCE); + + Document newAttr = XMLUtils.newDocument(); + + if (attr == null) { + logger.error("Collection attribute document not found!"); + return newAttr; + } + + newAttr.appendChild(newAttr.importNode(attr, true)); + + return newAttr; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/CreateCollectionResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.resource.ResourceException; +import org.restlet.Response; +import org.restlet.Request; + +import org.w3c.dom.Document; + +/** + * Resource to create a new collections within the artifact database. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class CreateCollectionResource +extends BaseResource +{ + /** The logger used in this class.*/ + private static Logger logger = + Logger.getLogger(CreateCollectionResource.class); + + /** The URL part for this resource.*/ + public static final String PATH = "/create-collection/{ownerid}"; + + + @Override + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + Document input = null; + + try { + DomRepresentation in = new DomRepresentation(requestRepr); + in.setNamespaceAware(true); + input = in.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + ArtifactDatabase db = getArtifactDatabase(); + + Request request = getRequest(); + + String ownerId = (String) request.getAttributes().get("ownerid"); + + logger.info("Create new collection owned by: " + ownerId); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.createCollection(ownerId, input, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/CreateResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; +import de.intevation.artifacts.ArtifactNamespaceContext; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Response; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; + +/** + * Resource to create a new artifact within artifact database. + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class CreateResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(CreateResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/create"; + + /** + * XPATH to figure out the name of the factory which should be used + * to create the new artifact. + */ + public static final String XPATH_FACTORY = "/art:action/art:factory/@name"; + + /** + * Error message if no factory was given. + */ + public static final String NO_FACTORY_MESSAGE = "No factory given"; + + /** + * Error message if no artifact was created. + */ + public static final String NO_ARTIFACT_CREATED = "No artifact created"; + + @Override + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + Document inputDocument = null; + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + String factory = XMLUtils.xpathString( + inputDocument, + XPATH_FACTORY, + ArtifactNamespaceContext.INSTANCE); + + if (factory == null || factory.length() == 0) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, NO_FACTORY_MESSAGE); + return new EmptyRepresentation(); + } + + if (logger.isDebugEnabled()) { + logger.debug("Create artifact with factory '" + factory + "'"); + } + + ArtifactDatabase db = getArtifactDatabase(); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.createArtifactWithFactory(factory, + getCallMeta(), + inputDocument)); + } + catch (ArtifactDatabaseException adbe) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/CreateUserResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Response; +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + + +/** + * Resource to create a new users within the artifact database. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class CreateUserResource +extends BaseResource +{ + /** The logger used in this class.*/ + private static Logger logger = Logger.getLogger(CreateUserResource.class); + + /** The URL part for this resource.*/ + public static final String PATH = "/create-user"; + + + @Override + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + Document input = null; + + try { + DomRepresentation in = new DomRepresentation(requestRepr); + in.setNamespaceAware(true); + input = in.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + logger.debug("Create user"); + + ArtifactDatabase db = getArtifactDatabase(); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.createUser(input, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ExportResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import org.apache.log4j.Logger; + +import org.restlet.Request; +import org.restlet.Response; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +/** + * Resource to produce an external XML representation of a given + * artifact to be import by ImportResource later on. + * + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class ExportResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(ExportResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/export/{uuid}"; + + @Override + protected Representation innerGet() + throws ResourceException + { + Request request = getRequest(); + + String identifier = (String)request.getAttributes().get("uuid"); + + if (logger.isDebugEnabled()) { + logger.debug("looking for artifact id '" + identifier + "'"); + } + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.exportArtifact(identifier, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/FactoriesResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactNamespaceContext; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Resource to list the available factories. + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class FactoriesResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(FactoriesResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/factories"; + + @Override + protected Representation innerGet() + throws ResourceException + { + Document document = XMLUtils.newDocument(); + + ElementCreator ec = new ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + Element root = ec.create("result"); + document.appendChild(root); + + Element type = ec.create("type"); + ec.addAttr(type, "name", "factory-list"); + root.appendChild(type); + + Element factories = ec.create("factories"); + root.appendChild(factories); + + String [][] factoryNames = db.artifactFactoryNamesAndDescriptions(); + + for (int i = 0; i < factoryNames.length; ++i) { + String [] nd = factoryNames[i]; + Element factoryElement = ec.create("factory"); + ec.addAttr(factoryElement, "name", nd[0]); + ec.addAttr(factoryElement, "description", nd[1]); + factories.appendChild(factoryElement); + } + + document.normalizeDocument(); + + return new DomRepresentation( + MediaType.APPLICATION_XML, document); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/FindUserResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.resource.ResourceException; +import org.restlet.Response; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +/** + * A Rest resource that finds the user provided by the artifact database. + * + */ +public class FindUserResource +extends BaseResource +{ + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(FindUserResource.class); + + /** server URL where to reach the resource.*/ + public static final String PATH = "/find-user"; + + + @Override + protected Representation innerPost(Representation requestRepr) + throws ResourceException + { + Document input = null; + + try { + DomRepresentation in = new DomRepresentation(requestRepr); + in.setNamespaceAware(true); + input = in.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getLocalizedMessage(), ioe); + + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + ArtifactDatabase db = getArtifactDatabase(); + + try { + logger.info(PATH); + + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.findUser(input, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_UNPROCESSABLE_ENTITY, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/HTTPServer.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,13 @@ +package de.intevation.artifactdatabase.rest; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.ArtifactDatabase; + +public interface HTTPServer +{ + void setup(Document document); + + void startAsServer(ArtifactDatabase database); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ImportResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Request; +import org.restlet.Response; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.w3c.dom.Document; + +/** + * Resource to import an XML document containg an artifact produced by + * the ExportResource. + * + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class ImportResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(ImportResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/import"; + + @Override + protected Representation innerPost(Representation requestRepr) { + + Document inputDocument = null; + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + Request request = getRequest(); + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + try { + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.importArtifact(inputDocument, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/JettyServer.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,44 @@ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; + +import org.restlet.Component; +import org.restlet.Server; + +import org.restlet.ext.jetty.HttpServerHelper; + +import org.apache.log4j.Logger; + +public class JettyServer +extends Standalone +{ + private static Logger logger = Logger.getLogger(JettyServer.class); + + @Override + public void startAsServer(ArtifactDatabase db) { + + Component component = new Component(); + + RestApp app = new RestApp(db); + + Server server = createServer(); + + // TODO: Do more sophisticated Jetty server configuration here. + + component.getServers().add(server); + + component.getDefaultHost().attach(app); + + logServerStart(); + + HttpServerHelper serverHelper = new HttpServerHelper(server); + + try { + serverHelper.start(); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ListCollectionsResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.Request; +import org.restlet.resource.ResourceException; +import org.restlet.Response; + + +/** + * A Rest resource that lists the collections of a specific user provided by + * the artifact database. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class ListCollectionsResource +extends BaseResource +{ + /** The logger that is used in this class.*/ + private static Logger logger = + Logger.getLogger(ListCollectionsResource.class); + + /** server URL where to reach the resource.*/ + public static final String PATH = "/list-collections/{ownerid}"; + + + @Override + protected Representation innerGet() + throws ResourceException + { + Request request = getRequest(); + + String ownerId = (String) request.getAttributes().get("ownerid"); + + ArtifactDatabase db = getArtifactDatabase(); + + try { + logger.info("List collections owned by " + ownerId); + + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.listCollections(ownerId, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ListUsersResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.resource.ResourceException; +import org.restlet.Response; + +/** + * A Rest resource that lists the users provided by the artifact database. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class ListUsersResource +extends BaseResource +{ + /** The logger that is used in this class.*/ + private static Logger logger = Logger.getLogger(ListUsersResource.class); + + /** server URL where to reach the resource.*/ + public static final String PATH = "/list-users"; + + + @Override + protected Representation innerGet() + throws ResourceException + { + ArtifactDatabase db = getArtifactDatabase(); + + try { + logger.info(PATH); + + return new DomRepresentation( + MediaType.APPLICATION_XML, + db.listUsers(getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_NOT_FOUND, adbe.getMessage()); + return new EmptyRepresentation(); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/OutRepresentation.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase.DeferredOutput; + +import java.io.IOException; +import java.io.OutputStream; + +import org.restlet.data.MediaType; + +import org.restlet.representation.OutputRepresentation; + +/** + * Special representation to serve the out()-outputs + * via DeferredOutput efficently . + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class OutRepresentation +extends OutputRepresentation +{ + /** + * The deferred output fetched from ArtifactDatabase.out(). + */ + protected DeferredOutput out; + + /** + * Constructor to create representation with a given MIME type and + * a deferred output. + * @param mediaType The MIME type of this representation. + * @param out The deferred output from the ArtifactDatabase.out() call. + */ + public OutRepresentation(MediaType mediaType, DeferredOutput out) { + super(mediaType); + this.out = out; + } + + /** + * Overwrites the write(OutputStream) of OutRepresentation to serve + * the data from the deferred output. + * @param output the stream where to write the data into. + * @throws IOException Thrown if an exception occurred while writing + * the data to the output stream. + */ + public void write(OutputStream output) throws IOException { + out.write(output); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/RestApp.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; + +import java.util.concurrent.ConcurrentMap; + +import org.restlet.Application; +import org.restlet.Context; +import org.restlet.Restlet; + +import org.restlet.routing.Router; + +/** + * This is the core REST application that binds the serveral resources + * used to manage the artifact database to the HTTP server provided + * by the Restlet framework. + * + * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> + */ +public class RestApp +extends Application +{ + /** + * The central artifact database instance to work with. + */ + protected ArtifactDatabase database; + + /** + * Default constructor + */ + public RestApp() { + } + + public RestApp(Context context, ArtifactDatabase database) { + super(context); + this.database = database; + } + + /** + * Constructor to create REST appliction bound to a specific + * artifact database. + * + * @param database The artifact database to be used. + */ + public RestApp(ArtifactDatabase database) { + this.database = database; + } + + /** + * Overwrites the createRoot() method of Application to + * build the resource tree to form the exposed server URLs. + * + * @return The root of the URL tree exposed by the HTTP server. + */ + @Override + public Restlet createRoot() { + + Context context = getContext(); + + ConcurrentMap map = context.getAttributes(); + map.put("database", database); + + Router router = new Router(context); + + router.attach(ServicesResource.PATH, ServicesResource.class); + router.attach(ServiceResource.PATH, ServiceResource.class); + router.attach(FactoriesResource.PATH, FactoriesResource.class); + router.attach(CreateResource.PATH, CreateResource.class); + router.attach(ArtifactResource.PATH, ArtifactResource.class); + router.attach(ArtifactOutResource.PATH, ArtifactOutResource.class); + router.attach(ExportResource.PATH, ExportResource.class); + router.attach(ImportResource.PATH, ImportResource.class); + router.attach(CreateUserResource.PATH, CreateUserResource.class); + router.attach(ListUsersResource.PATH, ListUsersResource.class); + router.attach(UserResource.PATH, UserResource.class); + router.attach(FindUserResource.PATH, FindUserResource.class); + router.attach( + CreateCollectionResource.PATH, CreateCollectionResource.class); + router.attach( + ListCollectionsResource.PATH, ListCollectionsResource.class); + router.attach( + CollectionResource.PATH, CollectionResource.class); + router.attach( + CollectionOutResource.PATH, CollectionOutResource.class); + + return router; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ServiceResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.Request; +import org.restlet.Response; +import org.restlet.data.MediaType; +import org.restlet.data.Status; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; + +import org.w3c.dom.Document; + +import de.intevation.artifacts.Service; + +/** + * Resource to process incoming XML documents with a given service. + * + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class ServiceResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(ServiceResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/service/{service}"; + + /** + * Error message if no corresponing service is provided by + * the artifact database. + */ + public static final String NO_SUCH_ACTION_MESSAGE = "no such service"; + + @Override + protected Representation innerPost(Representation requestRepr) { + + Document inputDocument = null; + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + Request request = getRequest(); + + String service = (String)request.getAttributes().get("service"); + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + try { + return guessRepresentation( + db.process(service, inputDocument, getCallMeta())); + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); + return new EmptyRepresentation(); + } + } + + protected static Representation guessRepresentation(Service.Output output) { + + MediaType mediaType = new MediaType(output.getMIMEType()); + Object data = output.getData(); + + if (data instanceof Document) { + return new DomRepresentation(mediaType, (Document)data); + } + + if (data instanceof byte []) { + return new ByteArrayRepresentation(mediaType, (byte [])data); + } + + return new EmptyRepresentation(); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/ServicesResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactNamespaceContext; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; + +import org.restlet.ext.xml.DomRepresentation; + +import org.restlet.representation.Representation; + +import org.restlet.resource.ResourceException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +/** + * Resource to list the available service offered by the artifact database. + * + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class ServicesResource +extends BaseResource +{ + private static Logger logger = Logger.getLogger(ServicesResource.class); + + /** + * server URL where to reach the resource. + */ + public static final String PATH = "/services"; + + @Override + protected Representation innerGet() + throws ResourceException + { + Document document = XMLUtils.newDocument(); + + ElementCreator ec = new ElementCreator( + document, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + ArtifactDatabase db = (ArtifactDatabase)getContext() + .getAttributes().get("database"); + + Element root = ec.create("result"); + document.appendChild(root); + + Element type = ec.create("type"); + ec.addAttr(type, "name", "service-list"); + root.appendChild(type); + + Element factories = ec.create("services"); + root.appendChild(factories); + + String [][] factoryNames = db.serviceNamesAndDescriptions(); + + for (int i = 0; i < factoryNames.length; ++i) { + String [] nd = factoryNames[i]; + Element factoryElement = ec.create("service"); + ec.addAttr(factoryElement, "name", nd[0]); + ec.addAttr(factoryElement, "description", nd[1]); + factories.appendChild(factoryElement); + } + + document.normalizeDocument(); + + return new DomRepresentation( + MediaType.APPLICATION_XML, document); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/Standalone.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2010 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ + +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifacts.ArtifactDatabase; + +import org.apache.log4j.Logger; + +import org.restlet.Component; +import org.restlet.Server; + +import org.restlet.data.Protocol; + +import org.w3c.dom.Document; + +/** + * Starts an HTTP server bound to a RestApp. + * The server (binding interface and port) is configure via the + * global configuration. + * + * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a> + */ +public class Standalone +implements HTTPServer +{ + private static Logger logger = Logger.getLogger(Standalone.class); + + /** + * XPath to figure out the port where to listen from the + * global configuration. + */ + public static final String REST_PORT = + "/artifact-database/rest-server/port/text()"; + + /** + * XPath to figure out from global configuration + * which network interface to use to bind the HTTP server. + */ + public static final String LISTEN_INTERFACE = + "/artifact-database/rest-server/listen/text()"; + + /** + * The default port of the HTTP server: 8181 + */ + public static final int DEFAULT_PORT = 8181; + + public static final String MAX_THREADS = + "/artifact-database/rest-server/max-threads/text()"; + + public static final String MAX_THREADS_DEFAULT = + "1024"; + + protected int port; + + protected String listen; + + protected String maxThreads; + + public Standalone() { + } + + @Override + public void setup(Document document) { + String portString = XMLUtils.xpathString(document, REST_PORT, null); + + port = DEFAULT_PORT; + + if (portString != null) { + try { + port = Integer.parseInt(portString); + if (port < 0) { + throw new NumberFormatException(); + } + } + catch (NumberFormatException nfe) { + logger.error("rest port is not a positive integer value.", nfe); + return; + } + } + + listen = XMLUtils.xpathString(document, LISTEN_INTERFACE, null); + maxThreads = XMLUtils.xpathString(document, MAX_THREADS, null); + } + + protected Server createServer() { + return listen != null && listen.length() > 0 + ? new Server(Protocol.HTTP, listen, port) + : new Server(Protocol.HTTP, port); + } + + protected void logServerStart() { + logger.info("Starting " + getClass().getName() + " HTTP server on " + + (listen != null ? listen : "*") + + ":" + port); + } + + /** + * Builds a RestApp wrapped around the given artifact database, + * and bind this application to HTTP server. The HTTP server + * is configured by the global configuration. If no port is + * given by the configuration the default port is used. If + * no interface is given the HTTP server is reachable from + * all interfaces. + * @param db The artifact database to be exposed via the + * REST application. + */ + @Override + public void startAsServer(ArtifactDatabase db) { + + RestApp app = new RestApp(db); + + Component component = new Component(); + + Server server = createServer(); + + component.getServers().add(server); + + server.getContext().getParameters().add( + "maxThreads", maxThreads != null && maxThreads.length() > 0 + ? maxThreads + : MAX_THREADS_DEFAULT); + + component.getDefaultHost().attach(app); + + logServerStart(); + + try { + component.start(); + } + catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/UserResource.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.rest; + +import de.intevation.artifacts.ArtifactDatabase; +import de.intevation.artifacts.ArtifactDatabaseException; + +import de.intevation.artifacts.common.ArtifactNamespaceContext; +import de.intevation.artifacts.common.utils.XMLUtils; + +import java.io.IOException; + +import org.apache.log4j.Logger; + +import org.restlet.data.MediaType; +import org.restlet.data.Status; +import org.restlet.ext.xml.DomRepresentation; +import org.restlet.representation.EmptyRepresentation; +import org.restlet.representation.Representation; +import org.restlet.Request; +import org.restlet.Response; + +import org.w3c.dom.Document; + +/** + * A resource that handles actions to a specific user. + * + * @author <a href="mailto:ingo.weinzierl@intevation">Ingo Weinzierl</a> + */ +public class UserResource +extends BaseResource +{ + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(UserResource.class); + + /** server URL where to reach the resource. */ + public static final String PATH = "/user/{uuid}"; + + /** + * XPath to figure out the type of action (feed, advance) via the + * incoming POST request. + */ + public static final String XPATH_ACTION = "/art:action/art:type/@name"; + + /** Error message if no action was given. */ + public static final String NO_ACTION_MSG = "no action given"; + + /** Error message if a unknown action was given. */ + public static final String NO_SUCH_ACTION_MSG = "no such action"; + + /** Action name for deleting users. */ + public static final String ACTION_DELETE = "delete"; + + + @Override + protected Representation innerPost(Representation requestRepr) { + Document inputDocument = null; + + try { + DomRepresentation input = new DomRepresentation(requestRepr); + input.setNamespaceAware(true); + inputDocument = input.getDocument(); + } + catch (IOException ioe) { + logger.error(ioe.getMessage()); + Response response = getResponse(); + response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST, ioe); + return new EmptyRepresentation(); + } + + String action = XMLUtils.xpathString( + inputDocument, + XPATH_ACTION, + ArtifactNamespaceContext.INSTANCE); + + if (action == null || action.length() == 0) { + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, NO_ACTION_MSG); + return new EmptyRepresentation(); + } + + Request request = getRequest(); + + String identifier = (String)request.getAttributes().get("uuid"); + + ArtifactDatabase db = getArtifactDatabase(); + + return dispatch(identifier, action, inputDocument, db); + } + + /** + * Method to figure out which POST action (feed or advance) was + * triggered and perform this operation on the artifact specified + * by 'identifier' and found in the artifact database 'db' + * @param identifier The identifier of the artifact. + * @param action The action to be performed. + * @param source The input document to further parameterize the + * operation. + * @param db The artifact database where to find the artifact. + * @return The representation produced by the performed action. + */ + protected Representation dispatch( + String identifier, + String action, + Document source, + ArtifactDatabase db) + { + Document out = null; + + logger.info("Action: " + action + " | User: " + identifier); + + try { + if (action.equals(ACTION_DELETE)) { + out = db.deleteUser(identifier, getCallMeta()); + } + else { + throw new ArtifactDatabaseException(NO_SUCH_ACTION_MSG); + } + } + catch (ArtifactDatabaseException adbe) { + logger.warn(adbe.getLocalizedMessage(), adbe); + Response response = getResponse(); + response.setStatus( + Status.CLIENT_ERROR_BAD_REQUEST, adbe.getMessage()); + return new EmptyRepresentation(); + } + + return new DomRepresentation(MediaType.APPLICATION_XML, out); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/rest/package.html Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,6 @@ +</head> +<body> +The REST interface of the artfifact database. This package contains classes +that offer the URL resources to manage the artifacts via REST. +</body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/AbstractState.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.state; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.xpath.XPathConstants; + +import org.apache.log4j.Logger; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifacts.common.utils.Config; +import de.intevation.artifacts.common.utils.XMLUtils; + +import de.intevation.artifactdatabase.data.StateData; + + +/** + * An abstract implementation of a {@link State}. It implements some basic + * methods that return the id, description and data. The methods + * <code>describe()</code> and <code>setup()</code> depend on the concrete class + * and need to be implemented by those. + */ +public abstract class AbstractState implements State { + + /** The XPath to the ID of the state relative to the state node in the + * configuration. */ + public static final String XPATH_ID = "@id"; + + /** The XPath to the description of the state relative to the state node in + * the configuration. */ + public static final String XPATH_DESCRIPTION = "@description"; + + /** The XPath that points to the help text.*/ + public static final String XPATH_HELP_TEXT = "@helpText"; + + /** The XPath to the output nodes of the state configuration. */ + public static final String XPATH_OUTPUT_MODES = "outputmodes/outputmode"; + + /** The XPath to the list of facets relative to the output mode it belongs + * to. */ + public static final String XPATH_FACETS = "facets/facet"; + + public static final String XPATH_HELP_URL = "/artifact-database/help-url/text()"; + + public static final String HELP_URL = "${help.url}"; + + + /** The logger that is used in this class. */ + private static Logger logger = Logger.getLogger(AbstractState.class); + + + /** The ID of the state. */ + protected String id; + + /** The description of the state. */ + protected String description; + + /** The help text for this state.*/ + protected String helpText; + + /** The data provided by this state. */ + protected Map<String, StateData> data; + + /** A list of output modes which are available for this state. */ + protected List<Output> outputs; + + private static String helpUrl; + + + public AbstractState() { + outputs = new ArrayList<Output>(); + } + + public static synchronized final String getHelpUrl() { + if (helpUrl == null) { + helpUrl = Config.getStringXPath(XPATH_HELP_URL, HELP_URL); + } + return helpUrl; + } + + public static String replaceHelpUrl(String string) { + return string.replace(HELP_URL, getHelpUrl()); + } + + + /** + * The default constructor. + * + * @param id The ID of the state. + * @param description The description of the state. + */ + public AbstractState(String id, String description) { + super(); + + this.id = id; + this.description = description; + } + + + public AbstractState(String id, String description, String helpText) { + this(id, description); + this.helpText = replaceHelpUrl(helpText); + } + + + /** + * Returns the ID of the state. + * + * @return the ID of the state. + */ + public String getID() { + return id; + } + + + /** + * Set the ID of the state. + * + * @param id The ID of the state. + */ + public void setID(String id) { + this.id = id; + } + + + /** + * Returns the description of the state. + * + * @return the description of the state. + */ + public String getDescription() { + return description; + } + + + /** + * Set the description of the state. + * + * @param description The description of the state. + */ + public void setDescription(String description) { + this.description = description; + } + + + /** + * Returns the help text of this state. + * + * @return the help text. + */ + public String getHelpText() { + return helpText; + } + + + /** + * Set the help text for this state. + * + * @param helpText The help text. + */ + public void setHelpText(String helpText) { + this.helpText = replaceHelpUrl(helpText); + } + + + /** + * Returns the data of the state. + * + * @return the data of the state. + */ + public Map<String, StateData> getData() { + return data; + } + + + /** + * Returns a specific data object of the state. + * + * @param name The name of the data object. + * + * @return a data object of the state or null if no such data object exists. + */ + public StateData getData(String name) { + if (data != null) { + return data.get(name); + } + + return null; + } + + + /** + * Add new data to the state. NOTE: If there is already an object existing + * with the key <i>name</i>, this object is overwritten by the new value. + * + * @param name The name of the data object. + * @param data The data object. + */ + public void addData(String name, StateData data) { + if (this.data == null) { + this.data = new HashMap<String, StateData>(); + } + + this.data.put(name, data); + } + + + /** + * Returns the list of possible outputs of this state. The list is empty + * if no output is available for this state. + * + * @return a list of possible outputs of this state. + */ + public List<Output> getOutputs() { + return outputs; + } + + + /** + * Initialize the state based on the state node in the configuration. + * + * @param config The state configuration node. + */ + public void setup(Node config) { + logger.info("AbstractState.setup"); + + id = (String) XMLUtils.xpath(config, XPATH_ID, XPathConstants.STRING); + + description = (String) XMLUtils.xpath( + config, XPATH_DESCRIPTION, XPathConstants.STRING); + + helpText = (String) XMLUtils.xpath( + config, XPATH_HELP_TEXT, XPathConstants.STRING); + + if (helpUrl != null) { + helpUrl = replaceHelpUrl(helpUrl); + } + + setupOutputs(config); + } + + + /** + * This default implementation does nothing at all. + * + * @param orig + * @param owner + * @param context + * @param callMeta + */ + public void initialize( + Artifact orig, + Artifact owner, + Object context, + CallMeta callMeta + ) { + // do nothing. + } + + + /** + * This method tries reading the available output nodes configured in the + * state configuration and adds possible Outputs to the outputs list. + * + * @param config The state configuration node. + */ + protected void setupOutputs(Node config) { + NodeList outs = (NodeList) XMLUtils.xpath( + config, + XPATH_OUTPUT_MODES, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (outs == null || outs.getLength() == 0) { + return; + } + + int size = outs.getLength(); + + for (int i = 0; i < size; i++) { + addOutput(buildOutput(outs.item(i))); + } + } + + /** + * This methods allows subclasses to manually add outputs + * + * @param out The output to add + */ + protected void addOutput(Output out) { + outputs.add(out); + } + + /** + * A helper method that creates an Output object based on the <i>out</i> + * node. + * + * @param out The output node configuration. + * + * @return an Output object. + */ + protected Output buildOutput(Node out) { + String name = XMLUtils.xpathString( + out, "@name", ArtifactNamespaceContext.INSTANCE); + + String desc = XMLUtils.xpathString( + out, "@description", ArtifactNamespaceContext.INSTANCE); + + String mimetype = XMLUtils.xpathString( + out, "@mime-type", ArtifactNamespaceContext.INSTANCE); + + String type = XMLUtils.xpathString( + out, "@type", ArtifactNamespaceContext.INSTANCE); + + if (name == null) { + return null; + } + + NodeList facets = (NodeList) XMLUtils.xpath( + out, + XPATH_FACETS, + XPathConstants.NODESET, + ArtifactNamespaceContext.INSTANCE); + + if (facets == null || facets.getLength() == 0) { + return new DefaultOutput(name, desc, mimetype, type); + } + + int num = facets.getLength(); + + List<Facet> facetList = new ArrayList<Facet>(num); + + for (int i = 0; i < num; i++) { + Facet facet = buildFacet(facets.item(i)); + + if (facet != null) { + facetList.add(facet); + } + } + + return new DefaultOutput(name, desc, mimetype, facetList, type); + } + + + /** + * A helper method that creates a Facet object based on the <i>facet</i> + * node. + * + * @param facet The facet node. + * + * @return a Facet object or null if no valid Facet was found. + */ + protected Facet buildFacet(Node facet) { + String name = XMLUtils.xpathString( + facet, "@name", ArtifactNamespaceContext.INSTANCE); + + String desc = XMLUtils.xpathString( + facet, "@description", ArtifactNamespaceContext.INSTANCE); + + return name != null ? new DefaultFacet(name, desc) : null; + } + + + /** + * Describes the UI of the state. This method needs to be implemented by + * concrete subclasses. + * + * @param artifact A reference to the artifact this state belongs to. + * @param document Describe doucment. + * @param rootNode Parent node for all new elements. + * @param context The CallContext. + * @param uuid The uuid of an artifact. + */ + public abstract Element describe( + Artifact artifact, + Document document, + Node rootNode, + CallContext context, + String uuid + ); + + + @Override + public void endOfLife(Artifact artifact, Object context) { + // nothing to do here + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/ArtifactAndFacet.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,97 @@ +package de.intevation.artifactdatabase.state; + +import java.util.List; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.DataProvider; + + +/** + * A bundle of a "native" Facet and its Artifact. + */ +public class ArtifactAndFacet implements DataProvider { + /** The Artifact. */ + protected Artifact artifact; + + /** The (native) facet. */ + protected Facet facet; + + /** An alternative facet description that might be set from outside. */ + protected String facetDescription; + + + /** Trivial constructor. */ + public ArtifactAndFacet( + Artifact a, + Facet f + ) { + this.artifact = a; + this.facet = f; + } + + + /** Get data (to plot). */ + public Object getData(CallContext context) { + return facet.getData(artifact, context); + } + + + /** Get data (for other facet). */ + @Override + public Object provideData(Object key, Object param, CallContext context) { + return facet.provideBlackboardData(artifact, key, param, context); + } + + + /** (Maybe) Register on blackboard (depending on facet). */ + @Override + public void register(CallContext context) { + List keys = facet.getDataProviderKeys(this.artifact, context); + if (keys == null) { + return; + } + for (Object key: keys) { + context.registerDataProvider(key, this); + } + } + + + /** Access the artifact. */ + public Artifact getArtifact() { + return artifact; + } + + + /** Access the (native) facet. */ + public Facet getFacet() { + return facet; + } + + + /** Shortcut to facets name. */ + public String getFacetName() { + return facet.getName(); + } + + + /** + * Returns the description for a facet. The return value depends on the + * internal <i>facetDescription</i> instance variable. If this has been set + * by setFacetDescription, this value is returned, otherwise the return + * value of facet.getDescription(). + */ + public String getFacetDescription() { + if (facetDescription == null) { + return facet.getDescription(); + } + + return facetDescription; + } + + + public void setFacetDescription(String facetDescription) { + this.facetDescription = facetDescription; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/Attribute.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,43 @@ +package de.intevation.artifactdatabase.state; + +import java.io.Serializable; + +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Attribute extends Serializable { + + /** + * Returns the name of this Attribute. + * + * @return the name of this Attribute. + */ + String getName(); + + /** + * Returns the value of this Attribute. + * + * @return the value of this Attribute. + */ + Object getValue(); + + /** + * Sets the value of this Attribute. + * + * @param value The new value. + */ + void setValue(Object value); + + /** + * Transforms this Attribute into XML. + * + * @param parent The parent node. + * + * @return the Node that represents this Attribute. + */ + Node toXML(Node parent); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/DefaultAttribute.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,54 @@ +package de.intevation.artifactdatabase.state; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultAttribute implements Attribute { + + protected String name; + + protected Object value; + + + public DefaultAttribute(String name, Object value) { + this.name = name; + this.value = value; + } + + + @Override + public String getName() { + return name; + } + + + @Override + public Object getValue() { + return value; + } + + + @Override + public void setValue(Object value) { + this.value = value; + } + + + @Override + public Node toXML(Node parent) { + Document owner = parent.getOwnerDocument(); + Element attr = owner.createElement(getName()); + + parent.appendChild(attr); + + attr.setTextContent(String.valueOf(getValue())); + + return attr; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/DefaultFacet.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,168 @@ +package de.intevation.artifactdatabase.state; + +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.ArtifactNamespaceContext; +import de.intevation.artifacts.CallContext; + +import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; + + +/** + * The default implementation of a Facet. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultFacet implements Facet { + + /** The index of this facet. */ + protected int index; + + /** The name of this facet. */ + protected String name; + + /** The description of this facet. */ + protected String description; + + + /** Trivial, empty constructor. */ + public DefaultFacet() { + } + + + /** + * The default constructor to create new Facet objects. + * + * @param name The name of this new facet. + * @param description The description of this new facet. + */ + public DefaultFacet(String name, String description) { + this(0, name, description); + } + + + /** + * The default constructor to create new Facet objects. + * + * @param index The index of this new facet. + * @param name The name of this new facet. + * @param description The description of this new facet. + */ + public DefaultFacet(int index, String name, String description) { + this.index = index; + this.name = name; + this.description = description; + } + + + /** Get index. */ + public int getIndex() { + return index; + } + + + /** Returns the name ('type'). */ + public String getName() { + return name; + } + + + /** Returns the description (e.g. displayed in gui). */ + public String getDescription() { + return description; + } + + + /** + * @return null + */ + public Object getData(Artifact artifact, CallContext context) { + return null; + } + + + /** + * (Do not) provide data. + * Override to allow other facets to access your data. + * @return always null. + */ + public Object provideBlackboardData( + Artifact artifact, + Object key, + Object param, + CallContext context + ) { + return null; + } + + + /* + * Return list of keys (objects) for which this facet can provide data + * ("external parameterization"), for other facets, via blackboard. + * These are the keys that are independent from the current call (thus + * 'static'). + * @param artifact that this facet belongs to. + */ + public List getStaticDataProviderKeys(Artifact artifact) { + return null; + } + + /** + * Return list of keys (objects) for which this facet can provide data + * ("external parameterization"), for other facets, via blackboard. + * @param artifact that this facet belongs to. + */ + public List getDataProviderKeys(Artifact artifact, CallContext context) { + return getStaticDataProviderKeys(artifact); + } + + + /** Create a xml represantation. */ + public Node toXML(Document doc) { + ElementCreator ec = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + + Element facet = ec.create("facet"); + ec.addAttr(facet, "description", description, true); + ec.addAttr(facet, "name", name, true); + ec.addAttr(facet, "index", String.valueOf(index), true); + + return facet; + } + + + /** Create a string representation. */ + public String toString() { + return new StringBuilder("name = '") + .append(name).append("', index = ") + .append(index).append(", description = '") + .append(description).append("'") + .toString(); + } + + + /** + * Copies name, index and description of other facet. + */ + public void set(Facet other) { + index = other.getIndex(); + name = other.getName(); + description = other.getDescription(); + } + + + /** Create a deep copy of this facet. */ + public Facet deepCopy() { + DefaultFacet copy = new DefaultFacet(); + copy.set(this); + return copy; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/DefaultOutput.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,164 @@ +package de.intevation.artifactdatabase.state; + +import java.util.ArrayList; +import java.util.List; + +/** + * The default implementation of an Output. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultOutput implements Output { + + protected String name; + + protected String description; + + protected String mimeType; + + protected String type; + + protected List<Facet> facets; + + protected Settings settings; + + + /** + * The default constructor that instantiates a new DefaultOutput object. + * + * @param name The name of this output. + * @param description The description of this output. + * @param mimeType The mimetype of this output. + */ + public DefaultOutput(String name, String description, String mimeType) { + this.name = name; + this.description = description; + this.mimeType = mimeType; + this.type = ""; + this.facets = new ArrayList<Facet>(); + } + + + public DefaultOutput( + String name, + String description, + String mimeType, + String type) + { + this(name, description, mimeType); + + this.facets = new ArrayList<Facet>(); + this.type = type; + } + + + public DefaultOutput( + String name, + String description, + String mimeType, + List<Facet> facets) + { + this(name, description, mimeType); + + this.type = ""; + this.facets = facets; + } + + + /** + * This constructor builds a new Output object that contains facets as well. + * + * @param name The name of this output. + * @param description The description of this output. + * @param mimeType The mimetype of this output. + * @param facets The list of facets supported by this output. + * @param type The type of the Output e.g. chart + */ + public DefaultOutput( + String name, + String description, + String mimeType, + List<Facet> facets, + String type) + { + this(name, description, mimeType, facets); + + this.type = type; + } + + + /** + * Returns the name of this output. + * + * @return the name of this output. + */ + public String getName() { + return name; + } + + + /** + * Returns the description of this output. + * + * @return the description of this output. + */ + public String getDescription() { + return description; + } + + + /** + * Returns the mimetype of this output. + * + * @return the mimetype of this output. + */ + public String getMimeType() { + return mimeType; + } + + + public String getType() { + return type; + } + + + /** + * Returns the list of facets supported by this output. + * + * @return the list of facets supported by this output. + */ + public List<Facet> getFacets() { + return facets; + } + + + public void addFacet(Facet facet) { + if (facet != null && !facets.contains(facet)) { + facets.add(facet); + } + } + + + public void addFacets(List<Facet> facets) { + this.facets.addAll(facets); + } + + + @Override + public void setFacets(List<Facet> facets) { + this.facets = facets; + } + + + @Override + public void setSettings(Settings settings) { + this.settings = settings; + } + + + @Override + public Settings getSettings() { + return settings; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/DefaultSection.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,112 @@ +package de.intevation.artifactdatabase.state; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * Attributes keep the order in which they were inserted. + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultSection implements Section { + + protected String id; + + protected List<Section> subsections; + + /** Attribute-map. */ + protected Map<String, Attribute> attributes; + + + /** + * Creates a new DefaultSection instance. <b>Note, that the <i>id</i> is used + * as Node name of the new Element that is created in toXML().</b> + */ + public DefaultSection(String id) { + this.id = id; + // Use LinkedHashMap to keep insertion order. + this.attributes = new LinkedHashMap<String, Attribute>(); + this.subsections = new ArrayList<Section>(); + } + + + @Override + public String getId() { + return id; + } + + + @Override + public void addSubsection(Section subsection) { + if (subsection != null) { + subsections.add(subsection); + } + } + + + @Override + public int getSubsectionCount() { + return subsections.size(); + } + + + @Override + public Section getSubsection(int pos) { + if (pos >= 0 && pos < getSubsectionCount()) { + return subsections.get(pos); + } + + return null; + } + + + /** Adding attribute to end of list. */ + @Override + public void addAttribute(String key, Attribute attribute) { + if (key != null && key.length() > 0 && attribute != null) { + attributes.put(key, attribute); + } + } + + + @Override + public Attribute getAttribute(String key) { + if (key == null || key.length() == 0) { + return null; + } + + return attributes.get(key); + } + + + @Override + public Set<String> getKeys() { + return attributes.keySet(); + } + + + @Override + public void toXML(Node parent) { + Document owner = parent.getOwnerDocument(); + Element sectionEl = owner.createElement(getId()); + + parent.appendChild(sectionEl); + + for (String key: getKeys()) { + Attribute attr = getAttribute(key); + attr.toXML(sectionEl); + } + + for (int i = 0, n = getSubsectionCount(); i < n; i++) { + Section subsection = getSubsection(i); + subsection.toXML(sectionEl); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/DefaultSettings.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,62 @@ +package de.intevation.artifactdatabase.state; + +import java.util.ArrayList; +import java.util.List; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class DefaultSettings implements Settings { + + protected List<Section> sections; + + public DefaultSettings() { + sections = new ArrayList<Section>(); + } + + @Override + public void addSection(Section section) { + if (section != null) { + sections.add(section); + } + } + + @Override + public int getSectionCount() { + return sections.size(); + } + + @Override + public Section getSection(int pos) { + if (pos >= 0 && pos < getSectionCount()) { + return sections.get(pos); + } + + return null; + } + + @Override + public void removeSection(Section section) { + if (section != null) { + sections.remove(section); + } + } + + @Override + public void toXML(Node parent) { + Document owner = parent.getOwnerDocument(); + Element settings = owner.createElement("settings"); + + parent.appendChild(settings); + + for (Section section: sections) { + section.toXML(settings); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/Facet.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,90 @@ +package de.intevation.artifactdatabase.state; + +import java.util.List; + +import java.io.Serializable; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Facet extends Serializable { + + /** + * Returns the index of this facet. + * + * @return the index of this facet. + */ + int getIndex(); + + /** + * Returns the name of this facet. + * + * @return the name of this facet. + */ + String getName(); + + + /** + * Returns the description of this facet. + * + * @return the description of this facet. + */ + String getDescription(); + + + /** + * Returns the data this facet requires. + * + * @param artifact The owner artifact. + * @param context The CallContext. + * + * @return the data. + */ + Object getData(Artifact artifact, CallContext context); + + + /** + * Get keys for which this Facet can provide data (for other facets, not + * for plot). + * @param artifact Artifact that this facet belongs to. + * @return list of keys + */ + List getDataProviderKeys(Artifact artifact, CallContext context); + + + /** + * Provide data to other facet. + * + * @param art The artifact that this facet belongs to. + * @param key the key of the requested service. + * @param prm optional parameters. + * @param ctxt the callcontext. + * + * @return the data + */ + Object provideBlackboardData( + Artifact art, + Object key, + Object prm, + CallContext ctxt); + + + /** + * Write the internal representation of a facet to a node. + * + * @param doc A Document. + * + * @return the representation as Node. + */ + Node toXML(Document doc); + + Facet deepCopy(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/FacetActivity.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,111 @@ +package de.intevation.artifactdatabase.state; + +import de.intevation.artifacts.Artifact; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + + +/** + * System used in practice used by AttributeWriter in flys-artifacts to decide + * whether a facet is initially active. + * Provides a singleton Registry into which FacetActivities can be registered + * under a key (in practice the artifacts name. This Registry is queried for + * new Facets in order to find whether they are active or inactive. + */ +public interface FacetActivity +{ + /** Static 'activity' that lets all facets be active. */ + public static final FacetActivity ACTIVE = new FacetActivity() { + @Override + public Boolean isInitialActive( + Artifact artifact, + Facet facet, + String output + ) { + return Boolean.TRUE; + } + }; + + /** Static 'activity' that lets all facets be inactive. */ + public static final FacetActivity INACTIVE = new FacetActivity() { + @Override + public Boolean isInitialActive( + Artifact artifact, + Facet facet, + String output + ) { + return Boolean.FALSE; + } + }; + + Boolean isInitialActive(Artifact artifact, Facet facet, String output); + + /** Singleton registry, that maps artifact names to the activities, which + * decide whether or not a facet should be (initially) active. */ + public static final class Registry { + + /** The logger for this class. */ + private static Logger logger = Logger.getLogger(Registry.class); + + /** Singleton instance. */ + private static final Registry INSTANCE = new Registry(); + + /** Map of keys (artifact names) to the activities. */ + private Map<String, List<FacetActivity>> activities; + + /** Private singleton constructor for the Facet-Activity-Registry. */ + private Registry() { + activities = new HashMap<String, List<FacetActivity>>(); + } + + /** Access Singleton instance. */ + public static Registry getInstance() { + return INSTANCE; + } + + /** Queries whether a given facet should be active or not. */ + public synchronized boolean isInitialActive( + String key, + Artifact artifact, + Facet facet, + String output + ) { + List<FacetActivity> activityList = activities.get(key); + if (activityList == null) { + logger.debug("FacetActivity.Registry: No activity " + + "registered for " + key); + return true; + } + if (activityList.size() != 1) { + logger.warn("FacetActivity.Registry: More than one " + + "FacetActivity registered for " + key); + } + for (FacetActivity activity: activityList) { + Boolean isActive = + activity.isInitialActive(artifact, facet, output); + // Nice. Only, in practice they never return NULL. + if (isActive != null) { + return isActive; + } + } + return true; + } + + + /** Add a FacetActivity under given key (usually artifacts name). */ + public synchronized void register(String key, FacetActivity activity) { + List<FacetActivity> activityList = activities.get(key); + if (activityList == null) { + activityList = new ArrayList<FacetActivity>(3); + activities.put(key, activityList); + } + activityList.add(activity); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/Output.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,80 @@ +package de.intevation.artifactdatabase.state; + +import java.util.List; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Output { + + /** + * Retrieve the name of this output mode. + * + * @return the name of this output mode. + */ + public String getName(); + + /** + * Retrieve the description of an output. + * + * @return the description. + */ + public String getDescription(); + + /** + * Retrieve the mimetype used for the output. + * + * @return the mimetype. + */ + public String getMimeType(); + + + /** + * Returns the type of this output. + * + * @return the type. + */ + public String getType(); + + /** + * Retrieve the facets of this output. + * + * @return the facets of this output. + */ + public List<Facet> getFacets(); + + /** + * Add a new facet to this output. + * + * @param facet The new facet. + */ + public void addFacet(Facet facet); + + /** + * Add a list of facet to this output. + * + * @param facets A list of facets. + */ + public void addFacets(List<Facet> facets); + + /** + * Replaces the old list of facets with a new one. + * + * @param facets A list of new facets. + */ + public void setFacets(List<Facet> facets); + + /** + * Returns a Settings object for this Output. + */ + public Settings getSettings(); + + /** + * Sets the Settings for this Output. + * + * @param settings the Settings for this Output. + */ + public void setSettings(Settings settings); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/Section.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,76 @@ +package de.intevation.artifactdatabase.state; + +import java.io.Serializable; +import java.util.Set; + +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Section extends Serializable { + + /** + * Returns an ID for this Section. + * + * @return an ID for this Section. + */ + String getId(); + + /** + * Adds a new subsection to this Section object. + * + * @param subsection the new Section. + */ + void addSubsection(Section subsection); + + /** + * Returns the number of subsections in this Section. + * + * @return the number of subsections. + */ + int getSubsectionCount(); + + /** + * Returns a subsection at position <i>pos</i>. + * + * @param pos The position of the target subsection. + * + * @return the subsection at position <i>pos</i>. + */ + Section getSubsection(int pos); + + /** + * Adds a new Attribute to this Section. + * + * @param key The key that is used to store/retrieve the Attribute. + * @param attribute The new Attribute. + */ + void addAttribute(String key, Attribute attribute); + + /** + * Returns an Attribute for the specified <i>key</i>. + * + * @param key The key that is used to retrieve the target Attribute. + * + * @return the Attribute specified by <i>key</i>. + */ + Attribute getAttribute(String key); + + /** + * Returns all keys of all Attributes currently stored in this Section. + * + * @return all keys of all Attributes. + */ + Set<String> getKeys(); + + /** + * Transforms this Section into XML using Attribute.toXML() for each + * Attribute and Section.toXML() for each subsection stored in this Section. + * + * @param parent The parent node. + */ + void toXML(Node parent); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/Settings.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,53 @@ +package de.intevation.artifactdatabase.state; + +import java.io.Serializable; + +import org.w3c.dom.Node; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Settings extends Serializable { + + /** + * Adds a new Section to this Settings object. + * + * @param section the new Section. + */ + void addSection(Section section); + + /** + * Returns the number of Sections in this Settings object. + * + * @return the number of sections. + */ + int getSectionCount(); + + /** + * Returns the section at position <i>pos</i>. + * + * @param pos the position of the target Section. + * + * @return the Section at position <i>pos</i> or null if no Section is + * existing at <i>pos</i>. + */ + Section getSection(int pos); + + /** + * Removes a Section if it is existing in this Settings. + * + * @param section The section that should be removed. + */ + void removeSection(Section section); + + /** + * Transforms this Settings object into a XML representation. Therefore, + * each Section object's <i>toXML</i> method is called to append its XML + * representation to the final document. + * + * @param parent The parent node. + */ + void toXML(Node parent); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/State.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2011 by Intevation GmbH + * + * This program is free software under the LGPL (>=v2.1) + * Read the file LGPL.txt coming with the software for details + * or visit http://www.gnu.org/licenses/ if it does not exist. + */ +package de.intevation.artifactdatabase.state; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; +import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; + +import de.intevation.artifactdatabase.data.StateData; + + +/** + * This interface describes the basic methods a concrete state class needs to + * implement. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface State extends Serializable { + + /** + * Return the id of the state. + * + * @return the id. + */ + public String getID(); + + + /** + * Return the description of the state. + * + * @return the description of the state. + */ + public String getDescription(); + + + /** + * Returns the help text configured for the state. + * + * @return the help text configured for the state. + */ + public String getHelpText(); + + + /** + * Returns the data provided by this state. + * + * @return the data stored in this state. + */ + public Map<String, StateData> getData(); + + + /** + * Returns a single desired StateData object based on its name. + * + * @param name The name of the desired StateData object. + * + * @return the desired StateData object. + */ + public StateData getData(String name); + + + /** + * This method should be used to add a new {@link StateData} object to the + * data pool of the state. + * + * @param name The name of the data object. + * @param data The data object. + */ + public void addData(String name, StateData data); + + + /** + * Returns the list of possible outputs of this state. The list is empty + * if no output is available for this state. + * + * @return a list of possible outputs of this state. + */ + public List<Output> getOutputs(); + + + /** + * Initialize the state based on the state node in the configuration. + * + * @param config The state configuration node. + */ + public void setup(Node config); + + + /** + * Initializes the internal state of this State based on an other State. + * + * @param orig The owner Artifact or the original State. + * @param owner The owner Artifact of this State. + * @param context The context object. + * @param callMeta The CallMeta of the current call. + */ + public void initialize( + Artifact orig, + Artifact owner, + Object context, + CallMeta callMeta); + + + /** + * This method is called when an artifacts retrieves a describe request. It + * creates the user interface description of the current state. + * + * @param artifact A reference to the artifact this state belongs to. + * @param document Describe doucment. + * @param rootNode Parent node for all new elements. + * @param context The CallContext. + * @param uuid The uuid of an artifact. + */ + public Element describe( + Artifact artifact, + Document document, + Node rootNode, + CallContext context, + String uuid + ); + + + /** + * This method should be called by an Artifact that removes this State + * (current State and previous States). E.g. this might be interesting to + * remove generated files or stuff like that. + * + * @param artifact A parent Artifact. + * @param context The CallContext. + */ + public void endOfLife(Artifact artifact, Object context); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/state/StateEngine.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,172 @@ +package de.intevation.artifactdatabase.state; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import de.intevation.artifactdatabase.data.StateData; + + +/** + * The StateEngine stores all states and associated information about + * outputs and facets for each Artifact. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class StateEngine { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(StateEngine.class); + + /** + * A map that contains the states of the artifacts. The key of this map is + * the name of an artifact, its value is a list of all states the artifact + * can reach. + */ + protected Map<String, List<State>> states; + + + /** + * A map that contains all existing states. The key of this map is the ID of + * the state, its value is the state itself. + */ + protected Map<String, State> allStates; + + + /** + * The default constructor. + */ + public StateEngine() { + states = new HashMap<String, List<State>>(); + allStates = new HashMap<String, State>(); + } + + + /** + * This method adds a state into the map <i>allStates</i>. + * + * @param state The state to add. + */ + protected void addState(State state) { + allStates.put(state.getID(), state); + } + + + /** + * Returns the state based on its ID. + * + * @param stateId The ID of the desired state. + * + * @return the state. + */ + public State getState(String stateId) { + return allStates.get(stateId); + } + + + public StateData getStateData(String artifact, String dataName) { + List<State> artifactStates = getStates(artifact); + + if (artifactStates == null || artifactStates.size() == 0) { + logger.warn("No States for Artifact '" + artifact + "' existing."); + return null; + } + + for (State state: artifactStates) { + StateData sd = state.getData(dataName); + + if (sd != null) { + return sd; + } + } + + logger.warn( + "No StateData for Artifact '" + artifact + + "' with name '" + dataName + "' existing."); + + return null; + } + + + /** + * Add new states for a specific artifact. + * + * @param artifact The name of the artifact. + * @param states A list of states that the artifact can reach. + * + * @return true, if the states were added, otherwise false. + */ + public boolean addStates(String artifact, List<State> states) { + List tmp = this.states.get(artifact); + + if (tmp != null) { + logger.info( + "States for the artifact '" + artifact + "' already stored."); + + return false; + } + + // add the state to the map with all existing states + for (State s: states) { + addState(s); + } + + logger.debug("Add new states for the artifact '" + artifact + "'"); + return this.states.put(artifact, states) != null; + } + + + /** + * Returns the state list of an artifact specified by its name. + * + * @param artifact The name of the artifact (e.g. "winfo"). + * + * @return the list of states of this artifact or <i>null</i> if no states + * are existing for this <i>artifact</i>. + */ + public List<State> getStates(String artifact) { + return states.get(artifact); + } + + + /** + * Return mapping of output to facets for an artifact in its states. + */ + public Map<String, List<String>> getCompatibleFacets(List<String> aStates) { + Map<String, List<String>> compatibilityMatrix = + new HashMap<String, List<String>>(); + + // For all states that the artifact had seen, add outputs facets. + logger.debug("Searching in " + aStates); + for (String stateId: aStates) { + + State state = allStates.get(stateId); + if (state == null) { + logger.debug("No state found for id " + stateId); + continue; + } + + for (Output output: state.getOutputs()) { + List<Facet> outFacets = output.getFacets(); + logger.debug("Facets for output " + output.getName() + " :" + outFacets); + + List<String> oldFacets = compatibilityMatrix.get(output.getName()); + + if (oldFacets == null) { + oldFacets = new ArrayList<String>(); + } + + for (Facet facet: outFacets) { + oldFacets.add(facet.getName()); + } + + compatibilityMatrix.put(output.getName(), oldFacets); + } + } + return compatibilityMatrix; + } +} +// vim:set ts=4 sw=4 et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/transition/Transition.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,58 @@ +package de.intevation.artifactdatabase.transition; + +import org.w3c.dom.Node; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.State; + + +/** + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public interface Transition { + + /** + * Initializes the transition. + * + * @param config The configuration node for the transition. + */ + public void init(Node config); + + /** + * Return the ID of the start State. + */ + public String getFrom(); + + /** + * Return the ID of the target State. + */ + public String getTo(); + + /** + * Set the ID of the current State. + * + * @param from The ID of the current state. + */ + public void setFrom(String from); + + /** + * Set the ID of the target State. + * + * @param to The ID of the target state. + */ + public void setTo(String to); + + /** + * Determines if its valid to step from state <i>a</i> of an artifact + * <i>artifact</i> to state <i>b</i>. + * + * @param artifact The owner artifact of state a and b. + * @param a The current state. + * @param b The target state. + * + * @return true, if it is valid to step from a to b, otherwise false. + */ + public boolean isValid(Artifact artifact, State a, State b); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/artifact-database/src/main/java/org/dive4elements/artifactdatabase/transition/TransitionEngine.java Thu Apr 25 10:57:18 2013 +0200 @@ -0,0 +1,141 @@ +package de.intevation.artifactdatabase.transition; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import de.intevation.artifacts.Artifact; + +import de.intevation.artifactdatabase.state.State; +import de.intevation.artifactdatabase.state.StateEngine; + + +/** + * The TransitionEngine stores all transitions for each Artifact and should be + * used to determine, if an Artifact is able to advance from one to another + * state. + * + * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> + */ +public class TransitionEngine { + + /** The logger used in this class. */ + private static Logger logger = Logger.getLogger(TransitionEngine.class); + + /** + * A map that contains the transitions of the artifacts. The key is the name + * of the artifact, its value is a list of all transitions of this artifact. + */ + protected Map<String, List> transitions; + + + /** + * The default constructor. + */ + public TransitionEngine() { + transitions = new HashMap<String, List>(); + } + + + /** + * Add new transitions for a specific artifact. + * + * @param stateId the name of the Artifact. + * @param transition the list of transition of the artifact. + * + * @return true, if the transitions were added, otherwise false. + */ + public boolean addTransition(String stateId, Transition transition) { + List tmp = transitions.get(stateId); + + if (tmp == null) { + tmp = new ArrayList<Transition>(); + } + + tmp.add(transition); + + logger.debug("Add new transitions for state '" + stateId + "'"); + + return transitions.put(stateId, tmp) != null; + } + + + /** + * This method returns all existing transitions of a state. + * + * @param state The state + * + * @return the existing transition of <i>state</i>. + */ + public List<Transition> getTransitions(State state) { + return transitions.get(state.getID()); + } + + + /** + * This method returns the reachable states of <i>state</i>. + * + * @param state The current state. + * @param engine The state engine. + * + * @return a list of reachable states. + */ + public List<State> getReachableStates( + Artifact artifact, + State state, + StateEngine engine) { + List<Transition> transitions = getTransitions(state); + List<State> reachable = new ArrayList<State>(); + + if (transitions == null) { + return reachable; + } + + for (Transition t: transitions) { + State target = engine.getState(t.getTo()); + + if (t.isValid(artifact, state, target)) { + reachable.add(target); + } + } + + return reachable; + } + + + /** + * Determines if a state with a given identifier is reachable from a current + * state. + * + * @param artifact The owner artifact of state <i>state</i>. + * @param targetId The identifier of the target state. + * @param state The start state. + * @param stateEngine The StateEngine. + * + * @return true, if the target state is reachable, otherwise false. + */ + public boolean isStateReachable( + Artifact artifact, + String targetId, + State state, + StateEngine stateEngine) + { + List<State> reachable = getReachableStates(artifact, state,stateEngine); + + if (reachable == null || reachable.size() == 0) { + return false; + } + + for (State s: reachable) { + if (targetId.equals(s.getID())) { + return true; + } + } + + return false; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :