You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by br...@apache.org on 2008/03/13 19:28:50 UTC

svn commit: r636822 [5/9] - in /maven/archiva/branches/springy: ./ archiva-base/archiva-common/ archiva-base/archiva-consumers/archiva-database-consumers/src/test/java/org/apache/maven/archiva/consumers/database/ archiva-base/archiva-consumers/archiva-...

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVOutputStream.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVOutputStream.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVOutputStream.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVOutputStream.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,187 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+
+/**
+ * <p>A specialized {@link OutputStream} to write to {@link DAVResource}s.</p>
+ * 
+ * <p>When writing to this {@link OutputStream} the data will be written to
+ * a temporary file. This temporary file will be moved to its final destination
+ * (the original file identifying the resource) when the {@link #close()}
+ * method is called.</p>
+ *
+ * <p>This specialized {@link OutputStream} never throws {@link IOException}s,
+ * but rather relies on the unchecked {@link DAVException} to notify the
+ * framework of the correct DAV errors.</p>
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVOutputStream extends OutputStream {
+
+    /** <p>The original resource {@link File}.</p> */
+    private File temporary = null;
+    /** <p>The {@link OutputStream} of the temporary {@link File}. </p> */
+    protected OutputStream output = null;
+    /** <p>The {@link DAVResource} associated with this instance. </p> */
+    private DAVResource resource = null;
+
+    /**
+     * <p>Create a new {@link DAVOutputStream} instance.</p>
+     */
+    protected DAVOutputStream(DAVResource resource) {
+        if (resource == null) throw new NullPointerException();
+        this.resource = resource;
+        init(resource);
+    }
+    
+    protected void init(DAVResource resource) {
+        try {
+            this.temporary = resource.getParent().getFile();
+            this.temporary = File.createTempFile(DAVResource.PREFIX,
+                                                 DAVResource.SUFFIX,
+                                                 this.temporary);
+            this.output = new FileOutputStream(this.temporary);
+        } catch (IOException e) {
+            String message = "Unable to create temporary file";
+            throw new DAVException(507, message, e, resource);
+        }
+    }
+
+    /**
+     * <p>Rename the temporary {@link File} to the original one.</p>
+     */
+    protected void rename(File temporary, File original)
+    throws IOException {
+        if ((original.exists()) && (!original.delete())) {
+            throw new IOException("Unable to delete original file");
+        }
+        if (!temporary.renameTo(original)) {
+            throw new IOException("Unable to rename temporary file");
+        }
+    }
+
+    /**
+     * <p>Abort any data written to the temporary file and delete it.</p>
+     */
+    public void abort() {
+        if (this.temporary.exists()) this.temporary.delete();
+        if (this.output != null) try {
+            this.output.close();
+        } catch (IOException exception) {
+            // Swallow the IOException on close
+        } finally {
+            this.output = null;
+        }
+    }
+
+    /**
+     * <p>Close this {@link OutputStream} {@link #rename(File,File) renaming}
+     * the temporary file to the {@link DAVResource#getFile() original} one.</p>
+     */
+    public void close() {
+        if (this.output == null) return;
+        try {
+            /* What kind of event should this invocation trigger? */
+            int event = this.resource.getFile().exists() ?
+                        DAVListener.RESOURCE_MODIFIED:
+                        DAVListener.RESOURCE_CREATED;
+
+            /* Make sure that everything is closed and named properly */
+            this.output.close();
+            this.output = null;
+            this.rename(this.temporary, this.resource.getFile());
+
+            /* Send notifications to all listeners of the repository */
+            this.resource.getRepository().notify(this.resource, event);
+
+        } catch (IOException e) {
+            String message = "Error processing temporary file";
+            throw new DAVException(507, message, e, this.resource);
+        } finally {
+            this.abort();
+        }
+    }
+
+    /**
+     * <p>Flush any unwritten data to the disk.</p>
+     */
+    public void flush() {
+        if (this.output == null) throw new IllegalStateException("Closed");
+        try {
+            this.output.flush();
+        } catch (IOException e) {
+            this.abort();
+            String message = "Unable to flush buffers";
+            throw new DAVException(507, message, e, this.resource);
+        }
+    }
+
+    /**
+     * <p>Write data to this {@link OutputStream}.</p>
+     */
+    public void write(int b) {
+        if (this.output == null) throw new IllegalStateException("Closed");
+        try {
+            this.output.write(b);
+        } catch (IOException e) {
+            this.abort();
+            String message = "Unable to write data";
+            throw new DAVException(507, message, e, this.resource);
+        }
+    }
+    
+    /**
+     * <p>Write data to this {@link OutputStream}.</p>
+     */
+    public void write(byte b[]) {
+        if (this.output == null) throw new IllegalStateException("Closed");
+        try {
+            this.output.write(b);
+        } catch (IOException e) {
+            this.abort();
+            String message = "Unable to write data";
+            throw new DAVException(507, message, e, this.resource);
+        }
+    }
+    
+    /**
+     * <p>Write data to this {@link OutputStream}.</p>
+     */
+    public void write(byte b[], int o, int l) {
+        if (this.output == null) throw new IllegalStateException("Closed");
+        try {
+            this.output.write(b, o, l);
+        } catch (IOException e) {
+            this.abort();
+            String message = "Unable to write data";
+            throw new DAVException(507, message, e, this.resource);
+        }
+    }
+    
+    /**
+     * <p>Finalize this {@link DAVOutputStream} instance.</p>
+     */
+    public void finalize() {
+        this.abort();
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVProcessor.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVProcessor.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVProcessor.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVProcessor.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,92 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/**
+ * <p>The <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
+ * transactions processor.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVProcessor {
+
+    /** <p>All the implemented methods, comma separated.</p> */
+    public static final String METHODS = "COPY,DELETE,GET,HEAD,MKCOL,MOVE," + 
+                                         "OPTIONS,PROPFIND,PROPPATCH,PUT";
+
+    /** <p>A static map of all known webdav methods.</p> */
+    private static Map INSTANCES = new HashMap();
+    static {
+        /* Load and verify all the known methods */
+        final String thisName = DAVProcessor.class.getName();
+        final int packageDelimiter = thisName.lastIndexOf('.');
+        final String packageName = packageDelimiter < 1 ? "methods." :
+                        thisName.substring(0, packageDelimiter) + ".methods.";
+        final StringTokenizer tokenizer = new StringTokenizer(METHODS, ",");
+        final ClassLoader classLoader = DAVProcessor.class.getClassLoader();
+        while (tokenizer.hasMoreTokens()) try {
+            final String method = tokenizer.nextToken();
+            final String className = packageName + method;
+            final Class clazz = classLoader.loadClass(className);
+            INSTANCES.put(method, (DAVMethod) clazz.newInstance());
+        } catch (Throwable throwable) {
+            InternalError error = new InternalError("Error loading method");
+            throw (InternalError) error.initCause(throwable);
+        }
+    }
+
+    /** <p>The {@link DAVRepository} associated with this instance.</p> */
+    private DAVRepository repository = null;
+
+    /**
+     * <p>Create a new {@link DAVProcessor} instance.</p>
+     */
+    public DAVProcessor(DAVRepository repository) {
+        if (repository == null) throw new NullPointerException();
+        this.repository = repository;
+    }
+
+    /**
+     * <p>Process the specified {@link DAVTransaction} fully.</p>
+     */
+    public void process(DAVTransaction transaction)
+    throws IOException {
+        try {
+            String method = transaction.getMethod();
+            if (INSTANCES.containsKey(method)) {
+                String path = transaction.getNormalizedPath();
+                DAVResource resource = this.repository.getResource(path);
+                DAVMethod instance = ((DAVMethod) INSTANCES.get(method));
+                instance.process(transaction, resource);
+            } else {
+                String message = "Method \"" + method + "\" not implemented";
+                throw new DAVException(501, message);
+            }
+        } catch (DAVException exception) {
+            exception.write(transaction);
+        }
+    }
+    
+    public void setMethod( String methodKey, DAVMethod method ) {
+        INSTANCES.put( methodKey, method );
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVProcessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVRepository.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVRepository.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVRepository.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVRepository.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,164 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * <p>A simple class representing a {@link File} based WebDAV repository.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVRepository {
+
+    /** <p>A {@link String} of all acceptable characters in a URI.</p> */
+    private static final String ACCEPTABLE =
+                                "ABCDEFGHIJLKMNOPQRSTUVWXYZ" + // ALPHA (UPPER)
+                                "abcdefghijklmnopqrstuvwxyz" + // ALPHA (LOWER)
+                                "0123456789" +                 // DIGIT
+                                "_-!.~'()*" +                  // UNRESERVED
+                                ",;:$&+=" +                    // PUNCT
+                                "?/[]@";                       // RESERVED
+
+
+    /** <p>The {@link File} identifying the root of this repository.</p> */
+    protected File root = null;  
+    /** <p>The {@link URI} associated with the root of this repository.</p> */
+    protected URI base = null;
+    /** <p>The {@link Set} of all configured {@link DAVListener}s.</p> */
+    private Set listeners = new HashSet();
+
+    /**
+     * <p>Create a new {@link DAVRepository} instance.</p>
+     *
+     * @param root The {@link File} identifying the root of the repository.
+     * @throws IOException If the specified root is not a directory.
+     * @throws NullPointerExceptoin If the specified root was <b>null</b>.
+     */
+    public DAVRepository(File root)
+    throws IOException {
+    	init(root);
+    }
+
+    protected void init(File root)
+    throws IOException {
+        if (root == null) throw new NullPointerException("Null root");
+        if (root.isDirectory()) {
+            this.root = root.getCanonicalFile();
+            this.base = this.root.toURI().normalize();
+        } else {
+            throw new IOException("Root \"" + root + "\" is not a directory");
+        }
+    }
+
+    /**
+     * <p>Return the {@link URI} representing the root directory of this
+     * {@link DAVRepository}.</p>
+     * 
+     * @return a <b>non-null</b> {@link URI} instance.
+     */
+    protected URI getRepositoryURI() {
+        return (this.base);
+    }
+
+    /**
+     * <p>Return the {@link DAVResource} associated with the given name.</p>
+     * 
+     * @param name a {@link String} identifying the resource name.
+     * @return a <b>non-null</b> {@link DAVResource} instance.
+     * @throws IOException If the resource could not be resolved.
+     */
+    public DAVResource getResource(String name)
+    throws IOException {
+        if (name == null) return this.getResource((URI) null);
+
+        try {
+            /* Encode the string into a URI */
+            StringBuffer buffer = new StringBuffer();
+            byte encoded[] = name.getBytes("UTF-8");
+            for (int x = 0; x < encoded.length; x ++) {
+                if (ACCEPTABLE.indexOf((int)encoded[x]) < 0) {
+                    buffer.append('%');
+                    buffer.append(DAVUtilities.toHexString(encoded[x]));
+                    continue;
+                }
+                buffer.append((char) encoded[x]);
+            }
+
+            return this.getResource(new URI(buffer.toString()));
+        } catch (URISyntaxException exception) {
+            String message = "Invalid resource name \"" + name + "\"";
+            throw (IOException) new IOException(message).initCause(exception);
+        }
+    }
+
+    /**
+     * <p>Return the {@link DAVResource} associated with a {@link URI}.</p>
+     * 
+     * <p>If the specified {@link URI} is relative it will be resolved against
+     * the root of this {@link DAVRepository}.</p>
+     * 
+     * @param uri an absolute or relative {@link URI} identifying the resource.
+     * @return a <b>non-null</b> {@link DAVResource} instance.
+     * @throws IOException If the resource could not be resolved.
+     */
+    public DAVResource getResource(URI uri)
+    throws IOException {
+        if (uri == null) return new DAVResource(this, this.root);
+
+        if (! uri.isAbsolute()) uri = this.base.resolve(uri).normalize();
+        return new DAVResource(this, new File(uri).getAbsoluteFile());
+    }
+    
+    /**
+     * <p>Add a new {@link DAVListener} to the list of instances notified by
+     * this {@link DAVRepository}.</p>
+     */
+    public void addListener(DAVListener listener) {
+        if (listener != null) this.listeners.add(listener);
+    }
+
+    /**
+     * <p>Remove a {@link DAVListener} from the list of instances notified by
+     * this {@link DAVRepository}.</p>
+     */
+    public void removeListener(DAVListener listener) {
+        if (listener != null) this.listeners.remove(listener);
+    }
+
+    /**
+     * <p>Notify all configured {@link DAVListener}s of an event.</p>
+     */
+    protected void notify(DAVResource resource, int event) {
+        if (resource == null) throw new NullPointerException("Null resource");
+        if (resource.getRepository() != this)
+            throw new IllegalArgumentException("Invalid resource");
+
+        Iterator iterator = this.listeners.iterator();
+        while (iterator.hasNext()) try {
+            ((DAVListener)iterator.next()).notify(resource, event);
+        } catch (RuntimeException exception) {
+            // Swallow any RuntimeException thrown by listeners.
+        }
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVRepository.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVResource.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVResource.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVResource.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVResource.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,514 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * <p>A simple representation of a WebDAV resource based on {@link File}s.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVResource implements Comparable {
+    
+    /** <p>The mime type when {@link #isCollection()} is <b>true</b>.</p> */ 
+    public static final String COLLECTION_MIME_TYPE = "httpd/unix-directory"; 
+
+    /** <p>The prefix for all temporary resources.</p> */
+    protected static final String PREFIX = ".dav_";
+    /** <p>The suffix for all temporary resources.</p> */
+    protected static final String SUFFIX = ".temp";
+    /** <p>The {@link DAVRepository} instance containing this resource.</p> */
+    private DAVRepository repository = null;
+    /** <p>The {@link File} associated with this resource.</p> */
+    private File file = null;
+
+    /* ====================================================================== */
+    /* Constructors                                                           */
+    /* ====================================================================== */
+
+    /**
+     * <p>Create a new {@link DAVResource} instance.</p>
+     */
+    protected DAVResource(DAVRepository repo, File file) {
+        if (repo == null) throw new NullPointerException("Null repository");
+        if (file == null) throw new NullPointerException("Null resource");
+        init(repo, file);
+    }
+
+    protected void init(DAVRepository repo, File file)
+    {
+        this.repository = repo;
+        this.file = file;
+
+        if (this.getRelativeURI().isAbsolute())
+            throw new DAVException(412, "Error relativizing resource");
+    }
+
+    /* ====================================================================== */
+    /* Generic object methods                                                 */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Return an integer number for the hash value of this instance.</p>
+     */
+    public int hashCode() {
+        return this.file.hashCode();
+    }
+
+    /**
+     * <p>Compare this instance to another object for equality.</p>
+     */
+    public boolean equals(Object object) {
+        if (object == null) return (false);
+        if (object instanceof DAVResource) {
+            DAVResource resource = (DAVResource) object;
+            boolean u = this.file.equals(resource.file);
+            boolean r = this.repository == resource.repository;
+            return (u && r);
+        } else {
+            return (false);
+        }
+    }
+
+    /**
+     * <p>Compare this instance to another object for sorting.</p>
+     */
+    public int compareTo(Object object) {
+        DAVResource resource = (DAVResource) object;
+        return (this.file.compareTo(resource.file));
+    }
+
+    /* ====================================================================== */
+    /* Resource checkers                                                      */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Checks if this {@link DAVResource} is a null (non existant) one.</p>
+     * 
+     * @return <b>true</b> if this resource does not esist (is a null resource).
+     */
+    public boolean isNull() {
+        return (! this.file.exists());
+    }
+
+    /**
+     * <p>Checks if this {@link DAVResource} is a collection.</p>
+     * 
+     * @return <b>true</b> if this resource is a collection.
+     */
+    public boolean isCollection() {
+        if (this.isNull()) return false;
+        return (this.file.isDirectory());
+    }
+
+    /**
+     * <p>Checks if this {@link DAVResource} is an existing resource.</p>
+     * 
+     * @return <b>true</b> if this resource is a collection.
+     */
+    public boolean isResource() {
+        if (this.isNull()) return false;
+        return (! this.isCollection());
+    }
+    
+    /* ====================================================================== */
+    /* Resource methods                                                       */
+    /* ====================================================================== */
+
+    /**
+     * <p>Return the {@link File} associated with this resource.</p>
+     */
+    protected File getFile() {
+        return this.file;
+    }
+
+    /**
+     * <p>Return the {@link DAVRepository} associated with this resource.</p>
+     */
+    public DAVRepository getRepository() {
+        return this.repository;
+    }
+
+    /**
+     * <p>Return the bare name of this resource (without any &quot;/&quot;
+     * slashes at the end if it is a collection).</p>
+     * 
+     * @return a <b>non null</b> {@link String}.
+     */
+    public String getName() {
+        return this.file.getName();
+    }
+
+    /**
+     * <p>Return the display name of this resource (with an added &quot;/&quot;
+     * slash at the end if it is a collection).</p>
+     * 
+     * @return a <b>non null</b> {@link String}.
+     */
+    public String getDisplayName() {
+        String name = this.getName();
+        if (this.isCollection()) return (name + "/");
+        return name;
+    }
+
+    /**
+     * <p>Return the path of this {@link DAVResource} relative to the root
+     * of the associated {@link DAVRepository}.</p>
+     * 
+     * @return a <b>non null</b> {@link String}.
+     */
+    public String getRelativePath() {
+        return this.getRelativeURI().toASCIIString();
+    }
+
+    /**
+     * <p>Return the {@link URI} of this {@link DAVResource} relative to the
+     * root of the associated {@link DAVRepository}.</p>
+     * 
+     * @return a <b>non-null</b> {@link URI} instance.
+     */
+    public URI getRelativeURI() {
+        URI uri = this.file.toURI();
+        return this.repository.getRepositoryURI().relativize(uri).normalize();
+    }
+
+    /**
+     * <p>Return the parent {@link DAVResource} of this instance.</p>
+     * 
+     * @return a <b>non-null</b> {@link DAVResource} instance or <b>null</b>
+     *         if this {@link DAVResource} is the repository root.
+     */
+    public DAVResource getParent() {
+        try {
+            return new DAVResource(this.repository, this.file.getParentFile());
+        } catch (Throwable throwable) {
+            return null;
+        }
+    }
+
+    /**
+     * <p>Return an {@link Iterator} over all children of this instance.</p> 
+     * 
+     * @return a <b>non-null</b> {@link Iterator} instance or <b>null</b> if
+     *         this {@link DAVResource} is not a collection.
+     * @throws IOException If the resource could not be resolved.
+     */
+    public Iterator getChildren() {
+        if (! this.isCollection()) return null;
+
+        File children[] = this.file.listFiles();
+        if (children == null) children = new File[0];
+        List resources = new ArrayList(children.length);
+
+        for (int x = 0; x < children.length; x++) {
+            String c = children[x].getName();
+            if (c.startsWith(PREFIX) && c.endsWith(SUFFIX)) continue;
+            resources.add(new DAVResource(this.repository, children[x]));
+        }
+
+        return resources.iterator();
+    }
+
+    /* ====================================================================== */
+    /* DAV Properties                                                         */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Return the MIME Content-Type of this {@link DAVResource}.</p>
+     * 
+     * <p>If the {@link #isCollection()} method returns <b>true</b> this
+     * method always returns <code>text/html</code>.</p>
+     * 
+     * @return a {@link String} instance or <b>null</b> if this resource does
+     *         not exist.
+     */
+    public String getContentType() {
+        if (this.isNull()) return null;
+        if (this.isCollection()) return COLLECTION_MIME_TYPE;
+        return DAVUtilities.getMimeType(this.getDisplayName());
+   }
+
+    /**
+     * <p>Return the MIME Content-Length of this {@link DAVResource}.</p>
+     * 
+     * @return a {@link Long} instance or <b>null</b> if this resource does
+     *         not exist or is a collection.
+     */
+    public Long getContentLength() {
+        if (this.isNull() || this.isCollection()) return null;
+        return new Long(this.file.length());
+    }
+
+    /**
+     * <p>Return the creation date of this {@link DAVResource}.</p>
+     * 
+     * <p>As this implementation relies on a {@link File} backend, this method
+     * will always return the same as {@link #getLastModified()}.</p>
+     *
+     * @return a {@link String} instance or <b>null</b> if this resource does
+     *         not exist.
+     */
+    public Date getCreationDate() {
+        if (this.isNull()) return null;
+        return new Date(this.file.lastModified());
+    }
+
+    /**
+     * <p>Return the last modification date of this {@link DAVResource}.</p>
+     * 
+     * @return a {@link String} instance or <b>null</b> if this resource does
+     *         not exist.
+     */
+    public Date getLastModified() {
+        if (this.isNull()) return null;
+        return new Date(this.file.lastModified());
+    }
+
+    /**
+     * <p>Return a {@link String} representing the Entity Tag of this
+     * {@link DAVResource} as described by the
+     * <a href="http://www.rfc-editor.org/rfc/rfc2616.txt">HTTP RFC</a>.</p>
+     * 
+     * @return a {@link String} instance or <b>null</b> if this resource does
+     *         not exist.
+     */
+    public String getEntityTag() {
+        if (this.isNull()) return null;
+
+        String path = this.getRelativePath();
+        StringBuffer etag = new StringBuffer();
+        etag.append('"');
+        
+        /* Append the MD5 hash of this resource name */
+        try {
+            MessageDigest digester = MessageDigest.getInstance("MD5");
+            digester.reset();
+            digester.update(path.getBytes("UTF8"));
+            etag.append(DAVUtilities.toHexString(digester.digest()));
+            etag.append('-');
+        } catch (Exception e) {
+            // If we can't get the MD5 HASH, let's ignore and hope...
+        }
+        
+        /* Append the hashCode of this resource name */
+        etag.append(DAVUtilities.toHexString(path.hashCode()));
+
+        /* Append the last modification date if possible */
+        Date date = this.getLastModified();
+        if (date != null) {
+            etag.append('-');
+            etag.append(DAVUtilities.toHexString(date.getTime()));
+        }
+
+        /* Close the ETag */
+        etag.append('"');
+        return(etag.toString());
+    }
+
+    /* ====================================================================== */
+    /* DAV Operations                                                         */
+    /* ====================================================================== */
+
+    /**
+     * <p>Delete this resource.</p>
+     *
+     * @throws DAVException If for any reason this resource cannot be deleted.
+     */
+    public void delete()
+    throws DAVMultiStatus {
+        if (this.isNull()) throw new DAVException(404, "Not found", this);
+
+        if (this.isResource()) {
+            if (!windowsSafeDelete(this.file)) {
+                throw new DAVException(403, "Can't delete resource", this);
+            } else {
+                this.repository.notify(this, DAVListener.RESOURCE_REMOVED);
+            }
+        } else if (this.isCollection()) {
+            DAVMultiStatus multistatus = new DAVMultiStatus();
+
+            Iterator children = this.getChildren();
+            while (children.hasNext()) try {
+                ((DAVResource)children.next()).delete();
+            } catch (DAVException exception) {
+                multistatus.merge(exception);
+            }
+            
+            if (multistatus.size() > 0) throw multistatus;
+            if (!this.file.delete()) {
+                throw new DAVException(403, "Can't delete collection", this);
+            } else {
+                this.repository.notify(this, DAVListener.COLLECTION_REMOVED);
+            }
+        }
+    }
+
+    /**
+     * <p>Copy this resource to the specified destination.</p>
+     *
+     * @throws DAVException If for any reason this resource cannot be deleted.
+     */
+    public void copy(DAVResource dest, boolean overwrite, boolean recursive)
+    throws DAVMultiStatus {
+
+        /*
+         * NOTE: Since the COPY operation relies on other operation defined in
+         * this class (and in DAVOutputStream for resources) rather than on
+         * files temselves, notifications are sent elsewhere, not here.
+         */
+
+        if (this.isNull()) throw new DAVException(404, "Not found", this);
+
+        /* Check if the destination exists and delete if possible */
+        if (!dest.isNull()) {
+            if (! overwrite) {
+                String msg = "Not overwriting existing destination";
+                throw new DAVException(412, msg, dest);
+            }
+            dest.delete();
+        }
+
+        /* Copy a single resource (destination is null as we deleted it) */
+        if (this.isResource()) {
+            DAVInputStream in = this.read();
+            DAVOutputStream out = dest.write();
+            byte buffer[] = new byte[4096];
+            int k = -1;
+            while ((k = in.read(buffer)) != -1) out.write(buffer, 0, k);
+            in.close();
+            out.close();
+        }
+
+        /* Copy the collection and all nested members */
+        if (this.isCollection()) {
+            dest.makeCollection();
+            if (! recursive) return;
+
+            DAVMultiStatus multistatus = new DAVMultiStatus();
+            Iterator children = this.getChildren();
+            while (children.hasNext()) try {
+                DAVResource childResource = (DAVResource) children.next();
+                File child = new File(dest.file, childResource.file.getName());
+                DAVResource target = new DAVResource(this.repository, child);
+                childResource.copy(target, overwrite, recursive);
+            } catch (DAVException exception) {
+                multistatus.merge(exception);
+            }
+            if (multistatus.size() > 0) throw multistatus;
+        }
+    }
+
+    /**
+     * <p>Moves this resource to the specified destination.</p>
+     *
+     * @throws DAVException If for any reason this resource cannot be deleted.
+     */
+    public void move(DAVResource dest, boolean overwrite, boolean recursive)
+    throws DAVMultiStatus {
+    	// the base class implementation is just copy-then-delete
+    	copy(dest, overwrite, recursive);
+    	this.delete();
+    }
+    
+    /**
+     * <p>Create a collection identified by this {@link DAVResource}.</p>
+     *
+     * <p>This resource must be {@link #isNull() non-null} and its
+     * {@link #getParent() parent} must be accessible and be a
+     * {@link #isCollection() collection}.</p>
+     * 
+     * @throws DAVException If for any reason a collection identified by this
+     *                      resource cannot be created.
+     */
+    public void makeCollection() {
+        DAVResource parent = this.getParent();
+        if (!this.isNull())
+            throw new DAVException(405, "Resource exists", this);
+        if (parent.isNull())
+            throw new DAVException(409, "Parent does not not exist", this);
+        if (!parent.isCollection())
+            throw new DAVException(403, "Parent not a collection", this);
+        if (!this.file.mkdir())
+            throw new DAVException(507, "Can't create collection", this);
+        this.repository.notify(this, DAVListener.COLLECTION_CREATED);
+    }
+
+    /**
+     * <p>Return an {@link InputStream} reading the resource.</p>
+     *
+     * @return a <b>non-null</b> {@link InputStream} instance.
+     */
+    public DAVInputStream read() {
+        if (this.isNull()) throw new DAVException(404, "Not found", this);
+        if (this.isCollection())
+            throw new DAVException (403, "Resource is collection", this);
+        return new DAVInputStream(this);
+    }
+    
+    /**
+     * <p>Return a {@link DAVOutputStream} writing to this {@link DAVResource}
+     * instance.</p>
+     *
+     * @return a <b>non-null</b> {@link DAVOutputStream} instance.
+     */
+    public DAVOutputStream write() {
+        DAVResource parent = this.getParent();
+        if (this.isCollection())
+            throw new DAVException(409, "Can't write a collection", this);
+        if (parent.isNull())
+            throw new DAVException(409, "Parent doesn't exist", this);
+        if (! parent.isCollection())
+            throw new DAVException(403, "Parent not a collection", this);
+        return new DAVOutputStream(this);
+    }
+    
+    /** File.delete(file) sometimes fails transiently on Windows.
+     * This occurs even in low-I/O conditions, with file Explorer closed.
+     * Delete can still fail (correctly) due to the separate Windows problem 
+     * of file sharing violations.
+     * @return the status of the last attempt of File.delete()
+     */
+    private static boolean windowsSafeDelete(File f)
+    {
+    	// www.mail-archive.com/java-user@lucene.apache.org/msg08994.html
+    	boolean success = f.delete();
+    	int attempts = 1;
+    	while(!success && f.exists() && attempts < 3) {
+    		if(attempts > 2) {
+    			System.gc();
+    		}
+            try {
+                Thread.sleep(20);
+            } catch (InterruptedException ignore) {
+            }
+            success = f.delete();
+            attempts++;
+    	}
+    	return success;    	 
+    }
+    
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVServlet.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVServlet.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVServlet.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVServlet.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,280 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+
+/**
+ * <p>A very simple servlet capable of processing very simple
+ * <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
+ * requests.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVServlet implements Servlet, DAVListener {
+
+    /** <p>The {@link DAVRepository} configured for this instance.</p> */
+    protected DAVRepository repository = null;
+    /** <p>The {@link DAVLogger} configured for this instance.</p> */
+    protected DAVLogger logger = null;
+    /** <p>The {@link DAVProcessor} configured for this instance.</p> */
+    protected DAVProcessor processor = null;
+    /** <p>The {@link ServletContext} associated with this instance.</p> */
+    private ServletContext context = null;
+    /** <p>The {@link ServletConfig} associated with this instance.</p> */
+    private ServletConfig config= null;
+
+    /**
+     * <p>Create a new {@link DAVServlet} instance.</p>
+     */
+    public DAVServlet() {
+        super();
+    }
+
+    /**
+     * <p>Initialize this {@link Servlet} instance.</p>
+     * 
+     * <p>The only initialization parameter required by this servlet is the
+     * &quot;<code>rootPath</code>&quot; parameter specifying the path
+     * of the repository root (either absolute or relative to the configured
+     * {@link ServletContext}.</p>
+     * 
+     * <p>If the specified root is relative, it will be considered to
+     * be relative to the {@link ServletContext} deployment path.</p>
+     * 
+     * <p>In any case, the specified root must ultimately point to an existing
+     * directory on a locally-accessible file system.</p>
+     * 
+     * <p>When set to <code>true</code>, an optional parameter called
+     * <code>xmlOnly</code> will force this {@link DAVServlet} to use an
+     * {@link XMLRepository} instead of the default {@link DAVRepository}.</p>
+     *
+     * <p>Finally, when set to <code>true</code>, the optional parameter
+     * <code>debugEnabled</code> will enable logging of method invocation and
+     * events in the repository.</p> 
+     */
+    public void init(ServletConfig config)
+    throws ServletException {
+        /* Remember the configuration instance */
+        this.config = config;
+        this.context = config.getServletContext();
+        
+        /* Setup logging */
+        boolean debug = "true".equals(config.getInitParameter("debugEnabled"));
+        this.logger = new DAVLogger(config, debug);
+
+        /* Try to retrieve the WebDAV root path from the configuration */
+        String rootPath = config.getInitParameter("rootPath");
+        if (rootPath == null)
+            throw new ServletException("Parameter \"rootPath\" not specified");
+        
+        /* Create repository and processor */
+        try {
+            File root = new File(rootPath);
+            // The repository may not be the local filesystem. It may be rooted at "/". 
+            // But then on Windows new File("/").isAbsolute() is false.
+            boolean unixAbsolute = rootPath.startsWith("/");
+            boolean localAbsolute = root.isAbsolute();
+            if (! unixAbsolute && !localAbsolute) {
+                URL url = this.context.getResource("/" + rootPath);
+                if (! "file".equals(url.getProtocol())) {
+                    throw new ServletException("Invalid root \"" + url + "\"");
+                } else {
+                    root = new File(url.getPath());
+                }
+            }
+
+            /* Discover the repository implementation at runtime */
+            String repositoryClass = config.getInitParameter("repositoryClass");
+            if(repositoryClass != null) {
+            	this.repository = DAVServlet.newRepository(repositoryClass, root);
+            } else {
+            	// legacy configuration format. keep for now 
+	            /* Make sure that we use the correct repository type */
+	            if ("true".equalsIgnoreCase(config.getInitParameter("xmlOnly"))) {
+	                this.repository = new XMLRepository(root);
+	            } else {
+	                this.repository = new DAVRepository(root);
+	            }
+            }
+
+            /* Initialize the processor and register ourselves as listeners */
+            this.processor = new DAVProcessor(this.repository);
+            this.repository.addListener(this);
+            this.logger.log("Initialized from " + root.getPath());
+
+        } catch (MalformedURLException e) {
+            throw new ServletException("Can't resolve \"" + rootPath + "\"", e);
+        } catch (IOException e) {
+            String msg = "Can't initialize repository at \"" + rootPath + "\"";
+            throw new ServletException(msg, e);
+        }
+        
+        /* Finally, register this repository in the servlet context */
+        final String key = getRepositoryKey(config.getServletName());
+        this.context.setAttribute(key, this.repository);
+    }
+
+    /**
+     * <p>Retrieve a {@link DAVRepository} for a given {@link File}.</p>
+     */
+    public DAVRepository getRepository(File root)
+    throws IOException {
+        return new XMLRepository(root);
+    }
+
+    /**
+     * <p>Detroy this {@link Servlet} instance.</p>
+     */
+    public void destroy() {
+        this.repository.removeListener(this);
+    }
+
+    /**
+     * <p>Return the {@link ServletConfig} associated with this instance.</p>
+     */
+    public ServletConfig getServletConfig() {
+        return (this.config);
+    }
+
+    /**
+     * <p>Return the {@link ServletContext} associated with this instance.</p>
+     */
+    public ServletContext getServletContext() {
+        return (this.context);
+    }
+
+    /**
+     * <p>Return a informative {@link String} about this servlet.</p>
+     */
+    public String getServletInfo() {
+        return DAVUtilities.SERVLET_INFORMATION;
+    }
+
+    /**
+     * <p>Execute the current request.</p>
+     */
+    public void service(ServletRequest request, ServletResponse response)
+    throws ServletException, IOException {
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse res = (HttpServletResponse) response;
+        
+        /* Mark our presence */
+        res.setHeader("Server", this.context.getServerInfo() + ' ' + 
+                                DAVUtilities.SERVLET_SIGNATURE);
+
+        /* Normal methods are processed by their individual instances */
+        DAVTransaction transaction = new DAVTransaction(req, res); 
+        try {
+            this.processor.process(transaction);
+        } catch (RuntimeException exception) {
+            final String header = req.getMethod() + ' ' + req.getRequestURI()
+                                  + ' ' + req.getProtocol();
+            this.context.log("Error processing: " + header);
+            this.context.log("Exception processing DAV transaction", exception);
+            throw exception;
+        }
+    }
+
+    /* ====================================================================== */
+    /* DAV LISTENER INTERFACE IMPLEMENTATION                                  */
+    /* ====================================================================== */
+
+    /**
+     * <p>Receive notification of an event occurred in a specific
+     * {@link DAVRepository}.</p>
+     */
+    public void notify(DAVResource resource, int event) {
+        String message = "Unknown event";
+        switch (event) {
+            case DAVListener.COLLECTION_CREATED:
+                message = "Collection created";
+                break;
+            case DAVListener.COLLECTION_REMOVED:
+                message = "Collection removed";
+                break;
+            case DAVListener.RESOURCE_CREATED:
+                message = "Resource created";
+                break;
+            case DAVListener.RESOURCE_REMOVED:
+                message = "Resource removed";
+                break;
+            case DAVListener.RESOURCE_MODIFIED:
+                message = "Resource modified";
+                break;
+        }
+        this.logger.debug(message + ": \"" + resource.getRelativePath() + "\"");
+    }
+
+    /* ====================================================================== */
+    /* CONTEXT METHODS                                                        */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Retrieve the key in the {@link ServletContext} where the instance of
+     * the {@link DAVRepository} associated with a named {@link DAVServlet}
+     * can be found.</p>
+     * 
+     * @param servletName the name of the {@link DAVServlet} as specified in
+     *                    the <code>web.xml</code> deployment descriptor.</p>
+     */
+    public static String getRepositoryKey(String servletName) {
+        if (servletName == null) throw new NullPointerException();
+        return DAVRepository.class.getName() + "." + servletName;
+    }
+    
+    /** factory for subclasses configured in web.xml 
+     * @param repositoryClass must extend DAVRepository and have a public constructor(File).
+     *  */
+    static DAVRepository newRepository(String repositoryClass, File root)
+    throws ServletException
+    {
+    	try {
+    		Class c = Class.forName(repositoryClass);
+    		Constructor ctor = c.getConstructor(new Class[]{File.class});
+    		DAVRepository repo = (DAVRepository)ctor.newInstance(new Object[]{root});
+    		return repo;
+    	} catch(ClassNotFoundException e) {
+    		throw new ServletException(e);
+    	} catch(LinkageError le) {
+    		throw new ServletException(le);        	
+    	} catch(NoSuchMethodException ns) {
+    		throw new ServletException(ns);
+    	} catch(InvocationTargetException it) {
+    		throw new ServletException(it);
+    	} catch(IllegalAccessException ia) {
+    		throw new ServletException(ia);
+    	} catch(InstantiationException ie) {
+    		throw new ServletException(ie);
+    	}
+    }
+    
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVTransaction.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVTransaction.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVTransaction.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVTransaction.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,280 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Date;
+
+
+/**
+ * <p>A simple wrapper isolating the Java Servlet API from this
+ * <a href="http://www.rfc-editor.org/rfc/rfc2518.txt">WebDAV</a>
+ * implementation.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class DAVTransaction {
+
+    /**
+     * <p>The identifyication of the <code>infinity</code> value
+     * in the <code>Depth</code> header.</p>
+     */
+    public static final int INFINITY = Integer.MAX_VALUE;
+
+    /** <p>The nested {@link HttpServletRequest}.</p> */
+    private HttpServletRequest request = null;
+    /** <p>The nested {@link HttpServletResponse}.</p> */
+    private HttpServletResponse response = null;
+    /** <p>The {@link URI} associated with the base of the repository.</p> */
+    private URI base = null;
+    /** <p>The status for the HTTP response.</p> */
+    private int status = -1;
+
+    /* ====================================================================== */
+    /* Constructors                                                           */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Create a new {@link DAVTransaction} instance.</p>
+     */
+    public DAVTransaction(ServletRequest request, ServletResponse response)
+    throws ServletException {
+        if (request == null) throw new NullPointerException("Null request");
+        if (response == null) throw new NullPointerException("Null response");
+        this.request = (HttpServletRequest) request;
+        this.response = (HttpServletResponse) response;
+        this.response.setHeader("DAV", "1");
+        this.response.setHeader("MS-Author-Via", "DAV");
+
+        try {
+            String scheme = this.request.getScheme();
+            String host = this.request.getServerName();
+            String path = this.request.getContextPath() + 
+                          this.request.getServletPath();
+            int port = this.request.getServerPort();
+            if (! path.endsWith("/")) path += "/";
+            this.base = new URI(scheme, null, host, port, path, null, null);
+            this.base = this.base.normalize();
+        } catch (URISyntaxException exception) {
+            throw new ServletException("Unable to create base URI", exception);
+        }
+    }
+
+    /* ====================================================================== */
+    /* Request methods                                                        */
+    /* ====================================================================== */
+
+    /**
+     * <p>Return the path originally requested by the client.</p>
+     */
+    public String getMethod() {
+        return this.request.getMethod();
+    }
+
+    /**
+     * <p>Return the path originally requested by the client.</p>
+     */
+    public String getOriginalPath() {
+        String path = this.request.getPathInfo();
+        if (path == null) return "";
+        if ((path.length() > 1) && (path.charAt(0) == '/')) {
+            return path.substring(1);
+        } else {
+            return path;
+        }
+    }
+
+    /**
+     * <p>Return the path originally requested by the client.</p>
+     */
+    public String getNormalizedPath() {
+        final String path = this.getOriginalPath();
+        if (! path.endsWith("/")) return path;
+        return path.substring(0, path.length() - 1);
+    }
+
+    /**
+     * <p>Return the depth requested by the client for this transaction.</p>
+     */
+    public int getDepth() {
+        String depth = request.getHeader("Depth");
+        if (depth == null) return INFINITY;
+        if ("infinity".equalsIgnoreCase(depth)) return INFINITY;
+        try {
+            return Integer.parseInt(depth);
+        } catch (NumberFormatException exception) {
+            throw new DAVException(412, "Unable to parse depth", exception);
+        }
+    }
+
+    /**
+     * <p>Return a {@link URI} 
+     */
+    public URI getDestination() {
+        String destination = this.request.getHeader("Destination");
+        if (destination != null) try {
+            return this.base.relativize(new URI(destination));
+        } catch (URISyntaxException exception) {
+            throw new DAVException(412, "Can't parse destination", exception);
+        }
+        return null;
+    }
+
+    /**
+     * <p>Return the overwrite flag requested by the client for this
+     * transaction.</p>
+     */
+    public boolean getOverwrite() {
+        String overwrite = request.getHeader("Overwrite");
+        if (overwrite == null) return true;
+        if ("T".equals(overwrite)) return true;
+        if ("F".equals(overwrite)) return false;
+        throw new DAVException(412, "Unable to parse overwrite flag");
+    }
+
+    /**
+     * <p>Check if the client requested a date-based conditional operation.</p>
+     */
+    public Date getIfModifiedSince() {
+        String name = "If-Modified-Since";
+        if (this.request.getHeader(name) == null) return null;
+        return new Date(this.request.getDateHeader(name));
+    }
+
+    /* ====================================================================== */
+    /* Response methods                                                       */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Set the HTTP status code of the response.</p>
+     */
+    public void setStatus(int status) {
+        this.response.setStatus(status);
+        this.status = status;
+    }
+
+    /**
+     * <p>Set the HTTP status code of the response.</p>
+     */
+    public int getStatus() {
+        return this.status;
+    }
+
+    /**
+     * <p>Set the HTTP <code>Content-Type</code> header.</p>
+     */
+    public void setContentType(String type) {
+        this.response.setContentType(type);
+    }
+
+    /**
+     * <p>Set an HTTP header in the response.</p>
+     */
+    public void setHeader(String name, String value) {
+        this.response.setHeader(name, value);
+    }
+
+    /* ====================================================================== */
+    /* I/O methods                                                            */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Check if there is a body in the request.</p>
+     * 
+     * <p>This method differs from checking if the return value of the
+     * {@link #read()} method is not <b>null</b> as a request body of length
+     * zero will return <b>false</b> in this case, while in the {@link #read()}
+     * method will return an empty {@link InputStream}.</p>
+     */
+    public boolean hasRequestBody()
+    throws IOException {
+        /* We don't support ranges */
+        if (request.getHeader("Content-Range") != null)
+            throw new DAVException(501, "Content-Range not supported");
+
+        if (this.request.getContentLength() > 0) return true;
+        String len = this.request.getHeader("Content-Length");
+        if (len != null) try {
+            return (Long.parseLong(len) > 0);
+        } catch (NumberFormatException exception) {
+            throw new DAVException(411, "Invalid Content-Length specified");
+        }
+        return false;
+    }
+
+    /**
+     * <p>Read from the body of the original request.</p>
+     */
+    public InputStream read()
+    throws IOException {
+        /* We don't support ranges */
+        if (request.getHeader("Content-Range") != null)
+            throw new DAVException(501, "Content-Range not supported");
+
+        if (this.request.getContentLength() >= 0) {
+            return this.request.getInputStream();
+        }
+
+        String len = this.request.getHeader("Content-Length");
+        if (len != null) try {
+            if (Long.parseLong(len) >= 0) return this.request.getInputStream();
+        } catch (NumberFormatException exception) {
+            throw new DAVException(411, "Invalid Content-Length specified");
+        }
+        return null;
+    }
+
+    /**
+     * <p>Write the body of the response.</p>
+     */
+    public OutputStream write()
+    throws IOException {
+        return this.response.getOutputStream();
+    }
+
+    /**
+     * <p>Write the body of the response.</p>
+     */
+    public PrintWriter write(String encoding)
+    throws IOException {
+        return new PrintWriter(new OutputStreamWriter(this.write(), encoding));
+    }
+
+    /* ====================================================================== */
+    /* Lookup methods                                                         */
+    /* ====================================================================== */
+    
+    /**
+     * <p>Look up the final URI of a {@link DAVResource} as visible from the
+     * HTTP client requesting this transaction.</p>
+     */
+    public URI lookup(DAVResource resource) {
+        URI uri = resource.getRelativeURI();
+        return this.base.resolve(uri).normalize();
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVTransaction.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVUtilities.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVUtilities.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVUtilities.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVUtilities.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,420 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+
+/**
+ * <p>A collection of static utilities.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public final class DAVUtilities {
+
+    /** <p>A {@link HashMap} of configured mime types.</p> */
+    private static Map MIME_TYPES = new HashMap(); 
+    /** <p>A {@link HashMap} of configured mime types.</p> */
+    private static Properties PROPERTIES = new Properties(); 
+    /** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
+    private static final String FORMAT_822 = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";
+    /** <p>The {@link SimpleDateFormat} RFC-822 date format.</p> */
+    private static final String FORMAT_ISO = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+    /** <p>The {@link TimeZone} to use for dates.</p> */
+    private static final TimeZone TIMEZONE = TimeZone.getTimeZone("GMT");
+    /** <p>The {@link Locale} to use for dates.</p> */
+    private static final Locale LOCALE = Locale.US;
+
+    /**
+     * <p>Load the mime types map from a resource.</p>
+     */
+    static {
+        Class clazz = DAVUtilities.class;
+        ClassLoader loader = clazz.getClassLoader();
+
+        /* Load up the properties file */
+        String webdavPropResource = "plexus-webdav/webdav.props";
+        InputStream prop = loader.getResourceAsStream(webdavPropResource);
+        if (prop != null) try {
+            DAVUtilities.PROPERTIES.load(prop);
+            prop.close();
+        } catch (Exception exception) {
+            exception.printStackTrace();
+        } else {
+            System.err.println("Invalid resource: " + webdavPropResource);
+        }
+
+        /* Load up the mime types table */
+        String mimeTypeResource = "plexus-webdav/mime.types";
+        InputStream mime = loader.getResourceAsStream(mimeTypeResource);
+        if (mime != null) try {
+            InputStreamReader read = new InputStreamReader(mime);
+            BufferedReader buff = new BufferedReader(read);
+            String line = null;
+            while ((line = buff.readLine()) != null) {
+                line = line.trim();
+                if (line.length() == 0) continue;
+                if (line.charAt(0) == '#') continue;
+                StringTokenizer tokenizer = new StringTokenizer(line);
+                if (tokenizer.countTokens() > 1) {
+                    String type = tokenizer.nextToken();
+                    while (tokenizer.hasMoreTokens()) {
+                        String extension = '.' + tokenizer.nextToken();
+                        DAVUtilities.MIME_TYPES.put(extension, type);
+                    }
+                }
+            }
+            buff.close();
+            read.close();
+            mime.close();
+        } catch (Exception exception) {
+            exception.printStackTrace();
+        } else {
+            System.err.println("Invalid resource: " + mimeTypeResource);
+        }
+    }
+
+    /** <p>The signature of this package usable from a servlet.</p> */
+    public static final String SERVLET_SIGNATURE = 
+            DAVUtilities.getProperty("servlet.signature") + '/' +
+            DAVUtilities.getProperty("version");
+
+    /** <p>The information detail of this package usable from a servlet.</p> */
+    public static final String SERVLET_INFORMATION = 
+            DAVUtilities.getProperty("servlet.information") + " version " +
+            DAVUtilities.getProperty("version");
+
+    /**
+     * <p>Deny public construction of {@link DAVUtilities} instances.</p>
+     */
+    private DAVUtilities() {
+        super();
+    }
+
+    /**
+     * <p>Return the value of a property configured for this package.</p>
+     * 
+     * @param name the property name
+     * @return a {@link String} instance or <b>null</b> if unknown.
+     */
+    public static String getProperty(String name) {
+        if (name == null) return null;
+        return DAVUtilities.PROPERTIES.getProperty(name);
+    }
+
+    /**
+     * <p>Return the MIME Type configured for a given resource.</p>
+     * 
+     * @param name the resource name whose MIME Type needs to be looked up.
+     * @return a {@link String} instance or <b>null</b> if the type is unknown.
+     */
+    public static String getMimeType(String name) {
+        if (name == null) return null;
+
+        Iterator iterator = DAVUtilities.MIME_TYPES.keySet().iterator();
+        while (iterator.hasNext()) {
+            String extension = (String) iterator.next();
+            if (name.endsWith(extension)) {
+                return (String) DAVUtilities.MIME_TYPES.get(extension);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>Return a {@link String} message given an HTTP status code.</p>
+     */
+    public static String getStatusMessage(int status) {
+        switch (status) {
+            /* HTTP/1.1 RFC-2616 */
+            case 100: return "100 Continue";
+            case 101: return "101 Switching Protocols";
+            case 200: return "200 OK";
+            case 201: return "201 Created";
+            case 202: return "202 Accepted";
+            case 203: return "203 Non-Authoritative Information";
+            case 204: return "204 No Content";
+            case 205: return "205 Reset Content";
+            case 206: return "206 Partial Content";
+            case 300: return "300 Multiple Choices";
+            case 301: return "301 Moved Permanently";
+            case 302: return "302 Found";
+            case 303: return "303 See Other";
+            case 304: return "304 Not Modified";
+            case 305: return "305 Use Proxy";
+            case 306: return "306 (Unused)";
+            case 307: return "307 Temporary Redirect";
+            case 400: return "400 Bad Request";
+            case 401: return "401 Unauthorized";
+            case 402: return "402 Payment Required";
+            case 403: return "403 Forbidden";
+            case 404: return "404 Not Found";
+            case 405: return "405 Method Not Allowed";
+            case 406: return "406 Not Acceptable";
+            case 407: return "407 Proxy Authentication Required";
+            case 408: return "408 Request Timeout";
+            case 409: return "409 Conflict";
+            case 410: return "410 Gone";
+            case 411: return "411 Length Required";
+            case 412: return "412 Precondition Failed";
+            case 413: return "413 Request Entity Too Large";
+            case 414: return "414 Request-URI Too Long";
+            case 415: return "415 Unsupported Media Type";
+            case 416: return "416 Requested Range Not Satisfiable";
+            case 417: return "417 Expectation Failed";
+            case 500: return "500 Internal Server Error";
+            case 501: return "501 Not Implemented";
+            case 502: return "502 Bad Gateway";
+            case 503: return "503 Service Unavailable";
+            case 504: return "504 Gateway Timeout";
+            case 505: return "505 HTTP Version Not Supported";
+
+            /* DAV/1.0 RFC-2518 */
+            case 102: return "102 Processing";
+            case 207: return "207 Multi-Status";
+            case 422: return "422 Unprocessable Entity";
+            case 423: return "423 Locked";
+            case 424: return "424 Failed Dependency";
+            case 507: return "507 Insufficient Storage";
+
+            /* Unknown */
+            default:  return null;
+        }
+    }
+
+    /**
+     * <p>Format a {@link Number} into a {@link String} making sure that
+     * {@link NullPointerException}s are not thrown.</p>
+     * 
+     * @param number the {@link Number} to format.
+     * @return a {@link String} instance or <b>null</b> if the object was null.
+     */
+    public static String formatNumber(Number number) {
+        if (number == null) return null;
+        return (number.toString());
+    }
+
+    /**
+     * <p>Parse a {@link String} into a {@link Long}.</p>
+     * 
+     * @param string the {@link String} to parse.
+     * @return a {@link Long} instance or <b>null</b> if the date was null or
+     *         if there was an error parsing the specified {@link String}.
+     */
+    public static Long parseNumber(String string) {
+        if (string == null) return null;
+        try {
+            return new Long(string);
+        } catch (NumberFormatException exception) {
+            return null;
+        }
+    }
+
+    /**
+     * <p>Format a {@link Date} according to the HTTP/1.1 RFC.</p>
+     * 
+     * @param date the {@link Date} to format.
+     * @return a {@link String} instance or <b>null</b> if the date was null.
+     */
+    public static String formatHttpDate(Date date) {
+        if (date == null) return null;
+        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
+        formatter.setTimeZone(TIMEZONE);
+        return formatter.format(date);
+    }
+
+    /**
+     * <p>Format a {@link Date} according to the ISO 8601 specification.</p>
+     * 
+     * @param date the {@link Date} to format.
+     * @return a {@link String} instance or <b>null</b> if the date was null.
+     */
+    public static String formatIsoDate(Date date) {
+        if (date == null) return null;
+        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
+        formatter.setTimeZone(TIMEZONE);
+        return formatter.format(date);
+    }
+
+    /**
+     * <p>Parse a {@link String} into a {@link Date} according to the
+     * HTTP/1.1 RFC (<code>Mon, 31 Jan 2000 11:59:00 GMT</code>).</p>
+     * 
+     * @param string the {@link String} to parse.
+     * @return a {@link Date} instance or <b>null</b> if the date was null or
+     *         if there was an error parsing the specified {@link String}.
+     */
+    public static Date parseHttpDate(String string) {
+        if (string == null) return null;
+        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_822, LOCALE);
+        formatter.setTimeZone(TIMEZONE);
+        try {
+            return formatter.parse(string);
+        } catch (ParseException exception) {
+            return null;
+        }
+    }
+
+    /**
+     * <p>Parse a {@link String} into a {@link Date} according to the ISO 8601
+     * specification (<code>2000-12-31T11:59:00Z</code>).</p>
+     * 
+     * @param string the {@link String} to parse.
+     * @return a {@link Date} instance or <b>null</b> if the date was null or
+     *         if there was an error parsing the specified {@link String}.
+     */
+    public static Date parseIsoDate(String string) {
+        if (string == null) return null;
+        SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_ISO, LOCALE);
+        formatter.setTimeZone(TIMEZONE);
+        try {
+            return formatter.parse(string);
+        } catch (ParseException exception) {
+            return null;
+        }
+    }
+
+    /**
+     * <p>Return the HEX representation of an array of bytes.</p>
+     * 
+     * @param buffer the array of bytes to convert in a HEX {@link String}.
+     * @return a <b>non-null</b> {@link String} instance.
+     */
+    public static String toHexString(byte buffer[]) {
+        char output[] = new char[buffer.length * 2];
+        int position = 0;
+        for (int x = 0; x < buffer.length; x++) {
+            output[position ++] = DAVUtilities.toHexDigit(buffer[x] >> 4);
+            output[position ++] = DAVUtilities.toHexDigit(buffer[x]);
+        }
+        return new String(output);
+    }
+
+    /**
+     * <p>Return the HEX representation of a long integer.</p>
+     * 
+     * @param number the long to convert in a HEX {@link String}.
+     * @return a <b>non-null</b> 16-characters {@link String} instance.
+     */
+    public static String toHexString(long number) {
+        char output[] = new char[16];
+        output[0] = DAVUtilities.toHexDigit((int)(number >> 60));
+        output[1] = DAVUtilities.toHexDigit((int)(number >> 56));
+        output[2] = DAVUtilities.toHexDigit((int)(number >> 52));
+        output[3] = DAVUtilities.toHexDigit((int)(number >> 48));
+        output[4] = DAVUtilities.toHexDigit((int)(number >> 44));
+        output[5] = DAVUtilities.toHexDigit((int)(number >> 40));
+        output[6] = DAVUtilities.toHexDigit((int)(number >> 36));
+        output[7] = DAVUtilities.toHexDigit((int)(number >> 32));
+        output[8] = DAVUtilities.toHexDigit((int)(number >> 28));
+        output[9] = DAVUtilities.toHexDigit((int)(number >> 24));
+        output[10] = DAVUtilities.toHexDigit((int)(number >> 20));
+        output[11] = DAVUtilities.toHexDigit((int)(number >> 16));
+        output[12] = DAVUtilities.toHexDigit((int)(number >> 12));
+        output[13] = DAVUtilities.toHexDigit((int)(number >> 8));
+        output[14] = DAVUtilities.toHexDigit((int)(number >> 4));
+        output[15] = DAVUtilities.toHexDigit((int)(number));
+        return new String(output);
+    }
+
+    /**
+     * <p>Return the HEX representation of an integer.</p>
+     * 
+     * @param number the int to convert in a HEX {@link String}.
+     * @return a <b>non-null</b> 8-characters {@link String} instance.
+     */
+    public static String toHexString(int number) {
+        char output[] = new char[8];
+        output[0] = DAVUtilities.toHexDigit((int)(number >> 28));
+        output[1] = DAVUtilities.toHexDigit((int)(number >> 24));
+        output[2] = DAVUtilities.toHexDigit((int)(number >> 20));
+        output[3] = DAVUtilities.toHexDigit((int)(number >> 16));
+        output[4] = DAVUtilities.toHexDigit((int)(number >> 12));
+        output[5] = DAVUtilities.toHexDigit((int)(number >> 8));
+        output[6] = DAVUtilities.toHexDigit((int)(number >> 4));
+        output[7] = DAVUtilities.toHexDigit((int)(number));
+        return new String(output);
+    }
+
+    /**
+     * <p>Return the HEX representation of a char.</p>
+     * 
+     * @param number the char to convert in a HEX {@link String}.
+     * @return a <b>non-null</b> 4-characters {@link String} instance.
+     */
+    public static String toHexString(char number) {
+        char output[] = new char[4];
+        output[0] = DAVUtilities.toHexDigit((int)(number >> 12));
+        output[1] = DAVUtilities.toHexDigit((int)(number >> 8));
+        output[2] = DAVUtilities.toHexDigit((int)(number >> 4));
+        output[3] = DAVUtilities.toHexDigit((int)(number));
+        return new String(output);
+    }
+
+    /**
+     * <p>Return the HEX representation of a byte.</p>
+     * 
+     * @param number the byte to convert in a HEX {@link String}.
+     * @return a <b>non-null</b> 2-characters {@link String} instance.
+     */
+    public static String toHexString(byte number) {
+        char output[] = new char[2];
+        output[0] = DAVUtilities.toHexDigit((int)(number >> 4));
+        output[1] = DAVUtilities.toHexDigit((int)(number));
+        return new String(output);
+    }
+
+    /**
+     * <p>Return the single digit character representing the HEX encoding of
+     * the lower four bits of a given integer.</p>
+     */
+    private static char toHexDigit(int number) {
+        switch (number & 0x0F) {
+            case 0x00: return '0';
+            case 0x01: return '1';
+            case 0x02: return '2';
+            case 0x03: return '3';
+            case 0x04: return '4';
+            case 0x05: return '5';
+            case 0x06: return '6';
+            case 0x07: return '7';
+            case 0x08: return '8';
+            case 0x09: return '9';
+            case 0x0A: return 'A';
+            case 0x0B: return 'B';
+            case 0x0C: return 'C';
+            case 0x0D: return 'D';
+            case 0x0E: return 'E';
+            case 0x0F: return 'F';
+        }
+        String message = "Invalid HEX digit " + Integer.toHexString(number);
+        throw new IllegalArgumentException(message);
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/DAVUtilities.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/XMLRepository.java
URL: http://svn.apache.org/viewvc/maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/XMLRepository.java?rev=636822&view=auto
==============================================================================
--- maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/XMLRepository.java (added)
+++ maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/XMLRepository.java Thu Mar 13 11:28:26 2008
@@ -0,0 +1,113 @@
+/* ========================================================================== *
+ *         Copyright (C) 2004-2006, Pier Fumagalli <http://could.it/>         *
+ *                            All rights reserved.                            *
+ * ========================================================================== *
+ *                                                                            *
+ * Licensed 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 it.could.webdav;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+
+/**
+ * <p>A {@link DAVRepository} instance enforcing all {@link DAVResource}s to
+ * be XML files.</p> 
+ *
+ * @author <a href="http://could.it/">Pier Fumagalli</a>
+ */
+public class XMLRepository extends DAVRepository {
+
+    /**
+     * <p>Create a new {@link XMLRepository} instance.</p>
+     */
+    public XMLRepository(File root)
+    throws IOException {
+        super(root);
+    }
+
+    /**
+     * <p>Return the {@link DAVResource} associated with a {@link URI}.</p>
+     */
+    public DAVResource getResource(URI uri)
+    throws IOException {
+        return new XMLResource(this, super.getResource(uri));
+    }
+    
+    /**
+     * <p>A simple {@link DAVResource} extension enforcing XML writes.</p>
+     */
+    private static final class XMLResource extends DAVResource {
+
+        /**
+         * <p>Create a new {@link XMLResource} instance.</p>
+         */
+        public XMLResource(XMLRepository repository, DAVResource resource) {
+            super(repository, resource.getFile());
+        }
+
+        /**
+         * <p>Override the MIME Content-Type to <code>text/xml</code> for
+         * normal resources.</p>
+         */
+        public String getContentType() {
+            if (this.isResource()) return "text/xml";
+            return super.getContentType();
+        }
+        
+        /**
+         * <p>Return a {@link DAVOutputStream} enforcing XML formatted data.</p>
+         */
+        public DAVOutputStream write() {
+            return new XMLOutputStream(this);
+        }
+    }
+
+    /**
+     * <p>A simple {@link DAVOutputStream} enforcing XML formatted data.</p>
+     */
+    private static final class XMLOutputStream extends DAVOutputStream {
+
+        /**
+         * <p>Create a new {@link XMLOutputStream} instance.</p>
+         */
+        protected XMLOutputStream(XMLResource resource) {
+            super(resource);
+        }
+
+        /**
+         * <p>Ensure that whatever is in the temporary file is XML.</p>
+         */
+        protected void rename(File temporary, File original)
+        throws IOException {
+            try {
+                SAXParserFactory factory = SAXParserFactory.newInstance();
+                factory.setNamespaceAware(true);
+                factory.setValidating(false);
+                SAXParser parser = factory.newSAXParser();
+                parser.parse(temporary, new DefaultHandler());
+                super.rename(temporary, original);
+            } catch (ParserConfigurationException exception) {
+                throw new DAVException(500, "JAXP parser error", exception);
+            } catch (SAXException exception) {
+                throw new DAVException(415, "Error parsing data", exception);
+            }
+        }
+    }
+}

Propchange: maven/archiva/branches/springy/archiva-web/archiva-webdav/src/main/java/it/could/webdav/XMLRepository.java
------------------------------------------------------------------------------
    svn:eol-style = native