jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r358296 [4/5] - in /incubator/jackrabbit/trunk/contrib/classloader: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/jackrabbit/ src/main/java/org/apache/jackrabbit/classloader/ src/main...
Date Wed, 21 Dec 2005 14:14:24 GMT
Added: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,775 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.net;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.classloader.Util;
+
+
+/**
+ * The <code>JCRURLConnection</code> is the <code>URLConnection</code>
+ * implementation to access the data addressed by a JCR Repository URL.
+ * <p>
+ * As the primary use of a <code>URLConnection</code> and thus the
+ * <code>JCRURLConnection</code> is to provide access to the content of a
+ * resource identified by the URL, it is the primary task of this class to
+ * identify and access a repository <code>Property</code> based on the URL. This
+ * main task is executed in the {@link #connect()} method.
+ * <p>
+ * Basically the guideposts to access content from a JCR Repository URl are
+ * the following:
+ * <ul>
+ * <li>The URL must ultimately resolve to a repository property to provide
+ *      content.
+ * <li>If the URL itself is the path to a property, that property is used to
+ *      provide the content.
+ * <li>If the URL is a path to a node, the primary item chain starting with
+ *      this node is followed until no further primary items exist. If the
+ *      final item is a property, that property is used to provide the content.
+ * <li>If neither of the above methods resolve to a property, the
+ *      {@link #connect()} fails and access to the content is not possible.
+ * </ul>
+ * <p>
+ * After having connected the property is available through the
+ * {@link #getProperty()} method. Other methods exist to retrieve repository
+ * related information defined when creating the URL: {@link #getSession()} to
+ * retrieve the session of the URL, {@link #getPath()} to retrieve the path
+ * with which the URL was created and {@link #getItem()} to retrieve the item
+ * with which the URL was created. The results of calling {@link #getProperty()}
+ * and {@link #getItem()} will be the same if the URL directly addressed the
+ * property. If the URL addressed the node whose primary item chain ultimately
+ * resolved to the property, the {@link #getItem()} will return the node and
+ * {@link #getProperty()} will return the resolved property.
+ * <p>
+ * A note on the <code>InputStream</code> available from
+ * {@link #getInputStream()}: Unlike other implementations - for example
+ * for <code>file:</code> or <code>http:</code> URLs - which return the same
+ * stream on each call, this implementation returns a new stream on each
+ * invocation.
+ * <p>
+ * The following header fields are implemented by this class:
+ * <dl>
+ * <dt><code>Content-Length</code>
+ * <dd>The size of the content is filled from the <code>Property.getLength()</code>
+ *      method, which returns the size in bytes of the property's value for
+ *      binary values and the number of characters used for the string
+ *      representation of the value for all other value types.
+ *
+ * <dt><code>Content-Type</code>
+ * <dd>If the property is a child of a <code>nt:resource</code> node, the
+ *      content type is retrieved from the <code>jcr:mimeType</code>
+ *      property of the parent node. If the parent node is not a
+ *      <code>nt:resource</code>, the <code>guessContentTypeFromName</code>
+ *      method is called on the {@link #getPath() path}. If this does not
+ *      yield a content type, it is set to <code>application/octet-stream</code>
+ *      for binary properties and to <code>text/plain</code> for other types.
+ *
+ * <dt><code>Content-Enconding</code>
+ * <dd>If the property is a child of a <code>nt:resource</code> node, the
+ *      content encoding is retrieved from the <code>jcr:econding</code>
+ *      property of the parent node. If the <code>jcr:encoding</code> property
+ *      is not set, this header field remains undefined (aka <code>null</code>).
+ *
+ * <dt><code>Last-Modified</code>
+ * <dd>If the property is a child of a <code>nt:resource</code> node, the
+ *      last modified type is retrieved from the <code>jcr:lastModified</code>
+ *      property of the parent node. If the parent node is not a
+ *      <code>nt:resource</code>, the last modification time is set to zero.
+ * </dl>
+ * <p>
+ * This class is not intended to be subclassed or instantiated by clients.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date:$
+ */
+public class JCRURLConnection extends URLConnection {
+
+    /** Default logging */
+    private static final Log log = LogFactory.getLog(JCRURLConnection.class);
+
+    /**
+     * The name of the header containing the content size (value is
+     * "content-length").
+     */
+    protected static final String CONTENT_LENGTH = "content-length";
+
+    /**
+     * The name of the header containing the MIME type of the content (value is
+     * "content-type").
+     */
+    protected static final String CONTENT_TYPE = "content-type";
+
+    /**
+     * The name of the header containing the content encoding (value is
+     * "content-encoding").
+     */
+    protected static final String CONTENT_ENCODING = "content-encoding";
+
+    /**
+     * The name of the header containing the last modification time stamp of
+     * the content (value is "last-modified").
+     */
+    protected static final String LAST_MODIFIED = "last-modified";
+
+    /**
+     * The default content type name for binary properties accessed by this
+     * connection (value is "application/octet-stream").
+     * @see #connect()
+     */
+    protected static final String APPLICATION_OCTET = "application/octet-stream";
+
+    /**
+     * The default content type name for non-binary properties accessed by this
+     * connection (value is "text/plain").
+     * @see #connect()
+     */
+    protected static final String TEXT_PLAIN = "text/plain";
+
+    /**
+     * The handler associated with the URL of this connection. This handler
+     * provides the connection with access to the repository and the item
+     * underlying the URL.
+     */
+    private final JCRURLHandler handler;
+
+    /**
+     * The {@link FileParts} encapsulating the repository name, workspace name,
+     * item path and optional archive entry path contained in the file part
+     * of the URL. This field is set on-demand by the {@link #getFileParts()}
+     * method.
+     *
+     * @see #getFileParts()
+     */
+    private FileParts fileParts;
+
+    /**
+     * The <code>Item</code> addressed by the path of this connection's URL.
+     * This field is set on-demand by the {@link #getItem()} method.
+     *
+     * @see #getItem()
+     */
+    private Item item;
+
+    /**
+     * The <code>Property</code> associated with the URLConnection. The field
+     * is only set after the connection has been successfully opened.
+     *
+     * @see #getProperty()
+     * @see #connect()
+     */
+    private Property property;
+
+    /**
+     * The (guessed) content type of the data. Currently the content type is
+     * guessed based on the path name of the page or the binary attribute of the
+     * atom.
+     * <p>
+     * Implementations are free to decide, how to define the content type. But
+     * they are required to set the type in the {@link #connect(Ticket)}method.
+     *
+     * @see #getContentType()
+     * @see #connect()
+     */
+    private String contentType;
+
+    /**
+     * The (guessed) content encoding of the data. Currently the content type is
+     * guessed based on the path name of the page or the binary attribute of the
+     * atom.
+     * <p>
+     * Implementations are free to decide, how to define the content type. But
+     * they are required to set the type in the {@link #connect(Ticket)}method.
+     *
+     * @see #getContentEncoding()
+     * @see #connect()
+     */
+    private String contentEncoding;
+
+    /**
+     * The content lentgh of the data, which is the size field of the atom
+     * status information of the base atom.
+     * <p>
+     * Implementations are free to decide, how to define the content length. But
+     * they are required to set the type in the {@link #connect(Ticket)}method.
+     *
+     * @see #getContentLength()
+     * @see #connect()
+     */
+    private int contentLength;
+
+    /**
+     * The last modification time in milliseconds since the epoch (1970/01/01)
+     * <p>
+     * Implementations are free to decide, how to define the last modification
+     * time. But they are required to set the type in the
+     * {@link #connect(Ticket)}method.
+     *
+     * @see #getLastModified()
+     * @see #connect()
+     */
+    private long lastModified;
+
+    /**
+     * Creates an instance of this class for the given <code>url</code>
+     * supported by the <code>handler</code>.
+     *
+     * @param url The URL to base the connection on.
+     * @param handler The URL handler supporting the given URL.
+     */
+    JCRURLConnection(URL url, JCRURLHandler handler) {
+        super(url);
+        this.handler = handler;
+    }
+
+    /**
+     * Returns the current session of URL.
+     * <p>
+     * Calling this method does not require this connection being connected.
+     */
+    public Session getSession() {
+        return handler.getSession();
+    }
+
+    /**
+     * Returns the path to the repository item underlying the URL of this
+     * connection.
+     * <p>
+     * Calling this method does not require this connection being connected.
+     */
+    public String getPath() {
+        return getFileParts().getPath();
+    }
+
+    /**
+     * Returns the repository item underlying the URL of this connection
+     * retrieved through the path set on the URL.
+     * <p>
+     * Calling this method does not require this connection being connected.
+     *
+     * @throws IOException If the item has to be retrieved from the repository
+     *      <code>Session</code> of this connection and an error occurrs. The
+     *      cause of the exception will refer to the exception thrown from the
+     *      repository. If the path addresses a non-existing item, the cause
+     *      will be a <code>PathNotFoundException</code>.
+     */
+    public Item getItem() throws IOException {
+        if (item == null) {
+            try {
+                item = getSession().getItem(getPath());
+            } catch (RepositoryException re) {
+                throw failure("getItem", re.toString(), re);
+            }
+        }
+
+        return item;
+    }
+
+    /**
+     * Returns the repository <code>Property</code> providing the contents of
+     * this connection.
+     * <p>
+     * Calling this method forces the connection to be opened by calling the
+     * {@link #connect()} method.
+     *
+     * @throws IOException May be thrown by the {@link #connect()} method called
+     *      by this method.
+     *
+     * @see #connect()
+     */
+    public Property getProperty() throws IOException {
+        // connect to set the property value
+        connect();
+
+        return property;
+    }
+
+    //---------- URLConnection overwrites -------------------------------------
+
+    /**
+     * Connects to the URL setting the header fields and preparing for the
+     * {@link #getProperty()} and {@link #getInputStream()} methods.
+     * <p>
+     * The following algorithm is applied:
+     * <ol>
+     * <li>The repository item is retrieved from the URL's
+     *      <code>URLHandler</code>.
+     * <li>If the item is a node, the <code>getPrimaryItem</code> method is
+     *      called on that node. If the node has no primary item, the connection
+     *      fails.
+     * <li>If the item - either from the handler or after calling
+     *      <code>getPrimaryItem</code> is still a node, this method fails
+     *      because a <code>Property</code> is required for a successfull
+     *      connection.
+     * <li>If the property found above is a multi-valued property, connection
+     *      fails, because multi-valued properties are not currently supported.
+     * <li>The content length header field is set from the property length
+     *      (<code>Property.getLength())</code>).
+     * <li>If the property's parent node is of node type <code>nt:resource</code>,
+     *      the header fields for the content type, content encoding and last
+     *      modification time are set from the <code>jcr:mimeType</code>,
+     *      <code>jcr:encoding</code>, and <code>jcr:lastModification</code>
+     *      properties. Otherwise the content encoding field is set to
+     *      <code>null</code> and the last modification time is set to zero.
+     *      The content type field is guessed from the name of the URL item.
+     *      If the content type cannot be guessed, it is set to
+     *      <code>application/octet-stream</code> if the property is of binary
+     *      type or <code>text/plain</code> otherwise.
+     * </ol>
+     * <p>
+     * When this method successfully returns, this connection is considered
+     * connected. In case of an exception thrown, the connection is not
+     * connected.
+     *
+     * @throws IOException if an error occurrs retrieving the data property or
+     *      any of the header field value properties or if any other errors
+     *      occurrs. Any cuasing exception is set as the cause of this
+     *      exception.
+     */
+    public synchronized void connect() throws IOException {
+        // todo: The ContentBus URL must also contain version information on
+        if (!connected) {
+
+            // Get hold of the data
+            try {
+                // resolve the URLs item to a property
+                Property property = Util.getProperty(getItem());
+                if (property == null) {
+                    throw failure("connect",
+                        "Multivalue property not supported", null);
+                }
+
+                // values to set later
+                String contentType;
+                String contentEncoding = null; // no defined content encoding
+                int contentLength = (int) property.getLength();
+                long lastModified;
+
+                Node parent = property.getParent();
+                if (parent.isNodeType("nt:resource")) {
+                    lastModified = parent.getProperty("jcr:lastModified").getLong();
+                    contentType = parent.getProperty("jcr:mimeType").getString();
+                    if (parent.hasProperty("jcr:encoding")) {
+                        contentEncoding =
+                            parent.getProperty("jcr:encoding").getString();
+                    }
+                } else {
+                    lastModified = 0;
+                    contentType = guessContentTypeFromName(getItem().getName());
+                    if (contentType == null) {
+                        contentType = (property.getType() == PropertyType.BINARY)
+                                            ? APPLICATION_OCTET
+                                            : TEXT_PLAIN;
+                    }
+                }
+
+                log.debug("connect: Using atom '" + property.getPath() +
+                    "' with content type '" + contentType + "' for " +
+                    String.valueOf(contentLength) + " bytes");
+
+                // set the fields
+                setProperty(property);
+                setContentType(contentType);
+                setContentEncoding(contentEncoding);
+                setContentLength(contentLength);
+                setLastModified(lastModified);
+
+                // mark connection open
+                connected = true;
+
+            } catch (RepositoryException re) {
+                throw failure("connect", re.toString(), re);
+            }
+        }
+    }
+
+    /**
+     * Returns an input stream that reads from this open connection.
+     * <p>
+     * <b>NOTES:</b>
+     * <ul>
+     * <li>Each call to this method returns a new <code>InputStream</code>.
+     * <li>Do not forget to close the return stream when not used anymore for
+     *      the system to be able to free resources.
+     * </ul>
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @throws IOException if an error occurrs opening the connection through
+     *      {@link #connect()} or creating the <code>InputStream</code> on the
+     *      repository <code>Property</code>.
+     *
+     * @see #connect()
+     */
+    public InputStream getInputStream() throws IOException {
+        try {
+            return getProperty().getStream();
+        } catch (RepositoryException re) {
+            throw failure("getInputStream", re.toString(), re);
+        }
+    }
+
+    /**
+     * Gets the named header field. This implementation only supports the
+     * Content-Type, Content-Encoding, Content-Length and Last-Modified header
+     * fields. All other names return <code>null</code>.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @param s The name of the header field value to return.
+     *
+     * @return The corresponding value or <code>null</code> if not one of the
+     *      supported fields or the named field's value cannot be retrieved
+     *      from the data source.
+     *
+     * @see #connect()
+     */
+    public String getHeaderField(String s) {
+        try {
+            connect();
+            if (CONTENT_LENGTH.equalsIgnoreCase(s)) {
+                return String.valueOf(contentLength);
+            } else if (CONTENT_TYPE.equalsIgnoreCase(s)) {
+                return contentType;
+            } else if (LAST_MODIFIED.equalsIgnoreCase(s)) {
+                return String.valueOf(lastModified);
+            } else if (CONTENT_ENCODING.equalsIgnoreCase(s)) {
+                return contentEncoding;
+            }
+        } catch (IOException ioe) {
+            log.info("getHeaderField: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the header field with the given index. As with
+     * {@link #getHeaderField(String)} only Content-Length, Content-Type,
+     * Content-Encoding, and Last-Modified are supported. All indexes other
+     * than 0, 1, 2 or 3 will return <code>null</code>.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @param i The index of the header field value to return.
+     *
+     * @return The corresponding value or <code>null</code> if not one of the
+     *      supported fields or the known field's value cannot be retrieved
+     *      from the data source.
+     *
+     * @see #connect()
+     */
+    public String getHeaderField(int i) {
+        try {
+            connect();
+            if (i == 0) {
+                return String.valueOf(contentLength);
+            } else if (i == 1) {
+                return contentType;
+            } else if (i == 2) {
+                return String.valueOf(lastModified);
+            } else if (i == 3) {
+                return contentEncoding;
+            }
+        } catch (IOException ioe) {
+            log.info("getHeaderField: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the name of the header field with the given index. As with
+     * {@link #getHeaderField(String)} only Content-Length, Content-Type,
+     * Content-Encoding and Last-Modified are supported. All indexes other than
+     * 0, 1, 2 or 3 will return <code>null</code>.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @param i The index of the header field name to return.
+     * @return The corresponding name or <code>null</code> if not one of the
+     *         supported fields.
+     *
+     * @see #connect()
+     */
+    public String getHeaderFieldKey(int i) {
+        try {
+            connect();
+            if (i == 0) {
+                return CONTENT_LENGTH;
+            } else if (i == 1) {
+                return CONTENT_TYPE;
+            } else if (i == 2) {
+                return LAST_MODIFIED;
+            } else if (i == 3) {
+                return CONTENT_ENCODING;
+            }
+        } catch (IOException ioe) {
+            log
+                .info("getHeaderFieldKey: Problem connecting: "
+                    + ioe.toString());
+            log.debug("dump", ioe);
+        }
+        return null;
+    }
+
+    /**
+     * Returns an unmodifiable map of all header fields. Each entry is indexed
+     * with a string key naming the field. The entry's value is an unmodifiable
+     * list of the string values of the respective header field.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @return An unmodifiable map of header fields and their values. The map
+     *      will be empty if an error occurrs connecting through
+     *      {@link #connect()}.
+     *
+     * @see #connect()
+     */
+    public Map getHeaderFields() {
+        Map fieldMap = new HashMap();
+
+        try {
+            connect();
+            fieldMap.put(CONTENT_LENGTH, toList(String.valueOf(contentLength)));
+            fieldMap.put(CONTENT_TYPE, toList(contentType));
+            fieldMap.put(LAST_MODIFIED, toList(String.valueOf(lastModified)));
+
+            // only include if not null))
+            if (contentEncoding != null) {
+                fieldMap.put(CONTENT_ENCODING, toList(contentEncoding));
+            }
+        } catch (IOException ioe) {
+            log.info("getHeaderFields: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+
+        return Collections.unmodifiableMap(fieldMap);
+    }
+
+    /**
+     * Returns the content type of the data as a string. This is just a
+     * perfomance convenience overwrite of the base class implementation.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @return The content length of the data or <code>null</code> if the
+     *      content type cannot be derived from the data source.
+     *
+     * @see #connect()
+     */
+    public String getContentType() {
+        try {
+            connect();
+            return contentType;
+        } catch (IOException ioe) {
+            log.info("getContentType: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the content encoding of the data as a string. This is just a
+     * perfomance convenience overwrite of the base class implementation.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @return The content encoding of the data or <code>null</code> if the
+     *      content encoding cannot be derived from the data source.
+     *
+     * @see #connect()
+     */
+    public String getContentEncoding() {
+        try {
+            connect();
+            return contentEncoding;
+        } catch (IOException ioe) {
+            log.info("getContentEncoding: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the content length of the data as an number. This is just a
+     * perfomance convenience overwrite of the base class implementation.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @return The content length of the data or -1 if the content length cannot
+     *         be derived from the data source.
+     *
+     * @see #connect()
+     */
+    public int getContentLength() {
+        try {
+            connect();
+            return contentLength;
+        } catch (IOException ioe) {
+            log.info("getContentLength: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the value of the <code>last-modified</code> header field. The
+     * result is the number of milliseconds since January 1, 1970 GMT.
+     * <p>
+     * Calling this method implicitly calls {@link #connect()} to ensure the
+     * connection is open.
+     *
+     * @return the date the resource referenced by this
+     *         <code>URLConnection</code> was last modified, or -1 if not
+     *         known.
+     *
+     * @see #connect()
+     */
+    public long getLastModified() {
+        try {
+            connect();
+            return lastModified;
+        } catch (IOException ioe) {
+            log.info("getLastModified: Problem connecting: " + ioe.toString());
+            log.debug("dump", ioe);
+        }
+        return -1;
+    }
+
+    //---------- implementation helpers ----------------------------------------
+
+    /**
+     * Returns the URL handler of the URL of this connection.
+     */
+    protected JCRURLHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Returns the {@link FileParts} object which contains the decomposed file
+     * part of this connection's URL.
+     */
+    FileParts getFileParts() {
+        if (fileParts == null) {
+            fileParts = new FileParts(getURL().getFile());
+        }
+
+        return fileParts;
+    }
+
+    /**
+     * @param contentEncoding The contentEncoding to set.
+     */
+    protected void setContentEncoding(String contentEncoding) {
+        this.contentEncoding = contentEncoding;
+    }
+
+    /**
+     * @param contentLength The contentLength to set.
+     */
+    protected void setContentLength(int contentLength) {
+        this.contentLength = contentLength;
+    }
+
+    /**
+     * @param contentType The contentType to set.
+     */
+    protected void setContentType(String contentType) {
+        this.contentType = contentType;
+    }
+
+    /**
+     * @param lastModified The lastModified to set.
+     */
+    protected void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+    }
+
+    /**
+     * @param property The property to set.
+     */
+    protected void setProperty(Property property) {
+        this.property = property;
+    }
+
+    //---------- internal -----------------------------------------------------
+
+    /**
+     * Logs the message and returns an IOException to be thrown by the caller.
+     * The log message contains the caller name, the external URL form and the
+     * message while the IOException is only based on the external URL form and
+     * the message given.
+     *
+     * @param method The method in which the error occurred. This is used for
+     *            logging.
+     * @param message The message to log and set in the exception
+     * @param cause The cause of failure. May be <code>null</code>.
+     *
+     * @return The IOException the caller may throw.
+     */
+    protected IOException failure(String method, String message, Throwable cause) {
+        log.info(method + ": URL: " + url.toExternalForm() + ", Reason: "
+            + message);
+
+        if (cause != null) {
+            log.debug("dump", cause);
+        }
+
+        IOException ioe = new IOException(url.toExternalForm() + ": " + message);
+        ioe.initCause(cause);
+        return ioe;
+    }
+
+    /**
+     * Returns an unmodifiable list containing just the given string value.
+     */
+    private List toList(String value) {
+        String[] values = { value };
+        List valueList = Arrays.asList(values);
+        return Collections.unmodifiableList(valueList);
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLConnection.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.net;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+
+import javax.jcr.Session;
+
+/**
+ * The <code>JCRURLHandler</code> is the <code>URLStreamHandler</code> for
+ * JCR Repository URLs identified by the scheme <code>jcr</code>.
+ * <p>
+ * JCR Repository URLs have not been standardized yet and may only be created
+ * in the context of an existing <code>Session</code>. Therefore this handler
+ * is not globally available and JCR Repository URLs may only be created through
+ * the factory methods in the {@link org.apache.jackrabbit.net.URLFactory}
+ * class.
+ * <p>
+ * This class is not intended to be subclassed or instantiated by clients.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date:$
+ *
+ * @see org.apache.jackrabbit.net.JCRURLConnection
+ * @see org.apache.jackrabbit.net.URLFactory
+ * @see org.apache.jackrabbit.net.URLFactory#createURL(Session, String)
+ */
+class JCRURLHandler extends URLStreamHandler {
+
+    /**
+     * The session used to create this handler, which is also used to open
+     * the connection object.
+     *
+     * @see #getSession()
+     */
+    private final Session session;
+
+    /**
+     * Creates a new instance of the <code>JCRURLHandler</code> with the
+     * given session.
+     *
+     * @param session The <code>Session</code> supporting this handler. This
+     *      must not be <code>null</code>.
+     *
+     * @throws NullPointerException if <code>session</code> is <code>null</code>.
+     */
+    JCRURLHandler(Session session) {
+        if (session == null) {
+            throw new NullPointerException("session");
+        }
+
+        this.session = session;
+    }
+
+    /**
+     * Returns the session supporting this handler.
+     */
+    Session getSession() {
+        return session;
+    }
+
+    //---------- URLStreamHandler abstracts ------------------------------------
+
+    /**
+     * Gets a connection object to connect to an JCR Repository URL.
+     *
+     * @param url The JCR Repository URL to connect to.
+     *
+     * @return An instance of the {@link JCRURLConnection} class.
+     *
+     * @see JCRURLConnection
+     */
+    protected URLConnection openConnection(URL url) {
+        return new JCRURLConnection(url, this);
+    }
+
+    /**
+     * Checks the new <code>authority</code> and <code>path</code> before
+     * actually setting the values on the url calling the base class
+     * implementation.
+     * <p>
+     * We check the authority to not have been modified from the original URL,
+     * as the authority is dependent on the repository <code>Session</code> on
+     * which this handler is based and which was used to create the original
+     * URL. Likewise the repository and workspace name parts of the path must
+     * not have changed.
+     *
+     * @param u the URL to modify.
+     * @param protocol the protocol name.
+     * @param host the remote host value for the URL.
+     * @param port the port on the remote machine.
+     * @param authority the authority part for the URL.
+     * @param userInfo the userInfo part of the URL.
+     * @param path the path component of the URL.
+     * @param query the query part for the URL.
+     * @param ref the reference.
+     *
+     * @throws IllegalArgumentException if the authority or the repository name
+     *             or workspace name parts of the path has changed.
+     */
+    protected void setURL(URL u, String protocol, String host, int port,
+        String authority, String userInfo, String path, String query, String ref) {
+
+        // check for authority
+        if (u.getAuthority() != authority) {
+            if (u.getAuthority() == null) {
+                if (authority != null) {
+                    throw new IllegalArgumentException("Authority " +
+                        authority + " not supported by this handler");
+                }
+            } else if (!u.getAuthority().equals(authority)) {
+                throw new IllegalArgumentException("Authority " +
+                    authority + " not supported by this handler");
+            }
+        }
+
+        // check for repository and/or workspace modifications
+        FileParts newParts = new FileParts(path);
+        if (!"_".equals(newParts.getRepository())) {
+            throw new IllegalArgumentException("Repository " +
+                newParts.getRepository() + " not supported by this handler");
+        }
+        if (!session.getWorkspace().getName().equals(newParts.getWorkspace())) {
+            throw new IllegalArgumentException("Workspace " +
+                newParts.getWorkspace() + " not supported by this handler");
+        }
+
+        // finally set the new values on the URL
+        super.setURL(u, protocol, host, port, authority, userInfo, path, query,
+            ref);
+    }
+}
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/JCRURLHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.net;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.jcr.Session;
+
+/**
+ * The <code>URLFactory</code> class provides factory methods for creating
+ * JCR Repository and JCRJar URLs.
+ * <p>
+ * This class is not intended to be subclassed or instantiated by clients.
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date:$
+ */
+public final class URLFactory {
+
+    /**
+     * The scheme for JCR Repository URLs (value is "jcr").
+     */
+    public static final String REPOSITORY_SCHEME = "jcr";
+
+    /**
+     * The scheme for JCRJar URLs (value is "jar").
+     */
+    public static final String REPOSITORY_JAR_SCHEME = "jar";
+
+    /** Private default constructor, not to be instantiated */
+    private URLFactory() {
+    }
+
+    /**
+     * Creates a new JCR Repository URL for the given session and item path.
+     *
+     * @param session The repository session providing access to the item.
+     * @param path The absolute path to the item. This must be an absolute
+     *      path with a leading slash character. If this is <code>null</code>
+     *      the root node path - <code>/</code> - is assumed.
+     *
+     * @return The JCR Repository URL
+     *
+     * @throws MalformedURLException If an error occurrs creating the
+     *      <code>URL</code> instance.
+     */
+    public static URL createURL(Session session, String path)
+        throws MalformedURLException {
+
+        return new URL(REPOSITORY_SCHEME, "", -1,
+            new FileParts(session, path, null).toString(),
+            new JCRURLHandler(session));
+    }
+
+    /**
+     * Creates a new JCRJar URL for the given session, archive and entry.
+     *
+     * @param session The repository session providing access to the archive.
+     * @param path The absolute path to the archive. This must either be the
+     *      property containing the archive or an item which resolves to such
+     *      a property through its primary item chain. This must be an absolute
+     *      path with a leading slash character. If this is <code>null</code>
+     *      the root node path - <code>/</code> - is assumed.
+     * @param entry The entry within the archive. If <code>null</code>, the URL
+     *      provides access to the archive itself.
+     *
+     * @return The JCRJar URL
+     *
+     * @throws MalformedURLException If an error occurrs creating the
+     *      <code>URL</code> instance.
+     */
+    public static URL createJarURL(Session session, String path, String entry)
+        throws MalformedURLException {
+
+        JCRJarURLHandler handler = new JCRJarURLHandler(session);
+        String file = createURL(session, path).toExternalForm();
+
+        // append entry spec if not null
+        if (entry != null) {
+            file += "!/" + entry;
+        }
+
+        return new URL(REPOSITORY_JAR_SCHEME, "", -1, file, handler);
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/main/java/org/apache/jackrabbit/net/URLFactory.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/main/resources/org/apache/jackrabbit/classloader/type.cnd
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/main/resources/org/apache/jackrabbit/classloader/type.cnd?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/main/resources/org/apache/jackrabbit/classloader/type.cnd (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/main/resources/org/apache/jackrabbit/classloader/type.cnd Wed Dec 21 06:13:56 2005
@@ -0,0 +1,41 @@
+/*
+ * The "type.cnd" file contains the (mixin) node type definition which is
+ * required by the ExpandingArchiveClassPathEntry class, which supports
+ * on-demand unpacking of JAR/ZIP archives for enhanced performances of class
+ * path access.
+ *
+ * NOTE: This file is read through a reader with encoding "ISO-8859-1".
+ *
+ * @author Felix Meschberger
+ * @version $Rev:$, $Date:$
+ * @see org.apache.jackrabbit.classloader.ExpandingArchiveClassPathEntry
+ * @see org.apache.jackrabbit.classloader.NodeTypeSupport
+ */
+
+// The "rep" namespace is expected to be present in the repository.
+// For Jackrabbit based repositories, this is true, for other repositories, we
+// present the namespace declaration here.
+// This declaration is inline with the Jackrabbit internal namespace declaration
+// for the "rep" namespace.
+<rep = 'internal'>
+<nt = 'http://www.jcp.org/jcr/nt/1.0'>
+
+// The node containing the JAR/ZIP archive is tagged with this mixin node
+// type to singal, that the archive has been unpacked into the subtree
+// rooted at the "rep:jarContents" child node.
+[rep:jarFile] mixin
+
+// The "rep:jarExpanded" property is set to the timestamp of the date/time at
+// which the JAR/ZIP archive has been expanded.
+- rep:jarExpanded (date)
+	mandatory copy
+
+// The "rep:jarContents" is the root node of the subtree into which the archive
+// is unpacked. There is no explicit type requirement for the type of this
+// node, except, that it must be allowed nodes of type "nt:file" and "nt:folder"
+// below. Unpacking the archive in the ExpandingArchiveClassPathEntry class
+// will create the "rep:jarContents" node as an nt:folder node and create files
+// and folders contained in the archive as "nt:file" and "nt:folder" nodes,
+// respectively.
++ rep:jarContents (nt:base) = nt:folder
+	mandatory copy
\ No newline at end of file

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.classloader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.jcr.Credentials;
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory;
+
+import com.sun.corba.se.ActivationIDL.Repository;
+import com.sun.org.omg.CORBA.ExcDescriptionSeqHelper;
+
+/**
+ * The <code>AbstractClassLoaderTest</code> TODO
+ *
+ * @author fmeschbe
+ * @version $Rev:$, $Date:$
+ */
+public class AbstractClassLoaderTest extends TestCase {
+
+    /** Logger for test cases */
+    protected static final Log log =
+        LogFactory.getLog("org.apache.jackrabbit.classloader.test");
+
+    protected static final String WORKSPACE = "default";
+    protected static final String USER = "admin";
+
+    protected static final String PROVIDER_URL = "ClassLoader";
+    protected static final String REPOSITORY_NAME = "ClassLoaderRepository";
+
+    protected RepositoryImpl repository;
+    protected Session session;
+
+    private Set createdItems = new HashSet();
+
+    public AbstractClassLoaderTest() {
+        super();
+    }
+
+    public AbstractClassLoaderTest(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        if (!"repositoryStart".equals(getName())) {
+            Context ctx = getInitialContext();
+            repository = (RepositoryImpl) ctx.lookup(REPOSITORY_NAME);
+
+            Credentials creds = new SimpleCredentials(USER, USER.toCharArray());
+            session = repository.login(creds, WORKSPACE);
+        }
+    }
+
+    public void repositoryStart() throws Exception {
+        InputStream config = getClass().getResourceAsStream("/repository.xml");
+        String home = new File("cltest").getAbsolutePath();
+        RepositoryConfig rc = RepositoryConfig.create(config, home);
+        RepositoryImpl repository = RepositoryImpl.create(rc);
+
+        try {
+            Context ctx = getInitialContext();
+            ctx.bind(REPOSITORY_NAME, repository);
+        } catch (NamingException ne) {
+            repository.shutdown();
+            throw ne;
+        }
+    }
+
+    public void repositoryStop() throws Exception {
+        // this is special, logout here and clean repository
+        disconnect();
+
+        if (repository != null) {
+            repository.shutdown();
+            repository = null;
+        }
+
+        Context ctx = getInitialContext();
+        ctx.unbind(REPOSITORY_NAME);
+    }
+
+    protected void tearDown() throws Exception {
+        disconnect();
+        repository = null;
+        super.tearDown();
+    }
+
+    private Context getInitialContext() throws NamingException {
+        Hashtable env = new Hashtable();
+        env.put(Context.INITIAL_CONTEXT_FACTORY,
+            DummyInitialContextFactory.class.getName());
+        env.put(Context.PROVIDER_URL, PROVIDER_URL);
+
+        return new InitialContext(env);
+    }
+
+    private void disconnect() {
+        if (session != null) {
+            clearRepository(session);
+            session.logout();
+            session = null;
+        }
+    }
+
+    //---------- RepositoryLoader ----------------------------------------------
+
+    protected void loadRepository(Session session, InputStream ins) {
+        if (ins == null) {
+            ins = getClass().getResourceAsStream("/preload.properties");
+            if (ins == null) {
+                log.warn("Cannot find preload properties /preload.properties");
+                return;
+            }
+        }
+
+        List keys = new ArrayList();
+        Properties props = new Properties();
+        BufferedReader reader = null;
+        try {
+            reader = new BufferedReader(new InputStreamReader(ins));
+            while (true) {
+                String line = reader.readLine();
+                if (line == null) {
+                    break;
+                }
+
+                // cut off line comment
+                int comment = line.indexOf('#');
+                if (comment >= 0) {
+                    line = line.substring(0, comment);
+                }
+
+                // trim leading and trailing whitespace
+                line = line.trim();
+
+                // ignore line of empty
+                if (line.length() == 0) {
+                    continue;
+                }
+
+                int sep = line.indexOf('=');
+                if (sep < 0) {
+                    continue;
+                }
+
+                String key = line.substring(0, sep).trim();
+
+                StringBuffer buf = new StringBuffer(line.substring(sep+1).trim());
+
+                while (line.endsWith("\\")) {
+                    // cut off last back slash
+                    buf.setLength(buf.length()-1);
+
+                    line = reader.readLine();
+                    if (line == null) {
+                        break;
+                    }
+
+                    buf.append(line);
+                }
+
+                key = loadConvert(key);
+                String value = loadConvert(buf.toString());
+
+                keys.add(key);
+                props.setProperty(key, value);
+            }
+        } catch (IOException ioe) {
+            // ignore
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException ignore) {}
+            }
+
+            try {
+                ins.close();
+            } catch (IOException ignore) {}
+        }
+
+        for (Iterator ki=keys.iterator(); ki.hasNext(); ) {
+            String path = (String) ki.next();
+            String config = props.getProperty(path);
+            StringTokenizer tokener = new StringTokenizer(config, ",");
+            if (!tokener.hasMoreTokens()) {
+                continue;
+            }
+
+            Node parent = null;
+            try {
+                parent = getParent(session, path);
+            } catch (RepositoryException re) {
+                log.warn("Cannot get parent of " + path, re);
+            }
+
+            if (parent == null) {
+                continue;
+            }
+
+            try {
+                String type = tokener.nextToken();
+                if ("n".equalsIgnoreCase(type)) {
+                    loadNode(parent, getName(path), tokener);
+                    createdItems.add(path);
+                } else if ("p".equalsIgnoreCase(type)) {
+                    loadProperty(parent, getName(path), tokener);
+                }
+            } catch (RepositoryException re) {
+                log.warn("Cannot create item " + path, re);
+            }
+        }
+
+        try {
+            if (session.hasPendingChanges()) {
+                session.save();
+            }
+        } catch (RepositoryException re) {
+            log.warn("Cannot save session", re);
+        } finally {
+            try {
+                if (session.hasPendingChanges()) {
+                    session.refresh(false);
+                }
+            } catch (RepositoryException re) {
+                log.warn("Cannot even refresh the session");
+            }
+        }
+    }
+
+    protected void clearRepository(Session session) {
+        for (Iterator ii=createdItems.iterator(); ii.hasNext(); ) {
+            String path = (String) ii.next();
+            try {
+                if (!session.itemExists(path)) {
+                    continue;
+                }
+
+                session.getItem(path).remove();
+            } catch (RepositoryException re) {
+                log.info("Cannot remove Item " + path + ": " + re);
+            }
+        }
+
+        try {
+            session.save();
+        } catch (RepositoryException re) {
+            log.warn("Cannot save removals", re);
+        }
+
+        createdItems.clear();
+    }
+
+    private void loadNode(Node parent, String name,
+            StringTokenizer config) throws RepositoryException {
+
+        // node type
+        String primaryType;
+        if (config.hasMoreTokens()) {
+            primaryType = config.nextToken();
+        } else {
+            primaryType = "nt:unstructured";
+        }
+
+        Node node = parent.addNode(name, primaryType);
+
+        // content URL
+        if (config.hasMoreTokens()) {
+            String urlString = config.nextToken();
+            try {
+                URL url;
+                if (urlString.startsWith("classpath:")) {
+                    urlString = urlString.substring("classpath:".length());
+                    url = getClass().getResource(urlString);
+                } else {
+                    url = new URL(urlString);
+                }
+                URLConnection connection = url.openConnection();
+                makeFileNode(node, connection);
+            } catch (IOException ioe) {
+                System.err.println(ioe);
+            }
+        }
+    }
+
+    private void loadProperty(Node parent, String name,
+            StringTokenizer config) throws RepositoryException {
+        String typeName;
+        if (config.hasMoreTokens()) {
+            typeName = config.nextToken();
+        } else {
+            typeName = "";
+        }
+        int type;
+        try {
+            type = PropertyType.valueFromName(typeName);
+        } catch (IllegalArgumentException iae) {
+            type = PropertyType.STRING;
+        }
+
+        String stringValue = ""; // default value
+        if (config.hasMoreTokens()) {
+            stringValue = config.nextToken();
+        }
+
+        /* Property prop = */ parent.setProperty(name, stringValue, type);
+    }
+
+    static void makeFileNode(Node node, URLConnection content)
+            throws RepositoryException {
+
+        Node contentNode = node.addNode("jcr:content", "nt:resource");
+        InputStream ins = null;
+        try {
+            ins = content.getInputStream();
+            contentNode.setProperty("jcr:data", ins);
+        } catch (IOException ioe) {
+            // ignore, but redefine content data
+            contentNode.setProperty("jcr:data", "mockdata", PropertyType.BINARY);
+        } finally {
+            if (ins != null) {
+                try {
+                    ins.close();
+                } catch (IOException ioe) {}
+            }
+        }
+
+        Calendar cal = Calendar.getInstance();
+        cal.setTimeInMillis(content.getLastModified());
+        contentNode.setProperty("jcr:lastModified", cal);
+
+        String mimeType = content.getContentType();
+        if (mimeType == null || mimeType.toLowerCase().indexOf("unknown") >= 0) {
+            mimeType = URLConnection.guessContentTypeFromName(node.getName());
+        }
+        if (mimeType == null) {
+            mimeType = "application/octet-stream";
+        }
+        contentNode.setProperty("jcr:mimeType", mimeType);
+
+        String encoding = content.getContentEncoding();
+        if (encoding != null) {
+            contentNode.setProperty("jcr:encoding", encoding);
+        }
+    }
+
+    static Node getParent(Session session, String path)
+            throws RepositoryException {
+
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash < 0) {
+            return null;
+        }
+
+        String parentPath = path.substring(0, lastSlash);
+        if (parentPath.length() == 0) {
+            return session.getRootNode();
+        }
+
+        try {
+            Item item = session.getItem(parentPath);
+            if (item.isNode()) {
+                return (Node) item;
+            }
+        } catch (PathNotFoundException pnfe) {
+
+            // create the intermediate node as an unstructured node
+            Node parent = getParent(session, parentPath);
+            if (parent != null) {
+                lastSlash = parentPath.lastIndexOf('/');
+                if (lastSlash < 0) {
+                    return null;
+                }
+                String name = parentPath.substring(lastSlash+1);
+
+                return parent.addNode(name, "nt:folder");
+            }
+        }
+
+        return null;
+    }
+
+    private String getName(String path) {
+        return path.substring(path.lastIndexOf('/')+1);
+    }
+
+    private String loadConvert(String theString) {
+        char aChar;
+        int len = theString.length();
+        StringBuffer outBuffer = new StringBuffer(len);
+
+        for (int x=0; x<len; ) {
+            aChar = theString.charAt(x++);
+            if (aChar == '\\') {
+                aChar = theString.charAt(x++);
+                if (aChar == 'u') {
+                    // Read the xxxx
+                    int value=0;
+            for (int i=0; i<4; i++) {
+                aChar = theString.charAt(x++);
+                switch (aChar) {
+                  case '0': case '1': case '2': case '3': case '4':
+                  case '5': case '6': case '7': case '8': case '9':
+                     value = (value << 4) + aChar - '0';
+                 break;
+              case 'a': case 'b': case 'c':
+                          case 'd': case 'e': case 'f':
+                 value = (value << 4) + 10 + aChar - 'a';
+                 break;
+              case 'A': case 'B': case 'C':
+                          case 'D': case 'E': case 'F':
+                 value = (value << 4) + 10 + aChar - 'A';
+                 break;
+              default:
+                              throw new IllegalArgumentException(
+                                           "Malformed \\uxxxx encoding.");
+                        }
+                    }
+                    outBuffer.append((char)value);
+                } else {
+                    if (aChar == 't') aChar = '\t';
+                    else if (aChar == 'r') aChar = '\r';
+                    else if (aChar == 'n') aChar = '\n';
+                    else if (aChar == 'f') aChar = '\f';
+                    outBuffer.append(aChar);
+                }
+            } else
+                outBuffer.append(aChar);
+        }
+        return outBuffer.toString();
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/AbstractClassLoaderTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.classloader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.apache.log4j.Logger;
+
+import sun.tools.jar.resources.jar_fr;
+
+/**
+ * The <code>DynamicRepositoryClassLoaderTest</code> class
+ *
+ * @author Felix Meschberger
+ */
+public class DynamicRepositoryClassLoaderTest extends AbstractClassLoaderTest {
+
+    private static final String CLASSES_FOLDER = "/node1/classes";
+    private static final String JAR_FILE = "/node1/mock.jar";
+    private static final String CLASS_FILE = "org/apache/jackrabbit/classloader/Util.class";
+    private static final String JAR_FILE_ENTRY = "mock/aDir/anotherFile.txt";
+    private static final String NON_EXISTING_JAR_FILE_ENTRY = "mock/aDir/missingFile.txt";
+
+    private static final String[] handles = { CLASSES_FOLDER, JAR_FILE };
+
+    private DynamicRepositoryClassLoader loader;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        loadRepository(session, null);
+
+        loader = new DynamicRepositoryClassLoader(session, handles, null);
+    }
+
+    protected void tearDown() throws Exception {
+        if (loader != null) {
+            loader.destroy();
+            loader = null;
+        }
+
+        super.tearDown();
+    }
+
+    public void testGetURLs() {
+        URL[] urls = loader.getURLs();
+
+        /*
+         * Expected URLs
+         *   urls[0] = jcr:/_/default/node1/classes/
+         *   urls[1] = jar:jcr:/_/default/node1/mock.jar
+         */
+
+        assertNotNull("Class loader URLs", urls);
+        assertEquals("Number of class path entries", handles.length, urls.length);
+        assertEquals("URL " + CLASSES_FOLDER, "jcr:/_/" + WORKSPACE + CLASSES_FOLDER + "/", urls[0].toString());
+        assertEquals("URL " + JAR_FILE, "jar:jcr:/_/" + WORKSPACE + JAR_FILE, urls[1].toString());
+    }
+
+    public void testClassFile() {
+        URL resource = loader.getResource(CLASS_FILE);
+        assertNotNull("Resource " + CLASS_FILE, resource);
+    }
+
+    public void testJarEntry() {
+        URL resource = loader.getResource(JAR_FILE_ENTRY);
+        assertNotNull("Resource " + JAR_FILE_ENTRY, resource);
+
+        resource = loader.getResource(NON_EXISTING_JAR_FILE_ENTRY);
+        assertNull("Resource " + NON_EXISTING_JAR_FILE_ENTRY + " not expected", resource);
+    }
+
+    public void testResources() throws IOException {
+        Enumeration res = loader.getResources(CLASS_FILE);
+        assertTrue("At least one resource " + CLASS_FILE, res.hasMoreElements());
+
+        URL url = (URL) res.nextElement();
+        assertNotNull(url);
+
+        assertFalse("Only expecint one resource " + CLASS_FILE, res.hasMoreElements());
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/DynamicRepositoryClassLoaderTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.classloader;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Date;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+
+import org.apache.jackrabbit.util.Text;
+
+public class ExpandingArchiveClassPathEntryTest extends AbstractClassLoaderTest {
+
+    private static final String NODE_TYPE = "rep:jarFile";
+
+    private static final String ROOT = "/test";
+    private static final String JAR_PATH = ROOT + "/mock.jar";
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        if (session.itemExists(ROOT)) {
+            log.info("Removing old test root entry");
+            session.getItem(ROOT).remove();
+            session.save();
+        }
+    }
+
+    protected void tearDown() throws Exception {
+        if (session.itemExists(ROOT)) {
+            session.getItem(ROOT).remove();
+            session.save();
+        }
+
+        super.tearDown();
+    }
+
+    public void testCanExpand() throws RepositoryException {
+        // check for the node type - may or may not exist
+        try {
+            session.getWorkspace().getNodeTypeManager().getNodeType(NODE_TYPE);
+            log.info("Node type " + NODE_TYPE + " already registered");
+        } catch (NoSuchNodeTypeException nsoe) {
+            // expected behaviour
+            log.info("Node type " + NODE_TYPE + " not registered yet");
+        }
+
+        boolean canExpand = ExpandingArchiveClassPathEntry.canExpandArchives(session);
+        assertTrue("Expecting archives to be expandable", canExpand);
+
+        // check for the node type - must exist
+        session.getWorkspace().getNodeTypeManager().getNodeType(NODE_TYPE);
+        log.info("Node type " + NODE_TYPE + " already registered");
+    }
+
+    public void testExpand() throws IOException, RepositoryException {
+        URL url = getClass().getResource("/mock.jar");
+
+        Node parent = getParent(session, JAR_PATH);
+        Node jar = parent.addNode(Text.getName(JAR_PATH), "nt:file");
+        makeFileNode(jar, url.openConnection());
+        session.save();
+
+        Property prop = Util.getProperty(session.getItem(JAR_PATH));
+
+        ExpandingArchiveClassPathEntry pe =
+            new ExpandingArchiveClassPathEntry(prop, JAR_PATH);
+
+        ClassLoaderResource res = pe.getResource("mock/aDir/anotherFile.txt");
+        assertNotNull("anotherFile.txt expected to exist", res);
+
+        url = res.getURL();
+        assertNotNull("anotherFile's URL missing", url);
+
+        String data = new String(res.getBytes());
+        log.info("URL : " + url);
+        log.info("Path: " + res.getPath());
+        log.info("Prop: " + res.getProperty().getPath());
+        log.info("Data: '" + data + "'");
+        log.info("Size: " + res.getContentLength() + " (bytes: " + res.getBytes().length + ")");
+        log.info("Time: " + new Date(res.getLastModificationTime()));
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/ExpandingArchiveClassPathEntryTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.classloader;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+/**
+ * The <code>PatternPathTest</code> class
+ *
+ * @author Felix Meschberger
+ */
+public class PatternPathTest extends AbstractClassLoaderTest {
+
+    private static final String DUMMY = "/dummy/classes";
+    private static final String[] patterns = {
+            "/apps|libs/*/classes",
+            "/*/classes", null, "", DUMMY, "/", "*", "|",
+            "/apps/developers/fmeschbe/lib/*.doc" };
+    private static final Set included;
+    private static final Set excluded;
+
+    static {
+        included = new HashSet();
+
+        // matches *
+        included.add("/apps");
+        included.add("/libs");
+        included.add("/jcr:system");       // Repository child node
+        included.add("/jcr:primaryType");  // Repository property
+
+        // matches "/"
+        included.add("/");
+
+        // matches: "/apps|libs/*/classes"
+        included.add("/apps/CFC/classes");
+        included.add("/apps/Forms/classes");
+        included.add("/apps/playground/classes");
+        included.add("/libs/CFC/classes");
+        included.add("/libs/Forms/classes");
+
+        // matches "/*/classes"
+        included.add(DUMMY);
+
+        // matches "/apps/developers/fmeschbe/lib/*.doc"
+        included.add("/apps/developers/fmeschbe/lib/test1.doc");
+        included.add("/apps/developers/fmeschbe/lib/other.doc");
+
+        excluded = new HashSet();
+        excluded.add("/apps/developers/fmeschbe/classes");
+        excluded.add("/libs/developers/fmeschbe/classes");
+
+        excluded.add("/apps/developers/fmeschbe/lib/readme.txt");
+    }
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        InputStream ins = getClass().getResourceAsStream("PatternPathTest.preload.properties");
+        if (ins != null) {
+            try {
+                loadRepository(session, ins);
+            } finally {
+                try {
+                    ins.close();
+                } catch (IOException ignore) {}
+            }
+        }
+    }
+
+    protected void tearDown() throws Exception {
+        if (session != null) {
+            clearRepository(session);
+            session.logout();
+            session = null;
+        }
+
+        super.tearDown();
+    }
+
+    public void testCreation() {
+        PatternPath ppl = new PatternPath(session, patterns);
+
+        // check completeness
+        Set pplSet = new HashSet(Arrays.asList(ppl.getPath()));
+        for (int i=0; i < patterns.length; i++) {
+            String pattern = patterns[i];
+            if (pattern == null || pattern.length() == 0) {
+                assertFalse("Empty Entry must not be conained", pplSet.contains(pattern));
+            } else {
+                assertTrue("Non Empty Entry must be contained", pplSet.contains(pattern));
+            }
+        }
+    }
+
+    public void testExpandedPaths() throws RepositoryException {
+        PatternPath ppl = new PatternPath(session, patterns);
+
+        // expand the path
+        List paths = ppl.getExpandedPaths();
+
+        // check whether all expanded entries are expected
+        Set expected = new HashSet(included);
+        assertEquals("Number of path entries", expected.size(), paths.size());
+        for (Iterator pi=paths.iterator(); pi.hasNext(); ) {
+            String entry = (String) pi.next();
+            assertTrue("Unexpected path entry " + entry, expected.remove(entry));
+        }
+
+        // check whether the expected inclusions have all been in the expansion
+        assertTrue("Not all inclusions: " + expected, expected.isEmpty());
+
+        // check that no exlusions are included
+        for (Iterator pi=paths.iterator(); pi.hasNext(); ) {
+            String entry = (String) pi.next();
+            assertFalse("Path entry must be excluded" + entry, excluded.contains(entry));
+        }
+    }
+
+    public void testMatchPath() {
+        PatternPath ppl = new PatternPath(session, patterns);
+
+        // paths expected to match
+        for (Iterator ii=included.iterator(); ii.hasNext(); ) {
+            String path = (String) ii.next();
+            assertTrue("Expect Match: " + path, ppl.matchPath(path));
+        }
+
+        // paths expected to not match
+        for (Iterator ei=excluded.iterator(); ei.hasNext(); ) {
+            String path = (String) ei.next();
+            assertFalse("Unexpect Match: " + path, ppl.matchPath(path));
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/PatternPathTest.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java Wed Dec 21 06:13:56 2005
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * 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 org.apache.jackrabbit.classloader;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TestAll {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite("Test for Jackrabbit Class Loader");
+        //$JUnit-BEGIN$
+        suite.addTest(new AbstractClassLoaderTest("repositoryStart"));
+        suite.addTestSuite(PatternPathTest.class);
+        suite.addTestSuite(ExpandingArchiveClassPathEntryTest.class);
+        suite.addTestSuite(DynamicRepositoryClassLoaderTest.class);
+        suite.addTest(new AbstractClassLoaderTest("repositoryStop"));
+        //$JUnit-END$
+        return suite;
+    }
+}

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/java/org/apache/jackrabbit/classloader/TestAll.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/log4j.properties?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/log4j.properties (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/log4j.properties Wed Dec 21 06:13:56 2005
@@ -0,0 +1,22 @@
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=INFO, file
+#log4j.rootLogger=DEBUG, stdout, file
+#log4j.rootLogger=ERROR, stdout, file
+
+log4j.logger.org.apache.jackrabbit.classloader.test=DEBUG
+
+# 'stdout' is set to be a ConsoleAppender.
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+
+# 'stdout' uses PatternLayout
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m (%F, line %L)\n
+
+# 'file' is set to be a FileAppender.
+log4j.appender.file=org.apache.log4j.FileAppender
+log4j.appender.file.File=jcr.log
+# log4j.appender.file.Append=false
+
+# 'file' uses PatternLayout.
+log4j.appender.file.layout=org.apache.log4j.PatternLayout
+log4j.appender.file.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m (%F, line %L)\n

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/log4j.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/mock.jar
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/mock.jar?rev=358296&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/mock.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/org/apache/jackrabbit/classloader/PatternPathTest.preload.properties
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/org/apache/jackrabbit/classloader/PatternPathTest.preload.properties?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/org/apache/jackrabbit/classloader/PatternPathTest.preload.properties (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/org/apache/jackrabbit/classloader/PatternPathTest.preload.properties Wed Dec 21 06:13:56 2005
@@ -0,0 +1,60 @@
+#
+# Properties file to preload the MockRepository with content.
+# Each session created by the MockRepository upon calling login
+# reads this file to define a structure of content.
+#
+# Each property in this file defines a single item. The name
+# of the property is the absolute path to the item defined.
+# Order of the definitions in this file is relevant in that
+# intermediate nodes are automatically defined as
+# nt:unstructured nodes and may not be redefined later.
+#
+# The value of each property is a series of comma-separated
+# fields. The first field must either be "n" to indicate a
+# Node or "p" to indicate a Property. Any other values results
+# in the property being ignored for item creation. The remaining
+# fields depend on whether a Node or Property is defined.
+#
+# For nodes the fields are:
+#   1  -  "n" to indicate the definition of a Node
+#   2  -  The name of the node type, may be any string not
+#         not containing a comma. The default value is
+#         "nt:unstructured"
+#   3  -  The name of the primary item. This may be missing
+#         in which case there is no primary item or a well
+#         known default value is used. For nt:file typed
+#         nodes, the default is "jcr:content" for nt:resource
+#         typed nodes, the default is "jcr:data"
+#
+# For Properties the fields are:
+#   1  -  "p" to indicate the definition of a Property
+#   2  -  The name of the property type. This must be one
+#         of the type names defined in the PropertyType
+#         class. If empty or or unknown, "String" is used
+#         as the default.
+#   3  -  The value of the property to the defined. The
+#         value must be acceptable for the
+#         ValueFactory.createValue(int type, String value)
+#         method. If empty or missing, an empty string is
+#         used as the property value, which may not be
+#         acceptable for all property types.
+#
+# Property values (last field in the property definition) may
+# span multiple lines. Each line must be terminated with the
+# backslash character indicating the continuation of the line on
+# the next line of file.
+
+# various class path entries
+/apps/CFC/classes = n,nt:folder
+/apps/Forms/classes = n,nt:folder
+/apps/playground/classes = n,nt:folder
+
+/apps/developers/fmeschbe/classes = n,nt:folder
+
+/apps/developers/fmeschbe/lib/readme.txt = n,nt:file,classpath:/readme.txt
+/apps/developers/fmeschbe/lib/test1.doc = n,nt:file,classpath:/test1.txt
+/apps/developers/fmeschbe/lib/other.doc = n,nt:file,classpath:/other.txt
+
+/libs/CFC/classes = n,nt:folder
+/libs/Forms/classes = n,nt:folder
+/libs/developers/fmeschbe/classes = n,nt:folder

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/org/apache/jackrabbit/classloader/PatternPathTest.preload.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/other.txt
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/other.txt?rev=358296&view=auto
==============================================================================
--- incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/other.txt (added)
+++ incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/other.txt Wed Dec 21 06:13:56 2005
@@ -0,0 +1 @@
+Some Test for other.doc
\ No newline at end of file

Propchange: incubator/jackrabbit/trunk/contrib/classloader/src/test/resources/other.txt
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message