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