# HG changeset patch # User Thomas Arendsen Hein # Date 1348827311 -7200 # Node ID 2c2981e53d4e449de25b975d919c09799aa854e4 # Parent 7536a3288fc604cbd17cccf48dcf473dfeb8475e# Parent 98f6e6ae9498d4b4529c4d9136b1b5a35198d1d6 dummy merge for repo head diff -r 98f6e6ae9498 -r 2c2981e53d4e ChangeLog --- a/ChangeLog Tue Jun 28 07:51:17 2011 +0000 +++ b/ChangeLog Fri Sep 28 12:15:11 2012 +0200 @@ -1,3 +1,1077 @@ +2012-09-26 Björn Ricks + + * 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 + + Tagged RELEASE 2.9.1 + +2012-09-10 Sascha L. Teichmann + + * artifacts/pom.xml, pom.xml: source 1.5 -> 1.6 + (@Override annotations for interface implementations is 1.6). + +2012-09-07 Björn Ricks + + * 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 + + Tagged RELEASE 2.9 + +2012-08-30 Felix Wolfsteller + + * artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java: + Use LinkedHashMap to keep insertion order of attributes. + +2012-08-27 Christian Lins + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java: + Also add the account information when listing users. + +2012-08-23 Björn Ricks + + * artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java: + Add account information to createUser + +2012-08-23 Björn Ricks + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: + Read the account information from the database. + +2012-08-23 Björn Ricks + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java: + Made Registry.getInstance() access static. + +2012-07-29 Sascha L. Teichmann + + * 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 + + Tagged trunk as '2.8.1' + +2012-07-19 Sascha L. Teichmann + + * artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java: + Added concat() to join two String arrays. + +2012-07-17 Felix Wolfsteller + + * 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 + + Tagged trunk as '2.8' + +2012-07-19 Sascha L. Teichmann + + * artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java: + Added concat() to join two String arrays. + +2012-07-17 Felix Wolfsteller + + * 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 + + Tagged trunk as '2.8' + +2012-07-15 Sascha L. Teichmann + + * 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 + + * artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java: + Added contains(String needle, String [] haystack) method. + +2012-07-03 Ingo Weinzierl + + * artifact-database/.settings/org.eclipse.jdt.core.prefs: Removed from + version control (Eclipse configurations should not be in SVN!). + +2012-02-26 Felix Wolfsteller + + * 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 + + * artifact-database/src/main/java/de/intevation/**/*.java: + Removed trailing whitespace. + +2012-06-05 Ingo Weinzierl + + * 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 + + * artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java: + Remove extra from the XML generated by JSON lists containing only + objects. + +2012-05-27 Ingo Weinzierl + + Tagged trunk as '2.7' + +2012-05-18 Ingo Weinzierl + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java: + Added Override annotations. + +2012-01-30 Ingo Weinzierl + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java: + (getDataProvider): Never return null. + +2011-12-16 Ingo Weinzierl + + * 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) to replace an existing list of + Facets. + +2011-12-14 Ingo Weinzierl + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java + (getFacetDescription): New, access facets description. + +2011-11-30 Felix Wolfsteller + + 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 + + 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 + + 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 + + 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 + + * 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 + + * 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 + + * 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 + + 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 + + * artifact-database/src/main/resources/sql/org-h2-driver.properties: + Bugfix: added missing bracket to an sql statement. + +2011-10-20 Sascha L. Teichmann + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java(getMasterArtifact): + Limit result set to one row. + +2011-10-19 Felix Wolfsteller + + Fix accidentally corruptd key to sql statement. + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: + Fix key. + +2011-10-19 Felix Wolfsteller + + 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 + + * 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 + + * 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 + + * 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 + + * 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 + + 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 + + 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 + + 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java: + Removed useless CollectionCallContext creation (was never used). + +2011-09-19 Ingo Weinzierl + + Tagged RELEASE 1.4 + + * Changes: Prepared changes for the release. + +2011-09-09 Ingo Weinzierl + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + 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 + + * 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 + + * 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 + + * 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 that stores the required + variables. + +2011-08-22 Sascha L. Teichmann + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/resources/sql/org-h2-driver.properties: + Fixed H2 SQL to set TTL of collection. + +2011-08-02 Sascha L. Teichmann + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: + Swapped SQL parameter indices when fetching collection name. + +2011-08-02 Sascha L. Teichmann + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java: + Fixed NPE. + +2011-08-01 Sascha L. Teichmann + + * 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 + + * 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 + + * 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 + + * 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 + + 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 + + * 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 + + 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 + + * 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 + + * 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 + + * artifact-database/pom.xml: Bumped H2 version up to latest stable 1.3.158 + +2011-07-22 Sascha L. Teichmann + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + Fix for flys/issue20 + + * artifact-database/pom.xml: Bumped version of SLF4J up to 1.6.1 + +2011-07-08 Ingo Weinzierl + + * 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 Tagged RELEASE 1.3 diff -r 98f6e6ae9498 -r 2c2981e53d4e Changes --- a/Changes Tue Jun 28 07:51:17 2011 +0000 +++ b/Changes Fri Sep 28 12:15:11 2012 +0200 @@ -1,3 +1,80 @@ +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: diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/.settings/org.eclipse.jdt.core.prefs --- a/artifact-database/.settings/org.eclipse.jdt.core.prefs Tue Jun 28 07:51:17 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/doc/schema-h2.sql --- a/artifact-database/doc/schema-h2.sql Tue Jun 28 07:51:17 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 ); diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/doc/schema-pg.sql --- a/artifact-database/doc/schema-pg.sql Tue Jun 28 07:51:17 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 ); diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/pom.xml --- a/artifact-database/pom.xml Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/pom.xml Fri Sep 28 12:15:11 2012 +0200 @@ -72,22 +72,17 @@ com.h2database h2 - 1.3.152 + 1.3.158 postgresql postgresql - 8.3-603.jdbc4 - - - commons-codec - commons-codec - 1.4 + 8.4-702.jdbc4 commons-dbcp commons-dbcp - 1.2.2 + 1.4 log4j @@ -97,12 +92,12 @@ org.slf4j jul-to-slf4j - 1.5.11 + 1.6.1 org.slf4j slf4j-log4j12 - 1.5.11 + 1.6.1 org.mortbay.jetty diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/AbstractCallContext.java Tue Jun 28 07:51:17 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. */ @@ -40,15 +48,20 @@ /** * Map to act like a clipboard when nesting calls like a proxy artifact. */ - protected HashMap customValues; + protected Map customValues; + + /** + * Map to act like a clipboard when nesting calls like a proxy artifact. + */ + protected Map> dataProviders; /** * The default constructor of this abstract CallContext. * + * @param artifactDatabase The artifact database. * @param action The action. * @param callMeta The CallMeta object. - * @param context The global context. */ public AbstractCallContext( ArtifactDatabaseImpl artifactDatabase, @@ -101,5 +114,41 @@ } return customValues.put(key, value); } + + /** + * Get list of DataProviders that registered for given key. + * @return list (empty list if none found, never null). + */ + public List getDataProvider(Object key) { + if (dataProviders != null) { + List list = dataProviders.get(key); + return list != null + ? list + : java.util.Collections.emptyList(); + } + return java.util.Collections.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 providers = null; + if (dataProviders == null) { + dataProviders = new HashMap(); + providers = new ArrayList(); + providers.add(value); + return dataProviders.put(key, providers); + } + providers = dataProviders.get(key); + + if (providers == null) { + providers = new ArrayList(); + } + providers.add(value); + return dataProviders.put(key, providers); + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/App.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/App.java Fri Sep 28 12:15:11 2012 +0200 @@ -70,7 +70,7 @@ bootstrap, backend); DatabaseCleaner cleaner = new DatabaseCleaner( - bootstrap.getContext(), backend); + bootstrap.getContext(), backend, backend.getConfig()); HTTPServer httpServer = bootstrap.getHTTPServer(); @@ -82,6 +82,8 @@ cleaner.start(); + db.start(); + httpServer.startAsServer(db); } } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactCallContext.java Tue Jun 28 07:51:17 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; @@ -73,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 getBackgroundMessages() { + return database.getBackgroundMessages( + artifact.getArtifact().identifier()); + } + + public Long getTimeToLive() { return artifact.getTTL(); } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java Tue Jun 28 07:51:17 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; @@ -38,6 +42,9 @@ 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; @@ -66,6 +73,10 @@ 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"; @@ -186,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"; @@ -194,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 /** @@ -401,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. @@ -413,11 +436,35 @@ * This artifacts should not be removed from the database by the * database cleaner. */ - protected HashSet backgroundIds; + protected HashSet backgroundIds; + + /** + * A list of background messages for Artifacts and Collections. + */ + protected Map> backgroundMsgs; + protected CallContext.Listener callContextListener; /** + * Hooks that are executed after an artifact has been fed. + */ + protected List postFeedHooks; + + /** + * Hooks that are executed after an artifact has advanced. + */ + protected List postAdvanceHooks; + + /** + * Hooks that are executed after an artifact's describe() operation was + * called. + */ + protected List postDescribeHooks; + + protected List lifetimeListeners; + + /** * Default constructor. */ public ArtifactDatabaseImpl() { @@ -442,18 +489,25 @@ */ public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { - backgroundIds = new HashSet(); + logger.debug("new ArtifactDatabaseImpl"); + + backgroundIds = new HashSet(); + backgroundMsgs = new HashMap>(); 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() { @@ -466,6 +520,19 @@ this.callContextListener = callContextListener; } + + public void setPostFeedHook(List postFeedHooks) { + this.postFeedHooks = postFeedHooks; + } + + public void setPostAdvanceHook(List postAdvanceHooks) { + this.postAdvanceHooks = postAdvanceHooks; + } + + public void setPostDescribeHook(List postDescribeHooks) { + this.postDescribeHooks = postDescribeHooks; + } + /** * Used to extract the artifact collection factory from bootstrap. * @@ -509,6 +576,28 @@ 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 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. */ @@ -554,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); } } @@ -583,6 +674,7 @@ logger.warn("operation not allowed in fromBackground"); } removeIdFromBackground(artifact.getId()); + removeBackgroundMessages(artifact.getArtifact().identifier()); } /** @@ -597,6 +689,21 @@ } } + + /** + * Removes all messages that have been added to the backgroundMsgs + * list. + * + * @param uuid The UUID of an artifact or collection. + */ + protected void removeBackgroundMessages(String uuid) { + logger.debug("Remove background messages for: " + uuid); + + synchronized (backgroundMsgs) { + backgroundMsgs.remove(uuid); + } + } + /** * Adds an artifact's database id to the set of artifacts * running in backgroound. To be in this set prevents the @@ -610,12 +717,52 @@ } } + /** + * Adds a Message 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 messages = backgroundMsgs.get(uuid); + + if (messages == null) { + messages = new LinkedList(); + backgroundMsgs.put(uuid, messages); + } + + messages.addLast(msg); + } + } + public Set getLockedIds() { synchronized (backgroundIds) { return new HashSet(backgroundIds); } } + /** + * Returns the background Messages for a specific Artifact or + * Collection. + * + * @param uuid The Artifact's or Collection's UUID. + * + * @return a List of Messages or null if no messages are + * existing. + */ + public LinkedList getBackgroundMessages(String uuid) { + logger.debug("Retrieve background message for: " + uuid); + + synchronized (backgroundMsgs) { + return backgroundMsgs.get(uuid); + } + } + public String [][] artifactFactoryNamesAndDescriptions() { return factoryNamesAndDescription; } @@ -643,6 +790,8 @@ ) throws ArtifactDatabaseException { + logger.debug("ArtifactDatabaseImpl.createArtifactWithFactory " + + factoryName); ArtifactFactory factory = getArtifactFactory(factoryName); if (factory == null) { @@ -652,6 +801,7 @@ Artifact artifact = factory.createArtifact( backend.newIdentifier(), context, + callMeta, data); if (artifact == null) { @@ -685,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, @@ -706,7 +870,16 @@ artifact); try { - return artifact.getArtifact().describe(data, cc); + Artifact art = artifact.getArtifact(); + Document res = art.describe(data, cc); + + if (postDescribeHooks != null) { + for (Hook hook: postDescribeHooks) { + hook.execute(art, cc, res); + } + } + + return res; } finally { cc.postCall(); @@ -734,7 +907,16 @@ 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(); @@ -758,7 +940,16 @@ 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(); @@ -983,7 +1174,7 @@ return serviceNamesAndDescription; } - public Document process( + public Service.Output process( String serviceName, Document input, CallMeta callMeta @@ -1001,6 +1192,7 @@ // User API + /** Returns user(s) elements. */ public Document listUsers(CallMeta callMeta) throws ArtifactDatabaseException { @@ -1026,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; } @@ -1061,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, @@ -1077,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); @@ -1130,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 { @@ -1612,18 +1924,7 @@ throw new ArtifactDatabaseException(NO_SUCH_COLLECTION); } - CollectionCallContext cc = new CollectionCallContext( - ArtifactDatabaseImpl.this, - CallContext.NOTHING, - callMeta, - c); - - try { - return new DeferredCollectionOutputImpl(c, type, format, callMeta); - } - finally { - cc.postCall(); - } + return new DeferredCollectionOutputImpl(c, type, format, callMeta); } protected void initCallContext(CallContext cc) { @@ -1639,5 +1940,37 @@ 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java Fri Sep 28 12:15:11 2012 +0200 @@ -10,22 +10,31 @@ import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactCollection; import de.intevation.artifacts.ArtifactCollectionFactory; +import de.intevation.artifacts.ArtifactDatabase.ArtifactLoadedCallback; import de.intevation.artifacts.ArtifactFactory; import de.intevation.artifacts.ArtifactSerializer; import de.intevation.artifacts.CollectionItem; import de.intevation.artifacts.User; import de.intevation.artifacts.UserFactory; +import de.intevation.artifacts.common.utils.StringUtils; import de.intevation.artifacts.common.utils.XMLUtils; +import de.intevation.artifacts.common.utils.LRUCache; + +import de.intevation.artifactdatabase.db.SQLExecutor; +import de.intevation.artifactdatabase.db.SQL; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; +import java.util.List; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.concurrent.CopyOnWriteArrayList; + import org.apache.log4j.Logger; import org.w3c.dom.Document; @@ -44,169 +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_UPDATE_COLLECTION_TTL = - SQL.get("collections.update.ttl"); - - public static final String SQL_UPDATE_COLLECTION_NAME = - SQL.get("collections.update.name"); - - 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_GET_ATTRIBUTE = - SQL.get("collection.get.attribute"); - - public static final String SQL_COLLECTION_SET_ATTRIBUTE = - SQL.get("collection.set.attribute"); - - 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 listeners; + + protected DBConfig config; + /** * The database cleaner. Reference is stored here because * the cleaner is woken up if the backend finds an outdated @@ -327,6 +263,14 @@ * Default constructor */ public Backend() { + listeners = new CopyOnWriteArrayList(); + } + + public Backend(DBConfig config) { + this(); + this.config = config; + sqlExecutor = new SQLExecutor(config.getDBConnection()); + setupSQL(config.getSQL()); } /** @@ -334,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. @@ -346,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 others) { + listeners.addAll(others); + logger.debug("# listeners: " + listeners.size()); + } + /** * Sets the factory lookup mechanism to decouple ArtifactDatabase * and Backend. @@ -497,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); @@ -516,11 +538,6 @@ String factoryName = result.getString(3); - if (factoryLookup == null) { - logger.error("factory lookup == null"); - return false; - } - ArtifactFactory factory = factoryLookup .getArtifactFactory(factoryName); @@ -592,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); @@ -607,7 +625,7 @@ reset(); - if (ID != null) { // already in database + if (stored[0] = ID != null) { // already in database prepareStatement(SQL_REPLACE); if (ttl == null) { @@ -663,6 +681,13 @@ throw new RuntimeException("failed insert artifact into database"); } + if (stored[0]) { + fireStoredArtifact(artifact); + } + else { + fireCreatedArtifact(artifact); + } + return id[0]; } @@ -680,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(); @@ -722,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()); @@ -748,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()); @@ -763,10 +796,22 @@ return true; } }.runWrite(); + + if (success) { + fireStoredArtifact(artifact.getArtifact()); + } } + protected void fireStoredArtifact(Artifact artifact) { + for (BackendListener listener: listeners) { + listener.storedArtifact(artifact, this); + } + } + + public User createUser( final String name, + final String account, final Document role, final UserFactory factory, final Object context @@ -775,7 +820,7 @@ final byte [] roleData = XMLUtils.toByteArray(role, true); - SQLExecutor exec = new SQLExecutor() { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { prepareStatement(SQL_USERS_NEXT_ID); @@ -796,24 +841,38 @@ stmnt.setInt(1, id); stmnt.setString(2, identifier); stmnt.setString(3, name); + stmnt.setString(4, account); if (roleData == null) { - stmnt.setNull(4, Types.BIGINT); + stmnt.setNull(5, Types.BIGINT); } else { - stmnt.setBytes(4, roleData); + stmnt.setBytes(5, roleData); } stmnt.execute(); conn.commit(); user[0] = factory.createUser( - identifier, name, role, context); + identifier, name, account, role, context); return true; } }; - 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) { @@ -822,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); @@ -873,7 +932,19 @@ } }; - 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( @@ -888,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); @@ -898,12 +969,50 @@ } // omit id String name = result.getString(2); - byte [] roleData = result.getBytes(3); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); Document role = XMLUtils.fromByteArray(roleData, true); user[0] = factory.createUser( - identifier, name, role, context); + identifier, name, account, role, context); + return true; + } + }; + + return exec.runRead() ? user[0] : null; + } + + /** + * Find/Get user by account + */ + public User findUser( + final String account, + final UserFactory factory, + final Object context + ) { + + final User [] user = new User[1]; + logger.debug("Tying to find user by account " + account); + + SQLExecutor.Instance exec = sqlExecutor.new Instance() { + public boolean doIt() throws SQLException { + prepareStatement(SQL_USERS_SELECT_ACCOUNT); + stmnt.setString(1, account); + result = stmnt.executeQuery(); + if (!result.next()) { // no such user + logger.debug("No user found."); + return false; + } + String identifier = result.getString(1); + String name = result.getString(2); + String account = result.getString(3); + byte [] roleData = result.getBytes(4); + + Document role = XMLUtils.fromByteArray(roleData, true); + + user[0] = factory.createUser( + identifier, name, account, role, context); return true; } }; @@ -917,7 +1026,7 @@ ) { final ArrayList users = new ArrayList(); - SQLExecutor exec = new SQLExecutor() { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { prepareStatement(SQL_USERS_SELECT_ALL); result = stmnt.executeQuery(); @@ -926,11 +1035,12 @@ // omit id String identifier = result.getString(2); String name = result.getString(3); - byte [] roleData = result.getBytes(4); + String account = result.getString(4); + byte [] roleData = result.getBytes(5); Document role = XMLUtils.fromByteArray(roleData, true); User user = factory.createUser( - identifier, name, role, context); + identifier, name, account, role, context); users.add(user); } return true; @@ -963,7 +1073,7 @@ final byte [] data = XMLUtils.toByteArray(attribute, true); - SQLExecutor exec = new SQLExecutor() { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { // fetch owner id prepareStatement(SQL_USERS_SELECT_ID_BY_GID); @@ -1038,11 +1148,28 @@ collection[0] = factory.createCollection( identifier, name, creationTime, ttl, attribute, context); + if (collection[0] != null) { + // XXX: Little hack to make the listeners happy + collection[0].setUser(new DefaultUser(ownerIdentifier)); + } + return true; } }; - 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( @@ -1058,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); @@ -1119,7 +1246,7 @@ final ArrayList collections = new ArrayList(); - SQLExecutor exec = new SQLExecutor() { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { @@ -1176,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); @@ -1195,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); @@ -1215,7 +1373,19 @@ return true; } }; - return exec.runWrite(); + boolean success = exec.runWrite(); + + if (success) { + fireDeletedCollection(collectionId); + } + + return success; + } + + protected void fireDeletedCollection(String identifier) { + for (BackendListener listener: listeners) { + listener.deletedCollection(identifier, this); + } } public Document getCollectionAttribute(final String collectionId) { @@ -1225,7 +1395,7 @@ 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_GET_ATTRIBUTE); stmnt.setString(1, collectionId); @@ -1256,7 +1426,7 @@ 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 @@ -1280,6 +1450,21 @@ 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( @@ -1297,7 +1482,7 @@ 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); @@ -1333,7 +1518,7 @@ 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 @@ -1358,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( @@ -1377,7 +1580,7 @@ final byte [] data = XMLUtils.toByteArray(attribute, true); - SQLExecutor exec = new SQLExecutor() { + SQLExecutor.Instance exec = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { // fetch artifact id prepareStatement(SQL_GET_ID); @@ -1439,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( @@ -1450,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 @@ -1490,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( @@ -1503,7 +1739,7 @@ final ArrayList collectionItems = new ArrayList(); - 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); @@ -1531,7 +1767,7 @@ return false; } - return new SQLExecutor() { + return sqlExecutor.new Instance() { public boolean doIt() throws SQLException { prepareStatement(SQL_UPDATE_COLLECTION_TTL); if (ttl == null) { @@ -1556,7 +1792,7 @@ return false; } - return new SQLExecutor() { + boolean success = sqlExecutor.new Instance() { public boolean doIt() throws SQLException { prepareStatement(SQL_UPDATE_COLLECTION_NAME); stmnt.setString(1, name); @@ -1567,6 +1803,106 @@ 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 alreadyLoaded = + new LRUCache(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 identifiers) { + logger.debug("killedArtifacts"); + for (BackendListener listener: listeners) { + listener.killedArtifacts(identifiers, this); + } + } + + @Override + public void killedCollections(List 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/BackendListener.java --- /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 identifiers, + Backend backend); + + void killedArtifacts( + List identifiers, + Backend backend); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/CollectionCallContext.java Tue Jun 28 07:51:17 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; /** @@ -51,6 +54,23 @@ } + public boolean isInBackground() { + log.debug("CollectionCallContext.isInBackground - NOT IMPLEMENTED"); + return false; + } + + + public void addBackgroundMessage(Message msg) { + log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); + } + + + public LinkedList getBackgroundMessages() { + log.debug("CollectionCallContext.addBackgroundMessage NOT IMPLEMENTED"); + return null; + } + + public Long getTimeToLive() { log.debug("CollectionCallContext.getTimeToLive - NOT IMPLEMENTED"); return null; diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DBConfig.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DBConnection.java Tue Jun 28 07:51:17 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 Sascha L. Teichmann - */ -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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DatabaseCleaner.java Tue Jun 28 07:51:17 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; @@ -57,6 +61,9 @@ */ Artifact reviveArtifact(String factoryName, byte [] bytes); + void killedArtifacts(List identifiers); + void killedCollections(List identifiers); + } // interface ArtifactReviver public interface LockedIdsProvider { @@ -77,23 +84,16 @@ /** * The SQL statement to select the outdated artifacts. */ - public static final String SQL_OUTDATED = - SQL.get("artifacts.outdated"); + public String SQL_OUTDATED; - public static final String SQL_OUTDATED_COLLECTIONS = - SQL.get("collections.outdated"); - - 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 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_ARTIFACT = - SQL.get("artifacts.delete"); + public String SQL_DELETE_ARTIFACT; /** * XPath to figure out how long the cleaner should sleep between @@ -135,6 +135,8 @@ */ protected ArtifactReviver reviver; + protected DBConnection dbConnection; + /** * Default constructor. */ @@ -147,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"); } /** @@ -159,7 +171,6 @@ * Living artifacts are artifacts which are currently active * inside the artifact database. Deleting them in this state * would create severe internal problems. - * @param filter */ public void setLockedIdsProvider(LockedIdsProvider lockedIdsProvider) { this.lockedIdsProvider = lockedIdsProvider; @@ -196,14 +207,30 @@ return SLEEP_DEFAULT; } - private static final class IdData { + private static class IdIdentifier { int id; + String identifier; + + private IdIdentifier(int id, String identifier) { + this.id = id; + this.identifier = identifier; + } + } // class IdIdentifier + + private static final class IdData + extends IdIdentifier + { byte [] data; String factoryName; - public IdData(int id, String factoryName, byte [] data) { - this.id = id; + public IdData( + int id, + String factoryName, + byte [] data, + String identifier + ) { + super(id, identifier); this.factoryName = factoryName; this.data = data; } @@ -227,10 +254,7 @@ PreparedStatement stmnt = null; ResultSet result = null; - int removedCollections = 0; - int removedArtifacts = 0; - - DataSource dataSource = DBConnection.getDataSource(); + DataSource dataSource = dbConnection.getDataSource(); Set lockedIds = lockedIdsProvider != null ? lockedIdsProvider.getLockedIds() @@ -240,6 +264,9 @@ ? "-666" // XXX: A bit hackish. : StringUtils.repeat('?', lockedIds.size(), ','); + List deletedCollections = new ArrayList(); + List deletedArtifacts = new ArrayList(); + try { connection = dataSource.getConnection(); connection.setAutoCommit(false); @@ -264,10 +291,12 @@ ++idx; } - ArrayList cs = new ArrayList(); + ArrayList cs = new ArrayList(); result = stmnt.executeQuery(); while (result.next()) { - cs.add(result.getInt(1)); + cs.add(new IdIdentifier( + result.getInt(1), + result.getString(2))); } result.close(); result = null; @@ -276,8 +305,9 @@ // delete collection items stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION_ITEMS); - for (Integer id: cs) { - stmnt.setInt(1, id); + for (IdIdentifier id: cs) { + logger.debug("Mark collection for deletion: " + id.id); + stmnt.setInt(1, id.id); stmnt.execute(); } @@ -286,15 +316,16 @@ // delete collections stmnt = connection.prepareStatement(SQL_DELETE_COLLECTION); - for (Integer id: cs) { - stmnt.setInt(1, id); + for (IdIdentifier id: cs) { + stmnt.setInt(1, id.id); stmnt.execute(); + deletedCollections.add(id.identifier); } stmnt.close(); stmnt = null; connection.commit(); - removedCollections = cs.size(); cs = null; + cs = null; // remove artifacts stmnt = connection.prepareStatement(SQL_DELETE_ARTIFACT); @@ -308,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; @@ -323,21 +355,27 @@ idData.factoryName, idData.data); idData.data = null; + logger.debug("Prepare Artifact (id=" + + idData.id + ") for deletion."); + stmnt.setInt(1, idData.id); stmnt.execute(); connection.commit(); try { if (artifact != null) { + logger.debug("Call endOfLife for Artifact: " + + artifact.identifier()); + artifact.endOfLife(context); } } catch (Exception e) { - logger.error(e.getLocalizedMessage(), e); + logger.error(e.getMessage(), e); } + + deletedArtifacts.add(idData.identifier); } // for all fetched data - - removedArtifacts += ids.size(); } } catch (SQLException sqle) { @@ -362,8 +400,20 @@ } } - logger.info("collections removed: " + removedCollections); - 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()); + } } /** diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifact.java Tue Jun 28 07:51:17 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()) { @@ -112,8 +115,12 @@ } } - public void setup(String identifier, ArtifactFactory factory, - Object context, Document data) { + public void setup(String identifier, + ArtifactFactory factory, + Object context, + CallMeta callMeta, + Document data) + { if (logger.isDebugEnabled()) { logger.debug("DefaultArtifact.setup: " + identifier); } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollection.java Tue Jun 28 07:51:17 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 artifacts; /** @@ -151,7 +141,7 @@ /** * Name of this collection. - * @return Returns the name of this collection + * @param name the name of this collection */ public void setName(String name) { this.name = name; diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactCollectionFactory.java Tue Jun 28 07:51:17 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, @@ -134,7 +132,13 @@ ArtifactCollection collection = (ArtifactCollection) clazz.newInstance(); - collection.setup(identifier, name, creationTime, ttl, this, context, data); + collection.setup(identifier, + name, + creationTime, + ttl, + this, + context, + data); return collection; } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContext.java Tue Jun 28 07:51:17 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 Sascha L. Teichmann */ -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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactContextFactory.java Tue Jun 28 07:51:17 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); } } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultArtifactFactory.java Tue Jun 28 07:51:17 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; } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultBackendListener.java --- /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 identifiers, Backend backend) { + log.debug("killedCollections"); + } + + @Override + public void killedArtifacts(List identifiers, Backend backend) { + log.debug("killedArtifacts"); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : + diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultService.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultServiceFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultServiceFactory.java Tue Jun 28 07:51:17 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( diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUser.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/DefaultUserFactory.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/FactoryBootstrap.java Fri Sep 28 12:15:11 2012 +0200 @@ -14,13 +14,17 @@ 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; @@ -104,6 +108,11 @@ 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()"; @@ -111,6 +120,12 @@ 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. * PLEASE CHANGE THE SECRET VIA THE XPATH EXPORT_SECRET @@ -122,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 @@ -151,6 +166,16 @@ */ protected CallContext.Listener callContextListener; + protected List postFeedHooks; + + protected List postAdvanceHooks; + + protected List postDescribeHooks; + + protected List lifetimeListeners; + + protected List backendListeners; + /** * byte array holding the export signing secret. */ @@ -428,6 +453,161 @@ } } + 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 ltls = new ArrayList(); + + 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 bls = new ArrayList(); + + 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(); + postAdvanceHooks = new ArrayList(); + postDescribeHooks = new ArrayList(); + + 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 @@ -456,6 +636,9 @@ loadUserFactory(); loadCallContextListener(); loadHTTPServer(); + loadHooks(); + loadLifetimeListeners(); + loadBackendListeners(); } /** @@ -496,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; } @@ -518,8 +701,28 @@ return callContextListener; } + public List getPostFeedHooks() { + return postFeedHooks; + } + + public List getPostAdvanceHooks() { + return postAdvanceHooks; + } + + public List getPostDescribeHooks() { + return postDescribeHooks; + } + public HTTPServer getHTTPServer() { return httpServer; } + + public List getLifetimeListeners() { + return lifetimeListeners; + } + + public List getBackendListeners() { + return backendListeners; + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/LazyBackendUser.java Fri Sep 28 12:15:11 2012 +0200 @@ -43,28 +43,39 @@ return user; } + @Override public String identifier() { return getUser().identifier(); } + @Override public String getName() { return getUser().getName(); } + @Override public void setName(String name) { getUser().setName(name); } + @Override public void setIdentifier(String identifier) { getUser().setIdentifier(identifier); } + @Override public Document getRole() { return getUser().getRole(); } + @Override public void setRole(Document document) { getUser().setRole(document); } + + @Override + public String getAccount() { + return getUser().getAccount(); + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/LifetimeListener.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/ProtocolUtils.java Fri Sep 28 12:15:11 2012 +0200 @@ -9,9 +9,14 @@ 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; @@ -152,24 +157,33 @@ * This method appends a node for each Output in the outputs list to * out. Note: an output node includes its provided facets! * - * @param creator The ElementCreator used to create new elements. + * @param doc The document to which to add new elements. * @param out The parent node for new elements. * @param outputs The list of reachable outputs. */ public static void appendOutputModes( - XMLUtils.ElementCreator creator, - Element out, - List outputs) + Document doc, + Element out, + List outputs) { + ElementCreator creator = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + for (Output o: outputs) { Element newOut = createArtNode( creator, "output", - new String[] {"name", "description", "mime-type"}, - 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(creator, facets, o.getFacets()); + appendFacets(doc, facets, o.getFacets()); newOut.appendChild(facets); out.appendChild(newOut); @@ -181,31 +195,30 @@ * This method appends a node for each Facet in the facets list to * facet. * - * @param creator The ElementCreator used to create new elements. + * @param doc The document to wich to add new elements. * @param facet The root node for new elements. * @param facets The list of facets. */ public static void appendFacets( - XMLUtils.ElementCreator creator, - Element facet, - List facets) + Document doc, + Element facet, + List facets) { if (facets == null || facets.size() == 0) { return; } + ElementCreator creator = new ElementCreator( + doc, + ArtifactNamespaceContext.NAMESPACE_URI, + ArtifactNamespaceContext.NAMESPACE_PREFIX); + for (Facet f: facets) { - Element newFacet = createArtNode( - creator, - "facet", - new String[] { "index", "name", "description" }, - new String[] { - String.valueOf(f.getIndex()), - f.getName(), - f.getDescription() - }); + Node node = f.toXML(doc); - facet.appendChild(newFacet); + if (node != null) { + facet.appendChild(node); + } } } } diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/SQL.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/SQL.java Tue Jun 28 07:51:17 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 Sascha L. Teichmann - */ -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:
- * org.postgresql.Driver results in loading of - * /sql/org-postgresql-driver.properties. - * @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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/SQLExecutor.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/SQLExecutor.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/StringUtils.java Tue Jun 28 07:51:17 2011 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +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 Sascha L. Teichmann - */ -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(); - } -} -// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/XMLService.java --- /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 Sascha L. Teichmann + */ +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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/data/DefaultStateData.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/data/StateData.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/data/StateData.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/db/DBConnection.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQL.java --- /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:
+ * org.postgresql.Driver results in loading of + * /sql/org-postgresql-driver.properties. + * @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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/db/SQLExecutor.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/h2/CollectionAccessUpdateTrigger.java Fri Sep 28 12:15:11 2012 +0200 @@ -6,7 +6,9 @@ import java.sql.SQLException; import java.sql.PreparedStatement; -import de.intevation.artifactdatabase.SQL; +import de.intevation.artifactdatabase.DBConfig; + +import de.intevation.artifactdatabase.db.SQL; import org.apache.log4j.Logger; @@ -16,8 +18,7 @@ private static Logger logger = Logger.getLogger(CollectionAccessUpdateTrigger.class); - public static final String COLLECTIONS_TOUCH_TRIGGER_FUNCTION = - SQL.get("collections.touch.trigger.function"); + public String COLLECTIONS_TOUCH_TRIGGER_FUNCTION; public void init( Connection conn, @@ -29,6 +30,12 @@ ) 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( diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ArtifactResource.java Tue Jun 28 07:51:17 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ByteArrayRepresentation.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/FindUserResource.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/RestApp.java Fri Sep 28 12:15:11 2012 +0200 @@ -81,6 +81,7 @@ router.attach(CreateUserResource.PATH, CreateUserResource.class); router.attach(ListUsersResource.PATH, ListUsersResource.class); router.attach(UserResource.PATH, UserResource.class); + router.attach(FindUserResource.PATH, FindUserResource.class); router.attach( CreateCollectionResource.PATH, CreateCollectionResource.class); router.attach( diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/ServiceResource.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/rest/UserResource.java Tue Jun 28 07:51:17 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"; diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/AbstractState.java Fri Sep 28 12:15:11 2012 +0200 @@ -24,6 +24,7 @@ import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactNamespaceContext; import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; import de.intevation.artifacts.common.utils.XMLUtils; @@ -46,15 +47,18 @@ * the configuration. */ public static final String XPATH_DESCRIPTION = "@description"; - /** The XPath to the output nodes of the state configuration.*/ + /** The XPath that points to the help text.*/ + public static final String XPATH_HELP_TEXT = "@helpText"; + + /** The XPath to the output nodes of the state configuration. */ public static final String XPATH_OUTPUT_MODES = "outputmodes/outputmode"; /** The XPath to the list of facets relative to the output mode it belongs - * to.*/ + * to. */ public static final String XPATH_FACETS = "facets/facet"; - /** The logger that is used in this class.*/ + /** The logger that is used in this class. */ private static Logger logger = Logger.getLogger(AbstractState.class); @@ -64,10 +68,13 @@ /** The description of the state. */ protected String description; + /** The help text for this state.*/ + protected String helpText; + /** The data provided by this state. */ protected Map 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 outputs; @@ -87,7 +94,12 @@ this.id = id; this.description = description; + } + + public AbstractState(String id, String description, String helpText) { + this(id, description); + this.helpText = helpText; } @@ -132,6 +144,26 @@ /** + * Returns the help text of this state. + * + * @return the help text. + */ + public String getHelpText() { + return helpText; + } + + + /** + * Set the help text for this state. + * + * @param helpText The help text. + */ + public void setHelpText(String helpText) { + this.helpText = helpText; + } + + + /** * Returns the data of the state. * * @return the data of the state. @@ -149,7 +181,11 @@ * @return a data object of the state or null if no such data object exists. */ public StateData getData(String name) { - return data.get(name); + if (data != null) { + return data.get(name); + } + + return null; } @@ -158,7 +194,7 @@ * with the key name, this object is overwritten by the new value. * * @param name The name of the data object. - * @param StateData The data object. + * @param data The data object. */ public void addData(String name, StateData data) { if (this.data == null) { @@ -193,11 +229,32 @@ description = (String) XMLUtils.xpath( config, XPATH_DESCRIPTION, XPathConstants.STRING); + helpText = (String) XMLUtils.xpath( + config, XPATH_HELP_TEXT, XPathConstants.STRING); + setupOutputs(config); } /** + * This default implementation does nothing at all. + * + * @param orig + * @param owner + * @param context + * @param callMeta + */ + public void initialize( + Artifact orig, + Artifact owner, + Object context, + CallMeta callMeta + ) { + // do nothing. + } + + + /** * This method tries reading the available output nodes configured in the * state configuration and adds possible Outputs to the outputs list. * @@ -217,10 +274,18 @@ int size = outs.getLength(); for (int i = 0; i < size; i++) { - outputs.add(buildOutput(outs.item(i))); + addOutput(buildOutput(outs.item(i))); } } + /** + * This methods allows subclasses to manually add outputs + * + * @param out The output to add + */ + protected void addOutput(Output out) { + outputs.add(out); + } /** * A helper method that creates an Output object based on the out @@ -240,6 +305,9 @@ String mimetype = XMLUtils.xpathString( out, "@mime-type", ArtifactNamespaceContext.INSTANCE); + String type = XMLUtils.xpathString( + out, "@type", ArtifactNamespaceContext.INSTANCE); + if (name == null) { return null; } @@ -251,7 +319,7 @@ ArtifactNamespaceContext.INSTANCE); if (facets == null || facets.getLength() == 0) { - return new DefaultOutput(name, desc, mimetype); + return new DefaultOutput(name, desc, mimetype, type); } int num = facets.getLength(); @@ -266,7 +334,7 @@ } } - return new DefaultOutput(name, desc, mimetype, facetList); + return new DefaultOutput(name, desc, mimetype, facetList, type); } @@ -306,5 +374,11 @@ 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/ArtifactAndFacet.java --- /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 facetDescription 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/Attribute.java --- /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 Ingo Weinzierl + */ +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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultAttribute.java --- /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 Ingo Weinzierl + */ +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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultFacet.java Fri Sep 28 12:15:11 2012 +0200 @@ -1,8 +1,17 @@ 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. @@ -11,16 +20,21 @@ */ public class DefaultFacet implements Facet { - /** The index of this facet.*/ + /** The index of this facet. */ protected int index; - /** The name of this facet.*/ + /** The name of this facet. */ protected String name; - /** The description of this facet.*/ + /** The description of this facet. */ protected String description; + /** Trivial, empty constructor. */ + public DefaultFacet() { + } + + /** * The default constructor to create new Facet objects. * @@ -46,25 +60,85 @@ } + /** 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 = ") @@ -72,5 +146,23 @@ .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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultOutput.java Fri Sep 28 12:15:11 2012 +0200 @@ -16,8 +16,12 @@ protected String mimeType; + protected String type; + protected List facets; + protected Settings settings; + /** * The default constructor that instantiates a new DefaultOutput object. @@ -30,10 +34,37 @@ this.name = name; this.description = description; this.mimeType = mimeType; + this.type = ""; this.facets = new ArrayList(); } + public DefaultOutput( + String name, + String description, + String mimeType, + String type) + { + this(name, description, mimeType); + + this.facets = new ArrayList(); + this.type = type; + } + + + public DefaultOutput( + String name, + String description, + String mimeType, + List facets) + { + this(name, description, mimeType); + + this.type = ""; + this.facets = facets; + } + + /** * This constructor builds a new Output object that contains facets as well. * @@ -46,11 +77,12 @@ String name, String description, String mimeType, - List facets) + List facets, + String type) { - this(name, description, mimeType); + this(name, description, mimeType, facets); - this.facets = facets; + this.type = type; } @@ -84,6 +116,11 @@ } + public String getType() { + return type; + } + + /** * Returns the list of facets supported by this output. * @@ -95,7 +132,32 @@ public void addFacet(Facet facet) { - facets.add(facet); + if (facet != null && !facets.contains(facet)) { + facets.add(facet); + } + } + + + public void addFacets(List facets) { + this.facets.addAll(facets); + } + + + @Override + public void setFacets(List 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSection.java --- /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 Ingo Weinzierl + */ +public class DefaultSection implements Section { + + protected String id; + + protected List
subsections; + + /** Attribute-map. */ + protected Map attributes; + + + /** + * Creates a new DefaultSection instance. Note, that the id is used + * as Node name of the new Element that is created in toXML(). + */ + public DefaultSection(String id) { + this.id = id; + // Use LinkedHashMap to keep insertion order. + this.attributes = new LinkedHashMap(); + this.subsections = new ArrayList
(); + } + + + @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 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/DefaultSettings.java --- /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 Ingo Weinzierl + */ +public class DefaultSettings implements Settings { + + protected List
sections; + + public DefaultSettings() { + sections = new ArrayList
(); + } + + @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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Facet.java Fri Sep 28 12:15:11 2012 +0200 @@ -1,7 +1,12 @@ 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; @@ -43,5 +48,43 @@ * @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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/FacetActivity.java --- /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> activities; + + private Registry() { + activities = new HashMap>(); + } + + public static Registry getInstance() { + return INSTANCE; + } + + public synchronized boolean isInitialActive( + String key, + Artifact artifact, + Facet facet, + String output + ) { + List 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 activityList = activities.get(key); + if (activityList == null) { + activityList = new ArrayList(3); + activities.put(key, activityList); + } + activityList.add(activity); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/Output.java Fri Sep 28 12:15:11 2012 +0200 @@ -29,6 +29,14 @@ */ public String getMimeType(); + + /** + * Returns the type of this output. + * + * @return the type. + */ + public String getType(); + /** * Retrieve the facets of this output. * @@ -42,5 +50,31 @@ * @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 facets); + + /** + * Replaces the old list of facets with a new one. + * + * @param facets A list of new facets. + */ + public void setFacets(List 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/Section.java --- /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 Ingo Weinzierl + */ +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 pos. + * + * @param pos The position of the target subsection. + * + * @return the subsection at position pos. + */ + 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 key. + * + * @param key The key that is used to retrieve the target Attribute. + * + * @return the Attribute specified by key. + */ + Attribute getAttribute(String key); + + /** + * Returns all keys of all Attributes currently stored in this Section. + * + * @return all keys of all Attributes. + */ + Set 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/Settings.java --- /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 Ingo Weinzierl + */ +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 pos. + * + * @param pos the position of the target Section. + * + * @return the Section at position pos or null if no Section is + * existing at pos. + */ + 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 toXML 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/state/State.java Fri Sep 28 12:15:11 2012 +0200 @@ -17,6 +17,7 @@ import de.intevation.artifacts.Artifact; import de.intevation.artifacts.CallContext; +import de.intevation.artifacts.CallMeta; import de.intevation.artifactdatabase.data.StateData; @@ -46,6 +47,14 @@ /** + * Returns the help text configured for the state. + * + * @return the help text configured for the state. + */ + public String getHelpText(); + + + /** * Returns the data provided by this state. * * @return the data stored in this state. @@ -91,6 +100,21 @@ /** + * Initializes the internal state of this State based on an other State. + * + * @param orig The owner Artifact or the original State. + * @param owner The owner Artifact of this State. + * @param context The context object. + * @param callMeta The CallMeta of the current call. + */ + public void initialize( + Artifact orig, + Artifact owner, + Object context, + CallMeta callMeta); + + + /** * This method is called when an artifacts retrieves a describe request. It * creates the user interface description of the current state. * @@ -107,5 +131,16 @@ 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/state/StateEngine.java Tue Jun 28 07:51:17 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 Ingo Weinzierl */ @@ -62,6 +67,30 @@ } + public StateData getStateData(String artifact, String dataName) { + List 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 null if no states * are existing for this artifact. @@ -101,5 +130,40 @@ public List getStates(String artifact) { return states.get(artifact); } + + + /** + * Return mapping of output to facets for an artifact in its states. + */ + public Map> getCompatibleFacets(List aStates) { + Map> compatibilityMatrix = + new HashMap>(); + + // 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 outFacets = output.getFacets(); + + List oldFacets = compatibilityMatrix.get(output.getName()); + + if (oldFacets == null) { + oldFacets = new ArrayList(); + } + + 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java --- a/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/java/de/intevation/artifactdatabase/transition/TransitionEngine.java Fri Sep 28 12:15:11 2012 +0200 @@ -44,7 +44,7 @@ * Add new transitions for a specific artifact. * * @param stateId the name of the Artifact. - * @param transitions the list of transition of the artifact. + * @param transition the list of transition of the artifact. * * @return true, if the transitions were added, otherwise false. */ diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/resources/sql/org-h2-driver.properties --- a/artifact-database/src/main/resources/sql/org-h2-driver.properties Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/resources/sql/org-h2-driver.properties Fri Sep 28 12:15:11 2012 +0200 @@ -9,7 +9,7 @@ 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$) \ @@ -33,16 +33,17 @@ users.id.nextval=SELECT NEXTVAL('USERS_ID_SEQ') -users.insert=INSERT INTO users (id, gid, name, role) VALUES (?, ?, ?, ?) +users.insert=INSERT INTO users (id, gid, name, account, role) VALUES (?, ?, ?, ?, ?) users.select.id.by.gid=SELECT id FROM users WHERE gid = ? -users.select.gid=SELECT id, name, role FROM users WHERE gid = ? +users.select.gid=SELECT id, name, account, role FROM users WHERE gid = ? +users.select.account=SELECT gid, name, account, role FROM users WHERE account = ? users.delete.id=DELETE FROM users WHERE id = ? users.delete.collections=DELETE FROM collections where owner_id = ? -users.select.all=SELECT id, gid, name, role FROM users +users.select.all=SELECT id, gid, name, account, role FROM users collection.check.artifact=SELECT id FROM collection_items \ WHERE artifact_id = ? AND collection_id = ? @@ -77,7 +78,7 @@ UPDATE artifacts \ SET last_access = DATEADD('MILLISECOND', -2, CURRENT_TIMESTAMP), ttl = 1 \ WHERE id = ? AND \ - NOT EXSITS \ + NOT EXISTS \ (SELECT id FROM collection_items WHERE collection_id <> ? AND artifact_id = ?) collection.item.delete=DELETE FROM collection_items WHERE id = ? @@ -91,14 +92,14 @@ # COLLECTIONS collections.outdated= \ - SELECT c.id FROM collections c \ + 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 id = ? +collections.update.ttl=UPDATE collections SET ttl = ? WHERE gid = ? collections.update.name=UPDATE collections SET name = ? WHERE gid = ? @@ -137,6 +138,11 @@ collection.creation.time=SELECT creation from collections WHERE id = ? +collections.artifacts.oldest=SELECT a.gid, ci.artifact_id \ + FROM artifacts AS a, collection_items AS ci, collections AS c \ + WHERE ci.collection_id = c.id AND c.gid = ?::uuid AND ci.artifact_id = a.id \ + ORDER BY ci.creation + collections.select.user= \ SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \ collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \ @@ -163,7 +169,7 @@ outdate.artifacts.collection=UPDATE artifacts \ SET last_access = DATEADD('MILLISECOND', -2, CURRENT_TIMESTAMP), ttl = 1 \ WHERE id IN \ - SELECT artifact_id FROM collection_items \ + (SELECT artifact_id FROM collection_items \ WHERE collection_id = ? AND \ artifact_id NOT IN (SELECT DISTINCT artifact_id FROM collection_items WHERE collection_id <> ?)) @@ -181,3 +187,12 @@ 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifact-database/src/main/resources/sql/org-postgresql-driver.properties --- a/artifact-database/src/main/resources/sql/org-postgresql-driver.properties Tue Jun 28 07:51:17 2011 +0000 +++ b/artifact-database/src/main/resources/sql/org-postgresql-driver.properties Fri Sep 28 12:15:11 2012 +0200 @@ -9,7 +9,7 @@ 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 CURRENT_TIMESTAMP - last_access > (ttl || ' milliseconds')::interval \ AND id NOT IN (SELECT DISTINCT artifact_id FROM collection_items) \ AND id NOT IN ($LOCKED_IDS$) \ @@ -33,16 +33,17 @@ users.id.nextval=SELECT NEXTVAL('USERS_ID_SEQ') -users.insert=INSERT INTO users (id, gid, name, role) VALUES (?, ?::uuid, ?, ?) +users.insert=INSERT INTO users (id, gid, name, account, role) VALUES (?, ?::uuid, ?, ?, ?) users.select.id.by.gid=SELECT id FROM users WHERE gid = ?::uuid -users.select.gid=SELECT id, name, role FROM users WHERE gid = ?::uuid +users.select.gid=SELECT id, name, account, role FROM users WHERE gid = ?::uuid +users.select.account=SELECT gid, name, account, role FROM users WHERE account = ? users.delete.id=DELETE FROM users WHERE id = ? users.delete.collections=DELETE FROM collections where owner_id = ? -users.select.all=SELECT id, gid, name, role FROM users +users.select.all=SELECT id, gid, name, account, role FROM users collection.check.artifact=SELECT id FROM collection_items \ WHERE artifact_id = ? AND collection_id = ? @@ -77,7 +78,7 @@ UPDATE artifacts \ SET last_access = CURRENT_TIMESTAMP - '2 milliseconds'::interval, ttl = 1 \ WHERE id = ? AND \ - NOT EXSITS \ + NOT EXISTS \ (SELECT id FROM collection_items WHERE collection_id <> ? AND artifact_id = ?) collection.item.delete=DELETE FROM collection_items WHERE id = ? @@ -91,7 +92,7 @@ # COLLECTIONS collections.outdated= \ - SELECT c.id FROM collections c \ + 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 \ @@ -128,6 +129,11 @@ collection.creation.time=SELECT creation from collections WHERE id = ? +collections.artifacts.oldest=SELECT a.gid, ci.artifact_id \ + FROM artifacts AS a, collection_items AS ci, collections AS c \ + WHERE ci.collection_id = c.id AND c.gid = ?::uuid AND ci.artifact_id = a.id \ + ORDER BY ci.creation + collections.select.user= \ SELECT c.gid, c.name, c.creation, u.gid, c.ttl FROM \ collections c LEFT OUTER JOIN users u ON c.owner_id = u.id \ @@ -170,3 +176,12 @@ 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/pom.xml --- a/artifacts-common/pom.xml Tue Jun 28 07:51:17 2011 +0000 +++ b/artifacts-common/pom.xml Fri Sep 28 12:15:11 2012 +0200 @@ -26,6 +26,11 @@ log4j 1.2.14 + + commons-codec + commons-codec + 1.4 + diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/model/KVP.java --- /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 { + + 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java --- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/ClientProtocolUtils.java Tue Jun 28 07:51:17 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; @@ -277,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. @@ -313,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. * @@ -729,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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/CreationFilter.java --- /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> outs; + + public CreationFilter() { + outs = new HashMap>(); + } + + public void add(String out, List facets) { + outs.put(out, facets); + } + + public Element toXML(XMLUtils.ElementCreator ec) { + Element filter = ec.create("filter"); + + for (Map.Entry> 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/DateUtils.java --- /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 date. + * + * @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); + } +} diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/FileTools.java --- /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 parts = new ArrayDeque(); + 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 + { + 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 uniqueFiles(List files) { + + Set set = new HashSet(); + + for (File file: files) { + if (!set.add(new HashedFile(file))) { + log.warn("file '" + file + "' is a duplicate."); + } + } + + ArrayList out = new ArrayList(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 stack = new ArrayDeque(); + + 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 file and everything in file 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 stack = new ArrayDeque(); + 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 src file to target. + * + * @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 src 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 source to a destination path dest. + * + * @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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/JSON.java --- /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 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 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 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 parse(InputStream in) + throws IOException + { + return parseObject(new PushbackInputStream(in, 1)); + } + + public static Map 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 parseList(PushbackInputStream in) + throws IOException + { + List list = new ArrayList(); + 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 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 parseObject(PushbackInputStream in) + throws IOException + { + Map pairs = new LinkedHashMap(); + + 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; + } +} diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/LRUCache.java --- /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 +extends LinkedHashMap +{ + 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 eldest) { + return size() > maxCapacity; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/MapXPathVariableResolver.java --- /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 Ingo Weinzierl + */ +public class MapXPathVariableResolver implements XPathVariableResolver { + + protected Map variables; + + + public MapXPathVariableResolver() { + this.variables = new HashMap(); + } + + + public MapXPathVariableResolver(Map 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/StringUtils.java --- /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 Sascha L. Teichmann + */ +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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java --- a/artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XMLUtils.java Tue Jun 28 07:51:17 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,10 @@ package de.intevation.artifacts.common.utils; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.LinkedHashMap; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -41,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; @@ -57,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() { @@ -77,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; /** @@ -152,7 +152,6 @@ else { element.setAttribute(name, value); } - } } // class ElementCreator @@ -174,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. @@ -198,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); @@ -222,7 +257,7 @@ * @return the new XPath. */ public static final XPath newXPath() { - return newXPath(null); + return newXPath(null, null); } /** @@ -231,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; } @@ -294,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 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); } @@ -422,5 +478,140 @@ } return null; } + + private static class BuildResult { + List children; + Map attributes; + BuildResult() { + children = new ArrayList(); + attributes = new LinkedHashMap(); + } + + void setAttributes(Element element) { + for (Map.Entry 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)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 map, + Document document + ) { + BuildResult result = new BuildResult(); + + List nodes = new ArrayList(); + for (Map.Entry entry: map.entrySet()) { + Object value = entry.getValue(); + if (value instanceof Map) { + Element element = document.createElement(entry.getKey()); + BuildResult subResult = recursiveBuild( + (Map)value, document); + subResult.finish(element); + result.add(element); + } + else if (value instanceof List) { + Element element = document.createElement(entry.getKey()); + BuildResult subResult = recursiveBuild((List)value, document); + subResult.finish(element); + result.add(element); + } + else { + result.add(entry.getKey(), value); + } + } + return result; + } + + public static Document jsonToXML(String input) { + Document document = newDocument(); + + if (document == null) { + return null; + } + + Map map; + try { + map = JSON.parse(input); + } + catch (IOException ioe) { + logger.error(ioe); + return null; + } + + BuildResult roots = recursiveBuild(map, document); + + int N = roots.children.size(); + + if (N == 1) { + document.appendChild(roots.children.get(0)); + } + else if (N > 1) { + Node root = document.createElement("root"); + for (int i = 0; i < N; ++i) { + root.appendChild(roots.children.get(i)); + } + document.appendChild(root); + } + + return document; + } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts-common/src/main/java/de/intevation/artifacts/common/utils/XSLTransformer.java --- /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 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 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(); + } + parameters.put(key, value); + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/pom.xml --- a/artifacts/pom.xml Tue Jun 28 07:51:17 2011 +0000 +++ b/artifacts/pom.xml Fri Sep 28 12:15:11 2012 +0200 @@ -18,6 +18,15 @@ + org.apache.maven.plugins + maven-compiler-plugin + 2.0.2 + + 1.6 + 1.6 + + + org.codehaus.mojo buildnumber-maven-plugin diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/Artifact.java --- a/artifacts/src/main/java/de/intevation/artifacts/Artifact.java Tue Jun 28 07:51:17 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: *
    - *
  1. {@link #setup(String, ArtifactFactory, Object, Document) setup()}: + *
  2. {@link #setup(String, ArtifactFactory, Object, CallMeta, Document) setup()}: * Called after created by the factory.
  3. *
  4. {@link #endOfLife(Object) endOfLife()}: Called when the artifact * is going to be removed from @@ -121,6 +121,7 @@ /** * 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. @@ -131,6 +132,7 @@ String identifier, ArtifactFactory factory, Object context, + CallMeta callMeta, Document data); /** diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/ArtifactContextFactory.java --- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactContextFactory.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java --- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactDatabase.java Tue Jun 28 07:51:17 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.NOTE: artifact modifications are not + * persisted to database! + */ + Artifact getRawArtifact(String identifier) + throws ArtifactDatabaseException; + + /** * Returns the describe document of artifact identified * with the string 'artifact'. * @param artifact The identifier of the artifact. @@ -198,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 @@ -206,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; @@ -214,6 +226,9 @@ // Collection API + Document getCollectionsMasterArtifact(String collectionId, CallMeta meta) + throws ArtifactDatabaseException; + Document listCollections(String userId, CallMeta callMeta) throws ArtifactDatabaseException; @@ -265,5 +280,19 @@ 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java --- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactFactory.java Tue Jun 28 07:51:17 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java --- a/artifacts/src/main/java/de/intevation/artifacts/ArtifactNamespaceContext.java Tue Jun 28 07:51:17 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 map; + /** * The default constructor. */ public ArtifactNamespaceContext() { + map = new HashMap(); + 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 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 results = new ArrayList(); + for (Map.Entry 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/CallContext.java --- a/artifacts/src/main/java/de/intevation/artifacts/CallContext.java Tue Jun 28 07:51:17 2011 +0000 +++ b/artifacts/src/main/java/de/intevation/artifacts/CallContext.java Fri Sep 28 12:15:11 2012 +0200 @@ -8,6 +8,9 @@ package de.intevation.artifacts; +import java.util.LinkedList; +import java.util.List; + import org.w3c.dom.Document; import org.w3c.dom.Node; @@ -65,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 getBackgroundMessages(); + + /** * Access to the global context of the runtime system. * @return The global context. */ @@ -87,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); @@ -106,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 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/DataProvider.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/GlobalContext.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/Hook.java --- /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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/Message.java --- /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 Ingo Weinzierl + */ +public interface Message extends Serializable { + + String getText(); +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/Service.java --- a/artifacts/src/main/java/de/intevation/artifacts/Service.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/ServiceFactory.java --- a/artifacts/src/main/java/de/intevation/artifacts/ServiceFactory.java Tue Jun 28 07:51:17 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 diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/User.java --- a/artifacts/src/main/java/de/intevation/artifacts/User.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e artifacts/src/main/java/de/intevation/artifacts/UserFactory.java --- a/artifacts/src/main/java/de/intevation/artifacts/UserFactory.java Tue Jun 28 07:51:17 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 : diff -r 98f6e6ae9498 -r 2c2981e53d4e pom.xml --- a/pom.xml Tue Jun 28 07:51:17 2011 +0000 +++ b/pom.xml Fri Sep 28 12:15:11 2012 +0200 @@ -27,8 +27,8 @@ maven-compiler-plugin 2.0.2 - 1.5 - 1.5 + 1.6 + 1.6