You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by vr...@apache.org on 2012/02/02 13:47:01 UTC

svn commit: r1239587 [6/9] - in /sling/whiteboard/resourceresolverfactory/jcr-resource: ./ src/ src/main/ src/main/appended-resources/ src/main/appended-resources/META-INF/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/a...

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/URIException.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/URIException.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/URIException.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/URIException.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The URI parsing and escape encoding exception.
+ * <p>
+ * This class is a slightly modified version of the URIException class
+ * distributed with Http Client 3.1. The changes are removal of deprecated
+ * methods and have the class itself extend the <code>SlingException</code> to
+ * adapt it to the exception hierarchy of Sling.
+ */
+@SuppressWarnings("serial")
+public class URIException extends SlingException {
+
+    // ----------------------------------------------------------- constructors
+
+    /**
+     * Default constructor.
+     */
+    public URIException() {
+    }
+
+    /**
+     * The constructor with a reason code argument.
+     *
+     * @param reasonCode the reason code
+     */
+    public URIException(int reasonCode) {
+        this.reasonCode = reasonCode;
+    }
+
+    /**
+     * The constructor with a reason string and its code arguments.
+     *
+     * @param reasonCode the reason code
+     * @param reason the reason
+     */
+    public URIException(int reasonCode, String reason) {
+        super(reason); // for backward compatibility of Throwable
+        this.reason = reason;
+        this.reasonCode = reasonCode;
+    }
+
+    /**
+     * The constructor with a reason string argument.
+     *
+     * @param reason the reason
+     */
+    public URIException(String reason) {
+        super(reason); // for backward compatibility of Throwable
+        this.reason = reason;
+        this.reasonCode = UNKNOWN;
+    }
+
+    // -------------------------------------------------------------- constants
+
+    /**
+     * No specified reason code.
+     */
+    public static final int UNKNOWN = 0;
+
+    /**
+     * The URI parsing error.
+     */
+    public static final int PARSING = 1;
+
+    /**
+     * The unsupported character encoding.
+     */
+    public static final int UNSUPPORTED_ENCODING = 2;
+
+    /**
+     * The URI escape encoding and decoding error.
+     */
+    public static final int ESCAPING = 3;
+
+    /**
+     * The DNS punycode encoding or decoding error.
+     */
+    public static final int PUNYCODE = 4;
+
+    // ------------------------------------------------------------- properties
+
+    /**
+     * The reason code.
+     */
+    protected int reasonCode;
+
+    /**
+     * The reason message.
+     */
+    protected String reason;
+
+    // ---------------------------------------------------------------- methods
+
+    /**
+     * Get the reason code.
+     *
+     * @return the reason code
+     */
+    public int getReasonCode() {
+        return reasonCode;
+    }
+
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/WrappedResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/WrappedResourceProvider.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/WrappedResourceProvider.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/WrappedResourceProvider.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The SF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper;
+
+import java.util.Iterator;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ *
+ */
+public class WrappedResourceProvider  implements ResourceProvider {
+
+    private ResourceProvider resourceProvider;
+    private Comparable<?> serviceReference;
+
+    /**
+     *
+     */
+    public WrappedResourceProvider(ResourceProvider resourceProvider, Comparable<?> serviceReference) {
+        this.resourceProvider = resourceProvider;
+        this.serviceReference = serviceReference;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, java.lang.String)
+     */
+    public Resource getResource(ResourceResolver arg0, String arg1) {
+        return resourceProvider.getResource(arg0, arg1);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, javax.servlet.http.HttpServletRequest, java.lang.String)
+     */
+    public Resource getResource(ResourceResolver arg0, HttpServletRequest arg1, String arg2) {
+        return resourceProvider.getResource(arg0, arg1, arg2);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.apache.sling.api.resource.ResourceProvider#listChildren(org.apache.sling.api.resource.Resource)
+     */
+    public Iterator<Resource> listChildren(Resource arg0) {
+        return resourceProvider.listChildren(arg0);
+    }
+
+    /**
+     *
+     */
+    public Comparable<?> getComparable() {
+        return serviceReference;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return resourceProvider.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if ( obj instanceof WrappedResourceProvider ) {
+            return resourceProvider.equals(((WrappedResourceProvider) obj).resourceProvider);
+        } else if ( obj instanceof ResourceProvider) {
+            return resourceProvider.equals(obj);
+        }
+        return super.equals(obj);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return resourceProvider.toString();
+    }
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrItemResource.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Iterator;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.api.resource.AbstractResource;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class JcrItemResource extends AbstractResource implements Resource {
+
+    /**
+     * default log
+     */
+    private static final Logger LOGGER = LoggerFactory.getLogger(JcrItemResource.class);
+
+    private final ResourceResolver resourceResolver;
+
+    private final String path;
+
+    private final ResourceMetadata metadata;
+
+    protected JcrItemResource(ResourceResolver resourceResolver,
+                              String path) {
+
+        this.resourceResolver = resourceResolver;
+        this.path = path;
+
+        metadata = new ResourceMetadata();
+        metadata.setResolutionPath(path);
+    }
+
+    public ResourceResolver getResourceResolver() {
+        return resourceResolver;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public ResourceMetadata getResourceMetadata() {
+        return metadata;
+    }
+
+    /**
+     * Compute the resource type of the given node, using either the
+     * SLING_RESOURCE_TYPE_PROPERTY, or the node's primary node type, if the
+     * property is not set
+     */
+    protected String getResourceTypeForNode(Node node)
+            throws RepositoryException {
+        String result = null;
+
+        if (node.hasProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY)) {
+            result = node.getProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY).getValue().getString();
+        }
+
+        if (result == null || result.length() == 0) {
+            result = node.getPrimaryNodeType().getName();
+        }
+
+        return result;
+    }
+
+    protected void setContentLength(final Property property) throws RepositoryException {
+        if (property.isMultiple()) {
+            return;
+        }
+
+        try {
+            final long length;
+            if (property.getType() == PropertyType.BINARY ) {
+                // we're interested in the number of bytes, not the
+                // number of characters
+                length = property.getLength();
+            } else {
+                length = property.getString().getBytes("UTF-8").length;
+            }
+            getResourceMetadata().setContentLength(length);
+        } catch (UnsupportedEncodingException uee) {
+            LOGGER.warn("getPropertyContentLength: Cannot determine length of non-binary property {}: {}",
+                    toString(), uee);
+        }
+    }
+
+    /**
+     * Returns an iterator over the child resources or <code>null</code> if
+     * there are none.
+     */
+    abstract Iterator<Resource> listJcrChildren();
+
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_CONTENT;
+import static org.apache.jackrabbit.JcrConstants.JCR_CREATED;
+import static org.apache.jackrabbit.JcrConstants.JCR_DATA;
+import static org.apache.jackrabbit.JcrConstants.JCR_ENCODING;
+import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIMETYPE;
+import static org.apache.jackrabbit.JcrConstants.NT_FILE;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessControlException;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.Item;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.net.URLFactory;
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adapter;
+import org.apache.sling.api.resource.PersistableValueMap;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.JcrModifiablePropertyMap;
+import org.apache.sling.jcr.resource.JcrPropertyMap;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** A Resource that wraps a JCR Node */
+@Adaptable(adaptableClass=Resource.class, adapters={
+        @Adapter({Node.class, Map.class, Item.class, ValueMap.class, URL.class}),
+        @Adapter(value=PersistableValueMap.class, condition="If the resource is a JcrNodeResource and the user has set property privileges on the node."),
+        @Adapter(value=InputStream.class, condition="If the resource is a JcrNodeResource and has a jcr:data property or is an nt:file node.")
+})
+public class JcrNodeResource extends JcrItemResource {
+
+    /** marker value for the resourceSupertType before trying to evaluate */
+    private static final String UNSET_RESOURCE_SUPER_TYPE = "<unset>";
+
+    /** default log */
+    private static final Logger LOGGER = LoggerFactory.getLogger(JcrNodeResource.class);
+
+    private final Node node;
+
+    private final String resourceType;
+
+    protected String resourceSuperType;
+
+    private final ClassLoader dynamicClassLoader;
+
+    /**
+     * Constructor
+     * @param resourceResolver
+     * @param node
+     * @param dynamicClassLoader Dynamic class loader for loading serialized objects.
+     * @throws RepositoryException
+     */
+    public JcrNodeResource(final ResourceResolver resourceResolver,
+                           final Node node,
+                           final ClassLoader dynamicClassLoader)
+    throws RepositoryException {
+        super(resourceResolver, node.getPath());
+        this.dynamicClassLoader = dynamicClassLoader;
+        this.node = node;
+        resourceType = getResourceTypeForNode(node);
+        resourceSuperType = UNSET_RESOURCE_SUPER_TYPE;
+
+        // check for nt:file metadata
+        setMetaData(node, getResourceMetadata());
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public String getResourceSuperType() {
+        // Yes, this isn't how you're supposed to compare Strings, but this is intentional.
+        if ( resourceSuperType == UNSET_RESOURCE_SUPER_TYPE ) {
+            try {
+                if (node.hasProperty(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY)) {
+                    resourceSuperType = node.getProperty(JcrResourceConstants.SLING_RESOURCE_SUPER_TYPE_PROPERTY).getValue().getString();
+                }
+            } catch (RepositoryException re) {
+                // we ignore this
+            }
+            if ( resourceSuperType == UNSET_RESOURCE_SUPER_TYPE ) {
+                resourceSuperType = null;
+            }
+        }
+        return resourceSuperType;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <Type> Type adaptTo(Class<Type> type) {
+        if (type == Node.class || type == Item.class) {
+            return (Type) getNode(); // unchecked cast
+        } else if (type == InputStream.class) {
+            return (Type) getInputStream(); // unchecked cast
+        } else if (type == URL.class) {
+            return (Type) getURL(); // unchecked cast
+        } else if (type == Map.class || type == ValueMap.class) {
+            return (Type) new JcrPropertyMap(getNode(), this.dynamicClassLoader); // unchecked cast
+        } else if (type == PersistableValueMap.class) {
+            // check write
+            try {
+                getNode().getSession().checkPermission(getNode().getPath(),
+                    "set_property");
+                return (Type) new JcrModifiablePropertyMap(getNode(), this.dynamicClassLoader);
+            } catch (AccessControlException ace) {
+                // the user has no write permission, cannot adapt
+                LOGGER.debug(
+                    "adaptTo(PersistableValueMap): Cannot set properties on {}",
+                    this);
+            } catch (RepositoryException e) {
+                // some other problem, cannot adapt
+                LOGGER.debug(
+                    "adaptTo(PersistableValueMap): Unexpected problem for {}",
+                    this);
+            }
+        }
+
+        // fall back to default implementation
+        return super.adaptTo(type);
+    }
+
+    public String toString() {
+        return getClass().getSimpleName()
+        	+ ", type=" + getResourceType()
+        	+ ", superType=" + getResourceSuperType()
+            + ", path=" + getPath();
+    }
+
+    // ---------- internal -----------------------------------------------------
+
+    private Node getNode() {
+        return node;
+    }
+
+    /**
+     * Returns a stream to the <em>jcr:data</em> property if the
+     * {@link #getNode() node} is an <em>nt:file</em> or <em>nt:resource</em>
+     * node. Otherwise returns <code>null</code>. If a valid stream can be
+     * returned, this method also sets the content length resource metadata.
+     */
+    private InputStream getInputStream() {
+        // implement this for nt:file only
+        if (node != null) {
+            try {
+                // find the content node: for nt:file it is jcr:content
+                // otherwise it is the node of this resource
+                Node content = node.isNodeType(NT_FILE)
+                        ? node.getNode(JCR_CONTENT)
+                        : node;
+
+                Property data;
+
+                // if the node has a jcr:data property, use that property
+                if (content.hasProperty(JCR_DATA)) {
+                    data = content.getProperty(JCR_DATA);
+                } else {
+                    // otherwise try to follow default item trail
+                    try {
+                        Item item = content.getPrimaryItem();
+                        while (item.isNode()) {
+                            item = ((Node) item).getPrimaryItem();
+                        }
+                        data = ((Property) item);
+
+                        // set the content length property as a side effect
+                        // for resources which are not nt:file based and whose
+                        // data is not in jcr:content/jcr:data this will lazily
+                        // set the correct content length
+                        this.setContentLength(data);
+
+                    } catch (ItemNotFoundException infe) {
+                        // we don't actually care, but log for completeness
+                        LOGGER.debug("getInputStream: No primary items for {}", toString(), infe);
+                        data = null;
+                    }
+                }
+
+                if (data != null) {
+                    return data.getStream();
+                }
+
+            } catch (RepositoryException re) {
+                LOGGER.error("getInputStream: Cannot get InputStream for " + this,
+                    re);
+            }
+        }
+
+        // fallback to non-streamable resource
+        return null;
+    }
+
+    private URL getURL() {
+        try {
+            return URLFactory.createURL(node.getSession(), node.getPath());
+        } catch (Exception ex) {
+            LOGGER.error("getURL: Cannot create URL for " + this, ex);
+        }
+
+        return null;
+    }
+
+    // ---------- Descendable interface ----------------------------------------
+
+    @Override
+    Iterator<Resource> listJcrChildren() {
+        try {
+            if (getNode().hasNodes()) {
+                return new JcrNodeResourceIterator(getResourceResolver(),
+                    getNode().getNodes(), this.dynamicClassLoader);
+            }
+        } catch (RepositoryException re) {
+            LOGGER.error("listChildren: Cannot get children of " + this, re);
+        }
+
+        return Collections.<Resource> emptyList().iterator();
+    }
+
+    private void setMetaData(Node node, ResourceMetadata metadata) {
+        try {
+
+            // check stuff for nt:file nodes
+            if (node.isNodeType(NT_FILE)) {
+                metadata.setCreationTime(node.getProperty(JCR_CREATED).getLong());
+
+                // continue our stuff with the jcr:content node
+                // which might be nt:resource, which we support below
+                node = node.getNode(JCR_CONTENT);
+            }
+
+            // check stuff for nt:resource (or similar) nodes
+            if (node.hasProperty(JCR_MIMETYPE)) {
+                metadata.setContentType(node.getProperty(JCR_MIMETYPE).getString());
+            }
+
+            if (node.hasProperty(JCR_ENCODING)) {
+                metadata.setCharacterEncoding(node.getProperty(JCR_ENCODING).getString());
+            }
+
+            if (node.hasProperty(JCR_LASTMODIFIED)) {
+                // We don't check node type, so JCR_LASTMODIFIED might not be a long
+                final Property prop = node.getProperty(JCR_LASTMODIFIED);
+                try {
+                    metadata.setModificationTime(prop.getLong());
+                } catch(ValueFormatException vfe) {
+                    LOGGER.debug("Property {} cannot be converted to a long, ignored ({})",
+                            prop.getPath(), vfe);
+                }
+            }
+
+            if (node.hasProperty(JCR_DATA)) {
+                final Property prop = node.getProperty(JCR_DATA);
+                try {
+                    metadata.setContentLength(prop.getLength());
+                } catch (ValueFormatException vfe) {
+                    LOGGER.debug(
+                        "Length of Property {} cannot be retrieved, ignored ({})",
+                        prop.getPath(), vfe);
+                }
+            }
+        } catch (RepositoryException re) {
+            LOGGER.info(
+                "setMetaData: Problem extracting metadata information for "
+                    + getPath(), re);
+        }
+    }
+
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResourceIterator.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.jcr.NodeIterator;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>JcrNodeResourceIterator</code> class is a resource iterator,
+ * which returns resources for each node of an underlying
+ * <code>NodeIterator</code>. Nodes in the node iterator which cannot be
+ * accessed or for which a resource cannot be created are skipped.
+ */
+public class JcrNodeResourceIterator implements Iterator<Resource> {
+
+    /** default log */
+    private static final Logger LOGGER = LoggerFactory.getLogger(JcrNodeResourceIterator.class);
+
+    /** resource resolver used to create resources from nodes */
+    private ResourceResolver resourceResolver;
+
+    /** underlying node iterator to be used for resources */
+    private NodeIterator nodes;
+
+    /** The prefetched next iterator entry, null at the end of iterating */
+    private Resource nextResult;
+
+    private final ClassLoader dynamicClassLoader;
+
+    /**
+     * Creates an instance using the given resource manager and the nodes
+     * provided as a node iterator.
+     */
+    public JcrNodeResourceIterator(final ResourceResolver resourceResolver,
+                                   final NodeIterator nodes,
+                                   final ClassLoader dynamicClassLoader) {
+        this.resourceResolver = resourceResolver;
+        this.nodes = nodes;
+        this.nextResult = seek();
+        this.dynamicClassLoader = dynamicClassLoader;
+    }
+
+    public boolean hasNext() {
+        return nextResult != null;
+    }
+
+    public Resource next() {
+        if (!hasNext()) {
+            throw new NoSuchElementException();
+        }
+
+        Resource result = nextResult;
+        nextResult = seek();
+        return result;
+    }
+
+    /**
+     * Throws <code>UnsupportedOperationException</code> as this method is not
+     * supported by this implementation.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    private Resource seek() {
+        while (nodes.hasNext()) {
+            try {
+                Resource resource = new JcrNodeResource(resourceResolver,
+                    nodes.nextNode(), dynamicClassLoader);
+                LOGGER.debug("seek: Returning Resource {}", resource);
+                return resource;
+            } catch (Throwable t) {
+                LOGGER.error(
+                    "seek: Problem creating Resource for next node, skipping",
+                    t);
+            }
+        }
+
+        // no more results
+        LOGGER.debug("seek: No more nodes, iterator exhausted");
+        return null;
+    }
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrPropertyResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrPropertyResource.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrPropertyResource.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrPropertyResource.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import java.io.InputStream;
+import java.util.Calendar;
+import java.util.Iterator;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adapter;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Adaptable(adaptableClass = Resource.class, adapters = {
+        @Adapter(value = { Item.class, Property.class, Value.class, String.class, Boolean.class, Long.class,
+                Double.class, Calendar.class, InputStream.class, Value[].class, String[].class,
+                Boolean[].class, Long[].class, Double[].class }),
+        @Adapter(value = Node.class, condition = "If the resource is a JcrPropertyResource and the property is a reference or weak reference property.") })
+public class JcrPropertyResource extends JcrItemResource {
+
+    /** default log */
+    private static final Logger LOGGER = LoggerFactory.getLogger(JcrPropertyResource.class);
+
+    private final Property property;
+
+    private final String resourceType;
+
+    public JcrPropertyResource(ResourceResolver resourceResolver,
+                               String path,
+                               Property property)
+    throws RepositoryException {
+        super(resourceResolver, path);
+        this.property = property;
+        this.resourceType = getResourceTypeForNode(property.getParent())
+                + "/" + property.getName();
+        if (PropertyType.BINARY != this.property.getType()) {
+            this.getResourceMetadata().setContentType("text/plain");
+            this.getResourceMetadata().setCharacterEncoding("UTF-8");
+        }
+
+        this.setContentLength(property);
+    }
+
+    public JcrPropertyResource(ResourceResolver resourceResolver,
+                               Property property)
+    throws RepositoryException {
+        this(resourceResolver, property.getPath(), property);
+    }
+
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    public String getResourceSuperType() {
+        return null;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+
+        // the property itself
+        if (type == Property.class || type == Item.class) {
+            return (AdapterType) getProperty();
+        }
+
+        // the property value
+        try {
+            if (type == String.class) {
+                return (AdapterType) getProperty().getString();
+
+            } else if (type == Boolean.class) {
+                return (AdapterType) Boolean.valueOf(getProperty().getBoolean());
+
+            } else if (type == Long.class) {
+                return (AdapterType) Long.valueOf(getProperty().getLong());
+
+            } else if (type == Double.class) {
+                return (AdapterType) new Double(getProperty().getDouble());
+
+            } else if (type == Calendar.class) {
+                return (AdapterType) getProperty().getDate();
+
+            } else if (type == Value.class) {
+                return (AdapterType) getProperty().getValue();
+
+            } else if (type == Node.class
+                && (getProperty().getType() == PropertyType.REFERENCE ||
+                    getProperty().getType() == PropertyType.WEAKREFERENCE)) {
+                return (AdapterType) getProperty().getNode();
+
+            } else if (type == InputStream.class) {
+                return (AdapterType) getInputStream();
+
+            } else if ( type == String[].class ) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    final Value[] values = getProperty().getValues();
+                    final String[] result = new String[values.length];
+                    for(int i=0; i<values.length; i++) {
+                        result[i] = values[i].getString();
+                    }
+                    return (AdapterType)result;
+                }
+                return (AdapterType)new String[] {getProperty().getString()};
+
+            } else if (type == Boolean[].class) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    final Value[] values = getProperty().getValues();
+                    final Boolean[] result = new Boolean[values.length];
+                    for(int i=0; i<values.length; i++) {
+                        result[i] = values[i].getBoolean();
+                    }
+                    return (AdapterType)result;
+                }
+                return (AdapterType)new Boolean[] {getProperty().getBoolean()};
+
+            } else if (type == Long[].class) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    final Value[] values = getProperty().getValues();
+                    final Long[] result = new Long[values.length];
+                    for(int i=0; i<values.length; i++) {
+                        result[i] = values[i].getLong();
+                    }
+                    return (AdapterType)result;
+                }
+                return (AdapterType)new Long[] {getProperty().getLong()};
+
+            } else if (type == Double[].class) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    final Value[] values = getProperty().getValues();
+                    final Double[] result = new Double[values.length];
+                    for(int i=0; i<values.length; i++) {
+                        result[i] = values[i].getDouble();
+                    }
+                    return (AdapterType)result;
+                }
+                return (AdapterType)new Double[] {getProperty().getDouble()};
+
+            } else if (type == Calendar[].class) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    final Value[] values = getProperty().getValues();
+                    final Calendar[] result = new Calendar[values.length];
+                    for(int i=0; i<values.length; i++) {
+                        result[i] = values[i].getDate();
+                    }
+                    return (AdapterType)result;
+                }
+                return (AdapterType)new Calendar[] {getProperty().getDate()};
+
+            } else if (type == Value[].class) {
+                if ( getProperty().getDefinition().isMultiple() ) {
+                    return (AdapterType)getProperty().getValues();
+                }
+                return (AdapterType)new Value[] {getProperty().getValue()};
+
+            }
+
+        } catch (ValueFormatException vfe) {
+            LOGGER.debug("adaptTo: Problem accessing the property value of {}: {}",
+                getPath(), vfe.getMessage());
+            LOGGER.debug("adaptTo: Cause", vfe);
+
+        } catch (RepositoryException re) {
+            LOGGER.debug("adaptTo: Problem accessing the property " + getPath(), re);
+        }
+
+        // try to use adapter factories
+        return super.adaptTo(type);
+    }
+
+    public String toString() {
+        return getClass().getSimpleName() + ", type=" + getResourceType()
+            + ", path=" + getPath();
+    }
+
+    private Property getProperty() {
+        return property;
+    }
+
+    private InputStream getInputStream() {
+        Property prop = getProperty();
+
+        try {
+            return prop.getStream();
+        } catch (RepositoryException re) {
+            LOGGER.error("getInputStream: Problem accessing the property "
+                + getPath() + " stream", re);
+        }
+
+        // fall back to none in case of an error
+        return null;
+    }
+
+    @Override
+    Iterator<Resource> listJcrChildren() {
+        return null;
+    }
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import java.util.Iterator;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceWrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>JcrResourceProvider</code> is the main resource provider of this
+ * bundle providing access to JCR resources. This resoure provider is created
+ * for each <code>JcrResourceResolver</code> instance and is bound to the JCR
+ * session for a single request.
+ */
+public class JcrResourceProvider implements ResourceProvider {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final Session session;
+    private final ClassLoader dynamicClassLoader;
+    private final boolean useMultiWorkspaces;
+
+    public JcrResourceProvider(final Session session,
+                               final ClassLoader dynamicClassLoader,
+                               boolean useMultiWorkspaces) {
+        this.session = session;
+        this.dynamicClassLoader = dynamicClassLoader;
+        this.useMultiWorkspaces = useMultiWorkspaces;
+    }
+
+    // ---------- ResourceProvider interface ----------------------------------
+
+    public String[] getRoots() {
+        return new String[] { "/" };
+    }
+
+    public Resource getResource(ResourceResolver resourceResolver,
+            HttpServletRequest request, String path) throws SlingException {
+        return getResource(resourceResolver, path);
+    }
+
+    public Resource getResource(ResourceResolver resourceResolver, String path)
+            throws SlingException {
+
+        try {
+            return createResource(resourceResolver, path);
+        } catch (RepositoryException re) {
+            throw new SlingException("Problem retrieving node based resource "
+                + path, re);
+        }
+
+    }
+
+    public Iterator<Resource> listChildren(Resource parent) {
+
+        JcrItemResource parentItemResource;
+
+        // short cut for known JCR resources
+        if (parent instanceof JcrItemResource) {
+
+            parentItemResource = (JcrItemResource) parent;
+
+        } else if (parent instanceof ResourceWrapper) {
+
+            return listChildren(((ResourceWrapper) parent).getResource());
+
+        } else {
+
+            // try to get the JcrItemResource for the parent path to list
+            // children
+            try {
+                parentItemResource = createResource(
+                    parent.getResourceResolver(), parent.getPath());
+            } catch (RepositoryException re) {
+                parentItemResource = null;
+            }
+
+        }
+
+        // return children if there is a parent item resource, else null
+        return (parentItemResource != null)
+                ? parentItemResource.listJcrChildren()
+                : null;
+    }
+
+    public Session getSession() {
+        return session;
+    }
+
+    // ---------- implementation helper ----------------------------------------
+
+    /**
+     * Creates a <code>Resource</code> instance for the item found at the
+     * given path. If no item exists at that path or the item does not have
+     * read-access for the session of this resolver, <code>null</code> is
+     * returned.
+     *
+     * @param path The absolute path
+     * @return The <code>Resource</code> for the item at the given path.
+     * @throws RepositoryException If an error occurrs accessingor checking the
+     *             item in the repository.
+     */
+    private JcrItemResource createResource(ResourceResolver resourceResolver,
+            String path) throws RepositoryException {
+        if (useMultiWorkspaces) {
+            final int wsSepPos = path.indexOf(":/");
+            if (wsSepPos != -1) {
+                final String workspaceName = path.substring(0, wsSepPos);
+                final String expectedWorkspaceName = getSession().getWorkspace().getName();
+                if (workspaceName.equals(expectedWorkspaceName)) {
+                    path = path.substring(wsSepPos + 1);
+                } else {
+                    throw new RepositoryException("Unexpected workspace name. Expected " +
+                            expectedWorkspaceName + ". Actual " + workspaceName);
+                }
+            }
+        }
+
+        if (itemExists(path)) {
+            Item item = getSession().getItem(path);
+            if (item.isNode()) {
+                log.debug(
+                    "createResource: Found JCR Node Resource at path '{}'",
+                    path);
+                return new JcrNodeResource(resourceResolver, (Node) item, dynamicClassLoader);
+            }
+
+            log.debug(
+                "createResource: Found JCR Property Resource at path '{}'",
+                path);
+            return new JcrPropertyResource(resourceResolver, path,
+                (Property) item);
+        }
+
+        log.debug("createResource: No JCR Item exists at path '{}'", path);
+        return null;
+    }
+
+    /**
+     * Checks whether the item exists and this content manager's session has
+     * read access to the item. If the item does not exist, access control is
+     * ignored by this method and <code>false</code> is returned.
+     *
+     * @param path The path to the item to check
+     * @return <code>true</code> if the item exists and this content manager's
+     *         session has read access. If the item does not exist,
+     *         <code>false</code> is returned ignoring access control.
+     */
+    public boolean itemExists(String path) {
+
+        try {
+            return getSession().itemExists(path);
+        } catch (RepositoryException re) {
+            log.debug("itemExists: Error checking for existence of {}: {}",
+                path, re.toString());
+            return false;
+        }
+
+    }
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderEntry.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.jcr;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.jcr.Session;
+
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
+
+public class JcrResourceProviderEntry extends ResourceProviderEntry {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 5672648586247261128L;
+
+    private final ResourceProviderEntry delegatee;
+
+    private final Session session;
+
+    public JcrResourceProviderEntry(Session session,
+            ResourceProviderEntry delegatee,
+            final ClassLoader dynamicClassLoader,
+            boolean useMultiWorkspaces) {
+        super("/", new ResourceProvider[] { new JcrResourceProvider(session,
+                dynamicClassLoader, useMultiWorkspaces) });
+
+        this.delegatee = delegatee;
+        this.session = session;
+    }
+
+    public Session getSession() {
+        return session;
+    }
+
+    @Override
+    public boolean addResourceProvider(String prefix, ResourceProvider provider, Comparable<?> comparable) {
+        return delegatee.addResourceProvider(prefix, provider, comparable);
+    }
+
+    @Override
+    public boolean removeResourceProvider(String prefix,
+            ResourceProvider provider, Comparable<?> comparable) {
+        return delegatee.removeResourceProvider(prefix, provider, comparable);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2#put(java.lang.String,
+     *      org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry2)
+     */
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#get(java.lang.String)
+     */
+    @Override
+    public ResourceProviderEntry get(String key) {
+        ResourceProviderEntry rpe = super.get(key);
+        if (rpe == null) {
+            rpe = delegatee.get(key);
+        }
+        return rpe;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#values()
+     */
+    @Override
+    public Collection<ResourceProviderEntry> values() {
+        List<ResourceProviderEntry> list = new ArrayList<ResourceProviderEntry>(
+                super.values());
+        list.addAll(delegatee.values());
+        return list;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#containsKey(java.lang.String)
+     */
+    @Override
+    public boolean containsKey(String key) {
+        return (super.containsKey(key) || delegatee.containsKey(key));
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#getResourceProviders()
+     */
+    @Override
+    public ResourceProvider[] getResourceProviders() {
+        // merge the delegatee and this set of providers,
+        // its probably better to do this every time, as
+
+        ResourceProvider[] delegateeProviders = delegatee
+                .getResourceProviders();
+        ResourceProvider[] superProviders = super.getResourceProviders();
+        if (delegateeProviders == null && superProviders == null) {
+            return null;
+        }
+        if (delegateeProviders == null) {
+            delegateeProviders = new ResourceProvider[0];
+        }
+        if (superProviders == null) {
+            superProviders = new ResourceProvider[0];
+        }
+        ResourceProvider[] resourceProviders = new ResourceProvider[delegateeProviders.length
+                + superProviders.length];
+        System.arraycopy(delegateeProviders, 0, resourceProviders, 0,
+                delegateeProviders.length);
+        System.arraycopy(superProviders, 0, resourceProviders,
+                delegateeProviders.length, superProviders.length);
+        return resourceProviders;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry#toString()
+     */
+    @Override
+    public String toString() {
+        return delegatee.toString();
+    }
+
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal.helper.starresource;
+
+import java.util.Map;
+
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adapter;
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.ValueMap;
+
+/** Used to provide the equivalent of an empty Node for GET requests
+ *  to *.something (SLING-344)
+ */
+@Adaptable(adaptableClass = Resource.class, adapters = @Adapter(value = { String.class }))
+public class StarResource extends SyntheticResource {
+
+    final static String SLASH_STAR = "/*";
+    public final static String DEFAULT_RESOURCE_TYPE = "sling:syntheticStarResource";
+
+    private static final String UNSET_RESOURCE_SUPER_TYPE = "<unset>";
+
+    private String resourceSuperType;
+
+    @SuppressWarnings("serial")
+    static class SyntheticStarResourceException extends SlingException {
+        SyntheticStarResourceException(String reason, Throwable cause) {
+            super(reason, cause);
+        }
+    }
+
+    /** True if a StarResource should be used for the given request, if
+     *  a real Resource was not found */
+    public static boolean appliesTo(String path) {
+        return path.contains(SLASH_STAR) || path.endsWith(SLASH_STAR);
+    }
+
+    /**
+     * Returns true if the path of the resource ends with the
+     * {@link #SLASH_STAR} and therefore should be considered a star
+     * resource.
+     */
+    public static boolean isStarResource(Resource res) {
+        return res.getPath().endsWith(SLASH_STAR);
+    }
+
+    public StarResource(ResourceResolver resourceResolver, String path) {
+        super(resourceResolver, getResourceMetadata(path), DEFAULT_RESOURCE_TYPE);
+        resourceSuperType = UNSET_RESOURCE_SUPER_TYPE;
+    }
+
+    /**
+     * Calls {@link ResourceUtil#getResourceSuperType(ResourceResolver, String)} method
+     * to dynamically resolve the resource super type of this star resource.
+     */
+    public String getResourceSuperType() {
+        // Yes, this isn't how you're supposed to compare Strings, but this is intentional.
+        if (resourceSuperType == UNSET_RESOURCE_SUPER_TYPE) {
+            resourceSuperType = ResourceUtil.getResourceSuperType(this.getResourceResolver(),
+                    this.getResourceType());
+        }
+        return resourceSuperType;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public <Type> Type adaptTo(Class<Type> type) {
+        if ( type == String.class ) {
+            return (Type)"";
+        }
+        return super.adaptTo(type);
+    }
+
+    /** Get our ResourceMetadata for given path */
+    static ResourceMetadata getResourceMetadata(String path) {
+    	ResourceMetadata result = new ResourceMetadata();
+
+    	// The path is up to /*, what follows is pathInfo
+        final int index = path.indexOf(SLASH_STAR);
+        if(index >= 0) {
+            result.setResolutionPath(path.substring(0, index) + SLASH_STAR);
+            result.setResolutionPathInfo(path.substring(index + SLASH_STAR.length()));
+        } else {
+            result.setResolutionPath(path);
+        }
+        return result;
+    }
+}
\ No newline at end of file

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/scripting/JcrObjectsBindingsValuesProvider.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/scripting/JcrObjectsBindingsValuesProvider.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/scripting/JcrObjectsBindingsValuesProvider.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/java/org/apache/sling/jcr/resource/internal/scripting/JcrObjectsBindingsValuesProvider.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.sling.jcr.resource.internal.scripting;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+import javax.script.Bindings;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.scripting.api.BindingsValuesProvider;
+import org.osgi.framework.Constants;
+
+/**
+ * BindingsValuesProvider for currentNode and currentSession object.
+ */
+@Component
+@Service
+@Properties({
+    @Property(name=Constants.SERVICE_DESCRIPTION, value="Apache Sling CurrentNode BindingsValuesProvider"),
+    @Property(name=Constants.SERVICE_VENDOR, value="The Apache Software Foundation")
+})
+public class JcrObjectsBindingsValuesProvider implements BindingsValuesProvider {
+
+
+    private static final String PROP_CURRENT_NODE = "currentNode";
+    private static final String PROP_CURRENT_SESSION = "currentSession";
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addBindings(Bindings bindings) {
+        Resource resource = (Resource) bindings.get("resource");
+        if (resource != null) {
+            Node node = resource.adaptTo(Node.class);
+            if (node != null) {
+                bindings.put(PROP_CURRENT_NODE, node);
+            }
+            if ( bindings.get(PROP_CURRENT_SESSION) == null ) {
+                final Session session = resource.getResourceResolver().adaptTo(Session.class);
+                if ( session != null ) {
+                    bindings.put(PROP_CURRENT_SESSION, session);
+                }
+            }
+        }
+    }
+}

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/OSGI-INF/metatype/metatype.properties (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/OSGI-INF/metatype/metatype.properties Thu Feb  2 12:46:58 2012
@@ -0,0 +1,78 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the SCR plugin
+
+#
+# Localizations for JcrResourceResolverFactoryImpl configuration
+resource.resolver.name = Apache Sling JCR Resource Resolver
+resource.resolver.description =  Configures the JCR Resource Resolver for request \
+ URL and resource path rewriting.
+
+resource.resolver.map.location.name = Mapping Location
+resource.resolver.map.location.description = The path to the root of the \
+ configuration to setup and configure the ResourceResolver mapping. The \
+ default value is /etc/map.
+
+resource.resolver.allowDirect.name = Allow Direct Mapping
+resource.resolver.allowDirect.description = Whether to add a direct URL \
+ mapping to the front of the mapping list.
+
+resource.resolver.virtual.name = Virtual URLs
+resource.resolver.virtual.description = List of virtual URLs and there \
+ mappings to real URLs. Format is <externalURL>:<internalURL>. Mappings are \
+ applied on the complete request URL only.
+
+resource.resolver.mapping.name = URL Mappings
+resource.resolver.mapping.description = List of mappings to apply to URLs. \
+ Incoming mappings are applied to request URLs to map to Content paths, \
+ outgoing mappings are applied to map Content paths to URLs used on subsequent \
+ requests. Form ist <externalURLPrefix><op><internalURLPrefix> where <op> is \
+ ">" for incoming mappings, "<" for outgoing mappings and ":" for mappings \
+ applied in both directions. Mappings are applied in configuration order by \
+ comparing and replacing URL prefixes. Note: The use of "-" as the <op> value \
+ indicating a mapping in both directions is deprecated.
+
+resource.resolver.searchpath.name = Resource Search Path
+resource.resolver.searchpath.description = The list of absolute path prefixes \
+ applied to find resources whose path is just specified with a relative path. \
+ The default value is [ "/apps", "/libs" ]. If an empty path is specified a \
+ single entry path of [ "/" ] is assumed.
+
+resource.resolver.manglenamespaces.name = Namespace Mangling
+resource.resolver.manglenamespaces.description = Defines whether namespace \
+ prefixes of resource names inside the path (e.g. "jcr:" in "/home/path/jcr:content") \
+ are mangled or not. Mangling means that any namespace prefix contained in the \
+ path is replaced as per the generic substitution pattern "/([^:]+):/_$1_/" \
+ when calling the "map" method of the resource resolver. Likewise the \
+ "resolve" methods will unmangle such namespace prefixes according to the \
+ substituation pattern "/_([^_]+)_/$1:/". This feature is provided since \
+ there may be systems out there in the wild which cannot cope with URLs \
+ containing colons, even though they are perfectly valid characters in the \
+ path part of URI references with a scheme. The default value of this property \
+ if no configuration is provided is "true".
+
+resource.resolver.multiworkspace.name=Enable Multi Workspaces
+resource.resolver.multiworkspace.description=If this is enabled, multiple workspaces are supported \
+ This includes registering observation listeners for all workspaces and allows to \
+ resolve resources from other workspaces than the default one.

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/folder.cnd
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/folder.cnd?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/folder.cnd (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/folder.cnd Thu Feb  2 12:46:58 2012
@@ -0,0 +1,46 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// Node type to be used as a replacement for nt:folder: it can be used
+// as a child of nt:folder and allows to add unstructured content
+// (See SLING-663)
+[sling:Folder] > nt:folder
+  - * (undefined) multiple
+  - * (undefined)
+  + * (nt:base) = sling:Folder version
+
+
+//-----------------------------------------------------------------------------
+// Mixin node type to turn any node into an nt:hierarchyNode to place
+// below any nt:folder (or extension thereof such as sling:Folder) 
+// (See SLING-663)
+[sling:HierarchyNode] > nt:hierarchyNode
+    mixin
+
+
+//-----------------------------------------------------------------------------
+// Node type extending sling:Folder supporting the creation of folder
+// structured with child node ordering.
+// (See SLING-663)
+[sling:OrderedFolder] > sling:Folder
+    orderable
+  + * (nt:base) = sling:OrderedFolder version 

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd Thu Feb  2 12:46:58 2012
@@ -0,0 +1,70 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+// for more details on this resource type refer to
+//    http://cwiki.apache.org/SLING/flexible-resource-resolution.html
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// Mixin node type to enable setting an alias on a resource
+[sling:ResourceAlias]
+    mixin
+  
+    // alias name(s) for the node (single or multi-value)
+  - sling:alias (string)
+  - sling:alias (string) multiple
+
+
+//-----------------------------------------------------------------------------
+// Mixin node type to allow building the /etc/map configuration
+[sling:MappingSpec]
+    mixin
+  
+    // optional regular expression used to match the request. If
+    // this property is not set, the node's name is used for matching
+    // this regular expression should only be used to match a single
+    // segment, thus it should not contain any slashes 
+  - sling:match (string)
+  
+    // Location header value for response
+  - sling:redirect (string)
+  
+    // status code for redirect, defaults to 302 if not defined
+  - sling:status (long)
+  
+    // internal redirect for further processing
+  - sling:internalRedirect (string)
+  - sling:internalRedirect (string) multiple
+
+
+//-----------------------------------------------------------------------------
+// Primary node type to allow building the /etc/map configuration
+//   * sling:MappingSpec  -> define properties for redirects
+//   * sling:Resource     -> allow setting the resource type
+//   * nt:hierarchyNode   -> allow placing below nt:folder
+[sling:Mapping] > sling:MappingSpec, sling:Resource, nt:hierarchyNode
+
+    // child node order is significant in the resolution process
+    // so allow for custom child node ordering
+    orderable
+    
+    // allow any child node, but prefer sling:Mapping nodes
+  + * (nt:base) = sling:Mapping version
+  
\ No newline at end of file

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/redirect.cnd
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/redirect.cnd?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/redirect.cnd (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/redirect.cnd Thu Feb  2 12:46:58 2012
@@ -0,0 +1,26 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// Mixin node type for Sling Redirect nodes
+[sling:Redirect] > sling:Resource
+    mixin
+  - sling:target (Undefined)

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/resource.cnd
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/resource.cnd?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/resource.cnd (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/resource.cnd Thu Feb  2 12:46:58 2012
@@ -0,0 +1,33 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// Mixin node type for Sling Resource nodes
+[sling:Resource]
+    mixin
+  - sling:resourceType (string)
+
+
+//-----------------------------------------------------------------------------
+// Mixin node type for nodes providing resource super type information
+[sling:ResourceSuperType]
+    mixin
+  - sling:resourceSuperType (string)

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/vanitypath.cnd
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/vanitypath.cnd?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/vanitypath.cnd (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/main/resources/SLING-INF/nodetypes/vanitypath.cnd Thu Feb  2 12:46:58 2012
@@ -0,0 +1,29 @@
+//
+//  Licensed to the Apache Software Foundation (ASF) under one
+//  or more contributor license agreements.  See the NOTICE file
+//  distributed with this work for additional information
+//  regarding copyright ownership.  The ASF licenses this file
+//  to you under the Apache License, Version 2.0 (the
+//  "License"); you may not use this file except in compliance
+//  with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing,
+//  software distributed under the License is distributed on an
+//  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+//  KIND, either express or implied.  See the License for the
+//  specific language governing permissions and limitations
+//  under the License.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// Mixin node type for defining vanity resource path.
+[sling:VanityPath]
+    mixin
+  - sling:vanityPath (string) multiple
+  - sling:redirect (boolean)
+  - sling:vanityOrder (long)
+  - sling:redirectStatus (long)

Added: sling/whiteboard/resourceresolverfactory/jcr-resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/resourceresolverfactory/jcr-resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java?rev=1239587&view=auto
==============================================================================
--- sling/whiteboard/resourceresolverfactory/jcr-resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java (added)
+++ sling/whiteboard/resourceresolverfactory/jcr-resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrModifiablePropertyMapTest.java Thu Feb  2 12:46:58 2012
@@ -0,0 +1,213 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.resource.internal;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.sling.api.resource.PersistableValueMap;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.JcrModifiablePropertyMap;
+import org.apache.sling.jcr.resource.JcrPropertyMap;
+import org.apache.sling.jcr.resource.JcrResourceUtil;
+
+public class JcrModifiablePropertyMapTest extends JcrPropertyMapTest {
+
+    private String rootPath;
+
+    private Node rootNode;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        rootPath = "/test_" + System.currentTimeMillis();
+        rootNode = getSession().getRootNode().addNode(rootPath.substring(1),
+            "nt:unstructured");
+
+        final Map<String, Object> values = this.initialSet();
+        for(Map.Entry<String, Object> entry : values.entrySet()) {
+            JcrResourceUtil.setProperty(rootNode, entry.getKey().toString(), entry.getValue());
+        }
+        session.save();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (rootNode != null) {
+            rootNode.remove();
+            session.save();
+        }
+
+        super.tearDown();
+    }
+
+    private Map<String, Object> initialSet() {
+        final Map<String, Object> values = new HashMap<String, Object>();
+        values.put("string", "test");
+        values.put("long", 1L);
+        values.put("bool", Boolean.TRUE);
+        return values;
+    }
+
+    public void testPut()
+    throws IOException {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm, initialSet());
+        assertNull(pvm.get("something"));
+
+        // now put two values and check set again
+        pvm.put("something", "Another value");
+        pvm.put("string", "overwrite");
+
+        final Map<String, Object> currentlyStored = this.initialSet();
+        currentlyStored.put("something", "Another value");
+        currentlyStored.put("string", "overwrite");
+        assertContains(pvm, currentlyStored);
+
+        pvm.save();
+        assertContains(pvm, currentlyStored);
+
+        final PersistableValueMap pvm2 = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm2, currentlyStored);
+    }
+
+    public void testReset()
+    throws IOException {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm, initialSet());
+        assertNull(pvm.get("something"));
+
+        // now put two values and check set again
+        pvm.put("something", "Another value");
+        pvm.put("string", "overwrite");
+
+        final Map<String, Object> currentlyStored = this.initialSet();
+        currentlyStored.put("something", "Another value");
+        currentlyStored.put("string", "overwrite");
+        assertContains(pvm, currentlyStored);
+
+        pvm.reset();
+        assertContains(pvm, initialSet());
+
+        final PersistableValueMap pvm2 = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm2, initialSet());
+    }
+
+    public void testSerializable()
+    throws IOException {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        assertContains(pvm, initialSet());
+        assertNull(pvm.get("something"));
+
+        // now put a serializable object
+        final List<String> strings = new ArrayList<String>();
+        strings.add("a");
+        strings.add("b");
+        pvm.put("something", strings);
+
+        // check if we get the list again
+        @SuppressWarnings("unchecked")
+        final List<String> strings2 = (List<String>) pvm.get("something");
+        assertEquals(strings, strings2);
+
+        pvm.save();
+
+        final PersistableValueMap pvm2 = new JcrModifiablePropertyMap(this.rootNode);
+        // check if we get the list again
+        @SuppressWarnings("unchecked")
+        final List<String> strings3 = (List<String>) pvm2.get("something", Serializable.class);
+        assertEquals(strings, strings3);
+
+    }
+
+    public void testExceptions() {
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(this.rootNode);
+        try {
+            pvm.put(null, "something");
+            fail("Put with null key");
+        } catch (NullPointerException iae) {}
+        try {
+            pvm.put("something", null);
+            fail("Put with null value");
+        } catch (NullPointerException iae) {}
+        try {
+            pvm.put("something", pvm);
+            fail("Put with non serializable");
+        } catch (IllegalArgumentException iae) {}
+    }
+
+    private Set<String> getMixinNodeTypes(final Node node) throws RepositoryException {
+        final Set<String> mixinTypes = new HashSet<String>();
+        for(final NodeType mixinNodeType : node.getMixinNodeTypes() ) {
+            mixinTypes.add(mixinNodeType.getName());
+        }
+        return mixinTypes;
+    }
+
+    public void testMixins() throws Exception {
+        final Node testNode = this.rootNode.addNode("testMixins" + System.currentTimeMillis());
+        testNode.getSession().save();
+        final PersistableValueMap pvm = new JcrModifiablePropertyMap(testNode);
+
+        final String[] types = pvm.get("jcr:mixinTypes", String[].class);
+        final Set<String> exNodeTypes = getMixinNodeTypes(testNode);
+
+        assertEquals(exNodeTypes.size(), (types == null ? 0 : types.length));
+        if ( types != null ) {
+            for(final String name : types) {
+                assertTrue(exNodeTypes.contains(name));
+            }
+            String[] newTypes = new String[types.length + 1];
+            System.arraycopy(types, 0, newTypes, 0, types.length);
+            newTypes[types.length] = "mix:referenceable";
+            pvm.put("jcr:mixinTypes", newTypes);
+        } else {
+            pvm.put("jcr:mixinTypes", "mix:referenceable");
+        }
+        pvm.save();
+
+        final Set<String> newNodeTypes = getMixinNodeTypes(testNode);
+        assertEquals(newNodeTypes.size(), exNodeTypes.size() + 1);
+    }
+
+    protected JcrPropertyMap createPropertyMap(final Node node) {
+        return new JcrModifiablePropertyMap(node);
+    }
+
+    /**
+     * Check that the value map contains all supplied values
+     */
+    private void assertContains(ValueMap map, Map<String, Object> values) {
+        for(Map.Entry<String, Object> entry : values.entrySet()) {
+            final Object stored = map.get(entry.getKey());
+            assertEquals(values.get(entry.getKey()), stored);
+        }
+    }
+}