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 "/"
+ * 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 "/"
+ * 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
+ * "<code>rootPath</code>" 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