changeset 428:d01e9f6a3dc1

dummy merge for repo head
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:15:12 +0200
parents 2c2981e53d4e (diff) c53ec9fdc758 (current diff)
children 48ae19f19f79
files
diffstat 64 files changed, 3316 insertions(+), 168 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Sep 19 13:55:26 2011 +0000
+++ b/ChangeLog	Fri Sep 28 12:15:12 2012 +0200
@@ -1,3 +1,624 @@
+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
--- a/artifact-database/.settings/org.eclipse.jdt.core.prefs	Mon Sep 19 13:55:26 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/doc/schema-h2.sql	Fri Sep 28 12:15:12 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
 );
 
--- a/artifact-database/doc/schema-pg.sql	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/doc/schema-pg.sql	Fri Sep 28 12:15:12 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
 );
 
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java	Fri Sep 28 12:15:12 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.
      */
@@ -40,15 +48,20 @@
     /**
      * Map to act like a clipboard when nesting calls like a proxy artifact.
      */
-    protected HashMap customValues;
+    protected Map customValues;
+
+    /**
+     * Map to act like a clipboard when nesting calls like a proxy artifact.
+     */
+    protected Map<Object, List<DataProvider>> dataProviders;
 
 
     /**
      * The default constructor of this abstract CallContext.
      *
+     * @param artifactDatabase The artifact database.
      * @param action The action.
      * @param callMeta The CallMeta object.
-     * @param context The global context.
      */
     public AbstractCallContext(
         ArtifactDatabaseImpl artifactDatabase,
@@ -101,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/ArtifactDatabaseImpl.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java	Fri Sep 28 12:15:12 2012 +0200
@@ -197,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";
@@ -205,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
 
     /**
@@ -444,6 +456,12 @@
      */
     protected List<Hook> postAdvanceHooks;
 
+    /**
+     * Hooks that are executed after an artifact's describe() operation was
+     * called.
+     */
+    protected List<Hook> postDescribeHooks;
+
     protected List<LifetimeListener> lifetimeListeners;
 
     /**
@@ -511,6 +529,10 @@
         this.postAdvanceHooks = postAdvanceHooks;
     }
 
+    public void setPostDescribeHook(List<Hook> postDescribeHooks) {
+        this.postDescribeHooks = postDescribeHooks;
+    }
+
     /**
      * Used to extract the artifact collection factory from bootstrap.
      *
@@ -558,6 +580,7 @@
     protected void setupHooks(FactoryBootstrap bootstrap) {
         setPostFeedHook(bootstrap.getPostFeedHooks());
         setPostAdvanceHook(bootstrap.getPostAdvanceHooks());
+        setPostDescribeHook(bootstrap.getPostDescribeHooks());
     }
 
     protected void setupBackendListeners(FactoryBootstrap bootstrap) {
@@ -767,6 +790,8 @@
     )
     throws ArtifactDatabaseException
     {
+        logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory "
+             + factoryName);
         ArtifactFactory factory = getArtifactFactory(factoryName);
 
         if (factory == null) {
@@ -845,7 +870,16 @@
             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();
@@ -1140,7 +1174,7 @@
         return serviceNamesAndDescription;
     }
 
-    public Document process(
+    public Service.Output process(
         String   serviceName,
         Document input,
         CallMeta callMeta
@@ -1158,6 +1192,7 @@
 
     // User API
 
+    /** Returns user(s) elements. */
     public Document listUsers(CallMeta callMeta)
         throws ArtifactDatabaseException
     {
@@ -1183,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;
     }
@@ -1218,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,
@@ -1234,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);
@@ -1287,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
     {
@@ -1769,12 +1924,6 @@
             throw new ArtifactDatabaseException(NO_SUCH_COLLECTION);
         }
 
-        CollectionCallContext cc = new CollectionCallContext(
-            ArtifactDatabaseImpl.this,
-            CallContext.NOTHING,
-            callMeta,
-            c);
-
         return new DeferredCollectionOutputImpl(c, type, format, callMeta);
     }
 
@@ -1793,7 +1942,7 @@
     }
 
     @Override
-    public void loadAllArtifacts(ArtifactLoadedCallback callback) 
+    public void loadAllArtifacts(ArtifactLoadedCallback callback)
         throws ArtifactDatabaseException
     {
         logger.debug("loadAllArtifacts");
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java	Fri Sep 28 12:15:12 2012 +0200
@@ -96,6 +96,7 @@
     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;
@@ -115,6 +116,7 @@
     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;
@@ -310,6 +312,7 @@
         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");
@@ -332,6 +335,7 @@
         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");
@@ -807,6 +811,7 @@
 
     public User createUser(
         final String      name,
+        final String      account,
         final Document    role,
         final UserFactory factory,
         final Object      context
@@ -836,19 +841,20 @@
                 stmnt.setInt(1, id);
                 stmnt.setString(2, identifier);
                 stmnt.setString(3, name);
+                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;
             }
         };
@@ -963,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, 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;
             }
         };
@@ -991,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, true);
                     User user = factory.createUser(
-                        identifier, name, role, context);
+                        identifier, name, account, role, context);
                     users.add(user);
                 }
                 return true;
@@ -1258,6 +1303,35 @@
     }
 
 
+    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 + "'");
@@ -1277,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);
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConfig.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConfig.java	Fri Sep 28 12:15:12 2012 +0200
@@ -57,7 +57,7 @@
     }
 
     private static DBConfig createInstance() {
-        
+
         String driver = Config.getStringXPath(
             DB_DRIVER, DBConnection.DEFAULT_DRIVER);
 
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java	Fri Sep 28 12:15:12 2012 +0200
@@ -171,7 +171,6 @@
      * 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 setLockedIdsProvider(LockedIdsProvider lockedIdsProvider) {
         this.lockedIdsProvider = lockedIdsProvider;
@@ -219,7 +218,7 @@
         }
     } // class IdIdentifier
 
-    private static final class IdData 
+    private static final class IdData
     extends IdIdentifier
     {
         byte [] data;
@@ -227,7 +226,7 @@
 
         public IdData(
             int     id,
-            String  factoryName, 
+            String  factoryName,
             byte [] data,
             String  identifier
         ) {
@@ -307,6 +306,7 @@
             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();
             }
@@ -355,17 +355,23 @@
                         idData.factoryName, idData.data);
                     idData.data = null;
 
+                    logger.debug("Prepare Artifact (id="
+                        + idData.id + ") for deletion.");
+
                     stmnt.setInt(1, idData.id);
                     stmnt.execute();
                     connection.commit();
 
                     try {
                         if (artifact != null) {
+                            logger.debug("Call endOfLife for Artifact: "
+                                + artifact.identifier());
+
                             artifact.endOfLife(context);
                         }
                     }
                     catch (Exception e) {
-                        logger.error(e.getLocalizedMessage(), e);
+                        logger.error(e.getMessage(), e);
                     }
 
                     deletedArtifacts.add(idData.identifier);
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java	Fri Sep 28 12:15:12 2012 +0200
@@ -36,6 +36,7 @@
      */
     protected String identifier;
 
+
     /**
      * Default constructor.
      */
@@ -58,6 +59,7 @@
         return this.identifier;
     }
 
+
     public String hash() {
         String hash = String.valueOf(hashCode());
         if (logger.isDebugEnabled()) {
@@ -113,8 +115,12 @@
         }
     }
 
-    public void setup(String identifier, ArtifactFactory factory,
-                      Object context, CallMeta callMeta, Document data) {
+    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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java	Fri Sep 28 12:15:12 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;
 
     /**
@@ -151,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;
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java	Fri Sep 28 12:15:12 2012 +0200
@@ -103,8 +103,6 @@
 
     /**
      * Returns the time to live of the given artifact.
-     *
-     * @param artifact
      */
     public Long timeToLiveUntouched(
         ArtifactCollection collection,
@@ -134,7 +132,13 @@
             ArtifactCollection collection =
                 (ArtifactCollection) clazz.newInstance();
 
-            collection.setup(identifier, name, creationTime, ttl, 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java	Fri Sep 28 12:15:12 2012 +0200
@@ -30,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/DefaultService.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java	Fri Sep 28 12:15:12 2012 +0200
@@ -8,8 +8,6 @@
 
 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;
@@ -30,14 +28,38 @@
 {
     private static Logger logger = Logger.getLogger(DefaultService.class);
 
+    public static class Output implements Service.Output {
+
+        protected Object data;
+        protected String mimeType;
+
+        public Output() {
+        }
+
+        public Output(Object data, String mimeType) {
+            this.data     = data;
+            this.mimeType = mimeType;
+        }
+
+        @Override
+        public Object getData() {
+            return data;
+        }
+
+        @Override
+        public String getMIMEType() {
+            return mimeType;
+        }
+    } // class Output
+
     @Override
-    public Document process(
+    public Service.Output process(
         Document      data,
         GlobalContext globalContext,
         CallMeta      callMeta
     ) {
         logger.debug("Service.process");
-        return XMLUtils.newDocument();
+        return new Output(new byte[0], "application/octet-stream");
     }
 
     @Override
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java	Fri Sep 28 12:15:12 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;
 
@@ -44,12 +47,15 @@
      *
      * @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;
     }
 
 
@@ -58,6 +64,7 @@
      *
      * @return the identifier of this user.
      */
+    @Override
     public String identifier() {
         return identifier;
     }
@@ -68,6 +75,7 @@
      *
      * @return the name of the user.
      */
+    @Override
     public String getName() {
         return name;
     }
@@ -78,6 +86,7 @@
      *
      * @param name The name for this user.
      */
+    @Override
     public void setName(String name) {
         this.name = name;
     }
@@ -88,6 +97,7 @@
      *
      * @param identifier The new identifier.
      */
+    @Override
     public void setIdentifier(String identifier) {
         this.identifier = identifier;
     }
@@ -98,6 +108,7 @@
      *
      * @param role The new role of the user.
      */
+    @Override
     public void setRole(Document role) {
         this.role = role;
     }
@@ -108,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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java	Fri Sep 28 12:15:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java	Fri Sep 28 12:15:12 2012 +0200
@@ -170,6 +170,8 @@
 
     protected List<Hook> postAdvanceHooks;
 
+    protected List<Hook> postDescribeHooks;
+
     protected List<LifetimeListener> lifetimeListeners;
 
     protected List<BackendListener> backendListeners;
@@ -542,8 +544,9 @@
     protected void loadHooks() {
         logger.info("loading hooks");
 
-        postFeedHooks    = new ArrayList<Hook>();
-        postAdvanceHooks = new ArrayList<Hook>();
+        postFeedHooks     = new ArrayList<Hook>();
+        postAdvanceHooks  = new ArrayList<Hook>();
+        postDescribeHooks = new ArrayList<Hook>();
 
         NodeList nodes = Config.getNodeSetXPath(HOOKS);
 
@@ -567,6 +570,9 @@
                 else if (a.equals("post-advance")) {
                     postAdvanceHooks.add(hook);
                 }
+                else if (a.equals("post-describe")) {
+                    postDescribeHooks.add(hook);
+                }
             }
         }
     }
@@ -703,6 +709,10 @@
         return postAdvanceHooks;
     }
 
+    public List<Hook> getPostDescribeHooks() {
+        return postDescribeHooks;
+    }
+
     public HTTPServer getHTTPServer() {
         return httpServer;
     }
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java	Fri Sep 28 12:15:12 2012 +0200
@@ -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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java	Fri Sep 28 12:15:12 2012 +0200
@@ -157,7 +157,7 @@
      * 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 creator The ElementCreator used to create new elements.
+     * @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.
      */
@@ -195,7 +195,7 @@
      * This method appends a node for each Facet in the <i>facets</i> list to
      * <i>facet</i>.
      *
-     * @param creator The ElementCreator used to create new elements.
+     * @param doc The document to wich to add new elements.
      * @param facet The root node for new elements.
      * @param facets The list of facets.
      */
--- /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:12 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/db/DBConnection.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java	Fri Sep 28 12:15:12 2012 +0200
@@ -96,7 +96,9 @@
             }
 
             try {
-                Class.forName(driver);
+                synchronized (DBConnection.class) {
+                    Class.forName(driver);
+                }
             }
             catch (ClassNotFoundException cnfe) {
                 log.error("cannot load driver", cnfe);
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java	Fri Sep 28 12:15:12 2012 +0200
@@ -50,7 +50,7 @@
         Class  clazz,
         String resourcePath,
         String driver
-    ) { 
+    ) {
         logger.debug("loadStatements");
 
         Properties properties = new Properties();
@@ -105,7 +105,7 @@
         }
 
         if (debug) {
-            logger.debug("-> '" + sql + "'"); 
+            logger.debug("-> '" + sql + "'");
         }
 
         return sql;
--- /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:12 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 :
--- /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:12 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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java	Fri Sep 28 12:15:12 2012 +0200
@@ -81,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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java	Fri Sep 28 12:15:12 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/UserResource.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java	Fri Sep 28 12:15:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java	Fri Sep 28 12:15:12 2012 +0200
@@ -24,6 +24,7 @@
 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;
 
@@ -46,15 +47,18 @@
      * 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.*/
+     * to. */
     public static final String XPATH_FACETS = "facets/facet";
 
 
-    /** The logger that is used in this class.*/
+    /** The logger that is used in this class. */
     private static Logger logger = Logger.getLogger(AbstractState.class);
 
 
@@ -64,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;
 
 
@@ -87,7 +94,12 @@
 
         this.id          = id;
         this.description = description;
+    }
 
+
+    public AbstractState(String id, String description, String helpText) {
+        this(id, description);
+        this.helpText = helpText;
     }
 
 
@@ -132,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.
@@ -149,7 +181,11 @@
      * @return a data object of the state or null if no such data object exists.
      */
     public StateData getData(String name) {
-        return data.get(name);
+        if (data != null) {
+            return data.get(name);
+        }
+
+        return null;
     }
 
 
@@ -158,7 +194,7 @@
      * 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) {
@@ -193,11 +229,32 @@
         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.
      *
@@ -217,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>
--- /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:12 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:12 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:12 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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java	Fri Sep 28 12:15:12 2012 +0200
@@ -1,5 +1,7 @@
 package de.intevation.artifactdatabase.state;
 
+import java.util.List;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
@@ -18,18 +20,21 @@
  */
 public class DefaultFacet implements Facet {
 
-    /** The index of this facet.*/
+    /** The index of this facet. */
     protected int index;
 
-    /** The name of this facet.*/
+    /** The name of this facet. */
     protected String name;
 
-    /** The description of this facet.*/
+    /** The description of this facet. */
     protected String description;
 
+
+    /** Trivial, empty constructor. */
     public DefaultFacet() {
     }
 
+
     /**
      * The default constructor to create new Facet objects.
      *
@@ -54,47 +59,70 @@
         this.description = description;
     }
 
-    public void set(Facet other) {
-        index       = other.getIndex();
-        name        = other.getName();
-        description = other.getDescription();
-    }
 
-    public Facet deepCopy() {
-        DefaultFacet copy = new DefaultFacet();
-        copy.set(this);
-        return copy;
-    }
-
-
+    /** 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;
     }
 
-    public String toString() {
-        return new StringBuilder("name = '")
-            .append(name).append("', index = ")
-            .append(index).append(", description = '")
-            .append(description).append("'")
-            .toString();
+
+    /**
+     * (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,
@@ -108,5 +136,33 @@
 
         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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java	Fri Sep 28 12:15:12 2012 +0200
@@ -20,6 +20,8 @@
 
     protected List<Facet> facets;
 
+    protected Settings settings;
+
 
     /**
      * The default constructor that instantiates a new DefaultOutput object.
@@ -130,12 +132,32 @@
 
 
     public void addFacet(Facet facet) {
-        facets.add(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:12 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:12 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 :
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java	Fri Sep 28 12:15:12 2012 +0200
@@ -1,5 +1,7 @@
 package de.intevation.artifactdatabase.state;
 
+import java.util.List;
+
 import java.io.Serializable;
 
 import org.w3c.dom.Document;
@@ -49,6 +51,32 @@
 
 
     /**
+     * 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.
--- /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:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java	Fri Sep 28 12:15:12 2012 +0200
@@ -57,5 +57,24 @@
      * @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:12 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:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java	Fri Sep 28 12:15:12 2012 +0200
@@ -17,6 +17,7 @@
 
 import de.intevation.artifacts.Artifact;
 import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
 
 import de.intevation.artifactdatabase.data.StateData;
 
@@ -46,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.
@@ -91,6 +100,21 @@
 
 
     /**
+     * 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.
      *
--- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java	Fri Sep 28 12:15:12 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>
  */
@@ -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/TransitionEngine.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java	Fri Sep 28 12:15:12 2012 +0200
@@ -44,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.
      */
--- a/artifact-database/src/main/resources/sql/org-h2-driver.properties	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/resources/sql/org-h2-driver.properties	Fri Sep 28 12:15:12 2012 +0200
@@ -33,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 = ?
@@ -77,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 = ?
@@ -137,6 +138,11 @@
 
 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, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \
@@ -163,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 <> ?))
 
--- a/artifact-database/src/main/resources/sql/org-postgresql-driver.properties	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifact-database/src/main/resources/sql/org-postgresql-driver.properties	Fri Sep 28 12:15:12 2012 +0200
@@ -33,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 = ?
@@ -77,7 +78,7 @@
     UPDATE artifacts \
     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 = ?
@@ -128,6 +129,11 @@
 
 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, c.ttl FROM \
     collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \
--- /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:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java	Fri Sep 28 12:15:12 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()";
 
 
@@ -325,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.
@@ -361,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.
      *
@@ -777,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/DateUtils.java	Fri Sep 28 12:15:12 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);
+    }
+}
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java	Fri Sep 28 12:15:12 2012 +0200
@@ -12,13 +12,19 @@
 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.Stack;
+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;
 
@@ -62,7 +68,7 @@
         if (file.exists()) {
             return file;
         }
-        Stack<String> parts = new Stack<String>();
+        Deque<String> parts = new ArrayDeque<String>();
         File curr = file;
         while (curr != null) {
             String name = curr.getName();
@@ -119,7 +125,7 @@
         return curr;
     }
 
-    public static class HashedFile 
+    public static class HashedFile
     implements Comparable<HashedFile>
     {
         protected File    file;
@@ -196,7 +202,7 @@
 
         @Override
         public boolean equals(Object other) {
-            return other instanceof HashedFile 
+            return other instanceof HashedFile
                 && ((HashedFile)other).compareTo(this) == 0;
         }
 
@@ -230,7 +236,7 @@
 
     public static void walkTree(File root, FileVisitor visitor) {
 
-        Stack<File> stack = new Stack<File>();
+        Deque<File> stack = new ArrayDeque<File>();
 
         stack.push(root);
 
@@ -315,7 +321,7 @@
         }
         else if (file.isDirectory()) {
 
-            Stack<PrefixDir> stack = new Stack<PrefixDir>();
+            Deque<PrefixDir> stack = new ArrayDeque<PrefixDir>();
             stack.push(new PrefixDir(file.getName() + "/", file));
 
             while (!stack.isEmpty()) {
@@ -344,6 +350,57 @@
         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.
      */
@@ -399,5 +456,134 @@
         }
         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:12 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;
+    }
+}
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/LRUCache.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/LRUCache.java	Fri Sep 28 12:15:12 2012 +0200
@@ -4,7 +4,7 @@
 import java.util.LinkedHashMap;
 
 public class LRUCache<K, V>
-extends      LinkedHashMap<K, V> 
+extends      LinkedHashMap<K, V>
 {
     public static final int DEFAULT_MAX_CAPACITY = 25;
 
--- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java	Fri Sep 28 12:15:12 2012 +0200
@@ -103,5 +103,46 @@
         }
         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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java	Fri Sep 28 12:15:12 2012 +0200
@@ -8,7 +8,10 @@
 
 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;
 
@@ -49,6 +52,7 @@
 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;
 
@@ -59,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() {
@@ -79,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;
 
         /**
@@ -154,7 +152,6 @@
             else {
                 element.setAttribute(name, value);
             }
-
         }
     } // class ElementCreator
 
@@ -176,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.
@@ -200,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);
@@ -445,5 +478,140 @@
         }
         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;
+        }
+
+        Map<String, Object> map;
+        try {
+            map = JSON.parse(input);
+        }
+        catch (IOException ioe) {
+            logger.error(ioe);
+            return null;
+        }
+
+        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:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/pom.xml	Fri Sep 28 12:15:12 2012 +0200
@@ -22,8 +22,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>
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java	Fri Sep 28 12:15:12 2012 +0200
@@ -207,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
@@ -215,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;
 
@@ -223,6 +226,9 @@
 
     // Collection API
 
+    Document getCollectionsMasterArtifact(String collectionId, CallMeta meta)
+        throws ArtifactDatabaseException;
+
     Document listCollections(String userId, CallMeta callMeta)
         throws ArtifactDatabaseException;
 
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java	Fri Sep 28 12:15:12 2012 +0200
@@ -13,8 +13,6 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
-import de.intevation.artifacts.GlobalContext;
-
 
 /**
  * Interface of an artifact producing factory.
--- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java	Fri Sep 28 12:15:12 2012 +0200
@@ -86,7 +86,7 @@
      */
     @Override
     public String getPrefix(String uri) {
-        
+
         if (uri == null) {
             throw new IllegalArgumentException("Null uri");
         }
--- a/artifacts/src/main/java/de/intevation/artifacts/CallContext.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/CallContext.java	Fri Sep 28 12:15:12 2012 +0200
@@ -9,6 +9,7 @@
 package de.intevation.artifacts;
 
 import java.util.LinkedList;
+import java.util.List;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
@@ -109,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);
@@ -128,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:12 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 :
--- a/artifacts/src/main/java/de/intevation/artifacts/Service.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/Service.java	Fri Sep 28 12:15:12 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, GlobalContext globalContext, CallMeta callMeta);
+    Output process(Document data, GlobalContext globalContext, CallMeta callMeta);
 
     /**
      * Setup the concrete processing service. This is done at startup time
--- a/artifacts/src/main/java/de/intevation/artifacts/User.java	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/User.java	Fri Sep 28 12:15:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/artifacts/src/main/java/de/intevation/artifacts/UserFactory.java	Fri Sep 28 12:15:12 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	Mon Sep 19 13:55:26 2011 +0000
+++ b/pom.xml	Fri Sep 28 12:15:12 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>

http://dive4elements.wald.intevation.org