changeset 426:7536a3288fc6

dummy merge for repo head
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:15:11 +0200
parents ad74e1ba88ba (diff) 574b1781baa6 (current diff)
children 2c2981e53d4e
files
diffstat 107 files changed, 9026 insertions(+), 1076 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Mar 30 13:35:37 2011 +0000
+++ b/ChangeLog	Fri Sep 28 12:15:11 2012 +0200
@@ -1,3 +1,1644 @@
+2012-09-26	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  New method addOutput to allow subclasses to add Output object manually.
+
+2012-09-17  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.9.1
+
+2012-09-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts/pom.xml, pom.xml: source 1.5 -> 1.6
+	  (@Override annotations for interface implementations is 1.6).
+
+2012-09-07	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Fix queries for finding a user.
+
+2012-09-07  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.9
+
+2012-08-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java:
+	  Use LinkedHashMap to keep insertion order of attributes.
+
+2012-08-27	Christian Lins	<christian.lins@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/CallContext.java:
+	  Fix for NPE on empty user database.
+
+2012-08-24	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FindUserResource.java:
+	  Add REST service to be able to find a user by its account name.
+
+2012-08-24	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  Implement findUser method. The findUser method in ArtifactDatabase
+	  converts a User object into its XML representation.
+
+2012-08-24	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java,
+	  artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Implement method to find/get a user by its account name.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Also add the account information when listing users.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Add account information to createUser
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Read the account information from the database.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java:
+	  Extend user class implementations to handle account information.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/UserFactory.java,
+	  artifacts/src/main/java/de/intevation/artifacts/User.java:
+	  Extend user interfaces to handle account information.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Update sql user queries to add the account column.
+
+2012-08-23	Björn Ricks	<bjoern.ricks@intevation.de>
+
+	* artifact-database/doc/schema-pg.sql,
+	  artifact-database/doc/schema-h2.sql:
+	  Add a account column to the users table.
+
+2012-07-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java:
+	  Refactored registry to use a Chain-of-responsibility pattern. This allows
+	  de-centralized facet activity treatment like in the facet producing states.
+
+2012-07-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java:
+	  Made Registry.getInstance() access static.
+
+2012-07-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java:
+	  New. Implementors of this interface can decide if a facet should be initially
+	  active or not. Contains a registry singleton.
+
+2012-07-27  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged trunk as '2.8.1'
+
+2012-07-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java:
+	  Added concat() to join two String arrays.
+
+2012-07-17	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Doc fixes.
+
+2012-07-16  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged trunk as '2.8'
+
+2012-07-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java:
+	  Added concat() to join two String arrays.
+
+2012-07-17	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Doc fixes.
+
+2012-07-16  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged trunk as '2.8'
+
+2012-07-15	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSettings.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java,
+	  artifacts/src/main/java/de/intevation/artifacts/DataProvider.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java:
+	  Removed same package imports.
+
+2012-07-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java:
+	  Added contains(String needle, String [] haystack) method.
+
+2012-07-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/.settings/org.eclipse.jdt.core.prefs: Removed from
+	  version control (Eclipse configurations should not be in SVN!).
+
+2012-02-26	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java:
+	  Cosmetics, doc.
+
+2012-06-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/**/*.java:
+	  Removed trailing whitespace.
+
+2012-06-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Added some more debug output during the process of removing Artifacts
+	  and Collections with the DatabaseCleaner.
+
+2012-06-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Remove extra <map></map> from the XML generated by JSON lists containing only 
+	  objects.
+
+2012-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged trunk as '2.7'
+
+2012-05-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/DateUtils.java:
+	  New utility class that provides functions that helps working with dates.
+
+2012-05-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/model/KVP.java:
+	  New class to store generic keys and values.
+
+2012-05-07  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added method to create a document from string.
+
+2012-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Defined and implemented a method getHelpText().
+
+2012-05-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XSLTransformer.java:
+	  Widen parameters to store more than strings.
+
+2012-04-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java:
+	  Added toJSONString() method.
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Fixed building XML documents from JSON strings.
+	
+2012-04-25  Raimund Renkert <raimund.renkert@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java:
+	  Added method to extract zip archives to a specified directory.
+
+2012-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XSLTransformer.java:
+	  New. Added new XSLT processor similiar to old GNV one.
+
+2012-04-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java:
+	  New. JSON parser from Artefact Server NG (which relies on JSON more heavily).
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added method to convert JSON to XML. This is needed by the client which
+	  internally uses JSON to talk to the server which services only understands
+	  XML atm.
+	
+2012-04-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Modified services so that they are now able to return more than just
+	XML documents. Needed if you want to return e.g. a PNG image from a service.
+
+	* artifacts/src/main/java/de/intevation/artifacts/Service.java:
+	  A service now returns a tuple (data, MIME type).
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Adjusted calls.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java:
+	  Returns a DefaultService.Output by default.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/XMLService.java: New.
+	  Convenience sub class if DefaultService to produce XML documents. Old
+	  Services should derive from this class now to main compatibility.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java:
+	  Adjusted to handle the new Output tuples.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ByteArrayRepresentation.java:
+	  New. Wraps a byte array as a Restlet representation.
+
+2012-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  Avoid adding duplicated facets into the output in addFacet().
+
+2012-02-10	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  Pass context when asking facet for keys to write on 'blackboard'.
+
+2012-02-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java:
+	  Added a method setFacetDescription() which sets an alternative
+	  description for the facet. getFacetDescription() will now return the
+	  Facet's descritpion itself if no alternative description has been set;
+	  otherwise the alternative description.
+
+012-02-09	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java(join):
+	  Added static method to join strings with a separator.
+
+012-01-30	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java:
+	  Added Override annotations.
+
+2012-01-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java:
+	  Added a method that returns a StateData objects of a specific Artifact
+	  based on the Artifact's StateData's name.
+
+2012-01-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Bugfix: search for StateData objects by name only the State has
+	  StateData objects set.
+
+2012-01-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added methods to extract the default value for a min and max item.
+
+2012-01-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added methods to retrieve the min and max values of a data item.
+
+2012-01-16	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java:
+	  Replaced legacy java.util.Stack with java.util.Deque.
+
+2012-01-16	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java,
+	  artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java,
+	  artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Cosmetics.
+
+2012-01-11	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java:
+	  Instance vars customValues and dataProviders are now of type Map 
+	  and not HashMap.
+
+2012-01-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Added the owner Artifact as further parameter to initialize().
+
+2012-01-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java:
+	  Added new functions to copy files (copyFile()) and to copy directories
+	  (copyDirectory()).
+
+2012-01-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Added an initialize() method which might be used to initialize the State
+	  based on model Artifact.
+
+2011-12-16	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java:
+	  (getDataProvider): Never return null.
+
+2011-12-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  Added a new method setFacets(List<Facet>) to replace an existing list of
+	  Facets.
+
+2011-12-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Attribute.java:
+	  Added a setValue(Object) method and modified the signature of toXML()
+	  which now returns a Node.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultAttribute.java:
+	  New. A default implementation of Attribute.
+
+2011-12-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Settings.java:
+	  Added a removeSection(Section) method.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Section.java:
+	  Improved the interface to allow section having subsections. Therefore,
+	  addSubsection(Section), getSubsectionCount() and getSubsection(int) have
+	  been added. In addition, a getId() method has been added which is used
+	  in toXML() to create a new DOM Node. The Node name is the result of
+	  getId().
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSettings.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java:
+	  Default implementations for Settings and Section.
+
+2011-12-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Settings.java:
+	  An interface that describes a flat API for specifying settings for
+	  something. A Settings object can store one or more Section instances and
+	  defines a toXML() operation that should append a XML representation of
+	  itself to a given parent Node.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Section.java:
+	  This interface is used to describe an API for storing and retrieving
+	  Attribute objects. Just as the Settings interface, it defines a toXML()
+	  operation that should append a XML representation of itself to a given
+	  parent Node.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Attribute.java:
+	  The interface for concrete attributes in a Section instance. An Attribute
+	  is the placed on the lowest level of the Settings hierachy and should be
+	  used to save concrete key value pairs. Even the Attribute defines the
+	  toXML() operation described above.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java:
+	  Added a getSettings() and setSettings(Settings) operation.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  A DefaultOutput is now able to store a Settings instance. It implements
+	  getSettings() and setSettings(Settings) defined in the Output interface.
+
+2011-12-09	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  (getDataProviderKeys): Added parameter artifact.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java:
+	  Pass artifact to changed getDataProviderKeys of Facet.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java:
+	  (getDataProvider): Never return null, empty list instead.
+
+2011-11-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java
+	  (getFacetDescription): New, access facets description.
+
+2011-11-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added convenienve bundle of artifact and facet that implements
+	DataProvider. This will help keeping things together in the upcoming
+	inter-facet pre-rendering communication ('blackboard') phase.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java:
+	  New class, keeping together an artifact and a facet (thus, should
+	  only have a short live span). Only implementation of the new
+	  DataProvider interface.
+
+2011-11-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Extended CallContext to also act as a blackboard/service broker.
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java
+	  (getDataProvider,registerDataProvider): New functions to register/
+	  consume data providers.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java
+	  (getDataProvider,registerDataProvider): Implemented.
+
+2011-11-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added DataProvider interface that defines api for inter-facet
+	communication.
+
+	* artifacts/src/main/java/de/intevation/artifacts/DataProvider.java:
+	  New interface to prepare inter-facet pre-rendering communication
+	  phase ('blackboard').
+
+2011-11-30	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Prepare inter-facet pre-rendering communication ('blackboard') phase.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java
+	  (getDataProviderKeys,provideBlackboardData): Extended interface to
+	  allow easy integration of blackboard mechanism (inter-facet
+	  pre-rendering communication).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java
+	  (getDataProviderKeys,provideBlackboardData): Trivial implementation.
+	  The DefaultFacet will not talk to other facets during inter-facet
+	  pre-rendering ("blackboard") phase. Cosmetics, docs.
+
+2011-11-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Changed Ingo's last commit to a more conservative notation (no auto-boxing).
+	  Strangely the flys-client does not compile for me with the auto-boxed
+	  syntax with OpenJDK!
+
+2011-11-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added a method that allows to specify if the document needs to be
+	  namespace aware or not while parsing from InputStream.
+
+2011-11-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Enabled "post-describe" hooks for the Artifact's describe operation.
+
+2011-10-28	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Let StateEngine compute a compatibiliy matrix based on given State
+	IDs.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java
+	  (getCompatibleFacets): New. Compute output/facet compatibility
+	  matrix based on given states.
+
+2011-10-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties:
+	  Bugfix: added missing bracket to an sql statement.
+
+2011-10-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java(getMasterArtifact):
+	  Limit result set to one row.
+
+2011-10-19	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix accidentally corruptd key to sql statement.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Fix key.
+
+2011-10-19	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Add backend and db functionality query artifact which is belongs to
+	a collection the longest (not neccessary the longest artifact itself).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java
+	  (getCollectionsMasterArtifact): New. Access master artifact for
+	   given collection (masterartifact defined as artifact which belongs
+	   to the collection the longest).
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java
+	  (getCollectionsMasterArtifact): New (extended interface).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java
+	 (getMasterArtifact): New, get UUID of oldest artifact which belongs to
+	 collection the longest.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Added SQL query to get list of artifacts in collection sorted by the
+	  entry date.
+
+2011-10-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java(toUpperCase):
+	  Added static method to convert an array of string to their uppercase counterparts.
+	  
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	(newRemoveArtifactDocument): New, create a "remove artifact" document.
+	Cosmetics, docs.
+
+2011-10-13	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Fix SQL typo.
+
+2011-10-10	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java:
+	  Cosmetics, (shortened jdoc comments).
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Cosmetics, (shortened jdoc comments). Also, removed
+	  toString(Element), instead fixed toString(Node) - Node is
+	  superinterface of Element.
+
+2011-10-07	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fixed flys/issue255
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java(getDataSource()):
+	  Serialized the loading of database drivers.
+
+2011-09-23	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Fix debug helper toString(Node).
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  (toString(Node)): Fix issue with node from "wrong" document.
+
+2011-09-23	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Added XMLUtils functions helping to debug parts of xml documents.
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  (toString(Node), toString(Element): New functions returning
+	  xml/string representations of parts of documents.
+
+2011-09-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Removed useless CollectionCallContext creation (was never used).
+
+2011-09-19  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 1.4
+
+	* Changes: Prepared changes for the release.
+
+2011-09-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Artifacts and Collections that started a background process might add
+	  Messages to a message board now. They (currently just implemented for
+	  the Artifact) can add new messages and receive a list of messages via
+	  the CallContext objects. If an Artifact or Collection instance is
+	  removed from background, all its messages are removed as well.
+
+	* artifacts/src/main/java/de/intevation/artifacts/Message.java: New. The
+	  message interface. Currently, there is just a single getText() method
+	  defined.
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java: Got
+	  two new methods to add new messages and retrieve a list of messages to
+	  the background messages.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java:
+	  Implement the new methods to add/get messages defined in CallContext.
+	  The CollectionCallContext just defines the two methods without real
+	  implementation (stub).
+
+2011-09-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java:
+	  The CallContext got a new method isInBackground() that determines, if
+	  the Artifact or Collection (currently not implemented) has started a
+	  background thread and is locked.
+
+2011-08-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java:
+	  New. A utility class for working with Files.
+
+2011-08-25	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java
+	  (outCollection): Called context listener too early before deferred out. This
+	  led to broken DB sessions.
+
+2011-08-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Enhanced the State interface with a endOfLife() method; the default
+	  implementation in AbstractState is empty.
+
+2011-08-25	Felix Wolfsteller	<felix.wolfsteller@intevation.de>
+
+	Minor cosmetics.
+
+	* artifacts/src/main/java/de/intevation/artifacts/Artifact.java:
+	  Corrected signature in link in comment, whitespace.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java:
+	  Punctuation, whitespace in comment.
+
+2011-08-24	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Write filters to create document if they are given.
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/CreationFilter.java:
+	  New. Model for the creation filter.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Renamed an attribute in the document to create new Artifacts.
+
+2011-08-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/MapXPathVariableResolver.java:
+	  New. An XPathVariableResolver instance that stores its variables in a
+	  Map.
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added support for variables in xpath expressions. There is a new xpath()
+	  method that takes a Map<String,String> that stores the required
+	  variables.
+
+2011-08-22	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  Added a deepCopy() method to make them cloneable (Not using java.lang.Cloneable for this).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/data/StateData.java:
+	  Implement the changed interfaces.
+
+2011-08-22	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/pom.xml: Bumped PostgreSQL driver from 
+	  8.3-603.jdbc4 up to 8.4-702.jdbc4 (same as flys-backend).
+
+2011-08-10	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Extract the collection and artifact creation times when doing the initial
+	  scan, too.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Adjusted the SQL statements.
+
+2011-08-08	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties:
+	  Fixed H2 SQL to set TTL of collection.
+
+2011-08-02	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Swapped SQL parameter indices when fetching collection name.
+
+2011-08-02	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  Change the ArtifactLoadedCallback interface to take the name of the
+	  collection, too.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Fetches the collection name, too.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Adjusted SQL statements.
+
+2011-08-02	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java:
+	  Added constructor only with the identifier of the owner.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Added dummy user to fresh created collection to have it accessible in the
+	  listeners.
+	
+2011-08-02	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Generate kill events if and only if there were killed collections or artifacts.
+
+2011-08-02	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Return uuid in statements used by database cleaner, too.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java:
+	  Two new methods to reports a list of external killed collections and artifacts.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Broadcast the lists of externally killed collections and artifacts to the listeners.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Fire lists of deleted collections and artifacts to backend.
+
+2011-08-01	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Reorganized code a bit.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: Added some
+	  debug output. Handle forgotten store/create event source.
+
+2011-08-01	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Fixed NPE.
+
+2011-08-01	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Made BackListeners loadable at boot time. To be configured with XPATH
+	  '/artifact-database/backend-listeners/listener'.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Wired listeners to backend.
+
+2011-08-01	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java:
+	  Completed interface and the trival implementation.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Call the listeners for the new defined events.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java:
+	  New. Interface to listener for backend events. TODO: Implement more
+	  events.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java:
+	  New. Trivial implementation of BackendListener.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Added list of backend listeners. TODO: Add them at boot time.
+
+2011-07-31	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/pom.xml: Bumped Apache DBCP up to 1.4
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java:
+	  Use real pooling to void races. Maybe it needs more configuration options!?
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: Removed
+	  superfluous imports.
+
+2011-07-31	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Fixed broken SQL statement.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: Added
+	  debug output.
+
+2011-07-31	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	Make artifact server bootable again.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Fixed NPE.
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java: Fixed NPE.
+	  Added some debug output.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: Fixed
+	  SQL key name. That was broken for long but not recognized.
+	
+2011-07-28	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/LRUCache.java:
+	  New. Simple LRU cache based on java.util.LinkedHashMap.
+
+2011-07-28	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	Refactorized the usage of dialect independent SQL to be reusable.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQLExecutor.java:
+	  New. Generalized versions to make code reusable for datacage.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/SQL.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/SQLExecutor.java:
+	  Deleted.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DBConfig.java:
+	  New. Centralizes the SQL database configuration of the backend.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/App.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  A lot of adjustment to make the new infrastructure work. Needs heavy testing!
+
+2011-07-27	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/LifetimeListener.java:
+	  New. Interface instances of are called when system is up and is going down.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Load lifetime listeners from configuration. XPATH
+	  /artifact-database/lifetime-listeners/listeners/text()
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Call the listeners after start up and before shutdown.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/App.java:
+	  Trigger the start of the artifact database explicitly when the boot process
+	  is finished.
+
+2011-07-27	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  Added a method loadAllArtifacts() to load all artifacts which are in
+	  a collection and have an owner. The loaded artifacts are passed one by
+	  one to an instance of the interface ArtifactLoadedCallback.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Adjusted to implement the interface.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Load the artifacts from the SQL backend.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Added statements to load all the artifacts.
+
+2011-07-26	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/pom.xml: Bumped H2 version up to latest stable 1.3.158
+
+2011-07-22	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java,
+	  artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java:
+	  Moved StringUtils to common package.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Adjusted imports.
+
+	* artifacts-common/pom.xml, artifact-database/pom.xml: Moved dependency to Apache codec
+	  to other package.
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java:
+	  Added Override annotations (forgot to commit last time).
+
+2011-07-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  New CREATE documents for Artifact creation might contain db-ids now.
+
+2011-07-21	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/Service.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ServiceFactory.java:
+	  Services now take GlobalContext on setup and process.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultServiceFactory.java:
+	  Adusted and added Override annotations.
+
+2011-07-21	Sascha L. Teichmann	<teichmann@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java:
+	  Made it more compatible with mixed namespaces and mixtures of namespaces
+	  and no namespaces.
+
+	* artifacts/pom.xml: Set Java compatibility to 1.5. Why isn't this inherited
+	  from main pom.xml? The artifact-database module uses 1.5 features but does
+	  not need any extra configuration.
+
+2011-07-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/Artifact.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java:
+	  Added a parameter of type CallMeta to the signature of setup(). The
+	  CallMeta object is required for i18n initial things.
+
+	  !! NOTE: This modification breaks the current API !!
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java:
+	  Call Artifact.setup() with an instance of CallMeta.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  The createArtifact() method got a CallMeta instance which is necessary
+	  to call Artifact.setup().
+
+2011-07-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  Added a method to add a list of Facets.
+
+2011-07-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  A facet can write its internal representation to XML using a public
+	  method toXML(Document).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java:
+	  Facets are appended using its toXML() method. Concrete facets can now
+	  change its xml representation.
+
+2011-07-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/Hook.java: The Hook's
+	  execute() method is called with a Document now.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  The hooks that are called after feed() and advance() are called with the
+	  documents which are returned by those operations.
+
+2011-07-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/GlobalContext.java: New.
+	  An interface for global context objects. It defines two basic operations
+	  to put objects into the context and to retrieve objects from context.
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  Added a method that returns an instance of an Artifact with one
+	  limitation: the internal state of this artifact is not persisted to
+	  database!
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added a method that creates a document used to create new Artifacts with
+	  the UUID of another one.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactContextFactory.java:
+	  Adapted method signatures to apply the new interface for global context
+	  objects.
+
+2011-07-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/Hook.java: New. A hook
+	  can be used to execute at a specific point in time. E.g. after an
+	  Artifact was fed or after an Artifact has advanced.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Call hooks after Artifact's feed() and advance() operations.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Load hooks from configuration that match the XPath "/artifact-database/hooks/hook".
+
+2011-07-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fix for flys/issue20
+
+	* artifact-database/pom.xml: Bumped version of SLF4J up to 1.6.1
+
+2011-07-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  An Output can have a type (string) declaration now that should make it
+	  easier to distinguish different Output types.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Read the attribute "type" from OutputMode's configuration section.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java:
+	  Write the member variable "type" to an OutputMode.
+
+2011-06-28  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 1.3
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* Changes: Prepared changes for the upcoming release.
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Declared toString(Document) method static.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added functions that generate documents to set the name and ttl of a
+	  collection and to delete an existing collection.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Add the time-to-live of a collection to the document that is returned
+	  after the collection has been created.
+
+2011-06-21	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  Added toString() method to help debugging.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Put the time-to-live of a collection into the document with the list of
+	  user collections.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactCollection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java:
+	  The ArtifactCollection now knows its time-to-live. This values is
+	  retrievable via getTTL().
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactCollectionFactory.java:
+	  Creating an ArtifactCollection requires the time-to-live of it.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Create new ArtifactCollections with their time-to-live.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Adapted SQL statements that retrieve collections. The TTL of the
+	  collection is fetched from database as well.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  Facets are able to fetch their necessary data from artifact now using a
+	  getData(Artifact, CallContext) method.
+
+2011-06-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java:
+	  Write index of a facet into the document.
+
+2011-06-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java:
+	  Extends Serializable.
+
+2011-06-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java:
+	  Added an index to a facet.
+
+2011-06-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added static toString(Document) method which transform XML documents to Strings.
+	  Useful for debugging.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  New methods to set the name of a collection.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java:
+	  Dispatch the "setname" action.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Added sql statements to update the name of a collection specified by its
+	  gid.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue50 (Markierung von "Master-Artefakten" ermöglichen)
+
+	* artifact-database/doc/schema-pg.sql,
+	  artifact-database/doc/schema-h2.sql: Added a column 'creation' to the
+	  collection_items table. This column is set to the current timestamp when a
+	  new row is inserted in this table. The master artifact of a collection
+	  is the artifact which collection item is the oldest collection item in
+	  the collection.
+	  Note: there might be older artifacts in the collection than the master
+	  artifact, but there will never be an older collection item than the first
+	  item when the collection was created.
+
+	  To update the db, use the following statements:
+
+	    ALTER TABLE collection_items ADD COLUMN creation TIMESTAMP;
+	    UPDATE collection_items SET creation = CURRENT_TIMESTAMP;
+	    ALTER TABLE collection_items ALTER COLUMN creation SET NOT NULL;
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Order the list of collection items based on their creation time.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  Added a setCollectionTTL() method that might be used to update the TTL
+	  of a collection. The new value needs to be from type long. There are two
+	  exceptions for the new values:
+	    1. the new value is "INF": this lets collections live forever.
+	    2. the new value is "DEFAULT": this sets the TTL of the collection to
+	       the configured default value.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Added a method to update the TTL attribute of a collection.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java:
+	  Dispatch the "settimetolive" action.
+	  
+	* artifact-database/src/main/resources/sql/org-postgresql-driver.properties,
+	  artifact-database/src/main/resources/sql/org-h2-driver.properties: Added
+	  sql statements to update the TTL of a specific collection based on the
+	  UUID of the collection.
+
+2011-06-07	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java:
+	  Use generics more precisely
+
+2011-06-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue75
+
+	* artifact-database/doc/schema-h2.sql: Fixed SQL syntax error in trigger creation.
+
+	* README: Adjusted to use new lib versions.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  The incoming attribute document of the method to create the document to
+	  trigger the out() of a collection needs to have the root node
+	  art:attribute now.
+
+2011-05-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseOutResource.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionOutResource.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java:
+	  The 'type' part of the url is extracted and used while dispatching the
+	  call to the artifact database.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java:
+	  The deferred output got a new out() method that takes the 'type'
+	  parameter specified in the url part.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactCollection.java,
+	  artifacts/src/main/java/de/intevation/artifacts/Artifact.java:
+	  Artifacts and ArtifactCollections have two out() operations to support
+	  the output type parameter now. I did not remove the out() without the
+	  'type' parameter to keep compatible with older versions.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  The setCollectionAttribute() method will now set the incoming document
+	  as new attribute for the collection. Before, we looked for a xpath
+	  expression that contained the attribute. In the current implementation,
+	  the incoming document IS the attribute.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java:
+	  Extract the new attribute for a collection and call the
+	  setCollectionAttribute() method of ArtifactDatabase with this document
+	  instead of calling it with the incoming request document.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  New method that creates the document that is used to set an attribute of
+	  a collection.
+
+2011-04-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/JettyServer.java:
+	  Do not bind context to restlet app for security reasons.
+
+2011-04-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java:
+	  XMLUtils.xpathString() returns empty string not null.
+
+2011-04-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue65: Added Jetty HTTP server as a replacement option to foster
+	better scalability. Needs testing.
+
+	* pom.xml: Added repository for Jetty.
+
+	* artifact-database/pom.xml:
+	  Added dependencies to Jetty (Licenses: Apache 2.0 or Eclipse).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java:
+	  Pass restlet context to super constructor.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/JettyServer.java:
+	  New. Use embedded Jetty server to serve rest API.
+	  To enable it set "/artifact-database/rest-server/http-server/text()"
+	  in global config to "de.intevation.artifactdatabase.rest.JettyServer".
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java:
+	  Refactored a bit to be useful as a base class.
+	
+2011-04-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/HTTPServer.java:
+	  New. Interface to run an HTTP server. Enables the system to run on different
+	  HTTP servers.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java:
+	  Implements the new interface.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Load and setup the HTTP server configured by the XPath 
+	  "/artifact-database/rest-server/http-server/text()" in the global config file.
+	  Defaults to "de.intevation.artifactdatabase.rest.Standalone" if not given.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/App.java: 
+	  Boot with the HTTP server configured by FactoryBootstrap.
+
+2011-04-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java:
+	  With '/artifact-database/rest-server/max-threads/text()' in conf.xml
+	  you can set the number of threads used by the restlet server.
+	  Defaults to 1024 now. Another attempt/workaround to cope with flys/issue65.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Bugfix: The output name and its concrete type are both written into the
+	  document that is used to trigger the OUT operation of a collection.
+
+2011-04-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/pom.xml: Bumped restlet to version 2.0.7 in hope
+	  to mitigate flys/issue65.
+
+2011-05-13  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged as RELEASE 1.2
+
+	* Changes, NEWS: Changes and news for release 1.2
+
+2011-05-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  The method that is used to create the request document for querying
+	  charts will now take a document which might contain parameters to adjust
+	  chart settings (e.g. chart height/width).
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/transition/Transition.java:
+	  Modified the isValid() method. The transition needs to know about the
+	  current artifact where its data is stored, the current state and maybe
+	  the target state to determine if it is allowed to step from the current
+	  state to a target state!
+	  NOTE: This breaks the current API!
+
+artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  States have now a method getData(String) to retrieve just a single StateData object.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added a function that creates the document that is used to set the
+	  attribute of a CollectionItem (setCollectionItemAttribute() operation).
+
+2011-04-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Bugfix: Do not create empty XML documents and save those into database -
+	  exceptions while serialization/deserialization have been the result of
+	  this.
+
+2011-04-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  New methods for querying and updating attributes of collections.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java:
+	  Enabled the request dispatcher to handle requests specific to collection
+	  attributes and collection item attributes.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  New statements for querying and inserting attributes of collections.
+
+2011-04-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Changed the names of the method names that set and retrieve the
+	  attributes of collection items (e.g. getCollectionAttribute() ->
+	  getCollectionItemAttribute()).
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java:
+	  Modified method calls based on the changes above.
+
+2011-04-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  Added a method to add further facets.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Bugfix: removed method CallContext specific method calls.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java:
+	  Enhanced the bootstrap - the CallContext.Listener is initialized (if
+	  configured).
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java:
+	  Added a setup() method as in the other factories.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>	
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Added instance variable to hold a call context listener.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>	
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Call the postCall() methods of all CallContextes. Removed superfluous context
+	  paramter to AbstractCallContext constructors.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java:
+	  call ArtifactDatabaseImpl.initCallContext() in constructor and 
+	  ArtifactDatabaseImpl.closeCallContext() in postCall().
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java:
+	  adjusted to guarantee that super.postCall() is called.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>	
+
+	* artifacts/src/main/java/de/intevation/artifacts/CallContext.java:
+	  Added a listener to be called if a call context was created and closed.
+	
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Added a reference to an artifact to the parameterlist of describe().
+	  This is needed to retrieve other necessary information of an artifact.
+
+2011-04-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Internal granularity should be milliseconds and not microseconds.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties:
+	  Forget last commit.
+
+2011-04-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fix for flys/issue9
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Removed code that checks for outdated artifact in user land.
+	   
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Do the check in the database and take into account if artifact
+	  is in a collection. XXX: Maybe this needs some speedup!
+
+2011-04-13	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fix for flys/issue8 (part 3)
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  'NOT IN (NULL)' does not work. Used 'NOT IN (-666)' instead.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added a method that returns a document that is used to trigger the
+	  artifact's DESCRIBE operation.
+
+2011-04-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java:
+	  Added a method that creates documents used as input document for the
+	  Collection's out() operation.
+
+2011-04-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Fix for flys/issue8 (part 2)
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  PostgreSQL does not like clauses like 'NOT IN ()' so write 
+	  'NOT IN (NULL)' in these cases.
+
+2011-04-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Possible fix for flys/issue8
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java:
+	  (repeat) The term was repeated one time too often leading to invalid
+	  SQL statements in database cleaner. I really should start writing 
+	  unit tests.
+
+2011-04-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultCollectionItem.java:
+	  Bugfix: Items are compressed in the Backend while adding an Artifact to
+	  a Collection. So this item needs to be decompressed when it is fetched
+	  from Backend again. This is done now!
+
+2011-04-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  The XML documents stored aside users, collections and collection items
+	  are now compressed/decompressed transparently, to reduce i/o costs
+	  as its already done with artifacts.
+	  
+	  !!! This breaks database content of release FLYS 2.2 but this is okay,
+	  !!! because 2.2 is not productive..
+
+2011-04-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java:
+	  Added a boolean flag to XML byte serialisation to compress/decompress, too.
+	  Defaults to false to keep compatibilty.
+
+2011-04-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/**/*.java: Removed trailing whitespace.
+
+2011-03-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Removes outdated collections, too.
+
+	* src/main/resources/sql/org-h2-driver.properties,
+	  src/main/resources/sql/org-postgresql-driver.properties:
+	  Added SQL statements to figure out outdated collections.
+
+2011-03-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java:
+	  Added methods to repeat chars or strings.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  Filtering out locked artifact ids is now done on database level.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties,
+	  artifact-database/src/main/resources/sql/org-postgresql-driver.properties:
+	  Added $LOCKED_ID$ template for the prepared statements to figure
+	  out the locked ids.
+
+2011-03-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java:
+	  The cleaner now fetches the explicit set of locked artifact ids from
+	  the artifact database. This is needed for the next step todo: Prevent
+	  locked artifact from beeing considered as outdated on database level.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/Id.java:
+	  Removed. Not needed any more.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Now provides a copy of the locked artifact ids.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/App.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java:
+	  Adjusted code to follow the new id locking semantics.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java:
+	  Removed needless import.
+
+2011-03-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* artifact-database/doc/schema-h2.sql, artifact-database/doc/schema-pg.sql:
+	  Added a trigger on artifacts. If an artifact is changed the last_access
+	  timestamp of the collections in which the artifact is in are updated, too.
+	  Needs testing!
+
+	  - In PostgreSQL it is done by a trigger written in plpgsql. So don't
+	    forget to add the language to the database!
+
+	  - In H2 it is done by a trigger written in Java, because H2 does not
+	    offer a script level trigger support.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java:
+	  The H2 trigger.
+
+	* artifact-database/src/main/resources/sql/org-h2-driver.properties:
+	  Added the statement which is executed if the trigger fires.
+
+2011-03-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java:
+	  Added an implementation of DeferredOutput for ArtifactCollections and
+	  implemented the out() operation of an ArtifactCollection.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java:
+	  The output nodes written to DESCRIBE document have facet nodes now.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java:
+	  New. A facet and its default implementation. A facet represents one
+	  piece of an concrete output. E.g. an output can be a chart, a facet can
+	  be a single curve in this chart.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java,
+	  artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java:
+	  Added methods to retrieve and set facets.
+
+	* artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java:
+	  Added code to parse facets in the inner of an output node of the
+	  transition configuration. The xpath to a facet relative to a state
+	  should look like this: state/outputmodes/outputmode/facets/facet.
+
 2011-03-30  Ingo Weinzierl <ingo@intevation.de>
 
 	Tagged as RELEASE 1.1
--- a/Changes	Wed Mar 30 13:35:37 2011 +0000
+++ b/Changes	Fri Sep 28 12:15:11 2012 +0200
@@ -1,3 +1,159 @@
+2011-09-19     RELEASE 1.4
+
+    !! Release 1.4 is no longer API compatible with old versions of this module !!
+
+    NEW:
+
+        * Modified the signature of Artifact.setup() -> Artifact.setup(CallMeta).
+          The CallMeta is required for I18N support while setting up an
+          Artifact. !! THIS MODIFICATION BREAKS THE CURRENT API !!
+
+        * Modified the signature of Service.setup() -> Service.setup(GlobalContext).
+
+        * Introduced a LifetimeListener interface: applications can register
+          instances of this interface to execute tasks after the system goes and
+          and before the system goes down.
+
+        * Introduced a BackendListener interface: applications can register
+          instances of this interface to execute tasks after an event has been
+          fired. See the JavaDoc of BackendListener for detailed descriptions of
+          the events.
+
+        * Introduced "Hooks": Hooks can be used to execute tasks at a specific
+          point in time. In our cases, Hooks can be executed before/after feed()
+          and advance() operations.
+
+        * Introduced a GlobalContext interface for the global context object:
+          this interface describes two method to put new objects into this
+          context and a method to retrieve objects from this context.
+
+        * Introduced a CreationFilter: this filter might be used to create
+          Artifacts with restricted Outputs/Facets.
+
+        * Introduced a Message interface: Messages might be used by background
+          threads to provide information about the process (e.g. status reports).
+
+        * Improved ArtifactDatabaseImpl to support background messages.
+
+        * Improved the CallContext interface: added a method isInBackground()
+          that determines if the current Artifact has started a background
+          thread which is still processing.
+
+        * Improved the CallContext interface: added methods to add/retrieve
+          background messages.
+
+        * Improved the State interface: added a endOfLife() method that should
+          be called by owner Artifacts.
+
+        * Improved the interface of Facets: added a deepCopy() method to be able
+          to clone Facets.
+
+        * Improved the interface of Facets: added a toXML() method.
+
+        * Improved the ArtifactDatabase: added a method loadAllArtifacts(ArtifactLoadedCallback)
+          to load all Artifacts of an ArtifactCollection.
+
+        * Improved XMLUtils: added support for variables in XPath expression.
+
+        * Added a FileTools helper class that implements some convinience
+          functions to work with files.
+
+        * Some little improvements in ClientProtocolUtils.
+
+        * Bumped SLF4J up to 2.0.7.
+
+        * Bumped H2 up to 1.3.158.
+
+        * Bumped Apache DBCP up to 1.4.
+
+        * Bumped PostgreSQL driver up to 8.4-702.jdbc4.
+
+
+    FIXED:
+
+        * flys/issue20 (Versions-Clash bei slf4j verhindert Start des Artefakt-Servers.)
+
+
+
+2011-06-27     RELEASE 1.3
+
+    NEW:
+
+        * Bumped Restlet to version 2.0.7.
+
+        * Use a Jetty server by default to handle HTTP requests.
+
+        * Introduced a config option that allows to switch the HTTP server
+          manually.
+
+        * Introduced a config option that allows to limit the max number of
+          threads used by the Restlet server. Defaults to 1024.
+
+        * New REST interface to set the time-to-live of a Collection. This
+          operation takes a java native long value or one of the strings "INF" -
+          which means a Collection lives forever - or "DEFAULT" which means to
+          reset the time-to-live to the default value configured in the artifact
+          server.
+
+        * New REST interface to set the name of a Collection.
+
+        * Improved the ClientProtocolUtils to create documents for the operation
+          to set the Collection's time-to-live.
+
+        * The time-to-live of Collections is accessible in Collection objects.
+
+        * Introduced a "creation" timestamp for CollectionItems.
+
+        * Facets got a new property "index".
+
+
+    FIXED:
+
+        * A a new out() method is called that takes the "type" parameter specified in
+          the REST url to specify the concrete output type. This parameter has
+          not been used until now.
+
+        * flys/issue75 Fixed SQL syntax error in trigger creation.
+
+
+
+2011-05-13     RELEASE 1.2
+
+    NEW:
+
+        * Introduced "facets" as a new concept to select only parts/concrete
+          types of an Artifact's output. E.g. a computation might return data
+          that is used to draw two curves into a chart. With a "facet", just a
+          single curve might be selected to be drawn into the chart.
+
+        * New REST interfaces to set/get attributes of an ArtifactCollection.
+
+        * Artifacts that live in ArtifactCollections don't die (because of their
+          last_access time). A database trigger is used to update their
+          last_access time if their owner ArtifactCollection is updated.
+
+        * DatabaseCleaner removes outdated ArtifactCollections.
+
+        * XML documents stored aside users, collections and collection items are
+          compressed to reduce i/o costs.
+
+        * Introduced a listener mechanism that is called if a context is
+          created/closed. This listener might be implemented in concrete
+          artifact packages.
+
+        * Improvements in the ClientProtocolUtils: new functions to create
+          special xml documents
+
+
+    FIXED:
+
+        * flys/issue8 (Cleanup des DatabaseCleaner schlägt fehl)
+
+        * flys/issue9 (Fehler beim Laden von Artefakten - NO SUCH ARTIFACT obwohl Artefakt in DB vorhanden)
+
+
+2011-03-30     RELEASE 1.1
+
 2010-04-28     RELEASE 1.0
 
     New:
--- a/NEWS	Wed Mar 30 13:35:37 2011 +0000
+++ b/NEWS	Fri Sep 28 12:15:11 2012 +0200
@@ -1,3 +1,21 @@
+2011-05-13     RELEASE 1.2
+
+        New:
+
+        * Introduced a new concept called "facets" to subselect parts/concrete
+          types of an Artifact's output.
+
+        * New REST interfaces to set/get attributes of an ArtifactCollection.
+
+        * Improved the DatabaseCleaner to cleanup Artifacts, ArtifactCollections
+          and CollectionItems properly.
+
+        * Reduce I/O costs while database transactions by compressing XML
+          documents.
+
+
+2011-03-30     RELEASE 1.1
+
 2010-04-28     RELEASE 1.0
 
         New:
--- a/README	Wed Mar 30 13:35:37 2011 +0000
+++ b/README	Fri Sep 28 12:15:11 2012 +0200
@@ -1,9 +1,13 @@
 
 Create a new H2 database for usage in the artifact database:
 
-$ java -cp `find ~/.m2/ -name h2\*.jar` org.h2.tools.RunScript \
-    -user USER \
-    -password PASSWORD \
-    -url jdbc:h2:artifact-database/doc/example-conf/artifacts.db \
-    -script artifact-database/doc/schema-h2.sql
+$ mkdir testdb
 
+$ for i in h2-1.3.152 artifact-database-1.0-SNAPSHOT artifacts-common-1.0-SNAPSHOT log4j-1.2.14 ; do
+>    export CLASSPATH=$CLASSPATH:`find ~/.m2/ -name $i.jar`
+> done
+
+$ java org.h2.tools.RunScript \
+>    -url jdbc:h2:`readlink -f testdb`/artifacts.db \
+>    -script artifact-database/doc/schema-h2.sql
+
--- a/artifact-database/.settings/org.eclipse.jdt.core.prefs	Wed Mar 30 13:35:37 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#Tue Sep 08 09:33:09 CEST 2009
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.source=1.5
-org.eclipse.jdt.core.compiler.compliance=1.5
--- a/artifact-database/doc/schema-h2.sql	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/doc/schema-h2.sql	Fri Sep 28 12:15:11 2012 +0200
@@ -23,7 +23,8 @@
 CREATE TABLE users (
     id   INT PRIMARY KEY NOT NULL,
     gid  UUID            NOT NULL UNIQUE,
-    name VARCHAR(256)    NOT NULL UNIQUE,
+    name VARCHAR(256)    NOT NULL,
+    account VARCHAR(256) NOT NULL UNIQUE,
     role BINARY
 );
 
@@ -47,7 +48,12 @@
     collection_id INT             NOT NULL REFERENCES collections(id),
     artifact_id   INT             NOT NULL REFERENCES artifacts(id),
     attribute     BINARY,
+    creation      TIMESTAMP       NOT NULL,
     UNIQUE (collection_id, artifact_id)
 );
 
+CREATE TRIGGER collections_access_update_trigger AFTER UPDATE
+    ON artifacts FOR EACH ROW 
+    CALL "de.intevation.artifactdatabase.h2.CollectionAccessUpdateTrigger";
+
 COMMIT;
--- a/artifact-database/doc/schema-pg.sql	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/doc/schema-pg.sql	Fri Sep 28 12:15:11 2012 +0200
@@ -23,7 +23,8 @@
 CREATE TABLE users (
     id   int PRIMARY KEY NOT NULL,
     gid  uuid            NOT NULL UNIQUE,
-    name VARCHAR(256)    NOT NULL UNIQUE,
+    name VARCHAR(256)    NOT NULL,
+    account VARCHAR(256) NOT NULL UNIQUE,
     role bytea
 );
 
@@ -47,7 +48,27 @@
     collection_id int             NOT NULL REFERENCES collections(id),
     artifact_id   int             NOT NULL REFERENCES artifacts(id),
     attribute     bytea,
+    creation      timestamp       NOT NULL,
     UNIQUE (collection_id, artifact_id)
 );
 
+CREATE FUNCTION collections_access_update() RETURNS trigger AS
+$$
+BEGIN
+    UPDATE collections SET last_access = current_timestamp 
+    WHERE id IN 
+        (SELECT c.id FROM collections c 
+         INNER JOIN collection_items ci ON c.id = ci.collection_id  
+         INNER JOIN artifacts a         ON a.id = ci.artifact_id 
+         WHERE a.id = NEW.id);
+    RETURN NEW;
+END;
+$$
+LANGUAGE 'plpgsql';
+
+
+CREATE TRIGGER collections_access_update_trigger AFTER UPDATE
+    ON artifacts FOR EACH ROW 
+    EXECUTE PROCEDURE collections_access_update();
+
 COMMIT;
--- a/artifact-database/pom.xml	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/pom.xml	Fri Sep 28 12:15:11 2012 +0200
@@ -57,32 +57,32 @@
     <dependency>
       <groupId>org.restlet.jse</groupId>
       <artifactId>org.restlet</artifactId>
-      <version>2.0.4</version>
+      <version>2.0.7</version>
     </dependency>
     <dependency>
       <groupId>org.restlet.jse</groupId>
       <artifactId>org.restlet.ext.xml</artifactId>
-      <version>2.0.4</version>
+      <version>2.0.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.restlet.jse</groupId>
+      <artifactId>org.restlet.ext.jetty</artifactId>
+      <version>2.0.7</version>
     </dependency>
     <dependency>
       <groupId>com.h2database</groupId>
       <artifactId>h2</artifactId>
-      <version>1.3.152</version>
+      <version>1.3.158</version>
     </dependency>
     <dependency>
       <groupId>postgresql</groupId>
       <artifactId>postgresql</artifactId>
-      <version>8.3-603.jdbc4</version>
-    </dependency>
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>1.4</version>
+      <version>8.4-702.jdbc4</version>
     </dependency>
     <dependency>
       <groupId>commons-dbcp</groupId>
       <artifactId>commons-dbcp</artifactId>
-      <version>1.2.2</version>
+      <version>1.4</version>
     </dependency>
     <dependency>
       <groupId>log4j</groupId>
@@ -92,12 +92,17 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>jul-to-slf4j</artifactId>
-      <version>1.5.11</version>
+      <version>1.6.1</version>
     </dependency>
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
-      <version>1.5.11</version>
+      <version>1.6.1</version>
+    </dependency>
+    <dependency>
+        <groupId>org.mortbay.jetty</groupId>
+        <artifactId>jetty</artifactId>
+        <version>6.1.26</version>
     </dependency>
   </dependencies>
 </project>
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -7,11 +7,17 @@
  */
 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;
 
 
 /**
@@ -22,6 +28,8 @@
  */
 public abstract class AbstractCallContext implements CallContext {
 
+    Logger logger = Logger.getLogger(AbstractCallContext.class);
+
     /**
      * The ArtifactDatabase instance.
      */
@@ -38,37 +46,39 @@
     protected CallMeta callMeta;
 
     /**
-     * The global context.
+     * Map to act like a clipboard when nesting calls like a proxy artifact.
      */
-    protected Object context;
+    protected Map customValues;
 
     /**
      * Map to act like a clipboard when nesting calls like a proxy artifact.
      */
-    protected HashMap customValues;
+    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.
-     * @param context The global context.
      */
     public AbstractCallContext(
         ArtifactDatabaseImpl artifactDatabase,
         int                  action,
-        CallMeta             callMeta,
-        Object               context)
-    {
+        CallMeta             callMeta
+    ) {
         this.database = artifactDatabase;
         this.action   = action;
         this.callMeta = callMeta;
-        this.context  = context;
+
+        database.initCallContext(this);
     }
 
 
-    public abstract void postCall();
+    public void postCall() {
+        database.closeCallContext(this);
+    }
 
     public abstract void afterCall(int action);
 
@@ -78,7 +88,7 @@
 
 
     public Object globalContext() {
-        return context;
+        return database.context;
     }
 
 
@@ -104,5 +114,41 @@
         }
         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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java	Fri Sep 28 12:15:11 2012 +0200
@@ -10,7 +10,7 @@
 
 import de.intevation.artifacts.common.utils.Config;
 
-import de.intevation.artifactdatabase.rest.Standalone;
+import de.intevation.artifactdatabase.rest.HTTPServer;
 
 import java.io.File;
 
@@ -70,17 +70,21 @@
             bootstrap, backend);
 
         DatabaseCleaner cleaner = new DatabaseCleaner(
-            bootstrap.getContext(), backend);
+            bootstrap.getContext(), backend, backend.getConfig());
+
+        HTTPServer httpServer = bootstrap.getHTTPServer();
 
         bootstrap = null;
 
         backend.setCleaner(cleaner);
 
-        cleaner.setFilter(db);
+        cleaner.setLockedIdsProvider(db);
 
         cleaner.start();
 
-        Standalone.startAsServer(db);
+        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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -7,9 +7,12 @@
  */
 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;
 
@@ -49,10 +52,9 @@
         ArtifactDatabaseImpl artifactDatabase,
         int                  action,
         CallMeta             callMeta,
-        Object               context,
         PersistentArtifact   artifact)
     {
-        super(artifactDatabase, action, callMeta, context);
+        super(artifactDatabase, action, callMeta);
 
         this.artifact = artifact;
     }
@@ -74,6 +76,22 @@
     }
 
 
+    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();
     }
@@ -84,23 +102,28 @@
      * the return of the concrete artifact call.
      */
     public void postCall() {
-        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);
+        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();
         }
     }
 }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java	Fri Sep 28 12:15:11 2012 +0200
@@ -9,6 +9,7 @@
 package de.intevation.artifactdatabase;
 
 import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.StringUtils;
 
 import de.intevation.artifactdatabase.Backend.PersistentArtifact;
 
@@ -23,6 +24,9 @@
 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;
@@ -34,12 +38,14 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 
-import java.util.ArrayList;
 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;
 
@@ -60,11 +66,17 @@
  * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
  */
 public class ArtifactDatabaseImpl
-implements   ArtifactDatabase, Id.Filter, Backend.FactoryLookup
+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";
@@ -185,6 +197,14 @@
     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";
@@ -193,6 +213,10 @@
     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
 
     /**
@@ -221,6 +245,12 @@
     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
@@ -234,6 +264,10 @@
          */
         protected PersistentArtifact artifact;
         /**
+         * The output type.
+         */
+        protected String type;
+        /**
          * The input document for the artifact's out() call.
          */
         protected Document           format;
@@ -258,10 +292,12 @@
          */
         public DeferredOutputImpl(
             PersistentArtifact artifact,
+            String             type,
             Document           format,
             CallMeta           callMeta
         ) {
             this.artifact = artifact;
+            this.type     = type;
             this.format   = format;
             this.callMeta = callMeta;
         }
@@ -272,11 +308,10 @@
                 ArtifactDatabaseImpl.this,
                 CallContext.TOUCH,
                 callMeta,
-                context,
                 artifact);
 
             try {
-                artifact.getArtifact().out(format, output, cc);
+                artifact.getArtifact().out(type, format, output, cc);
             }
             finally {
                 cc.postCall();
@@ -284,6 +319,74 @@
         }
     } // 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() }.
@@ -321,7 +424,7 @@
     /**
      * Reference of the global context of the artifact runtime system.
      */
-    protected Object      context;
+    protected GlobalContext context;
 
     /**
      * The signing secret to be used for ex-/importing artifacts.
@@ -333,7 +436,33 @@
      * This artifacts should not be removed from the database by the
      * database cleaner.
      */
-    protected HashSet     backgroundIds;
+    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.
@@ -360,17 +489,48 @@
      */
     public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) {
 
-        backgroundIds = new HashSet();
+        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 = bootstrap.getContext();
+        context.put(GLOBAL_CONTEXT_KEY, this);
+
         exportSecret = bootstrap.getExportSecret();
 
-        wireWithBackend(backend);
+        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;
     }
 
     /**
@@ -407,6 +567,36 @@
         }
     }
 
+    /**
+     * 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.
@@ -453,10 +643,12 @@
      * via the serializers of this artifact factories.
      * @param backend The backend to be wired with this artifact database.
      */
-    public void wireWithBackend(Backend backend) {
+    public void wireWithBackend(Backend backend, FactoryBootstrap bootstrap) {
+        logger.debug("wireWithBackend");
         if (backend != null) {
             this.backend = backend;
             backend.setFactoryLookup(this);
+            setupBackendListeners(bootstrap);
         }
     }
 
@@ -482,6 +674,7 @@
                 logger.warn("operation not allowed in fromBackground");
         }
         removeIdFromBackground(artifact.getId());
+        removeBackgroundMessages(artifact.getArtifact().identifier());
     }
 
     /**
@@ -492,7 +685,22 @@
      */
     protected void removeIdFromBackground(int id) {
         synchronized (backgroundIds) {
-            backgroundIds.remove(Integer.valueOf(id));
+            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);
         }
     }
 
@@ -509,19 +717,50 @@
         }
     }
 
-    public List filterIds(List ids) {
-        int N = ids.size();
-        ArrayList out = new ArrayList(N);
+    /**
+     * 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) {
-            for (int i = 0; i < N; ++i) {
-                Id id = (Id)ids.get(i);
-                // only delete artifact if its not in background.
-                if (!backgroundIds.contains(Integer.valueOf(id.getId()))) {
-                    out.add(id);
-                }
-            }
+            return new HashSet<Integer>(backgroundIds);
         }
-        return out;
+    }
+
+    /**
+     * 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() {
@@ -551,6 +790,8 @@
     )
     throws ArtifactDatabaseException
     {
+        logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory "
+             + factoryName);
         ArtifactFactory factory = getArtifactFactory(factoryName);
 
         if (factory == null) {
@@ -560,6 +801,7 @@
         Artifact artifact = factory.createArtifact(
             backend.newIdentifier(),
             context,
+            callMeta,
             data);
 
         if (artifact == null) {
@@ -583,7 +825,6 @@
             ArtifactDatabaseImpl.this,
             CallContext.NOTHING,
             callMeta,
-            context,
             persistentArtifact);
 
         try {
@@ -594,6 +835,20 @@
         }
     }
 
+
+    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,
@@ -612,11 +867,19 @@
             ArtifactDatabaseImpl.this,
             CallContext.TOUCH,
             callMeta,
-            context,
             artifact);
 
         try {
-            return artifact.getArtifact().describe(data, cc);
+            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();
@@ -641,11 +904,19 @@
             ArtifactDatabaseImpl.this,
             CallContext.STORE,
             callMeta,
-            context,
             artifact);
 
         try {
-            return artifact.getArtifact().advance(target, cc);
+            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();
@@ -666,11 +937,19 @@
             ArtifactDatabaseImpl.this,
             CallContext.STORE,
             callMeta,
-            context,
             artifact);
 
         try {
-            return artifact.getArtifact().feed(data, cc);
+            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();
@@ -680,6 +959,16 @@
     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
@@ -691,7 +980,7 @@
             throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT);
         }
 
-        return new DeferredOutputImpl(artifact, format, callMeta);
+        return new DeferredOutputImpl(artifact, type, format, callMeta);
     }
 
     public Document exportArtifact(String artifact, CallMeta callMeta)
@@ -871,7 +1160,6 @@
             ArtifactDatabaseImpl.this,
             CallContext.NOTHING,
             callMeta,
-            context,
             persistentArtifact);
 
         try {
@@ -886,7 +1174,7 @@
         return serviceNamesAndDescription;
     }
 
-    public Document process(
+    public Service.Output process(
         String   serviceName,
         Document input,
         CallMeta callMeta
@@ -904,6 +1192,7 @@
 
     // User API
 
+    /** Returns user(s) elements. */
     public Document listUsers(CallMeta callMeta)
         throws ArtifactDatabaseException
     {
@@ -929,20 +1218,74 @@
         Element root = ec.create("users");
         result.appendChild(root);
 
-        for (User user: users) {
-            Element ue = ec.create("user");
+        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));
             }
+        }
 
-            root.appendChild(ue);
-
-        }
+        result.appendChild(ue);
 
         return result;
     }
@@ -964,6 +1307,14 @@
             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,
@@ -980,7 +1331,7 @@
         User newUser = null;
 
         try {
-            newUser = backend.createUser(name, role, userFactory, context);
+            newUser = backend.createUser(name, account, role, userFactory, context);
         }
         catch (Exception e) {
             logger.error(e.getMessage(), e);
@@ -1033,6 +1384,64 @@
 
     // 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
     {
@@ -1073,6 +1482,7 @@
             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
@@ -1102,7 +1512,7 @@
 
         logger.debug("Create new collection with name: " + name);
 
-        Document attr = XMLUtils.newDocument();
+        Document attr = null;
 
         Node attrNode = (Node) XMLUtils.xpath(
             data,
@@ -1111,6 +1521,7 @@
             ArtifactNamespaceContext.INSTANCE);
 
         if (attrNode != null) {
+            attr = XMLUtils.newDocument();
             attr.appendChild(attr.importNode(attrNode, true));
         }
 
@@ -1133,6 +1544,7 @@
 
         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);
 
@@ -1184,31 +1596,71 @@
             throw new ArtifactDatabaseException(NO_SUCH_COLLECTION);
         }
 
-        CallContext cc = new CollectionCallContext(
+        CollectionCallContext cc = new CollectionCallContext(
             ArtifactDatabaseImpl.this,
             CallContext.NOTHING,
             callMeta,
-            context,
             c);
 
-        return c.describe(cc);
+        try {
+            return c.describe(cc);
+        }
+        finally {
+            cc.postCall();
+        }
     }
 
-    public Document getCollectionAttribute(String collectionId, String artifactId,
+
+    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.getCollectionAttribute(collectionId, artifactId);
+        return backend.getCollectionItemAttribute(collectionId, artifactId);
     }
 
-    public Document setCollectionAttribute(String collectionId, String 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 = XMLUtils.newDocument();
+        Document attribute = null;
 
         Node attr = (Node) XMLUtils.xpath(
             source,
@@ -1217,6 +1669,7 @@
             ArtifactNamespaceContext.INSTANCE);
 
         if (attr != null) {
+            attribute = XMLUtils.newDocument();
             attribute.appendChild(attribute.importNode(attr, true));
         }
 
@@ -1230,7 +1683,7 @@
         Element root = ec.create("result");
         result.appendChild(root);
 
-        boolean success = backend.setCollectionAttribute(
+        boolean success = backend.setCollectionItemAttribute(
             collectionId, artifactId, attribute);
 
         root.setTextContent(success ? OPERATION_SUCCESSFUL: OPERATION_FAILURE);
@@ -1352,10 +1805,172 @@
         return result;
     }
 
-    public DeferredOutput outCollection(String collectionId,
-        Document format, CallMeta callMeta)
-        throws ArtifactDatabaseException{
-        throw new ArtifactDatabaseException("Not implemented, yet!");
+    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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java	Fri Sep 28 12:15:11 2012 +0200
@@ -10,20 +10,30 @@
 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;
 
@@ -43,157 +53,96 @@
     /**
      * The SQL statement to create new artifact id inside the database.
      */
-    public static final String SQL_NEXT_ID =
-        SQL.get("artifacts.id.nextval");
+    public String SQL_NEXT_ID;
 
     /**
      * The SQL statement to insert an artifact into the database.
      */
-    public static final String SQL_INSERT =
-        SQL.get("artifacts.insert");
+    public String SQL_INSERT;
 
     /**
      * The SQL statement to update some columns of an existing
      * artifact in the database.
      */
-    public static final String SQL_UPDATE =
-        SQL.get("artifacts.update");
+    public String SQL_UPDATE;
 
     /**
      * The SQL statement to touch the access time of an
      * artifact inside the database.
      */
-    public static final String SQL_TOUCH =
-        SQL.get("artifacts.touch");
+    public String SQL_TOUCH;
 
     /**
      * The SQL statement to load an artifact by a given
      * identifier from the database.
      */
-    public static final String SQL_LOAD_BY_GID =
-        SQL.get("artifacts.select.gid");
+    public String SQL_LOAD_BY_GID;
 
     /**
      * The SQL statement to get the database id of an artifact
      * identified by the identifier.
      */
-    public static final String SQL_GET_ID =
-        SQL.get("artifacts.get.id");
+    public String SQL_GET_ID;
 
     /**
      * The SQL statement to replace the content of an
      * existing artifact inside the database.
      */
-    public static final String SQL_REPLACE =
-        SQL.get("artifacts.replace");
+    public String SQL_REPLACE;
 
     // USER SQL
 
-    public static final String SQL_USERS_NEXT_ID =
-        SQL.get("users.id.nextval");
-
-    public static final String SQL_USERS_INSERT =
-        SQL.get("users.insert");
-
-    public static final String SQL_USERS_SELECT_ID_BY_GID =
-        SQL.get("users.select.id.by.gid");
-
-    public static final String SQL_USERS_SELECT_GID =
-        SQL.get("users.select.gid");
-
-    public static final String SQL_USERS_DELETE_ID =
-        SQL.get("users.delete.id");
-
-    public static final String SQL_USERS_DELETE_COLLECTIONS =
-        SQL.get("users.delete.collections");
-
-    public static final String SQL_USERS_SELECT_ALL =
-        SQL.get("users.select.all");
-
-    public static final String SQL_USERS_COLLECTIONS =
-        SQL.get("users.collections");
-
-    public static final String SQL_USERS_COLLECTION_IDS =
-        SQL.get("users.collection.ids");
-
-    public static final String SQL_USERS_DELETE_ALL_COLLECTIONS =
-        SQL.get("users.delete.all.collections");
-
-    public static final String SQL_ARTIFACTS_IN_ONLY_COLLECTION_ONLY =
-        SQL.get("artifacts.in.one.collection.only");
-
-    public static final String SQL_OUTDATE_ARTIFACTS_COLLECTION =
-        SQL.get("outdate.artifacts.collection");
-
-    public static final String SQL_OUTDATE_ARTIFACTS_USER =
-        SQL.get("outdate.artifacts.user");
-
-    public static final String SQL_DELETE_USER_COLLECTION_ITEMS =
-        SQL.get("delete.user.collection.items");
-
-    public static final String SQL_COLLECTIONS_NEXT_ID =
-        SQL.get("collections.id.nextval");
-
-    public static final String SQL_COLLECTIONS_INSERT =
-        SQL.get("collections.insert");
-
-    public static final String SQL_COLLECTIONS_SELECT_USER =
-        SQL.get("collections.select.user");
-
-    public static final String SQL_COLLECTIONS_SELECT_ALL =
-        SQL.get("collections.select.all");
-
-    public static final String SQL_COLLECTIONS_SELECT_GID =
-        SQL.get("collections.select.by.gid");
-
-    public static final String SQL_COLLECTIONS_CREATION_TIME =
-        SQL.get("collection.creation.time");
-
-    public static final String SQL_COLLECTIONS_ID_BY_GID =
-        SQL.get("collections.id.by.gid");
-
-    public static final String SQL_DELETE_COLLECTION_ITEMS =
-        SQL.get("delete.collection.items");
-
-    public static final String SQL_DELETE_COLLECTION =
-        SQL.get("delete.collection");
-
-    public static final String SQL_COLLECTION_CHECK_ARTIFACT =
-        SQL.get("collection.check.artifact");
-
-    public static final String SQL_COLLECTION_ITEMS_ID_NEXTVAL =
-        SQL.get("collection.items.id.nextval");
-
-    public static final String SQL_COLLECTION_ITEMS_INSERT =
-        SQL.get("collection.items.insert");
-
-    public static final String SQL_COLLECTION_ITEM_GET_ATTRIBUTE =
-        SQL.get("collection.item.get.attribute");
-
-    public static final String SQL_COLLECTION_ITEM_SET_ATTRIBUTE =
-        SQL.get("collection.item.set.attribute");
-
-    public static final String SQL_COLLECTIONS_TOUCH_BY_GID =
-        SQL.get("collections.touch.by.gid");
-
-    public static final String SQL_COLLECTION_ITEM_ID_CID_AID =
-        SQL.get("collection.item.id.cid.aid");
-
-    public static final String SQL_COLLECTION_ITEM_OUTDATE_ARTIFACT =
-        SQL.get("collection.item.outdate.artifact");
-
-    public static final String SQL_COLLECTION_ITEM_DELETE =
-        SQL.get("collection.item.delete");
-
-    public static final String SQL_COLLECTIONS_TOUCH_BY_ID =
-        SQL.get("collections.touch.by.id");
-
-    public static final String SQL_COLLECTION_ITEMS_LIST_GID =
-        SQL.get("collection.items.list.gid");
+    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
@@ -234,8 +183,8 @@
      * back into the database.
      */
     public final class PersistentArtifact
-    extends            Id
     {
+        private int                id;
         private Artifact           artifact;
         private ArtifactSerializer serializer;
         private Long               ttl;
@@ -254,12 +203,16 @@
             Long               ttl,
             int                id
         ) {
-            super(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.
@@ -310,6 +263,14 @@
      * Default constructor
      */
     public Backend() {
+        listeners = new CopyOnWriteArrayList<BackendListener>();
+    }
+
+    public Backend(DBConfig config) {
+        this();
+        this.config = config;
+        sqlExecutor = new SQLExecutor(config.getDBConnection());
+        setupSQL(config.getSQL());
     }
 
     /**
@@ -317,10 +278,14 @@
      * @param cleaner The clean which periodically removes outdated
      * artifacts from the database.
      */
-    public Backend(DatabaseCleaner cleaner) {
+    public Backend(DBConfig config, DatabaseCleaner cleaner) {
+        this(config);
         this.cleaner = cleaner;
     }
 
+    public DBConfig getConfig() {
+        return config;
+    }
 
     /**
      * Returns the singleton of this Backend.
@@ -329,12 +294,81 @@
      */
     public static synchronized Backend getInstance() {
         if (instance == null) {
-            instance = new Backend();
+            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.
@@ -480,9 +514,14 @@
             return null;
         }
 
+        if (factoryLookup == null) {
+            logger.error("factory lookup == null");
+            return false;
+        }
+
         final Object [] loaded = new Object[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_LOAD_BY_GID);
                 stmnt.setString(1, identifer);
@@ -494,24 +533,10 @@
                 }
 
                 int  id   = result.getInt(1);
-                long ttlX = result.getLong(3);
-
-                Long ttl = result.wasNull() ? null : Long.valueOf(ttlX);
+                long ttlX = result.getLong(2);
+                Long ttl  = result.wasNull() ? null : ttlX;
 
-                if (ttl != null) { // real time to life
-                    long last_access = result.getTimestamp(2).getTime();
-                    if (last_access + ttlX < System.currentTimeMillis()) {
-                        artifactOutdated(id);
-                        return false;
-                    }
-                }
-
-                String factoryName = result.getString(4);
-
-                if (factoryLookup == null) {
-                    logger.error("factory lookup == null");
-                    return false;
-                }
+                String factoryName = result.getString(3);
 
                 ArtifactFactory factory = factoryLookup
                     .getArtifactFactory(factoryName);
@@ -521,7 +546,7 @@
                     return false;
                 }
 
-                byte [] bytes = result.getBytes(5);
+                byte [] bytes = result.getBytes(4);
 
                 loaded[0] = loader.load(factory, ttl, bytes, id);
                 return true;
@@ -584,9 +609,10 @@
             throw new RuntimeException("No valid UUID");
         }
 
-        final int [] id = new int[1];
+        final int     [] id     = new int[1];
+        final boolean [] stored = new boolean[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
 
                 prepareStatement(SQL_GET_ID);
@@ -599,7 +625,7 @@
 
                 reset();
 
-                if (ID != null) { // already in database
+                if (stored[0] = ID != null) { // already in database
                     prepareStatement(SQL_REPLACE);
 
                     if (ttl == null) {
@@ -655,6 +681,13 @@
             throw new RuntimeException("failed insert artifact into database");
         }
 
+        if (stored[0]) {
+            fireStoredArtifact(artifact);
+        }
+        else {
+            fireCreatedArtifact(artifact);
+        }
+
         return id[0];
     }
 
@@ -672,7 +705,7 @@
     ) {
         final int [] id = new int[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_NEXT_ID);
                 result = stmnt.executeQuery();
@@ -714,16 +747,24 @@
             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) {
-        new SQLExecutor() {
+        sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_TOUCH);
                 stmnt.setInt(1, artifact.getId());
@@ -740,7 +781,7 @@
      * artifact.
      */
     public void store(final PersistentArtifact artifact) {
-        new SQLExecutor() {
+        boolean success = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_UPDATE);
                 stmnt.setInt(2, artifact.getId());
@@ -755,17 +796,31 @@
                 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      name,
+        final String      account,
         final Document    role,
         final UserFactory factory,
         final Object      context
     ) {
         final User [] user = new User[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        final byte [] roleData = XMLUtils.toByteArray(role, true);
+
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
 
                 prepareStatement(SQL_USERS_NEXT_ID);
@@ -786,28 +841,38 @@
                 stmnt.setInt(1, id);
                 stmnt.setString(2, identifier);
                 stmnt.setString(3, name);
-
-                byte [] roleData = role == null
-                    ? null
-                    : XMLUtils.toByteArray(role);
+                stmnt.setString(4, account);
 
                 if (roleData == null) {
-                    stmnt.setNull(4, Types.BIGINT);
+                    stmnt.setNull(5, Types.BIGINT);
                 }
                 else {
-                    stmnt.setBytes(4, roleData);
+                    stmnt.setBytes(5, roleData);
                 }
 
                 stmnt.execute();
                 conn.commit();
 
                 user[0] = factory.createUser(
-                    identifier, name, role, context);
+                    identifier, name, account, role, context);
                 return true;
             }
         };
 
-        return exec.runWrite() ? user[0] : null;
+        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) {
@@ -816,7 +881,7 @@
             return false;
         }
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_USERS_SELECT_ID_BY_GID);
 
@@ -845,7 +910,7 @@
                 prepareStatement(SQL_DELETE_USER_COLLECTION_ITEMS);
                 stmnt.setInt(1, id);
                 stmnt.execute();
-                
+
                 reset();
 
                 // delete the collections of the user
@@ -867,11 +932,23 @@
             }
         };
 
-        return exec.runWrite();
+        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 String      identifier,
         final UserFactory factory,
         final Object      context
     ) {
@@ -882,7 +959,7 @@
 
         final User [] user = new User[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_USERS_SELECT_GID);
                 stmnt.setString(1, identifier);
@@ -892,12 +969,50 @@
                 }
                 // omit id
                 String  name     = result.getString(2);
-                byte [] roleData = result.getBytes(3);
+                String account   = result.getString(3);
+                byte [] roleData = result.getBytes(4);
 
-                Document role = XMLUtils.fromByteArray(roleData);
+                Document role = XMLUtils.fromByteArray(roleData, true);
 
                 user[0] = factory.createUser(
-                    identifier, name, role, context);
+                    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("Tying 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 = XMLUtils.fromByteArray(roleData, true);
+
+                user[0] = factory.createUser(
+                    identifier, name, account, role, context);
                 return true;
             }
         };
@@ -906,12 +1021,12 @@
     }
 
     public User [] getUsers(
-        final UserFactory factory, 
+        final UserFactory factory,
         final Object      context
     ) {
         final ArrayList<User> users = new ArrayList<User>();
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_USERS_SELECT_ALL);
                 result = stmnt.executeQuery();
@@ -920,11 +1035,12 @@
                     // omit id
                     String  identifier = result.getString(2);
                     String  name       = result.getString(3);
-                    byte [] roleData   = result.getBytes(4);
+                    String  account    = result.getString(4);
+                    byte [] roleData   = result.getBytes(5);
 
-                    Document role = XMLUtils.fromByteArray(roleData);
+                    Document role = XMLUtils.fromByteArray(roleData, true);
                     User user = factory.createUser(
-                        identifier, name, role, context);
+                        identifier, name, account, role, context);
                     users.add(user);
                 }
                 return true;
@@ -937,7 +1053,7 @@
     }
 
     public ArtifactCollection createCollection(
-        final String                    ownerIdentifier, 
+        final String                    ownerIdentifier,
         final String                    name,
         final ArtifactCollectionFactory factory,
         final Document                  attribute,
@@ -953,10 +1069,11 @@
             return null;
         }
 
-        final ArtifactCollection [] collection =
-            new ArtifactCollection[1];
+        final ArtifactCollection [] collection = new ArtifactCollection[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        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);
@@ -1000,8 +1117,6 @@
                     stmnt.setLong(5, ttl);
                 }
 
-                byte [] data = XMLUtils.toByteArray(attribute);
-
                 if (data == null) {
                     stmnt.setNull(6, Types.BINARY);
                 }
@@ -1031,13 +1146,30 @@
                 }
 
                 collection[0] = factory.createCollection(
-                    identifier, name, creationTime, attribute, context);
+                    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;
             }
         };
 
-        return exec.runWrite() ? collection[0]: null;
+        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(
@@ -1053,7 +1185,7 @@
 
         final ArtifactCollection[] ac = new ArtifactCollection[1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
 
                 prepareStatement(SQL_COLLECTIONS_SELECT_GID);
@@ -1065,20 +1197,22 @@
                     return false;
                 }
 
-                String collectionName       = result.getString(2);
-                String ownerId              = result.getString(3);
-                Date   creationTime         =
+                String collectionName = result.getString(2);
+                String ownerId        = result.getString(3);
+                Date   creationTime   =
                     new Date(result.getTimestamp(4).getTime());
-                Date   lastAccess           =
+                Date   lastAccess     =
                     new Date(result.getTimestamp(5).getTime());
-                Document attr               =
-                    XMLUtils.fromByteArray(result.getBytes(6));
+                Document attr         =
+                    XMLUtils.fromByteArray(result.getBytes(6), true);
+                long ttl              = result.getLong(7);
 
                 ArtifactCollection collection =
                     collectionFactory.createCollection(
                         collectionId,
                         collectionName,
                         creationTime,
+                        ttl,
                         attr,
                         context);
 
@@ -1103,7 +1237,7 @@
         final UserFactory               userFactory,
         final Object                    context
     ) {
-        if (ownerIdentifier != null 
+        if (ownerIdentifier != null
         && !isValidIdentifier(ownerIdentifier)) {
             logger.debug("Invalid owner id: '" + ownerIdentifier + "'");
             return null;
@@ -1112,7 +1246,7 @@
         final ArrayList<ArtifactCollection> collections =
             new ArrayList<ArtifactCollection>();
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
 
             public boolean doIt() throws SQLException {
 
@@ -1126,24 +1260,35 @@
 
                 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) {
-                        collection.setUser(new LazyBackendUser(
-                            userIdentifier, userFactory, Backend.this, context));
+                        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);
@@ -1158,12 +1303,41 @@
     }
 
 
+    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 exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 // fetch collection id
                 prepareStatement(SQL_COLLECTIONS_ID_BY_GID);
@@ -1177,6 +1351,8 @@
                 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);
@@ -1197,10 +1373,101 @@
                 return true;
             }
         };
-        return exec.runWrite();
+        boolean success = exec.runWrite();
+
+        if (success) {
+            fireDeletedCollection(collectionId);
+        }
+
+        return success;
     }
 
-    public Document getCollectionAttribute(
+    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
     ) {
@@ -1213,9 +1480,9 @@
             return null;
         }
 
-        final Document [] document = new Document[1];
+        final byte [][] data = new byte[1][1];
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_COLLECTION_ITEM_GET_ATTRIBUTE);
                 stmnt.setString(1, collectionId);
@@ -1225,16 +1492,18 @@
                     logger.debug("No such collection item");
                     return false;
                 }
-                document[0] = XMLUtils.fromByteArray(result.getBytes(1));
+                data[0] = result.getBytes(1);
                 return true;
             }
         };
 
-        return exec.runRead() ? document[0] : null;
+        return exec.runRead()
+            ? XMLUtils.fromByteArray(data[0], true)
+            : null;
     }
 
-    public boolean setCollectionAttribute(
-        final String   collectionId, 
+    public boolean setCollectionItemAttribute(
+        final String   collectionId,
         final String   artifactId,
         Document       attribute
     ) {
@@ -1247,9 +1516,9 @@
             return false;
         }
 
-        final byte [] data = XMLUtils.toByteArray(attribute);
+        final byte [] data = XMLUtils.toByteArray(attribute, true);
 
-        return new SQLExecutor() {
+        boolean success = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
 
                 // set the column in collection items
@@ -1274,6 +1543,24 @@
                 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(
@@ -1291,7 +1578,9 @@
             return false;
         }
 
-        SQLExecutor exec = new SQLExecutor() {
+        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);
@@ -1341,8 +1630,6 @@
                 stmnt.setInt(2, cid);
                 stmnt.setInt(3, aid);
 
-                byte [] data = XMLUtils.toByteArray(attribute);
-
                 if (data == null) {
                     stmnt.setNull(4, Types.BINARY);
                 }
@@ -1355,7 +1642,23 @@
                 return true;
             }
         };
-        return exec.runWrite();
+        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(
@@ -1366,7 +1669,8 @@
             logger.debug("Invalid collection id: '" + collectionId + "'");
             return false;
         }
-        return new SQLExecutor() {
+
+        boolean success = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
 
                 // fetch id, collection id and artitfact id
@@ -1406,6 +1710,22 @@
                 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(
@@ -1419,7 +1739,7 @@
         final ArrayList<CollectionItem> collectionItems =
             new ArrayList<CollectionItem>();
 
-        SQLExecutor exec = new SQLExecutor() {
+        SQLExecutor.Instance exec = sqlExecutor.new Instance() {
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_COLLECTION_ITEMS_LIST_GID);
                 stmnt.setString(1, collectionId);
@@ -1439,5 +1759,150 @@
                 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/de/intevation/artifactdatabase/BackendListener.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -7,10 +7,13 @@
  */
 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;
 
 
 /**
@@ -33,10 +36,9 @@
         ArtifactDatabaseImpl artifactDatabase,
         int                  action,
         CallMeta             callMeta,
-        Object               context,
         ArtifactCollection   collection)
     {
-        super(artifactDatabase, action, callMeta, context);
+        super(artifactDatabase, action, callMeta);
 
         this.collection = collection;
     }
@@ -52,14 +54,26 @@
     }
 
 
+    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;
     }
-
-
-    public void postCall() {
-        log.debug("CollectionCallContext.postCall - NOT IMPLEMENTED");
-    }
 }
 // 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/de/intevation/artifactdatabase/DBConfig.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java	Wed Mar 30 13:35:37 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +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 java.io.File;
-
-import java.sql.SQLException;
-
-import javax.sql.DataSource;
-
-import org.apache.commons.dbcp.BasicDataSource;
-
-import org.apache.log4j.Logger;
-
-/**
- * This class encapsulate the creation and pooling of database connections used
- * by the artifact database. The credential to open the database connections
- * are taken from the global configuratiion.
- * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
- */
-public class DBConnection
-{
-    private static Logger logger = Logger.getLogger(DBConnection.class);
-
-    /**
-     * 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()";
-
-    /**
-     * The default database driver: H2
-     */
-    public static final String DEFAULT_DRIVER =
-        "org.h2.Driver";
-
-    /**
-     * The default database name: artifacts.db
-     */
-    public static final String DEFAULT_DATABASE_FILE =
-        "artifacts.db";
-
-    /**
-     * The default database URL: This is created once by #getDefaultURL()
-     */
-    public static final String DEFAULT_URL = getDefaultURL();
-
-    /**
-     * The default database user: ""
-     */
-    public static final String DEFAULT_USER     = "";
-
-    /**
-     * The default database password: ""
-     */
-    public static final String DEFAULT_PASSWORD = "";
-
-    private DBConnection() {
-    }
-
-    /**
-     * Constructs the default databse URL. It concats the
-     * config directory and the #DEFAULT_DATABASE_FILE
-     * to the string with is needed to access H2 databases.
-     * @return The default URL.
-     */
-    public static final String getDefaultURL() {
-        File configDir = Config.getConfigDirectory();
-        File databaseFile = new File(configDir, DEFAULT_DATABASE_FILE);
-        return "jdbc:h2:" + databaseFile;
-    }
-
-    private static BasicDataSource dataSource;
-
-    private static final void addShutdownHook() {
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            @Override
-            public void run() {
-                if (dataSource != null) {
-                    try {
-                        dataSource.close();
-                    }
-                    catch (SQLException sqle) {
-                    }
-                    dataSource = null;
-                }
-            }
-        });
-    }
-
-    /**
-     * Static method to fetch a database connection.
-     * @return a DataSource to access the database connection.
-     */
-    public static synchronized DataSource getDataSource() {
-        if (dataSource == null) {
-            dataSource = new BasicDataSource();
-
-            String driver = Config.getStringXPath(
-                DB_DRIVER, DEFAULT_DRIVER);
-
-            String url = Config.getStringXPath(
-                DB_URL, DEFAULT_URL);
-
-            url = Config.replaceConfigDir(url);
-
-            String user = Config.getStringXPath(
-                DB_USER, DEFAULT_USER);
-
-            String password = Config.getStringXPath(
-                DB_PASSWORD, DEFAULT_PASSWORD);
-
-            logger.info("database driver: " + driver);
-            logger.info("database url: " + url);
-
-            dataSource.setDriverClassName(driver);
-            dataSource.setUsername(user);
-            dataSource.setPassword(password);
-            dataSource.setUrl(url);
-            addShutdownHook();
-        }
-
-        return dataSource;
-    }
-}
-//  vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Fri Sep 28 12:15:11 2012 +0200
@@ -9,9 +9,13 @@
 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;
@@ -19,6 +23,8 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.Collections;
 
 import javax.sql.DataSource;
 
@@ -55,8 +61,15 @@
          */
         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);
 
     /**
@@ -66,17 +79,21 @@
      */
     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 static final String SQL_OUTDATED =
-        SQL.get("artifacts.outdated");
+    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 static final String SQL_DELETE =
-        SQL.get("artifacts.delete");
+    public String SQL_DELETE_ARTIFACT;
 
     /**
      * XPath to figure out how long the cleaner should sleep between
@@ -110,7 +127,7 @@
      * A specialized Id filter which only delete some artifacts.
      * This is used to prevent deletion of living artifacts.
      */
-    protected Id.Filter filter;
+    protected LockedIdsProvider lockedIdsProvider;
 
     /**
      * The reviver used to bring the dead artifact on last
@@ -118,6 +135,8 @@
      */
     protected ArtifactReviver reviver;
 
+    protected DBConnection dbConnection;
+
     /**
      * Default constructor.
      */
@@ -130,11 +149,21 @@
      * @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) {
+    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");
     }
 
     /**
@@ -142,10 +171,9 @@
      * Living artifacts are artifacts which are currently active
      * inside the artifact database. Deleting them in this state
      * would create severe internal problems.
-     * @param filter
      */
-    public void setFilter(Id.Filter filter) {
-        this.filter = filter;
+    public void setLockedIdsProvider(LockedIdsProvider lockedIdsProvider) {
+        this.lockedIdsProvider = lockedIdsProvider;
     }
 
     /**
@@ -179,14 +207,30 @@
         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                    Id
+    extends IdIdentifier
     {
         byte [] data;
         String  factoryName;
 
-        public IdData(int id, String factoryName, byte [] data) {
-            super(id);
+        public IdData(
+            int     id,
+            String  factoryName,
+            byte [] data,
+            String  identifier
+        ) {
+            super(id, identifier);
             this.factoryName = factoryName;
             this.data        = data;
         }
@@ -205,26 +249,89 @@
     protected void cleanup() {
         logger.info("database cleanup");
 
-        Connection connection      = null;
-        PreparedStatement fetchIds = null;
-        PreparedStatement deleteId = null;
-        ResultSet         result   = null;
+        Connection        connection = null;
+        PreparedStatement fetchIds   = null;
+        PreparedStatement stmnt      = null;
+        ResultSet         result     = null;
 
-        int removedArtifacts = 0;
+        DataSource dataSource = dbConnection.getDataSource();
 
-        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);
-            deleteId = connection.prepareStatement(SQL_DELETE);
+
+            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 ids = new ArrayList();
+                List<IdData> ids = new ArrayList<IdData>();
 
                 result = fetchIds.executeQuery();
 
@@ -232,7 +339,8 @@
                     ids.add(new IdData(
                         result.getInt(1),
                         result.getString(2),
-                        result.getBytes(3)));
+                        result.getBytes(3),
+                        result.getString(4)));
                 }
 
                 result.close(); result = null;
@@ -241,31 +349,33 @@
                     break;
                 }
 
-                if (filter != null) {
-                    ids = filter.filterIds(ids);
-                }
-
                 for (int i = ids.size()-1; i >= 0; --i) {
-                    IdData idData = (IdData)ids.get(i);
+                    IdData idData = ids.get(i);
                     Artifact artifact = reviver.reviveArtifact(
                         idData.factoryName, idData.data);
                     idData.data = null;
 
-                    deleteId.setInt(1, idData.id);
-                    deleteId.execute();
+                    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.getLocalizedMessage(), e);
+                        logger.error(e.getMessage(), e);
                     }
+
+                    deletedArtifacts.add(idData.identifier);
                 } // for all fetched data
-
-                removedArtifacts += ids.size();
             }
         }
         catch (SQLException sqle) {
@@ -276,21 +386,34 @@
                 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 (deleteId != null) {
-                try { deleteId.close(); }
-                catch (SQLException sqle) {}
-            }
             if (connection != null) {
                 try { connection.close(); }
                 catch (SQLException sqle) {}
             }
         }
 
-        logger.info("artifacts removed: " + removedArtifacts);
+        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());
+        }
     }
 
     /**
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Fri Sep 28 12:15:11 2012 +0200
@@ -13,6 +13,7 @@
 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;
@@ -35,6 +36,7 @@
      */
     protected String identifier;
 
+
     /**
      * Default constructor.
      */
@@ -57,6 +59,7 @@
         return this.identifier;
     }
 
+
     public String hash() {
         String hash = String.valueOf(hashCode());
         if (logger.isDebugEnabled()) {
@@ -99,8 +102,25 @@
         }
     }
 
-    public void setup(String identifier, ArtifactFactory factory,
-                      Object context, Document data) {
+    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);
         }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java	Fri Sep 28 12:15:11 2012 +0200
@@ -36,33 +36,23 @@
 public class DefaultArtifactCollection
 implements   ArtifactCollection
 {
-    /** The logger used in this class.*/
+    /** The logger used in this class. */
     private static Logger logger =
         Logger.getLogger(DefaultArtifactCollection.class);
 
-    /**
-     * The identifier of the collection.
-     */
+    /** The identifier of the collection. */
     protected String identifier;
 
-    /**
-     * The identifier of the collection.
-     */
+    /** The identifier of the collection. */
     protected String name;
 
-    /**
-     * The owner of this collection.
-     */
+    /** The owner of this collection. */
     protected User user;
 
-    /**
-     * The attribute of this collection.
-     */
+    /** The attribute of this collection. */
     protected Document attribute;
 
-    /**
-     * The artifacts stored in this collection.
-     */
+    /** The artifacts stored in this collection. */
     protected List<Artifact> artifacts;
 
     /**
@@ -75,6 +65,8 @@
     /** The creation time of this collection.*/
     protected Date creationTime;
 
+    protected long ttl;
+
 
     /**
      * Default constructor.
@@ -96,6 +88,7 @@
         String                    identifier,
         String                    name,
         Date                      creationTime,
+        long                      ttl,
         ArtifactCollectionFactory factory,
         Object                    context,
         Document                  data)
@@ -108,6 +101,7 @@
         setIdentifier(identifier);
         setName(name);
         setCreationTime(creationTime);
+        setTTL(ttl);
         setAttribute(data);
     }
 
@@ -147,7 +141,7 @@
 
     /**
      * Name of this collection.
-     * @return Returns the name of this collection
+     * @param name the name of this collection
      */
     public void setName(String name) {
         this.name = name;
@@ -192,6 +186,16 @@
     }
 
 
+    public long getTTL() {
+        return ttl;
+    }
+
+
+    public void setTTL(long ttl) {
+        this.ttl = ttl;
+    }
+
+
     /**
      * Returns the attribute of the collection.
      *
@@ -330,12 +334,17 @@
 
     /**
      * 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(Document format, OutputStream out, CallContext context)
+    public void out(
+        String       type,
+        Document     format,
+        OutputStream out,
+        CallContext  context)
     throws IOException
     {
         logger.debug("DefaultArtifactCollection.out");
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -103,8 +103,6 @@
 
     /**
      * Returns the time to live of the given artifact.
-     *
-     * @param artifact
      */
     public Long timeToLiveUntouched(
         ArtifactCollection collection,
@@ -126,6 +124,7 @@
         String   identifier,
         String   name,
         Date     creationTime,
+        long     ttl,
         Document data,
         Object   context
     ) {
@@ -133,7 +132,13 @@
             ArtifactCollection collection =
                 (ArtifactCollection) clazz.newInstance();
 
-            collection.setup(identifier, name, creationTime, this, context, data);
+            collection.setup(identifier,
+                name,
+                creationTime,
+                ttl,
+                this,
+                context,
+                data);
 
             return collection;
         }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -12,13 +12,15 @@
 
 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
+public class DefaultArtifactContext implements GlobalContext
 {
     /**
      * The global configuration document of the artifact database.
@@ -28,7 +30,7 @@
     /**
      * Custom key/value pairs to be used globally in the whole server.
      */
-    protected HashMap  map;
+    protected HashMap map;
 
     /**
      * Default constructor
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -9,6 +9,7 @@
 package de.intevation.artifactdatabase;
 
 import de.intevation.artifacts.ArtifactContextFactory;
+import de.intevation.artifacts.GlobalContext;
 
 import org.w3c.dom.Document;
 
@@ -27,7 +28,7 @@
     public DefaultArtifactContextFactory() {
     }
 
-    public Object createArtifactContext(Document config) {
+    public GlobalContext createArtifactContext(Document config) {
         return new DefaultArtifactContext(config);
     }
 }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -13,6 +13,8 @@
 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;
 
@@ -100,15 +102,16 @@
     }
 
     public Artifact createArtifact(
-        String   identifier,
-        Object   context,
-        Document data
+        String        identifier,
+        GlobalContext context,
+        CallMeta      callMeta,
+        Document      data
     ) {
         try {
             Artifact artifact =
                 (Artifact)artifactClass.newInstance();
 
-            artifact.setup(identifier, this, context, data);
+            artifact.setup(identifier, this, context, callMeta, data);
 
             return artifact;
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java	Fri Sep 28 12:15:11 2012 +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 :
+
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultCollectionItem.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultCollectionItem.java	Fri Sep 28 12:15:11 2012 +0200
@@ -37,7 +37,7 @@
     public synchronized Document getAttribute() {
         if (document == null) {
             if (data != null) {
-                document = XMLUtils.fromByteArray(data);
+                document = XMLUtils.fromByteArray(data, true);
             }
         }
         return document;
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java	Fri Sep 28 12:15:11 2012 +0200
@@ -8,10 +8,9 @@
 
 package de.intevation.artifactdatabase;
 
-import de.intevation.artifacts.common.utils.XMLUtils;
-
 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;
@@ -29,21 +28,43 @@
 {
     private static Logger logger = Logger.getLogger(DefaultService.class);
 
-    public Document process(
-        Document data,
-        Object   globalContext,
-        CallMeta callMeta
+    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
     ) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Service.process");
-        }
-        return XMLUtils.newDocument();
+        logger.debug("Service.process");
+        return new Output(new byte[0], "application/octet-stream");
     }
 
-    public void setup(ServiceFactory factory, Object globalContext) {
-        if (logger.isDebugEnabled()) {
-            logger.debug("Service.setup");
-        }
+    @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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultServiceFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -11,6 +11,7 @@
 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;
@@ -80,15 +81,18 @@
     public DefaultServiceFactory() {
     }
 
+    @Override
     public String getName() {
         return name;
     }
 
+    @Override
     public String getDescription() {
         return description;
     }
 
-    public Service createService(Object globalContext) {
+    @Override
+    public Service createService(GlobalContext globalContext) {
         try {
             Service service = (Service)serviceClass.newInstance();
 
@@ -109,6 +113,7 @@
         return null;
     }
 
+    @Override
     public void setup(Document config, Node factoryNode) {
 
         description = Config.getStringXPath(
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java	Fri Sep 28 12:15:11 2012 +0200
@@ -25,6 +25,9 @@
     /** 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;
 
@@ -35,18 +38,24 @@
     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, Document role) {
+    public DefaultUser(String identifier, String name, String account,
+                       Document role) {
         this.identifier = identifier;
         this.name       = name;
         this.role       = role;
+        this.account    = account;
     }
 
 
@@ -55,6 +64,7 @@
      *
      * @return the identifier of this user.
      */
+    @Override
     public String identifier() {
         return identifier;
     }
@@ -65,6 +75,7 @@
      *
      * @return the name of the user.
      */
+    @Override
     public String getName() {
         return name;
     }
@@ -75,6 +86,7 @@
      *
      * @param name The name for this user.
      */
+    @Override
     public void setName(String name) {
         this.name = name;
     }
@@ -85,6 +97,7 @@
      *
      * @param identifier The new identifier.
      */
+    @Override
     public void setIdentifier(String identifier) {
         this.identifier = identifier;
     }
@@ -95,6 +108,7 @@
      *
      * @param role The new role of the user.
      */
+    @Override
     public void setRole(Document role) {
         this.role = role;
     }
@@ -105,8 +119,19 @@
      *
      * @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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -47,17 +47,19 @@
      *
      * @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, role);
+        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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java	Fri Sep 28 12:15:11 2012 +0200
@@ -13,10 +13,18 @@
 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;
 
@@ -95,6 +103,30 @@
         "/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>.
@@ -105,7 +137,7 @@
     /**
      * Reference to the global context build by the global context factory.
      */
-    protected Object context;
+    protected GlobalContext context;
 
     /**
      * List of the artifact factories to be exposed by the
@@ -130,10 +162,27 @@
     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
@@ -344,6 +393,221 @@
         }
     }
 
+
+    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);
+
+        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
@@ -370,6 +634,11 @@
         loadArtifactFactories();
         loadServiceFactories();
         loadUserFactory();
+        loadCallContextListener();
+        loadHTTPServer();
+        loadHooks();
+        loadLifetimeListeners();
+        loadBackendListeners();
     }
 
     /**
@@ -410,7 +679,7 @@
      * Returns the global context created by the global context factory.
      * @return The global context.
      */
-    public Object getContext() {
+    public GlobalContext getContext() {
         return context;
     }
 
@@ -422,5 +691,38 @@
     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/Id.java	Wed Mar 30 13:35:37 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +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.List;
-
-/**
- * Class to model the concept of an 'id' in terms of unique integer as
- * used in databases. Subclasses of this class are able to be processed
- * by this id.
- *
- * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
- */
-public class Id
-{
-    /**
-     * Interface to filter a list of ids.
-     */
-    public interface Filter {
-        /**
-         * A list of ids is processed in a functional way to be
-         * free from some ids by the implementing code. The resulting
-         * list maybe short or equal sized to the original one. The
-         * original list is not modified.
-         * @param ids The list of input ids.
-         * @return A list of processed ids.
-         */
-        List filterIds(List ids);
-    }
-
-    /**
-     * The backing int of the id.
-     */
-    protected int id;
-
-    /**
-     * Default constructor: id = 0
-     */
-    public Id() {
-    }
-
-    /**
-     * Constructor to create an id with a given value.
-     * @param id The id value
-     */
-    public Id(int id) {
-        this.id = id;
-    }
-
-    /**
-     * Returns the id value.
-     * @return The value of the id.
-     */
-    public int getId() {
-        return id;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java	Fri Sep 28 12:15:11 2012 +0200
@@ -23,7 +23,7 @@
 
     public LazyBackendUser(
         String      identifier,
-        UserFactory factory, 
+        UserFactory factory,
         Backend     backend,
         Object      context
     ) {
@@ -43,28 +43,39 @@
         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/de/intevation/artifactdatabase/LifetimeListener.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java	Fri Sep 28 12:15:11 2012 +0200
@@ -9,10 +9,16 @@
 
 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;
 
@@ -147,17 +153,72 @@
     }
 
 
+    /**
+     * 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(
-        XMLUtils.ElementCreator creator,
-        Element                 out,
-        List<Output>            outputs)
+        Document     doc,
+        Element      out,
+        List<Output> outputs)
     {
+        ElementCreator creator = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
         for (Output o: outputs) {
-            out.appendChild(createArtNode(
+            Element newOut = createArtNode(
                 creator,
                 "output",
-                new String[] {"name", "description", "mime-type"},
-                new String[] {o.getName(),o.getDescription(),o.getMimeType()}));
+                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);
+            }
         }
     }
 }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/SQL.java	Wed Mar 30 13:35:37 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +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 java.io.IOException;
-import java.io.InputStream;
-
-import java.util.Properties;
-
-import org.apache.log4j.Logger;
-
-/**
- * Singleton to provide SQL statement strings as key/value pairs.
- * This mechanism is used to encapsulate database specific SQL
- * dialects.
- *
- * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
- */
-public final class SQL
-{
-    private static Logger logger = Logger.getLogger(SQL.class);
-
-    private SQL() {
-    }
-
-    private static Properties statements;
-
-    /**
-     * 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.
-     */
-    public static final synchronized Properties getStatements() {
-        if (statements == null) {
-            statements = loadStatements();
-        }
-        return statements;
-    }
-
-    private static final Properties loadStatements() {
-        String driver = Config.getStringXPath(
-            DBConnection.DB_DRIVER, DBConnection.DEFAULT_DRIVER);
-
-        Properties properties = new Properties();
-
-        InputStream in = null;
-        try {
-            String res = "/sql/" + driver.replace('.', '-').toLowerCase()
-                + ".properties";
-            in = SQL.class.getResourceAsStream(res);
-
-            if (in == null) {
-                logger.warn("No SQL file for driver '" + driver + "' found.");
-                res = "/sql/"
-                    + DBConnection.DEFAULT_DRIVER.replace('.', '-')
-                        .toLowerCase()
-                    + ".properties";
-                if ((in = SQL.class.getResourceAsStream(res)) == null) {
-                    logger.error("No SQL file found");
-                }
-            }
-
-            properties.load(in);
-        }
-        catch (IOException ioe) {
-            logger.error(ioe.getLocalizedMessage(), ioe);
-        }
-        finally {
-            if (in != null) {
-                try { in.close(); } catch (IOException ioe) {}
-            }
-        }
-
-        return properties;
-    }
-
-    /**
-     * Returns a particular SQL statement for a given key.
-     * @param key The key of the statement.
-     * @return The corresponing SQL statement or null if none
-     * is found.
-     */
-    public static final String get(String key) {
-        return getStatements().getProperty(key);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/SQLExecutor.java	Wed Mar 30 13:35:37 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +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.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 Connection        conn;
-    public PreparedStatement stmnt;
-    public ResultSet         result;
-
-    public SQLExecutor() {
-    }
-
-    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;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java	Wed Mar 30 13:35:37 2011 +0000
+++ /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;
-
-import java.io.UnsupportedEncodingException;
-
-import java.util.UUID;
-
-import org.apache.commons.codec.DecoderException;
-
-import org.apache.commons.codec.binary.Hex;
-
-import org.apache.log4j.Logger;
-
-/**
- * Commonly used string functions.
- *
- * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
- */
-public final class StringUtils
-{
-    private static Logger logger = Logger.getLogger(StringUtils.class);
-
-    /**
-     * Generated a random UUIDv4 in form of a string.
-     * @return the UUID
-     */
-    public static final String newUUID() {
-        return UUID.randomUUID().toString();
-    }
-
-    /**
-     * Checks if a given string is a valid UUID.
-     * @param uuid The string to test.
-     * @return true if the string is a valid UUID else false.
-     */
-    public static final boolean checkUUID(String uuid) {
-        try {
-            UUID.fromString(uuid);
-        }
-        catch (IllegalArgumentException iae) {
-            logger.warn(iae.getLocalizedMessage());
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns the UTF-8 byte array representation of a given string.
-     * @param s The string to be transformed.
-     * @return The byte array representation.
-     */
-    public static final byte [] getUTF8Bytes(String s) {
-        try {
-            return s.getBytes("UTF-8");
-        }
-        catch (UnsupportedEncodingException usee) {
-            logger.error(usee.getLocalizedMessage(), usee);
-            return s.getBytes();
-        }
-    }
-
-    /**
-     * Tries to convert a Base64 encoded string into the
-     * corresponing byte array.
-     * @param s The Base64 encoded string
-     * @return The byte array representation or null if
-     * an decoding error occurs.
-     */
-    public static final byte [] decodeHex(String s) {
-        try {
-            return Hex.decodeHex(s.toCharArray());
-        }
-        catch (DecoderException de) {
-            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/de/intevation/artifactdatabase/XMLService.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java	Fri Sep 28 12:15:11 2012 +0200
@@ -25,6 +25,8 @@
     /** The value. */
     protected Object value;
 
+    public DefaultStateData() {
+    }
 
     /**
      * The default constructor. It creates empty StateData objects with no
@@ -40,6 +42,13 @@
         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
@@ -111,5 +120,12 @@
     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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/data/StateData.java	Fri Sep 28 12:15:11 2012 +0200
@@ -53,5 +53,7 @@
      * @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/de/intevation/artifactdatabase/db/DBConnection.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/db/SQL.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/db/SQLExecutor.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactOutResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -46,11 +46,19 @@
     }
 
 
+    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)
@@ -58,7 +66,7 @@
     {
         logger.debug("ArtifactOutResource.doOut");
 
-        return db.out(identifier, input, meta);
+        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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -121,7 +121,8 @@
     /**
      * 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'
+     * 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
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseOutResource.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/BaseOutResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -77,6 +77,7 @@
         Request request = getRequest();
 
         String identifier = getIdentifier();
+        String outType    = getType();
 
         if (logger.isDebugEnabled()) {
             logger.debug("looking for artifact id '" + identifier + "'");
@@ -101,7 +102,7 @@
         try {
             return new OutRepresentation(
                 mimeType,
-                doOut(identifier, inputDocument, db, getCallMeta()));
+                doOut(identifier, outType, inputDocument, db, getCallMeta()));
         }
         catch (ArtifactDatabaseException adbe) {
             Response response = getResponse();
@@ -118,11 +119,20 @@
      */
     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.
@@ -131,6 +141,7 @@
      */
     protected abstract ArtifactDatabase.DeferredOutput doOut(
         String           identifier,
+        String           type,
         Document         input,
         ArtifactDatabase db,
         CallMeta         meta)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ByteArrayRepresentation.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionOutResource.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionOutResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -45,11 +45,19 @@
     }
 
 
+    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)
@@ -57,7 +65,7 @@
     {
         logger.debug("CollectionOutResource.doOut");
 
-        return db.outCollection(identifier, input, meta);
+        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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/CollectionResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -14,8 +14,12 @@
 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;
@@ -27,6 +31,7 @@
 import org.restlet.Response;
 
 import org.w3c.dom.Document;
+import org.w3c.dom.Node;
 
 
 /**
@@ -66,13 +71,19 @@
     /** 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_ATTRIBUTE = "getattribute";
+    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_ATTRIBUTE = "setattribute";
+    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";
@@ -83,6 +94,12 @@
     /** 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
@@ -135,14 +152,35 @@
             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.setCollectionAttribute(identifier, art, source, meta);
+                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.getCollectionAttribute(identifier, art, meta);
+                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);
@@ -208,4 +246,31 @@
         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/de/intevation/artifactdatabase/rest/FindUserResource.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/rest/HTTPServer.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/rest/JettyServer.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java	Fri Sep 28 12:15:11 2012 +0200
@@ -39,6 +39,11 @@
     public RestApp() {
     }
 
+    public RestApp(Context context, ArtifactDatabase database) {
+        super(context);
+        this.database = database;
+    }
+
     /**
      * Constructor to create REST appliction bound to a specific
      * artifact database.
@@ -76,6 +81,7 @@
         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(
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -27,6 +27,8 @@
 
 import org.w3c.dom.Document;
 
+import de.intevation.artifacts.Service;
+
 /**
  * Resource to process incoming XML documents with a given service.
  *
@@ -72,8 +74,7 @@
             .getAttributes().get("database");
 
         try {
-            return new DomRepresentation(
-                MediaType.APPLICATION_XML,
+            return guessRepresentation(
                 db.process(service, inputDocument, getCallMeta()));
         }
         catch (ArtifactDatabaseException adbe) {
@@ -84,5 +85,21 @@
             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/Standalone.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/Standalone.java	Fri Sep 28 12:15:11 2012 +0200
@@ -8,7 +8,7 @@
 
 package de.intevation.artifactdatabase.rest;
 
-import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
 
 import de.intevation.artifacts.ArtifactDatabase;
 
@@ -19,6 +19,8 @@
 
 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
@@ -27,6 +29,7 @@
  * @author <a href="mailto:sascha.teichmann@intevation">Sascha L. Teichmann</a>
  */
 public class Standalone
+implements   HTTPServer
 {
     private static Logger logger = Logger.getLogger(Standalone.class);
 
@@ -49,21 +52,26 @@
      */
     public static final int DEFAULT_PORT = 8181;
 
-    /**
-     * 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.
-     */
-    public static void startAsServer(ArtifactDatabase db) {
-        String portString   = Config.getStringXPath(REST_PORT);
-        String listenString = Config.getStringXPath(LISTEN_INTERFACE);
+    public static final String MAX_THREADS =
+        "/artifact-database/rest-server/max-threads/text()";
 
-        int port = DEFAULT_PORT;
+    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 {
@@ -78,28 +86,51 @@
             }
         }
 
+        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 = null;
-
-        if (listenString != null) {
-            server = new Server(Protocol.HTTP, listenString, port);
-        }
-        else {
-            server = new Server(Protocol.HTTP, port);
-        }
+        Server server = createServer();
 
         component.getServers().add(server);
 
-        server.getContext().getParameters().add("maxThreads", "512");
+        server.getContext().getParameters().add(
+            "maxThreads", maxThreads != null && maxThreads.length() > 0
+                ? maxThreads
+                : MAX_THREADS_DEFAULT);
 
         component.getDefaultHost().attach(app);
 
-        logger.info("Starting rest HTTP server on "
-            + (listenString != null ? listenString : "*")
-            + ":" + port);
+        logServerStart();
 
         try {
             component.start();
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java	Fri Sep 28 12:15:11 2012 +0200
@@ -35,10 +35,10 @@
 public class UserResource
 extends      BaseResource
 {
-    /** The logger that is used in this class.*/
+    /** The logger that is used in this class. */
     private static Logger logger = Logger.getLogger(UserResource.class);
 
-    /** server URL where to reach the resource.*/
+    /** server URL where to reach the resource. */
     public static final String PATH = "/user/{uuid}";
 
     /**
@@ -47,13 +47,13 @@
      */
     public static final String XPATH_ACTION = "/art:action/art:type/@name";
 
-    /** Error message if no action was given.*/
+    /** 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.*/
+    /** Error message if a unknown action was given. */
     public static final String NO_SUCH_ACTION_MSG = "no such action";
 
-    /** Action name for deleting users.*/
+    /** Action name for deleting users. */
     public static final String ACTION_DELETE = "delete";
 
 
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java	Fri Sep 28 12:15:11 2012 +0200
@@ -14,13 +14,17 @@
 
 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.XMLUtils;
 
@@ -43,9 +47,20 @@
      * the configuration. */
     public static final String XPATH_DESCRIPTION = "@description";
 
-    /** The XPath to the output nodes of the state configuration.*/
+    /** 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";
+
+
+    /** The logger that is used in this class. */
+    private static Logger logger = Logger.getLogger(AbstractState.class);
+
 
     /** The ID of the state. */
     protected String id;
@@ -53,10 +68,13 @@
     /** 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.*/
+    /** A list of output modes which are available for this state. */
     protected List<Output> outputs;
 
 
@@ -76,7 +94,12 @@
 
         this.id          = id;
         this.description = description;
+    }
 
+
+    public AbstractState(String id, String description, String helpText) {
+        this(id, description);
+        this.helpText = helpText;
     }
 
 
@@ -121,6 +144,26 @@
 
 
     /**
+     * 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 = helpText;
+    }
+
+
+    /**
      * Returns the data of the state.
      *
      * @return the data of the state.
@@ -131,11 +174,27 @@
 
 
     /**
+     * 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 StateData The data object.
+     * @param data The data object.
      */
     public void addData(String name, StateData data) {
         if (this.data == null) {
@@ -163,16 +222,39 @@
      * @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);
+
         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.
      *
@@ -192,10 +274,18 @@
         int size = outs.getLength();
 
         for (int i = 0; i < size; i++) {
-            outputs.add(buildOutput(outs.item(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>
@@ -215,7 +305,55 @@
         String mimetype = XMLUtils.xpathString(
             out, "@mime-type", ArtifactNamespaceContext.INSTANCE);
 
-        return name != null ? new DefaultOutput(name, desc, mimetype) : null;
+        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;
     }
 
 
@@ -223,16 +361,24 @@
      * 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/de/intevation/artifactdatabase/state/ArtifactAndFacet.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/Attribute.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/DefaultAttribute.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/DefaultFacet.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java	Fri Sep 28 12:15:11 2012 +0200
@@ -1,5 +1,7 @@
 package de.intevation.artifactdatabase.state;
 
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The default implementation of an Output.
@@ -14,6 +16,12 @@
 
     protected String mimeType;
 
+    protected String type;
+
+    protected List<Facet> facets;
+
+    protected Settings settings;
+
 
     /**
      * The default constructor that instantiates a new DefaultOutput object.
@@ -26,6 +34,55 @@
         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.
+     */
+    public DefaultOutput(
+        String      name,
+        String      description,
+        String      mimeType,
+        List<Facet> facets,
+        String      type)
+    {
+        this(name, description, mimeType, facets);
+
+        this.type = type;
     }
 
 
@@ -57,5 +114,50 @@
     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/de/intevation/artifactdatabase/state/DefaultSection.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/DefaultSettings.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/Facet.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/FacetActivity.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,80 @@
+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;
+
+public interface FacetActivity
+{
+    public static final FacetActivity ACTIVE = new FacetActivity() {
+        @Override
+        public Boolean isInitialActive(
+            Artifact artifact,
+            Facet    facet,
+            String   output
+        ) {
+            return Boolean.TRUE;
+        }
+    };
+
+    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);
+
+    public static final class Registry {
+
+        private static final Registry INSTANCE = new Registry();
+
+        private Map<String, List<FacetActivity>> activities;
+
+        private Registry() {
+            activities = new HashMap<String, List<FacetActivity>>();
+        }
+
+        public static Registry getInstance() {
+            return INSTANCE;
+        }
+
+        public synchronized boolean isInitialActive(
+            String   key,
+            Artifact artifact,
+            Facet    facet,
+            String   output
+        ) {
+            List<FacetActivity> activityList = activities.get(key);
+            if (activityList == null) {
+                return true;
+            }
+            for (FacetActivity activity: activityList) {
+                Boolean isActive =
+                    activity.isInitialActive(artifact, facet, output);
+                if (isActive != null) {
+                    return isActive;
+                }
+            }
+            return true;
+        }
+
+        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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java	Fri Sep 28 12:15:11 2012 +0200
@@ -1,5 +1,7 @@
 package de.intevation.artifactdatabase.state;
 
+import java.util.List;
+
 
 /**
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
@@ -26,5 +28,53 @@
      * @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/de/intevation/artifactdatabase/state/Section.java	Fri Sep 28 12:15:11 2012 +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/de/intevation/artifactdatabase/state/Settings.java	Fri Sep 28 12:15:11 2012 +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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java	Fri Sep 28 12:15:11 2012 +0200
@@ -15,7 +15,9 @@
 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;
 
@@ -45,6 +47,14 @@
 
 
     /**
+     * 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.
@@ -53,6 +63,16 @@
 
 
     /**
+     * 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.
      *
@@ -80,19 +100,47 @@
 
 
     /**
+     * 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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java	Fri Sep 28 12:15:11 2012 +0200
@@ -1,13 +1,18 @@
 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 for each Artifact.
+ * 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>
  */
@@ -21,7 +26,7 @@
      * the name of an artifact, its value is a list of all states the artifact
      * can reach.
      */
-    protected Map<String, List> states;
+    protected Map<String, List<State>> states;
 
 
     /**
@@ -35,7 +40,7 @@
      * The default constructor.
      */
     public StateEngine() {
-        states    = new HashMap<String, List>();
+        states    = new HashMap<String, List<State>>();
         allStates = new HashMap<String, State>();
     }
 
@@ -62,6 +67,30 @@
     }
 
 
+    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.
      *
@@ -93,7 +122,7 @@
     /**
      * Returns the state list of an artifact specified by its name.
      *
-     * @param artifact The name of the artifact.
+     * @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>.
@@ -101,5 +130,40 @@
     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.
+        for (String stateId: aStates) {
+
+            State state = allStates.get(stateId);
+            if (state == null) {
+                continue;
+            }
+
+            for (Output output: state.getOutputs()) {
+                List<Facet> outFacets = output.getFacets();
+
+                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 si et sta sts=4 fenc=utf8 :
+// vim:set ts=4 sw=4 et sta sts=4 fenc=utf8 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/Transition.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/Transition.java	Fri Sep 28 12:15:11 2012 +0200
@@ -1,5 +1,9 @@
 package de.intevation.artifactdatabase.transition;
 
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+
 import de.intevation.artifactdatabase.state.State;
 
 
@@ -9,6 +13,13 @@
 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();
@@ -33,8 +44,15 @@
     public void setTo(String to);
 
     /**
-     * Determines if the transition from <code>state</code> is valid.
+     * 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(State state);
+    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	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java	Fri Sep 28 12:15:11 2012 +0200
@@ -7,6 +7,8 @@
 
 import org.apache.log4j.Logger;
 
+import de.intevation.artifacts.Artifact;
+
 import de.intevation.artifactdatabase.state.State;
 import de.intevation.artifactdatabase.state.StateEngine;
 
@@ -42,7 +44,7 @@
      * Add new transitions for a specific artifact.
      *
      * @param stateId the name of the Artifact.
-     * @param transitions the list of transition of the artifact.
+     * @param transition the list of transition of the artifact.
      *
      * @return true, if the transitions were added, otherwise false.
      */
@@ -81,7 +83,10 @@
      *
      * @return a list of reachable states.
      */
-    public List<State> getReachableStates(State state, StateEngine engine) {
+    public List<State> getReachableStates(
+        Artifact    artifact,
+        State       state,
+        StateEngine engine) {
         List<Transition> transitions = getTransitions(state);
         List<State>      reachable   = new ArrayList<State>();
 
@@ -90,8 +95,10 @@
         }
 
         for (Transition t: transitions) {
-            if (t.isValid(state)) {
-                reachable.add(engine.getState(t.getTo()));
+            State target = engine.getState(t.getTo());
+
+            if (t.isValid(artifact, state, target)) {
+                reachable.add(target);
             }
         }
 
@@ -103,6 +110,7 @@
      * 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.
@@ -110,11 +118,12 @@
      * @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(state, stateEngine);
+        List<State> reachable = getReachableStates(artifact, state,stateEngine);
 
         if (reachable == null || reachable.size() == 0) {
             return false;
--- a/artifact-database/src/main/resources/sql/org-h2-driver.properties	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/resources/sql/org-h2-driver.properties	Fri Sep 28 12:15:11 2012 +0200
@@ -9,12 +9,16 @@
 
 artifacts.touch=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP WHERE id = ?
 
-artifacts.outdated=SELECT id, factory, data FROM artifacts WHERE ttl IS NOT NULL \
+artifacts.outdated=SELECT id, factory, data, gid FROM artifacts WHERE ttl IS NOT NULL \
     AND DATEDIFF('MILLISECOND', last_access, CURRENT_TIMESTAMP) > ttl \
     AND id NOT IN (SELECT DISTINCT artifact_id FROM collection_items) \
+    AND id NOT IN ($LOCKED_IDS$) \
     LIMIT 50
 
-artifacts.select.gid=SELECT id, last_access, ttl, factory, data FROM artifacts WHERE gid = ?
+artifacts.select.gid=SELECT id, ttl, factory, data FROM artifacts WHERE gid = ?::uuid \
+    AND (ttl IS NULL \
+    OR  (DATEDIFF('MILLISECOND', last_access, CURRENT_TIMESTAMP) <= ttl)  \
+    OR  id IN (SELECT artifact_id FROM collection_items))
 
 artifacts.get.id=SELECT id FROM artifacts WHERE gid = ?
 
@@ -29,16 +33,17 @@
 
 users.id.nextval=SELECT NEXTVAL('USERS_ID_SEQ')
 
-users.insert=INSERT INTO users (id, gid, name, role) VALUES (?, ?, ?, ?)
+users.insert=INSERT INTO users (id, gid, name, account, role) VALUES (?, ?, ?, ?, ?)
 
 users.select.id.by.gid=SELECT id FROM users WHERE gid = ?
-users.select.gid=SELECT id, name, role FROM users WHERE gid = ?
+users.select.gid=SELECT id, name, account, role FROM users WHERE gid = ?
+users.select.account=SELECT gid, name, account, role FROM users WHERE account = ?
 
 users.delete.id=DELETE FROM users WHERE id = ?
 
 users.delete.collections=DELETE FROM collections where owner_id = ?
 
-users.select.all=SELECT id, gid, name, role FROM users
+users.select.all=SELECT id, gid, name, account, role FROM users
 
 collection.check.artifact=SELECT id FROM collection_items \
     WHERE artifact_id = ? AND collection_id = ?
@@ -47,8 +52,8 @@
 collection.items.id.nextval=SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ')
 
 collection.items.insert=INSERT INTO collection_items \
-    (id, collection_id, artifact_id, attribute) \
-    VALUES (?, ?, ?, ?)
+    (id, collection_id, artifact_id, attribute, creation) \
+    VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
 
 collection.item.get.attribute= \
     SELECT ci.attribute FROM collection_items ci \
@@ -73,7 +78,7 @@
     UPDATE artifacts \
     SET last_access = DATEADD('MILLISECOND', -2, CURRENT_TIMESTAMP), ttl = 1 \
     WHERE id = ? AND \
-    NOT EXSITS \
+    NOT EXISTS \
     (SELECT id FROM collection_items WHERE collection_id <> ? AND artifact_id = ?)
 
 collection.item.delete=DELETE FROM collection_items WHERE id = ?
@@ -81,9 +86,31 @@
 collection.items.list.gid= \
     SELECT a.gid, ci.attribute FROM collection_items ci \
     INNER JOIN artifacts a ON ci.artifact_id = a.id \
-    WHERE ci.collection_id IN (SELECT id FROM collections WHERE gid = ?)
+    WHERE ci.collection_id IN (SELECT id FROM collections WHERE gid = ?) \
+    ORDER BY ci.creation
 
 # COLLECTIONS
+
+collections.outdated= \
+    SELECT c.id, c.gid FROM collections c \
+        INNER JOIN collection_items ci ON c.id = ci.collection_id \
+        INNER JOIN artifacts        a  ON ci.artifact_id = a.id \
+        WHERE c.ttl IS NOT NULL \
+            AND DATEDIFF('MILLISECOND', c.last_access, CURRENT_TIMESTAMP) > c.ttl \
+            AND a.id NOT IN ($LOCKED_IDS$)
+
+collections.update.ttl=UPDATE collections SET ttl = ? WHERE gid = ?
+
+collections.update.name=UPDATE collections SET name = ? WHERE gid = ?
+
+collections.touch.trigger.function = \
+   UPDATE collections SET last_access = current_timestamp \
+   WHERE id IN \
+        (SELECT c.id FROM collections c \
+         INNER JOIN collection_items ci ON c.id = ci.collection_id  \
+         INNER JOIN artifacts a         ON a.id = ci.artifact_id \
+         WHERE a.id = ?)
+
 collections.touch.by.gid =\
     UPDATE collections SET last_access = CURRENT_TIMESTAMP \
         WHERE gid = ?
@@ -111,17 +138,22 @@
 
 collection.creation.time=SELECT creation from collections WHERE id = ?
 
+collections.artifacts.oldest=SELECT a.gid, ci.artifact_id  \
+    FROM artifacts AS a, collection_items AS ci, collections AS c \
+    WHERE ci.collection_id = c.id AND c.gid = ?::uuid AND ci.artifact_id = a.id \
+    ORDER BY ci.creation
+
 collections.select.user= \
-    SELECT c.gid, c.name, c.creation, u.gid FROM \
+    SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \
     WHERE u.gid = ?
 
 collections.select.all= \
-    SELECT c.gid, c.name, c.creation, u.gid FROM \
+    SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id
 
 collections.select.by.gid= \
-    SELECT id, name, owner_id, creation, last_access, attribute \
+    SELECT id, name, owner_id, creation, last_access, attribute, ttl \
     FROM collections WHERE gid = ?
 
 users.collections=SELECT collection_id, gid, name FROM collections WHERE owner_id = ?
@@ -137,7 +169,7 @@
 outdate.artifacts.collection=UPDATE artifacts \
     SET last_access = DATEADD('MILLISECOND', -2, CURRENT_TIMESTAMP), ttl = 1 \
     WHERE id IN \
-    SELECT artifact_id FROM collection_items \
+    (SELECT artifact_id FROM collection_items \
         WHERE collection_id = ? AND \
         artifact_id NOT IN (SELECT DISTINCT artifact_id FROM collection_items WHERE collection_id <> ?))
 
@@ -149,3 +181,18 @@
         AND artifact_id NOT IN \
             (SELECT artifact_id FROM collection_items WHERE collection_id IN \
                 (SELECT id FROM collections WHERE owner_id <> ?)))
+
+collection.get.attribute= \
+    SELECT c.attribute FROM collections c WHERE c.gid = ?
+
+collection.set.attribute= \
+    UPDATE collections SET attribute = ? WHERE gid = ?
+
+all.artifacts = \
+    SELECT u.gid AS u_gid, c.gid AS c_gid, c.name AS c_name, c.creation as c_creation, \
+           a.gid AS a_gid, a.factory AS factory, a.creation AS a_creation, a.data AS data \
+    FROM \
+        users u INNER JOIN collections c       ON u.id = c.owner_id \
+                INNER JOIN collection_items ci ON c.id = ci.collection_id \
+                INNER JOIN artifacts a         ON a.id = ci.artifact_id \
+        ORDER BY u_gid, c_gid
--- a/artifact-database/src/main/resources/sql/org-postgresql-driver.properties	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifact-database/src/main/resources/sql/org-postgresql-driver.properties	Fri Sep 28 12:15:11 2012 +0200
@@ -9,12 +9,16 @@
 
 artifacts.touch=UPDATE artifacts SET last_access = CURRENT_TIMESTAMP WHERE id = ?
 
-artifacts.outdated=SELECT id, factory, data FROM artifacts WHERE ttl IS NOT NULL \
-    AND CURRENT_TIMESTAMP - last_access > (ttl || ' microseconds')::interval \
+artifacts.outdated=SELECT id, factory, data, gid FROM artifacts WHERE ttl IS NOT NULL \
+    AND CURRENT_TIMESTAMP - last_access > (ttl || ' milliseconds')::interval \
     AND id NOT IN (SELECT DISTINCT artifact_id FROM collection_items) \
+    AND id NOT IN ($LOCKED_IDS$) \
     LIMIT 50
 
-artifacts.select.gid=SELECT id, last_access, ttl, factory, data FROM artifacts WHERE gid = ?::uuid
+artifacts.select.gid=SELECT id, ttl, factory, data FROM artifacts WHERE gid = ?::uuid \
+    AND (ttl IS NULL \
+    OR  (CURRENT_TIMESTAMP - last_access <= (ttl || ' milliseconds')::interval) \
+    OR  id IN (SELECT artifact_id FROM collection_items))
 
 artifacts.get.id=SELECT id FROM artifacts WHERE gid = ?::uuid
 
@@ -29,16 +33,17 @@
 
 users.id.nextval=SELECT NEXTVAL('USERS_ID_SEQ')
 
-users.insert=INSERT INTO users (id, gid, name, role) VALUES (?, ?::uuid, ?, ?)
+users.insert=INSERT INTO users (id, gid, name, account, role) VALUES (?, ?::uuid, ?, ?, ?)
 
 users.select.id.by.gid=SELECT id FROM users WHERE gid = ?::uuid
-users.select.gid=SELECT id, name, role FROM users WHERE gid = ?::uuid
+users.select.gid=SELECT id, name, account, role FROM users WHERE gid = ?::uuid
+users.select.account=SELECT gid, name, account, role FROM users WHERE account = ?
 
 users.delete.id=DELETE FROM users WHERE id = ?
 
 users.delete.collections=DELETE FROM collections where owner_id = ?
 
-users.select.all=SELECT id, gid, name, role FROM users
+users.select.all=SELECT id, gid, name, account, role FROM users
 
 collection.check.artifact=SELECT id FROM collection_items \
     WHERE artifact_id = ? AND collection_id = ?
@@ -47,8 +52,8 @@
 collection.items.id.nextval=SELECT NEXTVAL('COLLECTION_ITEMS_ID_SEQ')
 
 collection.items.insert=INSERT INTO collection_items \
-    (id, collection_id, artifact_id, attribute) \
-    VALUES (?, ?, ?, ?)
+    (id, collection_id, artifact_id, attribute, creation) \
+    VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
 
 collection.item.get.attribute= \
     SELECT ci.attribute FROM collection_items ci \
@@ -71,9 +76,9 @@
 
 collection.item.outdate.artifact= \
     UPDATE artifacts \
-    SET last_access = CURRENT_TIMESTAMP - '2 microseconds'::interval, ttl = 1 \
+    SET last_access = CURRENT_TIMESTAMP - '2 milliseconds'::interval, ttl = 1 \
     WHERE id = ? AND \
-    NOT EXSITS \
+    NOT EXISTS \
     (SELECT id FROM collection_items WHERE collection_id <> ? AND artifact_id = ?)
 
 collection.item.delete=DELETE FROM collection_items WHERE id = ?
@@ -81,9 +86,23 @@
 collection.items.list.gid= \
     SELECT a.gid, ci.attribute FROM collection_items ci \
     INNER JOIN artifacts a ON ci.artifact_id = a.id \
-    WHERE ci.collection_id IN (SELECT id FROM collections WHERE gid = ?::uuid)
+    WHERE ci.collection_id IN (SELECT id FROM collections WHERE gid = ?::uuid) \
+    ORDER BY ci.creation
 
 # COLLECTIONS
+
+collections.outdated= \
+    SELECT c.id, c.gid FROM collections c \
+        INNER JOIN collection_items ci ON c.id = ci.collection_id \
+        INNER JOIN artifacts        a  ON ci.artifact_id = a.id \
+        WHERE c.ttl IS NOT NULL \
+            AND CURRENT_TIMESTAMP - c.last_access > (c.ttl || ' milliseconds')::interval \
+            AND a.id NOT IN ($LOCKED_IDS$)
+
+collections.update.ttl=UPDATE collections SET ttl = ? WHERE gid = ?::uuid
+
+collections.update.name=UPDATE collections SET name = ? WHERE gid = ?::uuid
+
 collections.touch.by.gid =\
     UPDATE collections SET last_access = CURRENT_TIMESTAMP \
         WHERE gid = ?::uuid
@@ -110,17 +129,22 @@
 
 collection.creation.time=SELECT creation from collections WHERE id = ?
 
+collections.artifacts.oldest=SELECT a.gid, ci.artifact_id  \
+    FROM artifacts AS a, collection_items AS ci, collections AS c \
+    WHERE ci.collection_id = c.id AND c.gid = ?::uuid AND ci.artifact_id = a.id \
+    ORDER BY ci.creation
+
 collections.select.user= \
-    SELECT c.gid, c.name, c.creation, u.gid FROM \
+    SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \
     WHERE u.gid = ?::uuid
 
 collections.select.all= \
-    SELECT c.gid, c.name, c.creation, u.gid FROM \
+    SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id
 
 collections.select.by.gid= \
-    SELECT id, name, owner_id, creation, last_access, attribute \
+    SELECT id, name, owner_id, creation, last_access, attribute, ttl \
     FROM collections WHERE gid = ?::uuid
 
 users.collections=SELECT collection_id, gid, name FROM collections WHERE owner_id = ?
@@ -132,14 +156,14 @@
     artifact_id NOT IN (SELECT DISTINCT artifact_id FROM collection_items WHERE collection_id <> ?)
 
 outdate.artifacts.collection=UPDATE artifacts \
-    SET last_access = CURRENT_TIMESTAMP - '2 microseconds'::interval, ttl = 1 \
+    SET last_access = CURRENT_TIMESTAMP - '2 milliseconds'::interval, ttl = 1 \
     WHERE id IN \
     (SELECT artifact_id FROM collection_items \
         WHERE collection_id = ? AND \
         artifact_id NOT IN (SELECT DISTINCT artifact_id FROM collection_items WHERE collection_id <> ?))
 
 outdate.artifacts.user=UPDATE artifacts \
-    SET last_access = CURRENT_TIMESTAMP - '2 microseconds'::interval, ttl = 1 \
+    SET last_access = CURRENT_TIMESTAMP - '2 milliseconds'::interval, ttl = 1 \
     WHERE id IN \
     (SELECT artifact_id FROM collection_items WHERE \
         collection_id IN (SELECT id FROM collections WHERE owner_id = ?) \
@@ -147,3 +171,17 @@
             (SELECT artifact_id FROM collection_items WHERE collection_id IN \
                 (SELECT id FROM collections WHERE owner_id <> ?)))
 
+collection.get.attribute= \
+    SELECT c.attribute FROM collections c WHERE c.gid = ?::uuid
+
+collection.set.attribute= \
+    UPDATE collections SET attribute = ? WHERE gid = ?::uuid
+
+all.artifacts = \
+    SELECT u.gid AS u_gid, c.gid AS c_gid, c.name AS c_name, c.creation as c_creation, \
+           a.gid AS a_gid, a.factory AS factory, a.creation AS a_creation, a.data AS data \
+    FROM \
+        users u INNER JOIN collections c       ON u.id = c.owner_id \
+                INNER JOIN collection_items ci ON c.id = ci.collection_id \
+                INNER JOIN artifacts a         ON a.id = ci.artifact_id \
+        ORDER BY u_gid, c_gid
--- a/artifacts-common/pom.xml	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts-common/pom.xml	Fri Sep 28 12:15:11 2012 +0200
@@ -26,6 +26,11 @@
       <artifactId>log4j</artifactId>
       <version>1.2.14</version>
     </dependency>
+    <dependency>
+      <groupId>commons-codec</groupId>
+      <artifactId>commons-codec</artifactId>
+      <version>1.4</version>
+    </dependency>
   </dependencies>
 
   <build>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/model/KVP.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,32 @@
+/*
+ * 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.artifacts.common.model;
+
+
+public class KVP<K, V> {
+
+    private K key;
+    private V value;
+
+
+    public KVP(K key, V value) {
+        this.key   = key;
+        this.value = value;
+    }
+
+
+    public K getKey() {
+        return key;
+    }
+
+
+    public V getValue() {
+        return value;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java	Fri Sep 28 12:15:11 2012 +0200
@@ -25,36 +25,47 @@
  */
 public class ClientProtocolUtils {
 
-    /** The XPath to the current state in the DESCRIBE document.*/
+    /** The XPath to the current state in the DESCRIBE document. */
     public static final String XPATH_CURRENT_STATE = "/art:result/art:state";
 
-    /** The XPath to the static UI part in the DESCRIBE document.*/
+    /** The XPath to the static UI part in the DESCRIBE document. */
     public static final String XPATH_STATIC  = "/art:result/art:ui/art:static";
 
-    /** The XPath to the dynamic UI part in the DESCRIBE document.*/
+    /** The XPath to the dynamic UI part in the DESCRIBE document. */
     public static final String XPATH_DYNAMIC = "/art:result/art:ui/art:dynamic";
 
-    /** The XPath to the reachable states part in the DESCRIBE document.*/
+    /** The XPath to the reachable states part in the DESCRIBE document. */
     public static final String XPATH_STATES  =
         "/art:result/art:reachable-states";
 
-    /** The XPath to the output modes in the DESCRIBE document.*/
+    /** The XPath to the output modes in the DESCRIBE document. */
     public static final String XPATH_OUTPUT_MODES  =
         "/art:result/art:outputmodes/art:output";
 
-
     /** The XPath to the select node relative to the dynamic UI node in the
-     * DESCRIBE document.*/
+     * DESCRIBE document. */
     public static final String XPATH_DATA_SELECT = "art:select";
 
     /** The XPath to the choices nodes relative to the select node in the
-     * DESCRIBE document.*/
+     * DESCRIBE document. */
     public static final String XPATH_DATA_ITEMS = "art:choices/art:item";
 
-    /** The XPath to a label in the artifact's DESCRIBE document.*/
+    /** The XPath that points to the min value of a range.*/
+    public static final String XPATH_MIN_NODE = "art:min/@art:value";
+
+    /** The XPath that points to the max value of a range.*/
+    public static final String XPATH_MAX_NODE = "art:max/@art:value";
+
+    /** The XPath that points to the default min value of a range.*/
+    public static final String XPATH_DEF_MIN = "art:min/@art:default";
+
+    /** The XPath that points to the default max value of a range.*/
+    public static final String XPATH_DEF_MAX = "art:max/@art:default";
+
+    /** The XPath to a label in the artifact's DESCRIBE document. */
     public static final String XPATH_LABEL = "art:label/text()";
 
-    /** The XPath to a value in the artifact's DESCRIBE document.*/
+    /** The XPath to a value in the artifact's DESCRIBE document. */
     public static final String XPATH_VALUE = "art:value/text()";
 
 
@@ -71,6 +82,38 @@
      * @return the CREATE document.
      */
     public static Document newCreateDocument(String factory) {
+        return newCreateDocument(factory, null);
+    }
+
+
+    /**
+     * This method creates a new CREATE document.
+     *
+     * @return the CREATE document.
+     */
+    public static Document newCreateDocument(String factory, String uuid) {
+        return newCreateDocument(factory, uuid, null);
+    }
+
+    public static Document newCreateDocument(
+        String  factory,
+        String  uuid,
+        String  ids
+    ) {
+        return newCreateDocument(factory, uuid, ids, null);
+    }
+
+    /**
+     * This method creates a new CREATE document.
+     *
+     * @return the CREATE document.
+     */
+    public static Document newCreateDocument(
+        String         factory,
+        String         uuid,
+        String         ids,
+        CreationFilter filter
+    ) {
         Document doc = XMLUtils.newDocument();
 
         XMLUtils.ElementCreator cr = new XMLUtils.ElementCreator(
@@ -88,6 +131,22 @@
         action.appendChild(type);
         action.appendChild(fac);
 
+        if (uuid != null) {
+            Element templ = cr.create("template");
+            templ.setAttribute("uuid", uuid);
+            action.appendChild(templ);
+        }
+
+        if (ids != null) {
+            Element id = cr.create("ids");
+            id.setAttribute("value", ids);
+            action.appendChild(id);
+        }
+
+        if (filter != null) {
+            action.appendChild(filter.toXML(cr));
+        }
+
         doc.appendChild(action);
 
         return doc;
@@ -148,6 +207,52 @@
 
 
     /**
+     * This method creates a new DESCRIBE document.
+     *
+     * @param theUuid The identifier of the artifact.
+     * @param theHash The hash of the artifact.
+     * @param ui If true, the UI part is included.
+     *
+     * @return the DESCRIBE document.
+     */
+    public static Document newDescribeDocument(
+        String  theUuid,
+        String  theHash,
+        boolean incUI)
+    {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator cr = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = cr.create("action");
+        Element type   = cr.create("type");
+        Element uuid   = cr.create("uuid");
+        Element hash   = cr.create("hash");
+        Element ui     = cr.create("include-ui");
+
+        // XXX It is not nice that the type has no attribute namespace, but to
+        // be backward compatible, we don't change this now.
+        cr.addAttr(type, "name", "describe", false);
+        cr.addAttr(uuid, "value", theUuid, true);
+        cr.addAttr(hash, "value", theHash, true);
+
+        ui.setTextContent(incUI ? "true" : "false");
+
+        action.appendChild(type);
+        action.appendChild(uuid);
+        action.appendChild(hash);
+        action.appendChild(ui);
+
+        doc.appendChild(action);
+
+        return doc;
+    }
+
+
+    /**
      * This method creates a new ADVANCE document.
      *
      * @param theUuid The identifier of the artifact.
@@ -231,7 +336,7 @@
      * collection in the artifact server.
      *
      * @param artId The identifier of the artifact that should be added.
-     * @param attr A document that contains attributes for the attribute's
+     * @param attr A document that contains attributes for the artifact's
      * life in the collection.
      *
      * @return the document to add an artifact into a collection.
@@ -267,6 +372,38 @@
 
 
     /**
+     * Create a new Document that is used to remove an artifact from a
+     * collection in the artifact server.
+     *
+     * @param artId The identifier of the artifact that should be added.
+     *
+     * @return the document to add an artifact into a collection.
+     */
+    public static Document newRemoveArtifactDocument(String artId) {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator cr = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action    = cr.create("action");
+        Element type      = cr.create("type");
+        Element artifact  = cr.create("artifact");
+
+        cr.addAttr(artifact, "uuid", artId);
+        cr.addAttr(type, "name", "removeartifact");
+
+        action.appendChild(type);
+        type.appendChild(artifact);
+
+        doc.appendChild(action);
+
+        return doc;
+    }
+
+
+    /**
      * This method creates a new Document that is used to trigger the DESCRIBE
      * operation of a collection in the artifact server.
      *
@@ -294,6 +431,242 @@
     }
 
 
+
+    /**
+     * This function builds a document that is used as request document of the
+     * out() operation of Collections.
+     *
+     * @param uuid The identifier of the collection.
+     * @param mode The name of the desired output mode.
+     * @param type The name of the desired output type.
+     *
+     * @return the request document.
+     */
+    public static Document newOutCollectionDocument(
+        String uuid,
+        String mode,
+        String type) {
+        return newOutCollectionDocument(uuid, mode, type, null);
+    }
+
+
+    /**
+     * This function builds a document that is used as request document of the
+     * out() operation of Collections. The document <i>attr</i> might be used to
+     * adjust some settings specific to the output.
+     *
+     * @param uuid The identifier of the collection.
+     * @param mode The name of the desired output mode.
+     * @param type The name of the desired output type.
+     * @param attr A document that contains settings specific to the output.
+     *
+     * @return the request document.
+     */
+    public static Document newOutCollectionDocument(
+        String   uuid,
+        String   mode,
+        String   type,
+        Document attr)
+    {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator cr = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = cr.create("action");
+
+        cr.addAttr(action, "name", mode, true);
+        cr.addAttr(action, "type", type, true);
+
+        doc.appendChild(action);
+
+        if (attr != null) {
+            Node root = attr.getFirstChild();
+
+            if (root != null) {
+                action.appendChild(doc.importNode(root, true));
+            }
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * This function creates a document that is used to set the attribute of a
+     * Collection.
+     *
+     * @param uuid The identifier of the Collection.
+     * @param attr The new attribute value for the Collection.
+     *
+     * @return the document that is used to set the attribute.
+     */
+    public static Document newSetAttributeDocument(
+        String uuid,
+        Document attr)
+    {
+        Node root = attr != null ? attr.getFirstChild() : null;
+
+        if (root == null) {
+            return null;
+        }
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action     = ec.create("action");
+        Element type       = ec.create("type");
+        Element collection = ec.create("collection");
+
+        ec.addAttr(type, "name", "setattribute", false);
+        ec.addAttr(collection, "uuid", uuid, false);
+
+        doc.appendChild(action);
+        action.appendChild(type);
+        type.appendChild(collection);
+
+        collection.appendChild(doc.importNode(root, true));
+
+        return doc;
+    }
+
+    /**
+     * This function creates a document that is used to set the attribute of a
+     * CollectionItem.
+     *
+     * @param uuid The identifier of the CollectionItem.
+     * @param attr The new attribute value for the CollectionItem.
+     *
+     * @return the document that is used to set the attribute.
+     */
+    public static Document newSetItemAttributeDocument(
+        String uuid,
+        Document attr)
+    {
+        Node root = attr.getFirstChild();
+
+        if (root == null) {
+            return null;
+        }
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action   = ec.create("action");
+        Element type     = ec.create("type");
+        Element artifact = ec.create("artifact");
+
+        ec.addAttr(type, "name", "setitemattribute");
+        ec.addAttr(artifact, "uuid", uuid);
+
+        doc.appendChild(action);
+        action.appendChild(type);
+        type.appendChild(artifact);
+
+        artifact.appendChild(doc.importNode(root, true));
+
+        return doc;
+    }
+
+
+    /**
+     * This function creates a document that is used to set the time-to-live
+     * of a collection.
+     *
+     * @param ttl The ttl for the Collection.
+     *
+     * @return the document that is used to set the time-to-live.
+     */
+    public static Document newSetCollectionTTLDocument(String ttl) {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = ec.create("action");
+        Element type   = ec.create("type");
+        Element ttlEl  = ec.create("ttl");
+
+        ec.addAttr(type, "name", "settimetolive");
+        ec.addAttr(ttlEl, "value", ttl);
+
+        doc.appendChild(action);
+        action.appendChild(type);
+        type.appendChild(ttlEl);
+
+        return doc;
+    }
+
+
+    /**
+     * This function creates a document that is used to set the name of a
+     * collection.
+     *
+     * @param name The name for the Collection.
+     *
+     * @return the document that is used to set the name of a collection.
+     */
+    public static Document newSetCollectionNameDocument(String name) {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = ec.create("action");
+        Element type   = ec.create("type");
+        Element coll   = ec.create("collection");
+
+        ec.addAttr(type, "name", "setname");
+        ec.addAttr(coll, "name", name);
+
+        doc.appendChild(action);
+        action.appendChild(type);
+        type.appendChild(coll);
+
+        return doc;
+    }
+
+
+    /**
+     * This function creates a document that is used to delete an existing
+     * collection.
+     *
+     * @return the document that is used to delete an existing collection.
+     */
+    public static Document newDeleteCollectionDocument() {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = ec.create("action");
+        Element type   = ec.create("type");
+
+        ec.addAttr(type, "name", "delete");
+
+        doc.appendChild(action);
+        action.appendChild(type);
+
+        return doc;
+    }
+
+
     /**
      * Returns string value found by {@link XPATH_LABEL} relative to
      * <i>node</i>.
@@ -447,5 +820,41 @@
             XPathConstants.NODESET,
             ArtifactNamespaceContext.INSTANCE);
     }
+
+
+    public static String getMinNode(Node parent) {
+        return (String) XMLUtils.xpath(
+            parent,
+            XPATH_MIN_NODE,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    public static String getMaxNode(Node parent) {
+        return (String) XMLUtils.xpath(
+            parent,
+            XPATH_MAX_NODE,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    public static String getDefMin(Node parent) {
+        return (String) XMLUtils.xpath(
+            parent,
+            XPATH_DEF_MIN,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    public static String getDefMax(Node parent) {
+        return (String) XMLUtils.xpath(
+            parent,
+            XPATH_DEF_MAX,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.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/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/CreationFilter.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,61 @@
+package de.intevation.artifacts.common.utils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.w3c.dom.Element;
+
+public class CreationFilter
+{
+    public static class Facet {
+
+        protected String name;
+        protected String index;
+
+        public Facet() {
+        }
+
+        public Facet(String name, String index) {
+            this.name  = name;
+            this.index = index;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getIndex() {
+            return index;
+        }
+    }
+
+    protected Map<String, List<Facet>> outs;
+
+    public CreationFilter() {
+        outs = new HashMap<String, List<Facet>>();
+    }
+
+    public void add(String out, List<Facet> facets) {
+        outs.put(out, facets);
+    }
+
+    public Element toXML(XMLUtils.ElementCreator ec) {
+        Element filter = ec.create("filter");
+
+        for (Map.Entry<String, List<Facet>> entry: outs.entrySet()) {
+            Element out = ec.create("out");
+            out.setAttribute("name", entry.getKey());
+            for (Facet facet: entry.getValue()) {
+                Element f = ec.create("facet");
+                f.setAttribute("name", facet.getName());
+                f.setAttribute("index", facet.getIndex());
+                out.appendChild(f);
+            }
+            filter.appendChild(out);
+        }
+
+        return filter;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/DateUtils.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.artifacts.common.utils;
+
+import java.util.Calendar;
+import java.util.Date;
+
+
+public class DateUtils {
+
+    private DateUtils() {
+    }
+
+
+    /**
+     * This function extracts the year as int value from <i>date</i>.
+     *
+     * @param date The source date.
+     *
+     * @return the year as integer or -1 if date is empty.
+     */
+    public static int getYearFromDate(Date date) {
+        if (date == null) {
+            return -1;
+        }
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(date);
+
+        return cal.get(Calendar.YEAR);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,589 @@
+/*
+ * 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.artifacts.common.utils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.BufferedOutputStream;
+import java.nio.channels.FileChannel;
+
+import java.util.Deque;
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.log4j.Logger;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class FileTools
+{
+    private static Logger log = Logger.getLogger(FileTools.class);
+
+    public static final String DIGEST =
+        System.getProperty("artifacts.common.file.cmp.digest", "MD5");
+
+    private FileTools() {
+    }
+
+
+    public static File getDirectory(String path, String name) {
+        if (path == null || name == null) {
+            return null;
+        }
+
+        File dir = new File(path, name);
+
+        if (!dir.exists()) {
+            log.debug(
+                "Directory '" + dir.getAbsolutePath() + "' doesn't " +
+                "exist. Try to create it.");
+
+            return dir.mkdir() ? dir : null;
+        }
+        else {
+            return dir.isDirectory() ? dir : null;
+        }
+    }
+
+    public static File repair(File file) {
+        file = file.getAbsoluteFile();
+        if (file.exists()) {
+            return file;
+        }
+        Deque<String> parts = new ArrayDeque<String>();
+        File curr = file;
+        while (curr != null) {
+            String name = curr.getName();
+            if (name.length() > 0) {
+                parts.push(curr.getName());
+            }
+            curr = curr.getParentFile();
+        }
+
+        curr = null;
+        OUTER: while (!parts.isEmpty()) {
+            String f = parts.pop();
+            log.debug("fixing: '" + f + "'");
+            if (curr == null) {
+                // XXX: Not totaly correct because there
+                // more than one root on none unix systems.
+                for (File root: File.listRoots()) {
+                    File [] files = root.listFiles();
+                    if (files == null) {
+                        log.warn("cannot list '" + root);
+                        continue;
+                    }
+                    for (File candidate: files) {
+                        if (candidate.getName().equalsIgnoreCase(f)) {
+                            curr = new File(root, candidate.getName());
+                            continue OUTER;
+                        }
+                    }
+                }
+                break;
+            }
+            else {
+                File [] files = curr.listFiles();
+                if (files == null) {
+                    log.warn("cannot list: '" + curr + "'");
+                    return file;
+                }
+                for (File candidate: files) {
+                    if (candidate.getName().equalsIgnoreCase(f)) {
+                        curr = new File(curr, candidate.getName());
+                        continue OUTER;
+                    }
+                }
+                curr = null;
+                break;
+            }
+        }
+
+        if (curr == null) {
+            log.warn("cannot repair path '" + file + "'");
+            return file;
+        }
+
+        return curr;
+    }
+
+    public static class HashedFile
+    implements Comparable<HashedFile>
+    {
+        protected File    file;
+        protected long    length;
+        protected byte [] hash;
+
+        public HashedFile(File file) {
+            this.file = file;
+            length = file.length();
+        }
+
+        public File getFile() {
+            return file;
+        }
+
+        protected byte [] getHash() {
+            if (hash == null) {
+                InputStream in = null;
+
+                try {
+                    in = new FileInputStream(file);
+
+                    MessageDigest digest = MessageDigest.getInstance(DIGEST);
+
+                    byte [] buf = new byte[40*1024];
+                    int r;
+
+                    while ((r = in.read(buf)) >= 0) {
+                        digest.update(buf, 0, r);
+                    }
+
+                    hash = digest.digest();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                    hash = new byte[0];
+                }
+                catch (NoSuchAlgorithmException nsae) {
+                    log.error(nsae);
+                    hash = new byte[0];
+                }
+                finally {
+                    if (in != null) {
+                        try {
+                            in.close();
+                        }
+                        catch (IOException ioe) {
+                            log.error(ioe);
+                        }
+                    }
+                }
+            }
+            return hash;
+        }
+
+        @Override
+        public int compareTo(HashedFile other) {
+            if (length < other.length) return -1;
+            if (length > other.length) return +1;
+            return compare(getHash(), other.getHash());
+        }
+
+        private static int compare(byte [] a, byte [] b) {
+            if (a.length < b.length) return -1;
+            if (a.length > b.length) return +1;
+            for (int i = 0; i < a.length; ++i) {
+                int x = a[i] & 0xff;
+                int y = b[i] & 0xff;
+                if (x < y) return -1;
+                if (x > y) return +1;
+            }
+            return 0;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return other instanceof HashedFile
+                && ((HashedFile)other).compareTo(this) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return (int)(length ^ (length >>> 32));
+        }
+    } // class HashedFile
+
+    public static List<File> uniqueFiles(List<File> files) {
+
+        Set<HashedFile> set = new HashSet<HashedFile>();
+
+        for (File file: files) {
+            if (!set.add(new HashedFile(file))) {
+                log.warn("file '" + file + "' is a duplicate.");
+            }
+        }
+
+        ArrayList<File> out = new ArrayList<File>(set.size());
+        for (HashedFile hf: set) {
+            out.add(hf.file);
+        }
+
+        return out;
+    }
+
+    public interface FileVisitor {
+        boolean visit(File file);
+    } // Visitor
+
+    public static void walkTree(File root, FileVisitor visitor) {
+
+        Deque<File> stack = new ArrayDeque<File>();
+
+        stack.push(root);
+
+        while (!stack.isEmpty()) {
+            File current = stack.pop();
+            if (!visitor.visit(current)) break;
+            if (current.isDirectory()) {
+                File [] subs = current.listFiles();
+                if (subs != null) {
+                    for (File f: subs) {
+                        stack.push(f);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Deletes everything in a directory.
+     *
+     * @param dir The directory.
+     */
+    public final static void deleteContent(File dir) {
+        if (dir == null || !dir.isDirectory()) {
+            return;
+        }
+
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file: files) {
+                deleteRecursive(file);
+            }
+        }
+
+        return;
+    }
+
+    /**
+     * Delete <i>file</i> and everything in <i>file</i> if it is a directory.
+     *
+     * @param file The file or directory.
+     * @return true, if deletion was successful - otherwise false.
+     */
+    public final static boolean deleteRecursive(File file) {
+
+        if (file == null) {
+            return false;
+        }
+
+        if (file.isDirectory()) {
+            File [] files = file.listFiles();
+            if (files != null) {
+                for (File sub: files) {
+                    if (!deleteRecursive(sub)) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return file.delete();
+    }
+
+    /**
+     * Put the given file or directory into a zip archive.
+     *
+     * @param file The file or directory.
+     * @param outputStream The stream to write the archive to.
+     * @throws IOException if an error occured while zip creation or writing to
+     * output stream.
+     */
+    public static void createZipArchive(
+        File         file,
+        OutputStream outputStream
+    )
+    throws IOException
+    {
+        ZipOutputStream out = new ZipOutputStream(outputStream);
+
+        if (file.isFile()) {
+            copyFileToZip("", file, out);
+        }
+        else if (file.isDirectory()) {
+
+            Deque<PrefixDir> stack = new ArrayDeque<PrefixDir>();
+            stack.push(new PrefixDir(file.getName() + "/", file));
+
+            while (!stack.isEmpty()) {
+                PrefixDir pd = stack.pop();
+
+                ZipEntry dirEntry = new ZipEntry(pd.prefix);
+                out.putNextEntry(dirEntry);
+                out.closeEntry();
+
+                File [] files = pd.dir.listFiles();
+                if (files != null) {
+                    for (File sub: files) {
+                        if (sub.isDirectory()) {
+                            stack.push(new PrefixDir(
+                                pd.prefix + sub.getName() + "/",
+                                sub));
+                        }
+                        else if (sub.isFile()) {
+                            copyFileToZip(pd.prefix, sub, out);
+                        }
+                    }
+                }
+            }
+        }
+
+        out.finish();
+    }
+
+
+    public static void extractArchive(File archive, File destDir)
+    throws IOException {
+        if (!destDir.exists()) {
+            destDir.mkdir();
+        }
+
+        ZipFile zipFile = new ZipFile(archive);
+        Enumeration entries = zipFile.entries();
+
+        byte[] buffer = new byte[16384];
+        int len;
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = (ZipEntry) entries.nextElement();
+
+            String entryFileName = entry.getName();
+
+            File dir = dir = buildDirectoryHierarchyFor(entryFileName, destDir);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+
+            if (!entry.isDirectory()) {
+                BufferedOutputStream bos = new BufferedOutputStream(
+                        new FileOutputStream(new File(destDir, entryFileName)));
+
+                BufferedInputStream bis = new BufferedInputStream(zipFile
+                        .getInputStream(entry));
+
+                while ((len = bis.read(buffer)) > 0) {
+                    bos.write(buffer, 0, len);
+                }
+
+                bos.flush();
+                bos.close();
+                bis.close();
+            }
+        }
+        zipFile.close();
+    }
+
+    private static File buildDirectoryHierarchyFor(
+        String entryName,
+        File destDir)
+    {
+        int lastIndex = entryName.lastIndexOf('/');
+        String entryFileName = entryName.substring(lastIndex + 1);
+        String internalPathToEntry = entryName.substring(0, lastIndex + 1);
+        return new File(destDir, internalPathToEntry);
+    }
+
+    /**
+     * A class representing a directory with a prefix.
+     */
+    private static final class PrefixDir {
+
+        String prefix;
+        File   dir;
+
+        public PrefixDir(String prefix, File dir) {
+            this.prefix = prefix;
+            this.dir    = dir;
+        }
+
+    } // class PrefixDir
+
+    /**
+     * Write a file to zip archive.
+     *
+     * @param prefix A prefix.
+     * @param file The file.
+     * @param out The output stream.
+     * @throws IOException if an error occured while writing to zip output
+     * stream.
+     */
+    private static void copyFileToZip(
+        String          prefix,
+        File            file,
+        ZipOutputStream out
+    )
+    throws IOException
+    {
+        String   entryName = prefix + file.getName();
+        ZipEntry entry     = new ZipEntry(entryName);
+        out.putNextEntry(entry);
+        InputStream in = null;
+        try {
+            in =
+                new BufferedInputStream(
+                new FileInputStream(file), 20*1024);
+
+            byte [] buf = new byte[2048];
+
+            int r;
+            while ((r = in.read(buf)) > 0) {
+                out.write(buf, 0, r);
+            }
+        }
+        finally {
+            if (in != null) {
+                try { in.close(); }
+                catch (IOException ioe) {}
+            }
+        }
+        out.closeEntry();
+    }
+
+
+    /**
+     * Copies a <i>src</i> file to <i>target</i>.
+     *
+     * @param src A file (not a directory) that should be copied.
+     * @param target The destination. This might be a file or a directory.
+     *
+     * @return true, if <i>src</i> has been successfully copied; otherwise
+     * false.
+     */
+    public static boolean copyFile(File src, File target)
+    throws IOException
+    {
+        if (src == null || !src.exists()) {
+            log.warn("Source file does not exist!");
+            return false;
+        }
+
+        if (!src.canRead()) {
+            log.warn("Cannot read Source file!");
+            return false;
+        }
+
+        if (src.isDirectory()) {
+            log.warn("Source is a directory!");
+            return false;
+        }
+
+        if (target.isDirectory()) {
+            target = new File(target, src.getName());
+        }
+
+        FileInputStream  in  = null;
+        FileOutputStream out = null;
+
+        try {
+            in  = new FileInputStream(src);
+            out = new FileOutputStream(target);
+
+            FileChannel inChannel  = in.getChannel();
+            FileChannel outChannel = out.getChannel();
+
+            inChannel.transferTo(0l, inChannel.size(), outChannel);
+
+            return true;
+        }
+        catch (IOException ioe) {
+            log.warn(ioe, ioe);
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) { /* do nothing here */ }
+            }
+
+            if (out != null) {
+                try {
+                    out.close();
+                }
+                catch (IOException ioe) { /* do nothing here */ }
+            }
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Copies a directory <i>source</i> to a destination path <i>dest</i>.
+     *
+     * @param source A directory that should be copied.
+     * @param dest A destination directory which is created if it is not
+     * existing yet.
+     *
+     * @return true, if the directory has been successfully copied; otherwise
+     * false.
+     */
+    public static boolean copyDirectory(final File source, final File dest) {
+        if (source == null || !source.exists()) {
+            log.warn("Source directory does not exist!");
+            return false;
+        }
+
+        if (!source.isDirectory()) {
+            log.warn("Source is not a directory!");
+            return false;
+        }
+
+        if (dest == null) {
+            log.warn("Destination directory is null!");
+            return false;
+        }
+
+        if (!dest.exists()) {
+            if (!dest.mkdir()) {
+                log.warn("Cannot create destination directory!");
+                return false;
+            }
+        }
+
+        File[] children = source.listFiles();
+        int    failed   = 0;
+
+        if (children != null && children.length > 0) {
+            for (File child: children) {
+                if (child.isFile()) {
+                    try {
+                        if (!copyFile(child, dest)) {
+                            failed++;
+                        }
+                    }
+                    catch (IOException ioe) {
+                        log.warn(ioe, ioe);
+                        failed++;
+                    }
+                }
+                else if (child.isDirectory()) {
+                    copyDirectory(child, new File(dest, child.getName()));
+                }
+            }
+        }
+
+        log.debug("Failed to copy " + failed + " files.");
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,445 @@
+package de.intevation.artifacts.common.utils;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import java.io.IOException;
+import java.io.PushbackInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+
+public final class JSON
+{
+    private JSON() {
+    }
+
+    private static final boolean isDigit(int c) {
+        return c >= '0' && c <= '9';
+    }
+
+    public static final boolean isWhitespace(int c) {
+        return c == ' '  || c == '\n' || c == '\r'
+            || c == '\t' || c == '\f';
+    }
+
+    private static final void match(int c, int x) throws IOException {
+        if (c != x) {
+            throw new IOException(
+                "Expecting '" + (char)c + "' found '" + (char)x + "'");
+        }
+    }
+
+    private static final int eof(InputStream in)
+    throws IOException
+    {
+        int c = in.read();
+        if (c == -1) {
+            throw new IOException("EOF unexpected.");
+        }
+        return c;
+    }
+
+    private static final int whitespace(InputStream in)
+    throws IOException
+    {
+        int c;
+        while (isWhitespace(c = eof(in)));
+        return c;
+    }
+
+    private static final int parseHex(String hex) throws IOException {
+        try {
+            return Integer.parseInt(hex, 16);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IOException("'" + hex + "' is not a hex string.");
+        }
+    }
+
+    public static final String jsonString(String string) {
+        StringBuilder sb = new StringBuilder(string.length()+2);
+
+        sb.append('"');
+
+        for (int i = 0, N = string.length(); i < N; ++i) {
+            char c = string.charAt(i);
+            switch (c) {
+                case '"':  sb.append("\\\""); break;
+                case '\t': sb.append("\\t"); break;
+                case '\r': sb.append("\\r"); break;
+                case '\n': sb.append("\\n"); break;
+                case '\b': sb.append("\\b"); break;
+                case '\f': sb.append("\\f"); break;
+                default:
+                    if (c >= 128) {
+                        sb.append("\\u");
+                        String hex = Integer.toHexString((int)c);
+                        for (int j = 4-hex.length(); j > 0; --j) {
+                            sb.append('0');
+                        }
+                        sb.append(hex);
+                    }
+                    else {
+                        sb.append(c);
+                    }
+            }
+        }
+
+        sb.append('"');
+
+        return sb.toString();
+    }
+
+    public static String toJSONString(Map<String, Object> map) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        write(pw, map);
+        pw.flush();
+        return sw.toString();
+    }
+
+
+    public static void write(PrintWriter out, Map<String, Object> map) {
+        writeObject(out, map);
+    }
+
+    private static void writeValue(PrintWriter out, Object value) {
+        if (value instanceof Map) {
+            writeObject(out, (Map)value);
+        }
+        else if (value instanceof List) {
+            writeList(out, (List)value);
+        }
+        else if (value instanceof Number) {
+            out.print(value);
+        }
+        else if (value instanceof Boolean) {
+            out.print(((Boolean)value) ? "true" : "false");
+        }
+        else if (value == null) {
+            out.print("null");
+        }
+        else {
+            out.print(jsonString(value.toString()));
+        }
+    }
+
+    private static void writeObject(PrintWriter out, Map map) {
+
+        out.print('{');
+        Iterator iter = map.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry entry = (Map.Entry)iter.next();
+            out.print(jsonString(entry.getKey().toString()));
+            out.print(':');
+            writeValue(out, entry.getValue());
+            if (iter.hasNext()) {
+                out.print(',');
+            }
+        }
+        out.print('}');
+    }
+
+    private static void writeList(PrintWriter out, List list) {
+        out.print('[');
+        Iterator iter = list.iterator();
+        while (iter.hasNext()) {
+            writeValue(out, iter.next());
+            if (iter.hasNext()) {
+                out.print(',');
+            }
+        }
+        out.print(']');
+    }
+
+    public static Map<String, Object> parse(String in)
+    throws IOException
+    {
+        return parse(asInputStream(in));
+    }
+
+    private static InputStream asInputStream(String in) {
+        byte [] bytes;
+        try {
+            bytes = in.getBytes(Charset.forName("US-ASCII"));
+        }
+        catch (UnsupportedCharsetException uce) {
+            // Should not happen.
+            bytes = in.getBytes();
+        }
+        return new ByteArrayInputStream(bytes);
+    }
+
+    public static Map<String, Object> parse(InputStream in)
+    throws IOException
+    {
+        return parseObject(new PushbackInputStream(in, 1));
+    }
+
+    public static Map<String, Object> parse(PushbackInputStream in)
+    throws IOException
+    {
+        return parseObject(in);
+    }
+
+    private static final String parseString(
+        PushbackInputStream in
+    )
+    throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+
+        int mode = 0;
+
+        char [] hex = new char[4];
+
+        match('"', eof(in));
+
+        OUT: for (int c = eof(in);; c = eof(in)) {
+
+            switch (mode) {
+                case 0:
+                    if (c == '"') {
+                        break OUT;
+                    }
+                    if (c == '\\') {
+                        mode = 1;
+                    }
+                    else {
+                        sb.append((char)c);
+                    }
+                    break;
+                case 1:
+                    switch (c) {
+                        case 'u':
+                            mode = 2;
+                            continue;
+                        case 'b':
+                            sb.append('\b');
+                            break;
+                        case 'f':
+                            sb.append('\f');
+                            break;
+                        case 'n':
+                            sb.append('\n');
+                            break;
+                        case 'r':
+                            sb.append('\r');
+                            break;
+                        case 't':
+                            sb.append('\t');
+                            break;
+                        default:
+                            sb.append((char)c);
+                    }
+                    mode = 0;
+                    break;
+                case 2:
+                    hex[0] = (char)c;
+                    mode = 3;
+                    break;
+                case 3:
+                    hex[1] = (char)c;
+                    mode = 4;
+                    break;
+                case 4:
+                    hex[2] = (char)c;
+                    mode = 5;
+                    break;
+                case 5:
+                    hex[3] = (char)c;
+                    sb.append((char)parseHex(new String(hex)));
+                    mode = 0;
+                    break;
+            }
+        }
+        return sb.toString();
+    }
+
+    private static final Boolean parseTrue(InputStream in)
+    throws IOException
+    {
+        match('t', eof(in));
+        match('r', eof(in));
+        match('u', eof(in));
+        match('e', eof(in));
+        return Boolean.TRUE;
+    }
+
+    private static final Boolean parseFalse(InputStream in)
+    throws IOException
+    {
+        match('f', eof(in));
+        match('a', eof(in));
+        match('l', eof(in));
+        match('s', eof(in));
+        match('e', eof(in));
+        return Boolean.FALSE;
+    }
+
+    private static final Object parseNull(InputStream in)
+    throws IOException
+    {
+        match('n', eof(in));
+        match('u', eof(in));
+        match('l', eof(in));
+        match('l', eof(in));
+        return null;
+    }
+
+    private static final Number parseNumber(PushbackInputStream in)
+    throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+
+        boolean isInteger = true;
+
+        int c;
+        OUT: for (;;) {
+            switch (c = eof(in)) {
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                case '-': case '+':
+                    sb.append((char)c);
+                    break;
+                case '.': case 'e': case 'E':
+                    isInteger = false;
+                    sb.append((char)c);
+                    break;
+                default:
+                    in.unread(c);
+                    break OUT;
+            }
+        }
+
+        try {
+            if (isInteger) {
+                return sb.length() > 9
+                    ? (Number)Long   .valueOf(sb.toString())
+                    : (Number)Integer.valueOf(sb.toString());
+            }
+            return (Number)Double.valueOf(sb.toString());
+        }
+        catch (NumberFormatException nfe) {
+            throw new IOException("Not a number '" + sb + "'");
+        }
+    }
+
+    private static List<Object> parseList(PushbackInputStream in)
+    throws IOException
+    {
+        List<Object> list = new ArrayList<Object>();
+        match('[', whitespace(in));
+        int c = whitespace(in);
+        if (c == ']') {
+            return list;
+        }
+
+        for (;; c = whitespace(in)) {
+            Object value;
+            in.unread(c);
+            switch (c) {
+                case '{':
+                    value = parseObject(in);
+                    break;
+                case '[':
+                    value = parseList(in);
+                    break;
+                case '"':
+                    value = parseString(in);
+                    break;
+                case 't':
+                    value = parseTrue(in);
+                    break;
+                case 'f':
+                    value = parseFalse(in);
+                    break;
+                case 'n':
+                    value = parseNull(in);
+                    break;
+                default:
+                    value = parseNumber(in);
+            }
+            list.add(value);
+
+            if ((c = whitespace(in)) == ']') break;
+            match(',', c);
+        }
+        return list;
+    }
+
+    private static void parsePair(
+        PushbackInputStream in,
+        Map<String, Object> pairs
+    )
+    throws IOException
+    {
+        in.unread(whitespace(in));
+        String string = parseString(in);
+        match(':', whitespace(in));
+
+        Object value;
+
+        int c = whitespace(in);
+        in.unread(c);
+        switch (c) {
+            case '{':
+                value = parseObject(in);
+                break;
+            case '[':
+                value = parseList(in);
+                break;
+            case '"':
+                value = parseString(in);
+                break;
+            case 't':
+                value = parseTrue(in);
+                break;
+            case 'f':
+                value = parseFalse(in);
+                break;
+            case 'n':
+                value = parseNull(in);
+                break;
+            default:
+                value = parseNumber(in);
+        }
+        pairs.put(string, value);
+    }
+
+    private static Map<String, Object> parseObject(PushbackInputStream in)
+    throws IOException
+    {
+        Map<String, Object> pairs = new LinkedHashMap<String, Object>();
+
+        int c = whitespace(in);
+        match('{', c);
+
+        if ((c = whitespace(in)) == '}') {
+            return pairs;
+        }
+
+        in.unread(c);
+
+        for (;;) {
+            parsePair(in, pairs);
+
+            if ((c = whitespace(in)) == '}') {
+                break;
+            }
+
+            if (c == '}') break;
+            match(',', c);
+        }
+
+        return pairs;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/LRUCache.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,26 @@
+package de.intevation.artifacts.common.utils;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+
+public class LRUCache<K, V>
+extends      LinkedHashMap<K, V>
+{
+    public static final int DEFAULT_MAX_CAPACITY = 25;
+
+    private int maxCapacity;
+
+    public LRUCache() {
+        this(DEFAULT_MAX_CAPACITY);
+    }
+
+    public LRUCache(int maxCapacity) {
+        this.maxCapacity = maxCapacity;
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+        return size() > maxCapacity;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/MapXPathVariableResolver.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,48 @@
+/*
+ * 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.artifacts.common.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathVariableResolver;
+
+
+/**
+ *  @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapXPathVariableResolver implements XPathVariableResolver {
+
+    protected Map<String, String> variables;
+
+
+    public MapXPathVariableResolver() {
+        this.variables = new HashMap<String, String>();
+    }
+
+
+    public MapXPathVariableResolver(Map<String, String> variables) {
+        this.variables = variables;
+    }
+
+
+    public void addVariable(String name, String value) {
+        if (name != null && value != null) {
+            variables.put(name, value);
+        }
+    }
+
+
+    @Override
+    public Object resolveVariable(QName variableName) {
+        String key = variableName.getLocalPart();
+        return variables.get(key);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,148 @@
+/*
+ * 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.artifacts.common.utils;
+
+import java.io.UnsupportedEncodingException;
+
+import java.util.UUID;
+
+import org.apache.commons.codec.DecoderException;
+
+import org.apache.commons.codec.binary.Hex;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Commonly used string functions.
+ *
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public final class StringUtils
+{
+    private static Logger logger = Logger.getLogger(StringUtils.class);
+
+    /**
+     * Generated a random UUIDv4 in form of a string.
+     * @return the UUID
+     */
+    public static final String newUUID() {
+        return UUID.randomUUID().toString();
+    }
+
+    /**
+     * Checks if a given string is a valid UUID.
+     * @param uuid The string to test.
+     * @return true if the string is a valid UUID else false.
+     */
+    public static final boolean checkUUID(String uuid) {
+        try {
+            UUID.fromString(uuid);
+        }
+        catch (IllegalArgumentException iae) {
+            logger.warn(iae.getLocalizedMessage());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the UTF-8 byte array representation of a given string.
+     * @param s The string to be transformed.
+     * @return The byte array representation.
+     */
+    public static final byte [] getUTF8Bytes(String s) {
+        try {
+            return s.getBytes("UTF-8");
+        }
+        catch (UnsupportedEncodingException usee) {
+            logger.error(usee.getLocalizedMessage(), usee);
+            return s.getBytes();
+        }
+    }
+
+    /**
+     * Tries to convert a Base64 encoded string into the
+     * corresponing byte array.
+     * @param s The Base64 encoded string
+     * @return The byte array representation or null if
+     * an decoding error occurs.
+     */
+    public static final byte [] decodeHex(String s) {
+        try {
+            return Hex.decodeHex(s.toCharArray());
+        }
+        catch (DecoderException de) {
+            return null;
+        }
+    }
+
+    public static final String repeat(String s, int count, String sep) {
+        if (count <= 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder(s);
+        for (--count; count > 0; --count) {
+            sb.append(sep).append(s);
+        }
+        return sb.toString();
+    }
+
+    public static final String repeat(char c, int count, char sep) {
+        if (count <= 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder(2*count-1).append(c);
+        for (--count; count > 0; --count) {
+            sb.append(sep).append(c);
+        }
+        return sb.toString();
+    }
+
+    public static final String [] toUpperCase(String [] s) {
+        if (s == null) {
+            return null;
+        }
+        String [] d = new String[s.length];
+        for (int i = 0; i < s.length; ++i) {
+            if (s[i] != null) {
+                d[i] = s[i].toUpperCase();
+            }
+        }
+        return d;
+    }
+
+    public static String join(String sep, String [] strings) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < strings.length; ++i) {
+            if (i > 0) sb.append(sep);
+            sb.append(strings[i]);
+        }
+        return sb.toString();
+    }
+
+    public static final String [] join(String [] a, String [] b) {
+        if (a == null && b == null) return null;
+        if (a == null) return b;
+        if (b == null) return a;
+        String [] dst = new String[a.length + b.length];
+        System.arraycopy(a, 0, dst, 0, a.length);
+        System.arraycopy(b, 0, dst, a.length, b.length);
+        return dst;
+    }
+
+    public static final boolean contains(String needle, String [] haystack) {
+        for (String stray: haystack) {
+            if (needle.equals(stray)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java	Fri Sep 28 12:15:11 2012 +0200
@@ -8,6 +8,13 @@
 
 package de.intevation.artifacts.common.utils;
 
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
 import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.BufferedInputStream;
@@ -16,6 +23,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.StringWriter;
 
 import javax.xml.namespace.NamespaceContext;
 import javax.xml.namespace.QName;
@@ -37,12 +45,14 @@
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathExpressionException;
 import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathVariableResolver;
 
 import org.apache.log4j.Logger;
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import org.xml.sax.SAXException;
 
@@ -53,15 +63,13 @@
  */
 public final class XMLUtils
 {
-    /**
-     * W3C URL of XForms
-     */
+    /** W3C URL of XForms. */
     public static final String XFORM_URL    = "http://www.w3.org/2002/xforms";
-    /**
-     * W3C prefix of XForms
-     */
+
+    /** W3C prefix of XForms. */
     public static final String XFORM_PREFIX = "xform";
 
+    /** Logger for this class. */
     private static Logger logger = Logger.getLogger(XMLUtils.class);
 
     private XMLUtils() {
@@ -73,17 +81,13 @@
      */
     public static class ElementCreator
     {
-        /**
-         * owner document of the elements to be created
-         */
+        /** Owner document of the elements to be created. */
         protected Document document;
-        /**
-         * namespace to be used
-         */
+
+        /** Namespace to be used. */
         protected String   ns;
-        /**
-         * prefix to be used
-         */
+
+        /** Prefix to be used. */
         protected String   prefix;
 
         /**
@@ -148,7 +152,6 @@
             else {
                 element.setAttribute(name, value);
             }
-
         }
     } // class ElementCreator
 
@@ -170,6 +173,20 @@
         return null;
     }
 
+
+    /**
+     * Create xml/string representation of element (nested in otherwise empty
+     * document).
+     * @param element element to inspect in string.
+     * @return string with xml representation of element.
+     */
+    public final static String toString(Node node) {
+        Document doc = newDocument();
+        doc.appendChild(doc.importNode(node,true));
+        return toString(doc);
+    }
+
+
     /**
      * Loads a XML document namespace aware from a file
      * @param file The file to load.
@@ -194,9 +211,31 @@
         return null;
     }
 
+    /**
+     * Parses a String to a xml document.
+     *
+     * @param string The xml string
+     * @return the XML document or null if something went wrong.
+     */
+    public static final Document parseDocument(String string) {
+        InputStream inputStream = new ByteArrayInputStream(string.getBytes());
+        return parseDocument(inputStream);
+    }
+
+
     public static final Document parseDocument(InputStream inputStream) {
+        return parseDocument(inputStream, Boolean.TRUE);
+    }
+
+    public static final Document parseDocument(
+        InputStream inputStream,
+        Boolean     namespaceAware
+    ) {
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-        factory.setNamespaceAware(true);
+
+        if (namespaceAware != null) {
+            factory.setNamespaceAware(namespaceAware.booleanValue());
+        }
 
         try {
             return factory.newDocumentBuilder().parse(inputStream);
@@ -218,7 +257,7 @@
      * @return the new XPath.
      */
     public static final XPath newXPath() {
-        return newXPath(null);
+        return newXPath(null, null);
     }
 
     /**
@@ -227,12 +266,19 @@
      * if none should be used.
      * @return The new XPath
      */
-    public static final XPath newXPath(NamespaceContext namespaceContext) {
+    public static final XPath newXPath(
+        NamespaceContext      namespaceContext,
+        XPathVariableResolver resolver)
+    {
         XPathFactory factory = XPathFactory.newInstance();
         XPath        xpath   = factory.newXPath();
         if (namespaceContext != null) {
             xpath.setNamespaceContext(namespaceContext);
         }
+
+        if (resolver != null) {
+            xpath.setXPathVariableResolver(resolver);
+        }
         return xpath;
     }
 
@@ -290,12 +336,26 @@
         QName            returnType,
         NamespaceContext namespaceContext
     ) {
+        return xpath(root, query, returnType, namespaceContext, null);
+    }
+
+    public static final Object xpath(
+        Object           root,
+        String           query,
+        QName            returnType,
+        NamespaceContext namespaceContext,
+        Map<String, String> variables)
+    {
         if (root == null) {
             return null;
         }
 
+        XPathVariableResolver resolver = variables != null
+            ? new MapXPathVariableResolver(variables)
+            : null;
+
         try {
-            XPath xpath = newXPath(namespaceContext);
+            XPath xpath = newXPath(namespaceContext, resolver);
             if (xpath != null) {
                 return xpath.evaluate(query, root, returnType);
             }
@@ -335,27 +395,223 @@
         return false;
     }
 
+    public static String toString(Document document) {
+        try {
+            Transformer transformer =
+                TransformerFactory.newInstance().newTransformer();
+            DOMSource    source = new DOMSource(document);
+            StringWriter out    = new StringWriter();
+            StreamResult result = new StreamResult(out);
+            transformer.transform(source, result);
+            out.flush();
+            return out.toString();
+        }
+        catch (TransformerConfigurationException tce) {
+            logger.error(tce.getLocalizedMessage(), tce);
+        }
+        catch (TransformerFactoryConfigurationError tfce) {
+            logger.error(tfce.getLocalizedMessage(), tfce);
+        }
+        catch (TransformerException te) {
+            logger.error(te.getLocalizedMessage(), te);
+        }
+
+        return null;
+    }
+
+    public static byte [] toByteArray(Document document) {
+        return toByteArray(document, false);
+    }
+
     /**
      * Transforms an XML document into a byte array.
      * @param document The document to be streamed out.
+     * @param compress The document should be compressed, too.
      * @return the byte array or null if operation failed or
      * document is null.
      */
-    public static byte [] toByteArray(Document document) {
+    public static byte [] toByteArray(Document document, boolean compress) {
+        if (document != null) {
+            try {
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                OutputStream out = compress
+                    ? new GZIPOutputStream(baos)
+                    : baos;
+                boolean success = toStream(document, out);
+                out.flush();
+                out.close();
+                return success
+                    ? baos.toByteArray()
+                    : null;
+            }
+            catch (IOException ioe) {
+                logger.error(ioe);
+            }
+        }
+        return null;
+    }
+
+    public static Document fromByteArray(byte [] data) {
+        return fromByteArray(data, false);
+    }
+
+    public static Document fromByteArray(byte [] data, boolean decompress) {
+        if (data != null) {
+            InputStream in = new ByteArrayInputStream(data);
+            try {
+                if (decompress) {
+                    in = new GZIPInputStream(in);
+                }
+                return parseDocument(in);
+            }
+            catch (IOException ioe) {
+                logger.error(ioe);
+            }
+            finally {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) {
+                    logger.error(ioe);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static class BuildResult {
+        List<Node>          children;
+        Map<String, String> attributes;
+        BuildResult() {
+            children   = new ArrayList<Node>();
+            attributes = new LinkedHashMap<String, String>();
+        }
+
+        void setAttributes(Element element) {
+            for (Map.Entry<String, String> entry: attributes.entrySet()) {
+                element.setAttribute(entry.getKey(), entry.getValue());
+            }
+        }
+
+        void finish(Element element) {
+            setAttributes(element);
+            for (Node child: children) {
+                element.appendChild(child);
+            }
+        }
+
+        void add(Node node) {
+            children.add(node);
+        }
+
+        void add(String key, Object value) {
+            attributes.put(key, value != null ? value.toString() : "null");
+        }
+
+        int numChildren() {
+            return children.size();
+        }
+
+        Node firstChild() {
+            return children.get(0);
+        }
+    } // class BuildResult
+
+    private static BuildResult recursiveBuild(
+        List     list,
+        Document document
+    ) {
+        BuildResult result = new BuildResult();
+        for (Object entry: list) {
+            if (entry instanceof Map) {
+                BuildResult subResult = recursiveBuild(
+                    (Map<String, Object>)entry, document);
+                if (subResult.numChildren() == 1) {
+                    result.add(subResult.firstChild());
+                }
+                else {
+                    Element element = document.createElement("map");
+                    subResult.finish(element);
+                    result.add(element);
+                }
+            }
+            else if (entry instanceof List) {
+                Element element = document.createElement("list");
+                BuildResult subResult = recursiveBuild((List)entry, document);
+                subResult.finish(element);
+                result.add(element);
+            }
+            else {
+                Element element = document.createElement("entry");
+                element.setAttribute(
+                    "value",
+                    entry != null ? entry.toString() : "null");
+            }
+        }
+        return result;
+    }
+
+    private static BuildResult recursiveBuild(
+        Map<String, Object> map,
+        Document            document
+    ) {
+        BuildResult result = new BuildResult();
+
+        List<Node> nodes = new ArrayList<Node>();
+        for (Map.Entry<String, Object> entry: map.entrySet()) {
+            Object value = entry.getValue();
+            if (value instanceof Map) {
+                Element element = document.createElement(entry.getKey());
+                BuildResult subResult = recursiveBuild(
+                    (Map<String, Object>)value, document);
+                subResult.finish(element);
+                result.add(element);
+            }
+            else if (value instanceof List) {
+                Element element = document.createElement(entry.getKey());
+                BuildResult subResult = recursiveBuild((List)value, document);
+                subResult.finish(element);
+                result.add(element);
+            }
+            else {
+                result.add(entry.getKey(), value);
+            }
+        }
+        return result;
+    }
+
+    public static Document jsonToXML(String input) {
+        Document document = newDocument();
+
         if (document == null) {
             return null;
         }
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        return toStream(document, baos)
-            ? baos.toByteArray()
-            : null;
-    }
 
-    public static Document fromByteArray(byte [] data) {
-        if (data == null) {
+        Map<String, Object> map;
+        try {
+            map = JSON.parse(input);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe);
             return null;
         }
-        return parseDocument(new ByteArrayInputStream(data));
+
+        BuildResult roots = recursiveBuild(map, document);
+
+        int N = roots.children.size();
+
+        if (N == 1) {
+            document.appendChild(roots.children.get(0));
+        }
+        else if (N > 1) {
+            Node root = document.createElement("root");
+            for (int i = 0; i < N; ++i) {
+                root.appendChild(roots.children.get(i));
+            }
+            document.appendChild(root);
+        }
+
+        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/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XSLTransformer.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,73 @@
+package de.intevation.artifacts.common.utils;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+
+import javax.xml.transform.dom.DOMSource;
+
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+
+public class XSLTransformer {
+
+    private static Logger log = Logger.getLogger(XSLTransformer.class);
+
+    protected Map<String, Object> parameters;
+
+    public XSLTransformer() {
+    }
+
+    public String transform(Node source, InputStream transform) {
+
+        try {
+            Source templateSource = new StreamSource(transform);
+            TransformerFactory xformFactory =
+                TransformerFactory.newInstance();
+            Transformer transformer =
+                xformFactory.newTransformer(templateSource);
+
+            if (parameters != null) {
+                for (Map.Entry<String, Object> entry: parameters.entrySet()) {
+                    transformer.setParameter(entry.getKey(), entry.getValue());
+                }
+            }
+
+            StringWriter result = new StringWriter();
+
+            DOMSource    src = new DOMSource(source);
+            StreamResult dst = new StreamResult(result);
+            transformer.transform(src, dst);
+
+            return result.toString();
+        }
+        catch (TransformerConfigurationException tce) {
+            log.error(tce, tce);
+        }
+        catch (TransformerException te) {
+            log.error(te, te);
+        }
+
+        return null;
+    }
+
+    public void addParameter(String key, Object value) {
+        if (parameters == null) {
+            parameters = new HashMap<String, Object>();
+        }
+        parameters.put(key, value);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/pom.xml	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/pom.xml	Fri Sep 28 12:15:11 2012 +0200
@@ -18,6 +18,15 @@
   <build>
     <plugins>
       <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <version>2.0.2</version>
+        <configuration>
+          <source>1.6</source>
+          <target>1.6</target>
+        </configuration>
+      </plugin>
+      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>buildnumber-maven-plugin</artifactId>
         <executions>
--- a/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Artifact.java	Fri Sep 28 12:15:11 2012 +0200
@@ -34,7 +34,7 @@
  *
  * There are two more methods involved with the life cycle of the are:
  * <ol>
- *   <li>{@link #setup(String, ArtifactFactory, Object, Document) setup()}:
+ *   <li>{@link #setup(String, ArtifactFactory, Object, CallMeta, Document) setup()}:
  *   Called after created by the factory.</li>
  *   <li>{@link #endOfLife(Object) endOfLife()}: Called when the artifact
  *                                               is going to be removed from
@@ -104,8 +104,24 @@
     throws IOException;
 
     /**
+     * Produce output for this artifact.
+     * @param type Specifies the type of the output.
+     * @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.
+     */
+    void out(
+        String       type,
+        Document     format,
+        OutputStream out,
+        CallContext  context)
+    throws IOException;
+
+    /**
      * When created by a factory this method is called to
      * initialize the artifact.
+     *
      * @param identifier The identifier from artifact database
      * @param factory    The factory which created this artifact.
      * @param context    The global context of the runtime system.
@@ -116,6 +132,7 @@
         String          identifier,
         ArtifactFactory factory,
         Object          context,
+        CallMeta        callMeta,
         Document        data);
 
     /**
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactCollection.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactCollection.java	Fri Sep 28 12:15:11 2012 +0200
@@ -39,6 +39,10 @@
 
     void setCreationTime(Date creationTime);
 
+    long getTTL();
+
+    void setTTL(long ttl);
+
     Document getAttribute();
 
     void setAttribute(Document attribute);
@@ -68,6 +72,7 @@
         String                    identifier,
         String                    name,
         Date                      creationTime,
+        long                      ttl,
         ArtifactCollectionFactory factory,
         Object                    context,
         Document                  data);
@@ -110,12 +115,14 @@
 
     /**
      * Produce output for this collection.
+     * @param type Specifies the output type of the action.
      * @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.
      */
     void out(
+        String       type,
         Document     format,
         OutputStream out,
         CallContext  context)
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactCollectionFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactCollectionFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -36,10 +36,11 @@
      * @return a new {@linkplain de.intevation.artifacts.ArtifactCollection ArtifactCollection}
      */
     ArtifactCollection createCollection(
-        String   identifier, 
+        String   identifier,
         String   name,
         Date     creationTime,
-        Document data, 
+        long     ttl,
+        Document data,
         Object   context);
 
     /**
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactContextFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactContextFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -24,6 +24,6 @@
      *   {@link de.intevation.artifacts.ArtifactFactory#createArtifact(String, Object, Document) createArtifact()}
      *   {@link de.intevation.artifacts.Artifact Artifact}
      */
-    Object createArtifactContext(Document config);
+    GlobalContext createArtifactContext(Document config);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Fri Sep 28 12:15:11 2012 +0200
@@ -13,6 +13,8 @@
 
 import org.w3c.dom.Document;
 
+import java.util.Date;
+
 /**
  * Interface of an artifact managing database.
  *
@@ -73,6 +75,13 @@
     ) throws ArtifactDatabaseException;
 
     /**
+     * Used to retrieve an artifact.<b>NOTE: artifact modifications are not
+     * persisted to database!</b>
+     */
+    Artifact getRawArtifact(String identifier)
+    throws ArtifactDatabaseException;
+
+    /**
      * Returns the describe document of artifact identified
      * with the string 'artifact'.
      * @param artifact The identifier of the artifact.
@@ -129,6 +138,27 @@
     DeferredOutput out(String artifact, Document format, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
+
+    /**
+     * Produces output for a given artifact identified by 'artifact' in
+     * a requested format 'format'. The writing of the data is done when
+     * the write() method of the returned DeferredOutput is called. This
+     * optimizes the out streaming of the data because the call can be
+     * deferred into to the calling context.
+     * @param artifact The identifier of the artifact.
+     * @param format The request format of the output.
+     * @param callMeta The meta information (language et. al.) of the output.
+     * @return The deferred output to be written later in the calling context.
+     * @throws ArtifactDatabaseException Thrown if something went wrong during
+     * producing the output.
+     */
+    DeferredOutput out(
+        String   artifact,
+        String   type,
+        Document format,
+        CallMeta callMeta)
+    throws ArtifactDatabaseException;
+
     /**
      * Produces an extenal represention of the artifact identified by
      * 'artifact' to be re-imported by #importArtifact(Document, CallMeta)
@@ -177,7 +207,7 @@
      * @throws ArtifactDatabaseException Thrown if someting went wrong during
      * the service processing.
      */
-    Document process(String service, Document input, CallMeta callMeta)
+    Service.Output process(String service, Document input, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
     // User API
@@ -185,6 +215,9 @@
     Document listUsers(CallMeta callMeta)
         throws ArtifactDatabaseException;
 
+    Document findUser(Document data, CallMeta callMeta)
+        throws ArtifactDatabaseException;
+
     Document createUser(Document data, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
@@ -193,6 +226,9 @@
 
     // Collection API
 
+    Document getCollectionsMasterArtifact(String collectionId, CallMeta meta)
+        throws ArtifactDatabaseException;
+
     Document listCollections(String userId, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
@@ -206,10 +242,19 @@
     Document describeCollection(String collectionId, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
-    Document getCollectionAttribute(String collectionId, String artifactId,
+    Document getCollectionAttribute(String collectionId, CallMeta callMeta)
+    throws ArtifactDatabaseException;
+
+    Document setCollectionAttribute(
+        String   collectionId,
+        CallMeta callMeta,
+        Document attribute)
+    throws ArtifactDatabaseException;
+
+    Document getCollectionItemAttribute(String collectionId, String artifactId,
         CallMeta callMeta) throws ArtifactDatabaseException;
 
-    Document setCollectionAttribute(String collectionId, String artifactId,
+    Document setCollectionItemAttribute(String collectionId, String artifactId,
         Document attribute, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
@@ -225,5 +270,29 @@
     DeferredOutput outCollection(String collectionId,
         Document format, CallMeta callMeta)
         throws ArtifactDatabaseException;
+
+    DeferredOutput outCollection(String collectionId, String type,
+        Document format, CallMeta callMeta)
+        throws ArtifactDatabaseException;
+
+    Document setCollectionTTL(String collectionId, Document doc, CallMeta meta)
+    throws ArtifactDatabaseException;
+
+    Document setCollectionName(String collectionId, Document doc, CallMeta meta)
+    throws ArtifactDatabaseException;
+
+    public interface ArtifactLoadedCallback {
+        void artifactLoaded(
+            String   userId,
+            String   collectionId,
+            String   collectionName,
+            Date     collectionCreated,
+            String   artifactId,
+            Date     artifactCreated,
+            Artifact artifact);
+    };
+
+    public void loadAllArtifacts(ArtifactLoadedCallback callback)
+        throws ArtifactDatabaseException;
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -13,6 +13,7 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
+
 /**
  * Interface of an artifact producing factory.
  *
@@ -40,7 +41,11 @@
      * @param data  the data containing more details for the setup of an Artifact.
      * @return a new {@linkplain de.intevation.artifacts.Artifact Artifact}
      */
-    Artifact createArtifact(String identifier, Object context, Document data);
+    Artifact createArtifact(
+        String        identifier,
+        GlobalContext context,
+        CallMeta      callMeta,
+        Document      data);
 
     /**
      * Setup the factory with a given configuration
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -9,6 +9,9 @@
 package de.intevation.artifacts;
 
 import java.util.Iterator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
 
 import javax.xml.XMLConstants;
 
@@ -39,10 +42,25 @@
     public static final ArtifactNamespaceContext INSTANCE =
         new ArtifactNamespaceContext();
 
+    protected Map<String, String> map;
+
     /**
      * The default constructor.
      */
     public ArtifactNamespaceContext() {
+        map = new HashMap<String, String>();
+        map.put(
+            XMLConstants.XML_NS_PREFIX, XMLConstants.XML_NS_URI);
+        map.put(
+            XMLConstants.DEFAULT_NS_PREFIX, XMLConstants.DEFAULT_NS_PREFIX);
+        map.put(
+            XMLConstants.XMLNS_ATTRIBUTE, XMLConstants.XMLNS_ATTRIBUTE_NS_URI);
+        map.put(
+            NAMESPACE_PREFIX, NAMESPACE_URI);
+    }
+
+    public void add(String prefix, String uri) {
+        map.put(prefix, uri);
     }
 
     /**
@@ -50,41 +68,51 @@
      * @param prefix The prefix
      * @return The corresponing URI
      */
+    @Override
     public String getNamespaceURI(String prefix) {
 
         if (prefix == null) {
-            throw new NullPointerException("Null prefix");
+            throw new IllegalArgumentException("Null prefix");
         }
 
-        if (NAMESPACE_PREFIX.equals(prefix)) {
-            return NAMESPACE_URI;
-        }
+        String namespace = map.get(prefix);
 
-        if ("xml".equals(prefix)) {
-            return XMLConstants.XML_NS_URI;
-        }
-
-        return XMLConstants.NULL_NS_URI;
+        return namespace != null ? namespace : XMLConstants.NULL_NS_URI;
     }
 
     /**
      * @see javax.xml.namespace.NamespaceContext#getPrefix(String)
      * @param uri The URI
-     * @return nothing.
-     * @throws java.lang.UnsupportedOperationException
      */
+    @Override
     public String getPrefix(String uri) {
-        throw new UnsupportedOperationException();
+
+        if (uri == null) {
+            throw new IllegalArgumentException("Null uri");
+        }
+
+        for (Map.Entry<String, String> entry: map.entrySet()) {
+            if (entry.getValue().equals(uri)) {
+                return entry.getKey();
+            }
+        }
+
+        return XMLConstants.DEFAULT_NS_PREFIX;
     }
 
     /**
      * @see javax.xml.namespace.NamespaceContext#getPrefixes(java.lang.String)
      * @param uri The URI
-     * @return nothing
-     * @throws java.lang.UnsupportedOperationException
      */
+    @Override
     public Iterator getPrefixes(String uri) {
-        throw new UnsupportedOperationException();
+        ArrayList<String> results = new ArrayList<String>();
+        for (Map.Entry<String, String> entry: map.entrySet()) {
+            if (entry.getValue().equals(uri)) {
+                results.add(entry.getKey());
+            }
+        }
+        return results.iterator();
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/CallContext.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/CallContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -8,6 +8,12 @@
 
 package de.intevation.artifacts;
 
+import java.util.LinkedList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
 /**
  * Instances of this interface are given to feed(), advance(), describe()
  * and out() to enable the artifact to communicate with the runtime system.
@@ -15,6 +21,12 @@
  */
 public interface CallContext
 {
+    interface Listener {
+        void setup(Document config, Node listenerNode);
+        void init(CallContext callContext);
+        void close(CallContext callContext);
+    }
+
     /**
      * Constant to signal that nothing should be done
      * with the artifact after method return.
@@ -56,6 +68,26 @@
     void afterBackground(int action);
 
     /**
+     * Returns true, if the object forked a background thread and has not
+     * finished it yet.
+     */
+    boolean isInBackground();
+
+    /**
+     * Adds a background message for the current Artifact or Collection.
+     *
+     * @param msg The message.
+     */
+    void addBackgroundMessage(Message msg);
+
+    /**
+     * Returns the background messages of the current Artifact or Collection.
+     *
+     * @return the list of background messages.
+     */
+    LinkedList<Message> getBackgroundMessages();
+
+    /**
      * Access to the global context of the runtime system.
      * @return The global context.
      */
@@ -78,7 +110,7 @@
      * Each call context has a clipboard.
      * getContextValue is used to fetch data from this board.
      * @param key Key of the requested item.
-     * @return The value stored for the secified value, null if
+     * @return The value stored for the specified value, null if
      *         no item with this key exists.
      */
     Object getContextValue(Object key);
@@ -97,5 +129,17 @@
      * @return The time to live of the current artifact.
      */
     Long getTimeToLive();
+
+    /**
+     * Get a list of DataProvider that get provide 'key' type of data to
+     * other facets.
+     */
+    public List<DataProvider> getDataProvider(Object key);
+
+    /**
+     * Register a DataProvider that can provide 'key' type of data to
+     * other facets.
+     */
+    public Object registerDataProvider(Object key, DataProvider provider);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/DataProvider.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.artifacts;
+
+/**
+ * DataProviders register on a Blackboard with a key (basically shouting
+ * "I can or know X!").
+ *
+ * Consumers look at the blackboard and then consume data from these
+ * DataProvider, passing them (optional) parameterization and the blackboard
+ * itself.
+ *
+ * Through the blackboard-passing-when-consuming, also recursive patterns can
+ * be modelled (but take care, there is no in-built cycle detection).
+ */
+public interface DataProvider {
+    /** Register this DataProvider on a blackboard under a key. */
+    public void register(CallContext blackboard);
+
+    /** Provide data, given parameterization and a "blackboard". */
+    public Object provideData(Object key, Object param, CallContext 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/artifacts/src/main/java/de/intevation/artifacts/GlobalContext.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,23 @@
+package de.intevation.artifacts;
+
+public interface GlobalContext {
+
+    /**
+     * 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.
+     */
+    Object get(Object 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.
+     */
+    Object put(Object key, Object value);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Hook.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.artifacts;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+public interface Hook {
+
+    void setup(Node config);
+
+    void execute(Artifact artifact, CallContext context, Document 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/artifacts/src/main/java/de/intevation/artifacts/Message.java	Fri Sep 28 12:15:11 2012 +0200
@@ -0,0 +1,20 @@
+/*
+ * 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.artifacts;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Message extends Serializable {
+
+    String getText();
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/Service.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Service.java	Fri Sep 28 12:15:11 2012 +0200
@@ -20,14 +20,20 @@
 public interface Service
 extends          Serializable
 {
+    interface Output {
+        Object getData();
+
+        String getMIMEType();
+    } // interface Output
+
     /**
      * Processes some input XML document
      * @param data The input data
      * @param globalContext The global context of the artifact database.
      * @param callMeta The call meta contex, e.g. preferred languages.
-     * @return The result output XML document.
+     * @return The result.
      */
-    Document process(Document data, Object globalContext, CallMeta callMeta);
+    Output process(Document data, GlobalContext globalContext, CallMeta callMeta);
 
     /**
      * Setup the concrete processing service. This is done at startup time
@@ -35,6 +41,6 @@
      * @param factory The service factory which created this service.
      * @param globalContext The global context of the artifact database.
      */
-    void setup(ServiceFactory factory, Object globalContext);
+    void setup(ServiceFactory factory, GlobalContext globalContext);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/ServiceFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ServiceFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -39,7 +39,7 @@
      * @param globalContext The global context of the artifact database.
      * @return The created service.
      */
-    Service createService(Object globalContext);
+    Service createService(GlobalContext globalContext);
 
     /**
      * Configures this factory. This is called before
--- a/artifacts/src/main/java/de/intevation/artifacts/User.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/User.java	Fri Sep 28 12:15:11 2012 +0200
@@ -25,5 +25,7 @@
     void setRole(Document role);
 
     Document getRole();
+
+    String getAccount();
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/de/intevation/artifacts/UserFactory.java	Wed Mar 30 13:35:37 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/UserFactory.java	Fri Sep 28 12:15:11 2012 +0200
@@ -14,6 +14,7 @@
 {
     void setup(Document config, Node factoryNode);
 
-    User createUser(String identifier, String name, Document role, Object context);
+    User createUser(String identifier, String name, String account,
+                    Document role, Object context);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/pom.xml	Wed Mar 30 13:35:37 2011 +0000
+++ b/pom.xml	Fri Sep 28 12:15:11 2012 +0200
@@ -27,8 +27,8 @@
         <artifactId>maven-compiler-plugin</artifactId>
         <version>2.0.2</version>
         <configuration>
-          <source>1.5</source>
-          <target>1.5</target>
+          <source>1.6</source>
+          <target>1.6</target>
         </configuration>
       </plugin>
       <plugin>
@@ -69,10 +69,15 @@
       <name>Public online Restlet repository</name>
       <url>http://maven.restlet.org</url>
     </repository>
+    <repository>
+      <id>maven-jetty</id>
+      <name>Public jetty repository</name>
+      <url>http://oss.sonatype.org/content/groups/jetty</url>
+    </repository>
   </repositories>
   <modules>
     <module>artifacts</module>
     <module>artifact-database</module>
     <module>artifacts-common</module>
   </modules>
-</project>
\ No newline at end of file
+</project>

http://dive4elements.wald.intevation.org