You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2006/08/15 09:09:08 UTC

svn commit: r431546 - in /jackrabbit/trunk/jcr-server: server/src/java/org/apache/jackrabbit/server/io/ server/src/java/org/apache/jackrabbit/webdav/simple/ webapp/src/webapp/WEB-INF/

Author: angela
Date: Tue Aug 15 00:09:07 2006
New Revision: 431546

URL: http://svn.apache.org/viewvc?rev=431546&view=rev
Log:
Webdav Simple: Delegate PROPPATCH to (extended) IOHandlers

Added:
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java   (with props)
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java   (with props)
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java   (with props)
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java   (with props)
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java   (with props)
Modified:
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DefaultHandler.java
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DirListingExportHandler.java
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java
    jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/ResourceConfig.java
    jackrabbit/trunk/jcr-server/webapp/src/webapp/WEB-INF/config.xml

Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DefaultHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DefaultHandler.java?rev=431546&r1=431545&r2=431546&view=diff
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DefaultHandler.java (original)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DefaultHandler.java Tue Aug 15 00:09:07 2006
@@ -17,7 +17,12 @@
 package org.apache.jackrabbit.server.io;
 
 import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.webdav.DavResource;
+import org.apache.jackrabbit.webdav.xml.Namespace;
+import org.apache.jackrabbit.webdav.property.DavPropertyName;
+import org.apache.jackrabbit.webdav.property.DavProperty;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -26,10 +31,19 @@
 import javax.jcr.NodeIterator;
 import javax.jcr.Property;
 import javax.jcr.RepositoryException;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Session;
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.nodetype.PropertyDefinition;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Iterator;
 
 /**
  * <code>DefaultHandler</code> implements a simple IOHandler that creates 'file'
@@ -53,13 +67,15 @@
  * Subclasses therefore should provide their own {@link #importData(ImportContext, boolean, Node)
  * importData} method, that handles the data according their needs.
  */
-public class DefaultHandler implements IOHandler {
+public class DefaultHandler implements IOHandler, PropertyHandler {
 
     private static Logger log = LoggerFactory.getLogger(DefaultHandler.class);
 
     private String collectionNodetype = JcrConstants.NT_FOLDER;
     private String defaultNodetype = JcrConstants.NT_FILE;
-    private String contentNodetype = JcrConstants.NT_RESOURCE;
+    /* IMPORTANT NOTE: for webDAV compliancy the default nodetype of the content
+       node has been changed from nt:resource to nt:unstructured. */
+    private String contentNodetype = JcrConstants.NT_UNSTRUCTURED;
 
     private IOManager ioManager;
 
@@ -97,7 +113,7 @@
      */
     public DefaultHandler(IOManager ioManager, String collectionNodetype, String defaultNodetype, String contentNodetype) {
         this.ioManager = ioManager;
-        
+
         this.collectionNodetype = collectionNodetype;
         this.defaultNodetype = defaultNodetype;
         this.contentNodetype = contentNodetype;
@@ -406,7 +422,8 @@
         try {
             // only non-collections: 'jcr:created' is present on the parent 'fileNode' only
             if (!isCollection && contentNode.getDepth() > 0 && contentNode.getParent().hasProperty(JcrConstants.JCR_CREATED)) {
-                context.setCreationTime(contentNode.getParent().getProperty(JcrConstants.JCR_CREATED).getValue().getLong());
+                long cTime = contentNode.getParent().getProperty(JcrConstants.JCR_CREATED).getValue().getLong();
+                context.setCreationTime(cTime);
             }
 
             long length = IOUtil.UNDEFINED_LENGTH;
@@ -444,6 +461,7 @@
             }
         } catch (RepositoryException e) {
             // should never occur
+            log.error("Unexpected error {0} while exporting properties: {1}", e.getClass().getName(), e.getMessage());
             throw new IOException(e.getMessage());
         }
     }
@@ -494,5 +512,194 @@
      */
     protected String getContentNodeType() {
         return contentNodetype;
+    }
+
+    //----------------------------------------------------< PropertyHandler >---
+
+    public boolean canExport(PropertyExportContext context, boolean isCollection) {
+        return canExport((ExportContext) context, isCollection);
+    }
+
+    public boolean exportProperties(PropertyExportContext exportContext, boolean isCollection) throws RepositoryException {
+        if (!canExport(exportContext, isCollection)) {
+            throw new RepositoryException("PropertyHandler " + getName() + " failed to export properties.");
+        }
+
+        Node cn = getContentNode(exportContext, isCollection);
+        try {
+            // export the properties common with normal IO handling
+            exportProperties(exportContext, isCollection, cn);
+            
+            // export all other properties as well
+            PropertyIterator it = cn.getProperties();
+            while (it.hasNext()) {
+                Property p = it.nextProperty();
+                String name = p.getName();
+                PropertyDefinition def = p.getDefinition();
+                if (def.isMultiple()) {
+                    log.debug("Multivalued property '" + name + "' not added to webdav property set.");
+                    continue;
+                }
+                if (JcrConstants.JCR_DATA.equals(name)
+                    || JcrConstants.JCR_MIMETYPE.equals(name)
+                    || JcrConstants.JCR_ENCODING.equals(name)
+                    || JcrConstants.JCR_LASTMODIFIED.equals(name)) {
+                    continue;
+                }
+
+                DavPropertyName davName = getDavName(name, p.getSession());
+                exportContext.setProperty(davName, p.getValue().getString());
+            }
+            return true;
+        } catch (IOException e) {
+            // should not occur (log output see 'exportProperties')
+            return false;
+        }
+    }
+
+    public boolean canImport(PropertyImportContext context, boolean isCollection) {
+        if (context == null || context.isCompleted()) {
+            return false;
+        }
+        Item contextItem = context.getImportRoot();
+        try {
+            return contextItem != null && contextItem.isNode() && (!isCollection || ((Node)contextItem).hasNode(JcrConstants.JCR_CONTENT));
+        } catch (RepositoryException e) {
+            log.error("Unexpected error: " + e.getMessage());
+            return false;
+        }
+    }
+
+    public Map importProperties(PropertyImportContext importContext, boolean isCollection) throws RepositoryException {
+        if (!canImport(importContext, isCollection)) {
+            throw new RepositoryException("PropertyHandler " + getName() + " failed import properties");
+        }
+
+        // loop over List and remember all properties and propertyNames
+        // that failed to be imported (set or remove).
+        Map failures = new HashMap();
+        List changeList = importContext.getChangeList();
+
+        // for collections the import-root is the target node where properties
+        // are altered. in contrast 'non-collections' are with the handler
+        // represented by 'file' nodes, that must have a jcr:content child
+        // node, which holds all properties except jcr:created.
+        // -> see canImport for the corresponding assertions
+        Node cn = (Node) importContext.getImportRoot();
+        if (!isCollection && cn.hasNode(JcrConstants.JCR_CONTENT)) {
+            cn = cn.getNode(JcrConstants.JCR_CONTENT);
+        }
+
+        if (changeList != null) {
+            Iterator it = changeList.iterator();
+            while (it.hasNext()) {
+                Object propEntry = it.next();
+                try {
+                    if (propEntry instanceof DavPropertyName) {
+                        // remove
+                        DavPropertyName propName = (DavPropertyName)propEntry;
+                        removeJcrProperty(propName, cn);
+                    } else if (propEntry instanceof DavProperty) {
+                        // add or modify property
+                        DavProperty prop = (DavProperty)propEntry;
+                        setJcrProperty(prop, cn);
+                    } else {
+                        // ignore any other entry in the change list
+                        log.error("unknown object in change list: " + propEntry.getClass().getName());
+                    }
+                } catch (RepositoryException e) {
+                    failures.put(propEntry, e);
+                }
+            }
+        }
+        return failures;
+    }
+
+    //------------------------------------------------------------< private >---
+    /**
+     * Builds a webdav property name from the given jcrName. In case the jcrName
+     * contains a namespace prefix that would conflict with any of the predefined
+     * webdav namespaces a new prefix is assigned.<br>
+     * Please note, that the local part of the jcrName is checked for XML
+     * compatibility by calling {@link ISO9075#encode(String)}
+     *
+     * @param jcrName
+     * @param session
+     * @return a <code>DavPropertyName</code> for the given jcr name.
+     */
+    private DavPropertyName getDavName(String jcrName, Session session) throws RepositoryException {
+        // make sure the local name is xml compliant
+        String localName = ISO9075.encode(Text.getLocalName(jcrName));
+        String prefix = Text.getNamespacePrefix(jcrName);
+        String uri = session.getNamespaceURI(prefix);
+        Namespace namespace = Namespace.getNamespace(prefix, uri);
+        DavPropertyName name = DavPropertyName.create(localName, namespace);
+        return name;
+    }
+
+    /**
+     * Build jcr property name from dav property name. If the property name
+     * defines a namespace uri, that has not been registered yet, an attempt
+     * is made to register the uri with the prefix defined. Note, that no
+     * extra effort is made to generated a unique prefix.
+     *
+     * @param propName
+     * @return jcr name
+     * @throws RepositoryException
+     */
+    private String getJcrName(DavPropertyName propName, Session session) throws RepositoryException {
+        // remove any encoding necessary for xml compliance
+        String pName = ISO9075.decode(propName.getName());
+        Namespace propNamespace = propName.getNamespace();
+        if (!Namespace.EMPTY_NAMESPACE.equals(propNamespace)) {
+            String prefix;
+            String emptyPrefix = Namespace.EMPTY_NAMESPACE.getPrefix();
+            try {
+                // lookup 'prefix' in the session-ns-mappings / namespace-registry
+                prefix = session.getNamespacePrefix(propNamespace.getURI());
+            } catch (NamespaceException e) {
+                // namespace uri has not been registered yet
+                NamespaceRegistry nsReg = session.getWorkspace().getNamespaceRegistry();
+                prefix = propNamespace.getPrefix();
+                // avoid trouble with default namespace
+                if (emptyPrefix.equals(prefix)) {
+                    prefix = "_pre" + nsReg.getPrefixes().length + 1;
+                }
+                // NOTE: will fail if prefix is already in use in the namespace registry
+                nsReg.registerNamespace(prefix, propNamespace.getURI());
+            }
+            if (prefix != null && !emptyPrefix.equals(prefix)) {
+                pName = prefix + ":" + pName;
+            }
+        }
+        return pName;
+    }
+
+
+    /**
+     * @param property
+     * @throws RepositoryException
+     */
+    private void setJcrProperty(DavProperty property, Node contentNode) throws RepositoryException {
+        // Retrieve the property value. Note, that a 'null' value is replaced
+        // by empty string, since setting a jcr property value to 'null'
+        // would be equivalent to its removal.
+        String value = "";
+        if (property.getValue() != null) {
+            value = property.getValue().toString();
+        }
+        contentNode.setProperty(getJcrName(property.getName(), contentNode.getSession()), value);
+    }
+
+    /**
+     * @param propertyName
+     * @throws RepositoryException
+     */
+    private void removeJcrProperty(DavPropertyName propertyName, Node contentNode) throws RepositoryException {
+        String jcrName = getJcrName(propertyName, contentNode.getSession());
+        if (contentNode.hasProperty(jcrName)) {
+            contentNode.getProperty(jcrName).remove();
+        }
+        // removal of non existing property succeeds
     }
 }

Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DirListingExportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DirListingExportHandler.java?rev=431546&r1=431545&r2=431546&view=diff
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DirListingExportHandler.java (original)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/DirListingExportHandler.java Tue Aug 15 00:09:07 2006
@@ -31,6 +31,7 @@
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Date;
+import java.util.Map;
 
 /**
  * <code>DirListingExportHandler</code> represents a simple export for collections:
@@ -38,7 +39,7 @@
  * Note: If {@link #exportContent(ExportContext, boolean)} is called the view list
  * child nodes only, without respecting their representation as <code>DavResource</code>s.
  */
-public class DirListingExportHandler implements IOHandler {
+public class DirListingExportHandler implements IOHandler, PropertyHandler {
 
     private static Logger log = LoggerFactory.getLogger(DirListingExportHandler.class);
 
@@ -46,6 +47,12 @@
 
     /**
      * Creates a new <code>DirListingExportHandler</code>
+     */
+    public DirListingExportHandler() {
+    }
+
+    /**
+     * Creates a new <code>DirListingExportHandler</code>
      *
      * @param ioManager
      */
@@ -251,11 +258,42 @@
     public void setIOManager(IOManager ioManager) {
         this.ioManager = ioManager;
     }
-    
+
     /**
      * @see IOHandler#getName()
      */
     public String getName() {
         return "DirListing Export";
+    }
+
+    //----------------------------------------------------< PropertyHandler >---
+
+    public boolean canExport(PropertyExportContext context, boolean isCollection) {
+        return canExport((ExportContext)context, isCollection);
+    }
+
+    /**
+     * @see PropertyHandler#exportProperties(PropertyExportContext, boolean)
+     */
+    public boolean exportProperties(PropertyExportContext exportContext, boolean isCollection) throws RepositoryException {
+        if (!canExport(exportContext, isCollection)) {
+            throw new RepositoryException(getName() + ": Cannot export properties for context " + exportContext);
+        }
+        exportContext.setModificationTime(new Date().getTime());
+        exportContext.setContentType("text/html", "UTF-8");
+        exportContext.setETag("");
+        return true;
+    }
+
+    public boolean canImport(PropertyImportContext context, boolean isCollection) {
+        return false;
+    }
+
+    /**
+     * @see PropertyHandler#importProperties(PropertyImportContext, boolean)
+     */
+    public Map importProperties(PropertyImportContext importContext, boolean isCollection) throws RepositoryException {
+        // export facilities only -> throw
+        throw new RepositoryException(getName() + ": Cannot import properties.");
     }
 }

Added: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java?rev=431546&view=auto
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java (added)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java Tue Aug 15 00:09:07 2006
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.server.io;
+
+/**
+ * <code>PropertyExportContext</code> represents a marker interface to distinguish
+ * the ExportContext (which is mainly used to export data and some fundamental
+ * properties) from a context that is used to export properties only.
+ */
+public interface PropertyExportContext extends ExportContext {
+
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyExportContext.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java?rev=431546&view=auto
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java (added)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java Tue Aug 15 00:09:07 2006
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.server.io;
+
+import javax.jcr.RepositoryException;
+import java.util.Map;
+
+/**
+ * <code>PropertyHandler</code> interface defines methods for importing and
+ * exporting resource properties.
+ */
+public interface PropertyHandler {
+
+    /**
+     * Returns true, if this handler can run a successful export based on the
+     * specified context.
+     *
+     * @param context
+     * @param isCollection
+     * @return true if this <code>PropertyHandler</code> is export properties
+     * given the specified parameters.
+     */
+    public boolean canExport(PropertyExportContext context, boolean isCollection);
+
+    /**
+     * Exports properties to the given context. Note that the export must
+     * be consistent with properties that might be exposed by content export
+     * such as defined by {@link IOHandler#exportContent(ExportContext, boolean)}.
+     *
+     * @param exportContext
+     * @param isCollection
+     * @return true if the export succeeded.
+     * @throws RepositoryException If an attempt is made to export properties
+     * even if {@link #canExport{PropertyExportContext, boolean)} returns false or if
+     * some other unrecoverable error occurs. Note, that
+     */
+    public boolean exportProperties(PropertyExportContext exportContext, boolean isCollection) throws RepositoryException;
+
+    /**
+     * Returns true, if this handler can run a property import based on the
+     * specified context.
+     *
+     * @param context
+     * @param isCollection
+     * @return true if this <code>PropertyHandler</code> can import properties
+     * given the specified parameters.
+     */
+    public boolean canImport(PropertyImportContext context, boolean isCollection);
+
+    /**
+     * Imports, modifies or removes properties according the the
+     * {@link PropertyImportContext#getChangeList() change list} available from
+     * the import context. Note, that according to JSR 170 setting a property
+     * value to <code>null</code> is equivalent to its removal.<p/>
+     * The return value of this method must be used to provided detailed
+     * information about any kind of failures.
+     *
+     * @param importContext
+     * @param isCollection
+     * @return Map listing those properties that failed to be updated. An empty
+     * map indicates a successful import for all properties listed in the context.
+     * @throws RepositoryException If {@link #canImport(PropertyImportContext, boolean)}
+     * returns false for the given parameters or if some other unrecoverable
+     * error occured. Note, that normal failure of a property update must be
+     * reported with the return value and should not result in an exception.
+     */
+    public Map importProperties(PropertyImportContext importContext, boolean isCollection) throws RepositoryException;
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java?rev=431546&view=auto
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java (added)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java Tue Aug 15 00:09:07 2006
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.server.io;
+
+import javax.jcr.Item;
+import java.util.List;
+
+/**
+ * <code>PropertyImportContext</code>...
+ */
+public interface PropertyImportContext extends IOContext {
+
+    /**
+     * Returns the import root for the properties to be altered. Note, that
+     * a particular implementation may still apply the modifications to
+     * child items at any depth as long as property import is consistent with
+     * the corresponding export.
+     *
+     * @return the import root of the resource to import.
+     */
+    public Item getImportRoot();
+
+    /**
+     * Returns a list of properties to be modified by a call to
+     * {@link PropertyHandler#importProperties(PropertyImportContext, boolean)}.
+     *
+     * @return list of properties to be modified
+     */
+    public List getChangeList();
+}

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyImportContext.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java?rev=431546&view=auto
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java (added)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java Tue Aug 15 00:09:07 2006
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.server.io;
+
+import javax.jcr.RepositoryException;
+import java.util.Map;
+
+/**
+ * <code>PropertyManager</code>...
+ */
+public interface PropertyManager {
+
+    /**
+     *
+     * @param exportContext
+     * @param isCollection
+     * @return
+     * @throws RepositoryException
+     */
+    public boolean exportProperties(PropertyExportContext exportContext, boolean isCollection) throws RepositoryException;
+
+    /**
+     *
+     * @param importContext
+     * @param isCollection
+     * @return
+     * @throws RepositoryException
+     */
+    public Map alterProperties(PropertyImportContext importContext, boolean isCollection) throws RepositoryException;
+
+    /**
+     *
+     * @param propertyHandler
+     */
+    public void addPropertyHandler(PropertyHandler propertyHandler);
+
+    /**
+     * 
+     * @return
+     */
+    public PropertyHandler[] getPropertyHandlers();
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java?rev=431546&view=auto
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java (added)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java Tue Aug 15 00:09:07 2006
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.server.io;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * <code>PropertyManagerImpl</code>...
+ */
+public class PropertyManagerImpl implements PropertyManager {
+
+    private static Logger log = LoggerFactory.getLogger(PropertyManagerImpl.class);
+
+    private static PropertyManager DEFAULT_MANAGER;
+
+    private final List propertyHandlers = new ArrayList();
+
+    /**
+     * Create a new <code>PropertyManagerImpl</code>.
+     * Note, that this manager does not define any <code>PropertyHandler</code>s by
+     * default. Use {@link #addPropertyHandler(PropertyHandler)} in order to populate the
+     * internal list of handlers that are called for <code>importProperties</code> and
+     * <code>exportProperties</code> respectively. See {@link #getDefaultManager()}
+     * for an instance of this class populated with default handlers.
+     */
+    public PropertyManagerImpl() {
+    }
+
+    /**
+     * @see PropertyManager#exportProperties(PropertyExportContext, boolean)
+     */
+    public boolean exportProperties(PropertyExportContext context, boolean isCollection) throws RepositoryException {
+        boolean success = false;
+        PropertyHandler[] propertyHandlers = getPropertyHandlers();
+        for (int i = 0; i < propertyHandlers.length && !success; i++) {
+            PropertyHandler ph = propertyHandlers[i];
+            if (ph.canExport(context, isCollection)) {
+                success = ph.exportProperties(context, isCollection);
+            }
+        }
+        context.informCompleted(success);
+        return success;
+    }
+
+    /**
+     * @see PropertyManager#alterProperties(PropertyImportContext, boolean)
+     */
+    public Map alterProperties(PropertyImportContext context, boolean isCollection) throws RepositoryException {
+        boolean foundHandler = false;
+        Map failures = new HashMap(0);
+        PropertyHandler[] propertyHandlers = getPropertyHandlers();
+        for (int i = 0; i < propertyHandlers.length && !foundHandler; i++) {
+            PropertyHandler ph = propertyHandlers[i];
+            if ((foundHandler = ph.canImport(context, isCollection))) {
+                failures = ph.importProperties(context, isCollection);
+            }
+        }
+        if (!foundHandler) {
+            throw new RepositoryException("Unable to alter properties: No matching handler found.");
+        }
+        context.informCompleted(failures.isEmpty());
+        return failures;
+    }
+
+    /**
+     * @see PropertyManager#addPropertyHandler(PropertyHandler)
+     */
+    public void addPropertyHandler(PropertyHandler propertyHandler) {
+        if (propertyHandler == null) {
+            throw new IllegalArgumentException("'null' is not a valid IOHandler.");
+        }
+        propertyHandlers.add(propertyHandler);
+    }
+
+    /**
+     * @see PropertyManager#getPropertyHandlers()
+     */
+    public PropertyHandler[] getPropertyHandlers() {
+        return (PropertyHandler[]) propertyHandlers.toArray(new PropertyHandler[propertyHandlers.size()]);
+    }
+
+    /**
+     * @return an instance of PropertyManager populated with default handlers.
+     */
+    public static PropertyManager getDefaultManager() {
+        if (DEFAULT_MANAGER == null) {
+            DEFAULT_MANAGER = new PropertyManagerImpl();
+            DEFAULT_MANAGER.addPropertyHandler(new ZipHandler());
+            DEFAULT_MANAGER.addPropertyHandler(new XmlHandler());
+            DEFAULT_MANAGER.addPropertyHandler(new DirListingExportHandler());
+            DEFAULT_MANAGER.addPropertyHandler(new DefaultHandler());
+        }
+        return DEFAULT_MANAGER;
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/server/io/PropertyManagerImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java?rev=431546&r1=431545&r2=431546&view=diff
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java (original)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/DavResourceImpl.java Tue Aug 15 00:09:07 2006
@@ -17,14 +17,18 @@
 package org.apache.jackrabbit.webdav.simple;
 
 import org.apache.jackrabbit.JcrConstants;
-import org.apache.jackrabbit.server.io.AbstractExportContext;
 import org.apache.jackrabbit.server.io.ExportContext;
 import org.apache.jackrabbit.server.io.ExportContextImpl;
 import org.apache.jackrabbit.server.io.IOManager;
 import org.apache.jackrabbit.server.io.IOUtil;
 import org.apache.jackrabbit.server.io.ImportContext;
 import org.apache.jackrabbit.server.io.ImportContextImpl;
-import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.server.io.PropertyManager;
+import org.apache.jackrabbit.server.io.PropertyImportContext;
+import org.apache.jackrabbit.server.io.IOListener;
+import org.apache.jackrabbit.server.io.PropertyExportContext;
+import org.apache.jackrabbit.server.io.AbstractExportContext;
+import org.apache.jackrabbit.server.io.DefaultIOListener;
 import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.webdav.DavConstants;
 import org.apache.jackrabbit.webdav.DavException;
@@ -57,22 +61,16 @@
 import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
 import org.apache.jackrabbit.webdav.property.ResourceType;
 import org.apache.jackrabbit.webdav.property.DavPropertyNameIterator;
-import org.apache.jackrabbit.webdav.xml.Namespace;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.jcr.Item;
-import javax.jcr.NamespaceException;
-import javax.jcr.NamespaceRegistry;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.PathNotFoundException;
-import javax.jcr.Property;
-import javax.jcr.PropertyIterator;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.lock.Lock;
-import javax.jcr.nodetype.PropertyDefinition;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
@@ -80,6 +78,8 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Collections;
 
 /**
  * DavResourceImpl implements a DavResource.
@@ -110,6 +110,7 @@
 
     private ItemFilter filter;
     private IOManager ioManager;
+    private PropertyManager propManager;
 
     private long modificationTime = IOUtil.UNDEFINED_TIME;
 
@@ -128,6 +129,7 @@
         this.locator = locator;
         this.filter = config.getItemFilter();
         this.ioManager = config.getIOManager();
+        this.propManager = config.getPropertyManager();
 
         if (locator != null && locator.getResourcePath() != null) {
             try {
@@ -286,11 +288,12 @@
         }
 
         try {
-            ioManager.exportContent(new PropertyExportCtx(), this);
-        } catch (IOException e) {
-            // should not occure....
+            propManager.exportProperties(getPropertyExportContext(), isCollection());
+        } catch (RepositoryException e) {
+            log.warn("Error while accessing resource properties", e);
         }
 
+        // set (or reset) fundamental properties
         if (getDisplayName() != null) {
             properties.add(new DefaultDavProperty(DavPropertyName.DISPLAYNAME, getDisplayName()));
         }
@@ -313,24 +316,6 @@
         supportedLock.addEntry(Type.WRITE, Scope.EXCLUSIVE);
         properties.add(supportedLock);
 
-        // non-protected JCR properties defined on the underlying jcr node
-        try {
-            PropertyIterator it = node.getProperties();
-            while (it.hasNext()) {
-                Property p = it.nextProperty();
-                String pName = p.getName();
-                PropertyDefinition def = p.getDefinition();
-                if (def.isMultiple() || isFilteredItem(p)) {
-                    log.debug("Property '" + pName + "' not added to webdav property set (either multivalue or filtered).");
-                    continue;
-                }
-                DavPropertyName name = getDavName(pName, node.getSession());
-                String value = p.getValue().getString();
-                properties.add(new DefaultDavProperty(name, value, def.isProtected()));
-            }
-        } catch (RepositoryException e) {
-            log.error("Unexpected error while retrieving properties: " + e.getMessage());
-        }
         inited = true;
     }
 
@@ -340,25 +325,7 @@
      * @see DavResource#setProperty(org.apache.jackrabbit.webdav.property.DavProperty)
      */
     public void setProperty(DavProperty property) throws DavException {
-        if (isLocked(this)) {
-            throw new DavException(DavServletResponse.SC_LOCKED);
-        }
-        if (!exists()) {
-            throw new DavException(DavServletResponse.SC_NOT_FOUND);
-        }
-        try {
-            setJcrProperty(property);
-            node.save();
-        } catch (RepositoryException e) {
-            // revert any changes made so far an throw exception
-            JcrDavException je = new JcrDavException(e);
-            try {
-                node.refresh(false);
-            } catch (RepositoryException re) {
-                // should not happen...
-            }
-            throw je;
-        }
+        alterProperty(property);
     }
 
     /**
@@ -367,6 +334,10 @@
      * @see DavResource#removeProperty(org.apache.jackrabbit.webdav.property.DavPropertyName)
      */
     public void removeProperty(DavPropertyName propertyName) throws DavException {
+        alterProperty(propertyName);
+    }
+
+    private void alterProperty(Object prop) throws DavException {
         if (isLocked(this)) {
             throw new DavException(DavServletResponse.SC_LOCKED);
         }
@@ -374,9 +345,19 @@
             throw new DavException(DavServletResponse.SC_NOT_FOUND);
         }
         try {
-            removeJcrProperty(propertyName);
-            node.save();
+            List l = new ArrayList(1);
+            l.add(prop);
+            alterProperties(l);
+            Map failure = propManager.alterProperties(getPropertyImportContext(l), isCollection());
+            if (failure.isEmpty()) {
+                node.save();
+            } else {
+                node.refresh(false);
+                // TODO: retrieve specific error from failure-map
+                throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
         } catch (RepositoryException e) {
+            // revert any changes made so far
             JcrDavException je = new JcrDavException(e);
             try {
                 node.refresh(false);
@@ -406,7 +387,6 @@
                 changeList.add(it.next());
             }
         }
-
         return alterProperties(changeList);
     }
 
@@ -418,39 +398,9 @@
             throw new DavException(DavServletResponse.SC_NOT_FOUND);
         }
         MultiStatusResponse msr = new MultiStatusResponse(getHref(), null);
-        boolean success = true;
-
-        // loop over List and remember all properties and propertyNames
-        // that have successfully been altered
-        List successList = new ArrayList();
-        Iterator it = changeList.iterator();
-        while (it.hasNext()) {
-            Object propEntry = it.next();
-            if (propEntry instanceof DavPropertyName) {
-                DavPropertyName propName = (DavPropertyName)propEntry;
-                try {
-                    removeJcrProperty(propName);
-                    successList.add(propName);
-                } catch (RepositoryException e) {
-                    msr.add(propName, new JcrDavException(e).getErrorCode());
-                    success = false;
-                }
-            } else if (propEntry instanceof DavProperty) {
-                DavProperty prop = (DavProperty)propEntry;
-                try {
-                    setJcrProperty(prop);
-                    successList.add(prop);
-                } catch (RepositoryException e) {
-                    msr.add(prop.getName(), new JcrDavException(e).getErrorCode());
-                    success = false;
-                }
-            } else {
-                throw new IllegalArgumentException("unknown object in change list: " + propEntry.getClass().getName());
-            }
-        }
-
         try {
-            if (success) {
+            Map failures = propManager.alterProperties(getPropertyImportContext(changeList), isCollection());
+            if (failures.isEmpty()) {
                 // save all changes together (reverted in case this fails)
                 node.save();
             } else {
@@ -462,14 +412,22 @@
                complete action. in case of failure set the status to 'failed-dependency'
                in order to indicate, that altering those names/properties would
                have succeeded, if no other error occured.*/
-            it = successList.iterator();
+            Iterator it = changeList.iterator();
             while (it.hasNext()) {
                 Object o = it.next();
-                int status = (success) ? DavServletResponse.SC_OK : DavServletResponse.SC_FAILED_DEPENDENCY;
+                int statusCode;
+                if (failures.containsKey(o)) {
+                    Object error = failures.get(o);
+                    statusCode = (error instanceof RepositoryException)
+                        ? new JcrDavException((RepositoryException)o).getErrorCode()
+                        : DavServletResponse.SC_INTERNAL_SERVER_ERROR;
+                } else {
+                    statusCode = (failures.isEmpty()) ? DavServletResponse.SC_OK : DavServletResponse.SC_FAILED_DEPENDENCY;
+                }
                 if (o instanceof DavProperty) {
-                    msr.add(((DavProperty) o).getName(), status);
+                    msr.add(((DavProperty) o).getName(), statusCode);
                 } else {
-                    msr.add((DavPropertyName) o, status);
+                    msr.add((DavPropertyName) o, statusCode);
                 }
             }
             return msr;
@@ -840,6 +798,25 @@
     }
 
     /**
+     * Returns a new <code>PropertyImportContext</code>.
+     *
+     * @param changeList
+     * @return
+     */
+    protected PropertyImportContext getPropertyImportContext(List changeList) {
+        return new ProperyImportCtx(changeList);
+    }
+
+    /**
+     * Returns a new <code>PropertyExportContext</code>.
+     *
+     * @return
+     */
+    protected PropertyExportContext getPropertyExportContext() {
+        return new PropertyExportCtx();
+    }
+
+    /**
      * Returns true, if the underlying node is nodetype jcr:lockable,
      * without checking its current lock status. If the node is not jcr-lockable
      * an attempt is made to add the mix:lockable mixin type.
@@ -885,97 +862,6 @@
         }
     }
 
-    /**
-     * Builds a webdav property name from the given jcrName. In case the jcrName
-     * contains a namespace prefix that would conflict with any of the predefined
-     * webdav namespaces a new prefix is assigned.<br>
-     * Please note, that the local part of the jcrName is checked for XML
-     * compatibility by calling {@link ISO9075#encode(String)}
-     *
-     * @param jcrName
-     * @param session
-     * @return a <code>DavPropertyName</code> for the given jcr name.
-     */
-    private DavPropertyName getDavName(String jcrName, Session session) throws RepositoryException {
-        // make sure the local name is xml compliant
-        String localName = ISO9075.encode(Text.getLocalName(jcrName));
-        String prefix = Text.getNamespacePrefix(jcrName);
-        String uri = session.getNamespaceURI(prefix);
-        // check for conflicts with reserved webdav-namespaces
-        if (reservedNamespaces.containsKey(prefix) && !reservedNamespaces.get(prefix).equals(uri)) {
-            prefix = prefix + "0";
-        }
-        Namespace namespace = Namespace.getNamespace(prefix, uri);
-        DavPropertyName name = DavPropertyName.create(localName, namespace);
-        return name;
-    }
-
-    /**
-     * Build jcr property name from dav property name. If the property name
-     * defines a namespace uri, that has not been registered yet, an attempt
-     * is made to register the uri with the prefix defined. Note, that no
-     * extra effort is made to generated a unique prefix.
-     *
-     * @param propName
-     * @return jcr name
-     * @throws RepositoryException
-     */
-    private String getJcrName(DavPropertyName propName) throws RepositoryException {
-        // remove any encoding necessary for xml compliance
-        String pName = ISO9075.decode(propName.getName());
-        Namespace propNamespace = propName.getNamespace();
-        if (!Namespace.EMPTY_NAMESPACE.equals(propNamespace)) {
-            Session s = getJcrSession();
-            String prefix;
-            String emptyPrefix = Namespace.EMPTY_NAMESPACE.getPrefix();
-            try {
-                // lookup 'prefix' in the session-ns-mappings / namespace-registry
-                prefix = s.getNamespacePrefix(propNamespace.getURI());
-            } catch (NamespaceException e) {
-                // namespace uri has not been registered yet
-                NamespaceRegistry nsReg = s.getWorkspace().getNamespaceRegistry();
-                prefix = propNamespace.getPrefix();
-                // avoid trouble with default namespace
-                if (emptyPrefix.equals(prefix)) {
-                    prefix = "_pre" + nsReg.getPrefixes().length + 1;
-                }
-                // NOTE: will fail if prefix is already in use in the namespace registry
-                nsReg.registerNamespace(prefix, propNamespace.getURI());
-            }
-            if (prefix != null && !emptyPrefix.equals(prefix)) {
-                pName = prefix + ":" + pName;
-            }
-        }
-        return pName;
-    }
-
-    /**
-     * @param property
-     * @throws RepositoryException
-     */
-    private void setJcrProperty(DavProperty property) throws RepositoryException {
-        // Retrieve the property value. Note, that a 'null' value is replaced
-        // by empty string, since setting a jcr property value to 'null'
-        // would be equivalent to its removal.
-        String value = "";
-        if (property.getValue() != null) {
-            value = property.getValue().toString();
-        }
-        node.setProperty(getJcrName(property.getName()), value);
-    }
-
-    /**
-     * @param propertyName
-     * @throws RepositoryException
-     */
-    private void removeJcrProperty(DavPropertyName propertyName) throws RepositoryException {
-        String jcrName = getJcrName(propertyName);
-        if (node.hasProperty(jcrName)) {
-            node.getProperty(jcrName).remove();
-        }
-        // removal of non existing property succeeds
-    }
-
     private Session getJcrSession() {
         return session.getRepositorySession();
     }
@@ -994,7 +880,7 @@
      * ExportContext that writes the properties of this <code>DavResource</code>
      * and provides not stream.
      */
-    private class PropertyExportCtx extends AbstractExportContext {
+    private class PropertyExportCtx extends AbstractExportContext implements PropertyExportContext {
 
         private PropertyExportCtx() {
             super(node, false, null);
@@ -1048,9 +934,81 @@
         }
 
         public void setProperty(Object propertyName, Object propertyValue) {
-            if (propertyName instanceof DavPropertyName) {
-                DavPropertyName pName = (DavPropertyName)propertyName;
+            if (propertyValue == null) {
+                log.warn("Ignore 'setProperty' for " + propertyName + "with null value.");
+                return;
+            }
+
+            if (propertyValue instanceof DavProperty) {
+                properties.add((DavProperty)propertyValue);
+            } else {
+                DavPropertyName pName;
+                if (propertyName instanceof DavPropertyName) {
+                    pName = (DavPropertyName)propertyName;
+                } else {
+                    // create property name with default DAV: namespace
+                    pName = DavPropertyName.create(propertyName.toString());
+                }
                 properties.add(new DefaultDavProperty(pName, propertyValue));
+            }
+        }
+    }
+
+    private class ProperyImportCtx implements PropertyImportContext {
+
+        private final IOListener ioListener = new DefaultIOListener(log);
+        private final List changeList;
+        private boolean completed;
+
+        private ProperyImportCtx(List changeList) {
+            this.changeList = changeList;
+        }
+
+        /**
+         * @see PropertyImportContext#getImportRoot()
+         */
+        public Item getImportRoot() {
+            return node;
+        }
+
+        /**
+         * @see PropertyImportContext#getChangeList()
+         */
+        public List getChangeList() {
+            return Collections.unmodifiableList(changeList);
+        }
+
+        public IOListener getIOListener() {
+            return ioListener;
+        }
+
+        public boolean hasStream() {
+            return false;
+        }
+
+        /**
+         * @see PropertyImportContext#informCompleted(boolean)
+         */
+        public void informCompleted(boolean success) {
+            checkCompleted();
+            completed = true;
+        }
+
+        /**
+         * @see PropertyImportContext#isCompleted()
+         */
+        public boolean isCompleted() {
+            return completed;
+        }
+
+        /**
+         * @throws IllegalStateException if the context is already completed.
+         * @see #isCompleted()
+         * @see #informCompleted(boolean)
+         */
+        private void checkCompleted() {
+            if (completed) {
+                throw new IllegalStateException("PropertyImportContext has already been consumed.");
             }
         }
     }

Modified: jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/ResourceConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/ResourceConfig.java?rev=431546&r1=431545&r2=431546&view=diff
==============================================================================
--- jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/ResourceConfig.java (original)
+++ jackrabbit/trunk/jcr-server/server/src/java/org/apache/jackrabbit/webdav/simple/ResourceConfig.java Tue Aug 15 00:09:07 2006
@@ -19,6 +19,9 @@
 import org.apache.jackrabbit.server.io.IOManager;
 import org.apache.jackrabbit.server.io.DefaultIOManager;
 import org.apache.jackrabbit.server.io.IOHandler;
+import org.apache.jackrabbit.server.io.PropertyManager;
+import org.apache.jackrabbit.server.io.PropertyHandler;
+import org.apache.jackrabbit.server.io.PropertyManagerImpl;
 import org.apache.jackrabbit.webdav.xml.ElementIterator;
 import org.apache.jackrabbit.webdav.xml.DomUtil;
 import org.slf4j.Logger;
@@ -47,6 +50,7 @@
 
     private ItemFilter itemFilter;
     private IOManager ioManager;
+    private PropertyManager propManager;
     private String[] nodetypeNames = new String[0];
     private boolean collectionNames = false;
 
@@ -54,9 +58,11 @@
      * Tries to parse the given xml configuration file.
      * The xml must match the following structure:<br>
      * <pre>
-     * &lt;!ELEMENT config (iomanager, (collection | noncollection)?, filter?) &gt;
+     * &lt;!ELEMENT config (iomanager, propertymanager, (collection | noncollection)?, filter?) &gt;
      * &lt;!ELEMENT iomanager (class, iohandler*) &gt;
      * &lt;!ELEMENT iohandler (class) &gt;
+     * &lt;!ELEMENT propertymanager (class, propertyhandler*) &gt;
+     * &lt;!ELEMENT propertyhandler (class) &gt;
      * &lt;!ELEMENT collection (nodetypes) &gt;
      * &lt;!ELEMENT noncollection (nodetypes) &gt;
      * &lt;!ELEMENT filter (class, namespaces?, nodetypes?) &gt;
@@ -81,7 +87,7 @@
             Element config = document.getDocumentElement();
 
             if (config == null) {
-                log.error("Resource configuration: mandatory 'iomanager' element is missing.");
+                log.warn("Resource configuration: mandatory 'config' element is missing.");
                 return;
             }
 
@@ -97,16 +103,40 @@
                         Element iohEl = iohElements.nextElement();
                         inst = buildClassFromConfig(iohEl);
                         if (inst != null && inst instanceof IOHandler) {
-                            this.ioManager.addIOHandler((IOHandler) inst);
+                            ioManager.addIOHandler((IOHandler) inst);
                         } else {
-                            log.error("Resource configuration: the handler is not a valid IOHandler.");
+                            log.warn("Resource configuration: the handler is not a valid IOHandler.");
                         }
                     }
                 } else {
-                    log.error("Resource configuration: 'iomanager' does not define a valid IOManager.");
+                    log.warn("Resource configuration: 'iomanager' does not define a valid IOManager.");
                 }
             } else {
-                log.error("Resource configuration: mandatory 'iomanager' element is missing.");
+                log.warn("Resource configuration: 'iomanager' element is missing.");
+            }
+
+            el = DomUtil.getChildElement(config, "propertymanager", null);
+            if (el != null) {
+                Object inst = buildClassFromConfig(el);
+                if (inst != null && inst instanceof PropertyManager) {
+                    propManager = (PropertyManager)inst;
+                    // get optional 'iohandler' child elements and populate the
+                    // ioManager with the instances
+                    ElementIterator iohElements = DomUtil.getChildren(el, "propertyhandler", null);
+                    while (iohElements.hasNext()) {
+                        Element iohEl = iohElements.nextElement();
+                        inst = buildClassFromConfig(iohEl);
+                        if (inst != null && inst instanceof PropertyHandler) {
+                            propManager.addPropertyHandler((PropertyHandler) inst);
+                        } else {
+                            log.warn("Resource configuration: the handler is not a valid PropertyHandler.");
+                        }
+                    }
+                } else {
+                    log.warn("Resource configuration: 'propertymanager' does not define a valid PropertyManager.");
+                }
+            } else {
+                log.debug("Resource configuration: 'propertymanager' element is missing.");
             }
 
             el = DomUtil.getChildElement(config, "collection", null);
@@ -202,13 +232,28 @@
         return ntNames;
     }
 
-
+    /**
+     *
+     * @return
+     */
     public IOManager getIOManager() {
         if (ioManager == null) {
             log.debug("ResourceConfig: missing io-manager > building DefaultIOManager ");
             ioManager = new DefaultIOManager();
         }
         return ioManager;
+    }
+
+    /**
+     *
+     * @return
+     */
+    public PropertyManager getPropertyManager() {
+        if (propManager == null) {
+            log.debug("ResourceConfig: missing property-manager > building default.");
+            propManager = PropertyManagerImpl.getDefaultManager();
+        }
+        return propManager;
     }
 
     /**

Modified: jackrabbit/trunk/jcr-server/webapp/src/webapp/WEB-INF/config.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jcr-server/webapp/src/webapp/WEB-INF/config.xml?rev=431546&r1=431545&r2=431546&view=diff
==============================================================================
--- jackrabbit/trunk/jcr-server/webapp/src/webapp/WEB-INF/config.xml (original)
+++ jackrabbit/trunk/jcr-server/webapp/src/webapp/WEB-INF/config.xml Tue Aug 15 00:09:07 2006
@@ -1,11 +1,14 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
 <!DOCTYPE config [
-        <!ELEMENT config (iomanager , (collection | noncollection)? , filter?) >
+        <!ELEMENT config (iomanager , propertymanager, (collection | noncollection)? , filter?) >
 
         <!ELEMENT iomanager (class, iohandler*) >
         <!ELEMENT iohandler (class) >
 
+        <!ELEMENT propertymanager (class, propertyhandler*) >
+        <!ELEMENT propertyhandler (class) >
+
         <!ELEMENT collection (nodetypes) >
         <!ELEMENT noncollection (nodetypes) >
 
@@ -31,38 +34,57 @@
     -->
     <iomanager>
         <!-- class element defines the manager to be used. The specified class
-             must implement the IOManager interface. -->
-        <!-- the DefaultIOManager builds a predefined set of handlers. the
-             optional 'iohandler' elements are therefore left out. -->
-        <class name="org.apache.jackrabbit.server.io.DefaultIOManager" />
-        
-    </iomanager>
-    <!--
-    Example config for iomanager that defines the set of handlers to be used.
-    Note, that the handlers are being added and called in the order they appear
-    in the configuration.
-    -->
-    <!--
-    <iomanager>
+             must implement the IOManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
         <class name="org.apache.jackrabbit.server.io.IOManagerImpl" />
         <iohandler>
-           <class name="org.apache.jackrabbit.server.io.XmlHandler" />
+            <class name="org.apache.jackrabbit.server.io.ZipHandler" />
         </iohandler>
         <iohandler>
-           <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+            <class name="org.apache.jackrabbit.server.io.XmlHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DirListingExportHandler" />
+        </iohandler>
+        <iohandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
         </iohandler>
     </iomanager>
+    <!--
+    Example config for iomanager that populates its list of handlers with
+    default values. Therefore the 'iohandler' elements are omited.
     -->
     <!--
-    Defines nodetypes, that should always be displayed as 'collection'.
+    <iomanager>
+        <class name="org.apache.jackrabbit.server.io.DefaultIOManager" />
+    </iomanager>
     -->
     <!--
-    <collection>
-        <nodetypes>
-            <nodetype>nt:folder</nodetype>
-        </nodetypes>
-    </collection>
+    Defines the PropertyManager implementation that is responsible for export
+    and import of resource properties.
     -->
+    <propertymanager>
+        <!-- class element defines the manager to be used. The specified class
+             must implement the PropertyManager interface.
+             Note, that the handlers are being added and called in the order
+             they appear in the configuration.
+        -->
+        <class name="org.apache.jackrabbit.server.io.PropertyManagerImpl" />
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.ZipHandler" />
+        </propertyhandler>
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.XmlHandler" />
+        </propertyhandler>
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.DirListingExportHandler" />
+        </propertyhandler>
+        <propertyhandler>
+            <class name="org.apache.jackrabbit.server.io.DefaultHandler" />
+        </propertyhandler>
+    </propertymanager>
     <!--
     Define nodetypes, that should never by displayed as 'collection'
     -->
@@ -72,6 +94,17 @@
             <nodetype>nt:resource</nodetype>
         </nodetypes>
     </noncollection>
+    <!--
+    Example: Defines nodetypes, that should always be displayed as 'collection'.
+    -->
+    <!--
+    <collection>
+        <nodetypes>
+            <nodetype>nt:folder</nodetype>
+            <nodetype>rep:root</nodetype>
+        </nodetypes>
+    </collection>
+    -->
     <!--
      Filter that allows to prevent certain items from being displayed.
      Please note, that this has an effect on PROPFIND calls only and does not