You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by fm...@apache.org on 2008/11/26 00:23:29 UTC

svn commit: r720647 - in /incubator/sling/trunk/jcr/resource/src: main/java/org/apache/sling/jcr/resource/internal/ main/java/org/apache/sling/jcr/resource/internal/helper/ main/java/org/apache/sling/jcr/resource/internal/helper/starresource/ main/reso...

Author: fmeschbe
Date: Tue Nov 25 15:23:28 2008
New Revision: 720647

URL: http://svn.apache.org/viewvc?rev=720647&view=rev
Log:
SLING-249 First implementation of a new JcrResourceResolver2
  + move RedirectResource in its own class file
  + add some node type definitions for JcrResourceResolver2
  + add unit tests

Added:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/RedirectResource.java
    incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java
Modified:
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
    incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java
    incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java
    incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java Tue Nov 25 15:23:28 2008
@@ -18,7 +18,6 @@
  */
 package org.apache.sling.jcr.resource.internal;
 
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
@@ -41,10 +40,10 @@
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.jcr.resource.JcrResourceUtil;
 import org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl.ResourcePattern;
 import org.apache.sling.jcr.resource.internal.helper.Mapping;
+import org.apache.sling.jcr.resource.internal.helper.RedirectResource;
 import org.apache.sling.jcr.resource.internal.helper.ResourcePathIterator;
 import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResourceIterator;
 import org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderEntry;
@@ -78,20 +77,25 @@
     // ---------- ResourceResolver interface ----------------------------------
 
     public Resource resolve(HttpServletRequest request) throws SlingException {
-        String pathInfo = request.getPathInfo();
+        return resolve(request, request.getPathInfo());
+    }
+
+    public Resource resolve(HttpServletRequest request, String absPath)
+            throws SlingException {
 
         // servlet directly address, so there is no path info, use "/" then
-        if (pathInfo == null) {
-            pathInfo = "/";
+        if (absPath == null) {
+            absPath = "/";
         }
 
-        Resource result = resolve(pathInfo);
+        Resource result = resolve(absPath);
 
         if (result == null) {
-            if(StarResource.appliesTo(request)) {
-                result = new StarResource(this, pathInfo, rootProvider.getResourceTypeProviders());
+            if (StarResource.appliesTo(absPath)) {
+                result = new StarResource(this, absPath,
+                    rootProvider.getResourceTypeProviders());
             } else {
-                result = new NonExistingResource(this, pathInfo);
+                result = new NonExistingResource(this, absPath);
             }
         }
 
@@ -153,6 +157,16 @@
         return href;
     }
 
+    public String map(HttpServletRequest request, String resourcePath) {
+        // apply normal reverse mapping first
+        String mappedPath = map(resourcePath);
+        
+        // apply additional mapping based on the request
+        
+        // return the final URL
+        return mappedPath;
+    }
+    
     public Resource getResource(String path) {
 
         // if the path is absolute, normalize . and .. segements and get res
@@ -260,7 +274,7 @@
 
     // ---------- implementation helper ----------------------------------------
 
-    public Session getSession() {
+    private Session getSession() {
         return rootProvider.getSession();
     }
 
@@ -379,7 +393,7 @@
                 rsrc = ResourceUtil.getParent(rsrc);
             }
             if ( needsWrapper ) {
-                rsrc = new RedirectResource(rsrc, path, rsrc.getPath());
+                rsrc = new RedirectResource(this, path, rsrc.getPath());
             }
             return rsrc;
         }
@@ -426,68 +440,4 @@
         log.debug("Cannot resolve path '{}' to a resource", path);
         return null;
     }
-
-    private static final class RedirectResource implements Resource {
-
-        final Resource resource;
-
-        final String target;
-
-        final String path;
-
-        public RedirectResource(final Resource rsrc,
-                                final String path,
-                                final String target) {
-            this.resource = rsrc;
-            this.path = path;
-            this.target = target;
-        }
-
-        /**
-         * @see org.apache.sling.api.resource.Resource#getPath()
-         */
-        public String getPath() {
-            return this.path;
-        }
-
-        /**
-         * @see org.apache.sling.api.resource.Resource#getResourceMetadata()
-         */
-        public ResourceMetadata getResourceMetadata() {
-            return this.resource.getResourceMetadata();
-        }
-
-        /**
-         * @see org.apache.sling.api.resource.Resource#getResourceResolver()
-         */
-        public ResourceResolver getResourceResolver() {
-            return this.resource.getResourceResolver();
-        }
-
-        /**
-         * @see org.apache.sling.api.resource.Resource#getResourceSuperType()
-         */
-        public String getResourceSuperType() {
-            return null;
-        }
-
-        /**
-         * @see org.apache.sling.api.resource.Resource#getResourceType()
-         */
-        public String getResourceType() {
-            return "sling:redirect";
-        }
-
-        /**
-         * @see org.apache.sling.api.adapter.Adaptable#adaptTo(java.lang.Class)
-         */
-        @SuppressWarnings("unchecked")
-        public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
-            if ( type == ValueMap.class ) {
-                return (AdapterType) new ValueMapDecorator(Collections.singletonMap("sling:target", (Object)this.target));
-            }
-            return null;
-        }
-
-    }
 }

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java?rev=720647&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java Tue Nov 25 15:23:28 2008
@@ -0,0 +1,618 @@
+/*
+ * 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.adapter.SlingAdaptable;
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.NonExistingResource;
+import org.apache.sling.api.resource.QuerySyntaxException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceNotFoundException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.JcrResourceUtil;
+import org.apache.sling.jcr.resource.internal.helper.MapEntry;
+import org.apache.sling.jcr.resource.internal.helper.RedirectResource;
+import org.apache.sling.jcr.resource.internal.helper.ResourcePathIterator;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResourceIterator;
+import org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderEntry;
+import org.apache.sling.jcr.resource.internal.helper.starresource.StarResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JcrResourceResolver2 extends SlingAdaptable implements
+        ResourceResolver {
+
+    private static final String MAP_ROOT = "/etc/map";
+
+    public static final String PROP_REG_EXP = "sling:regexp";
+
+    public static final String PROP_REDIRECT_INTERNAL = "sling:alias";
+
+    public static final String PROP_ALIAS = "sling:alias";
+
+    public static final String PROP_REDIRECT_EXTERNAL = "sling:redirect";
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private final JcrResourceProviderEntry rootProvider;
+
+    private final JcrResourceResolverFactoryImpl factory;
+
+    private final List<MapEntry> maps;
+
+    public JcrResourceResolver2(JcrResourceProviderEntry rootProvider,
+            JcrResourceResolverFactoryImpl factory) {
+        this.rootProvider = rootProvider;
+        this.factory = factory;
+
+        this.maps = getMap();
+    }
+
+    // ---------- resolving resources
+
+    public Resource resolve(HttpServletRequest request, String absPath) {
+
+        // Assume root if absPath is null
+        if (absPath == null) {
+            absPath = "/";
+        }
+
+        // Assume http://localhost:80 if request is null
+        String realPath = absPath;
+        String requestPath;
+        if (request != null) {
+            requestPath = getMapPath(request.getScheme(),
+                request.getServerName(), request.getServerPort(), absPath);
+        } else {
+            requestPath = getMapPath("http", "localhost", 80, absPath);
+        }
+
+        log.debug("resolve: Resolving request path {}", requestPath);
+
+        // loop while finding internal or external redirect into the
+        // content out of the virtual host mapping tree
+        // the counter is to ensure we are not caught in an endless loop here
+        // TODO: might do better to be able to log the loop and help the user
+        for (int i = 0; i < 100; i++) {
+
+            String mappedPath = null;
+            for (MapEntry mapEntry : maps) {
+                mappedPath = mapEntry.replace(requestPath);
+                if (mappedPath != null) {
+                    log.debug(
+                        "resolve: MapEntry {} matches, mapped path is {}",
+                        mapEntry, mappedPath);
+
+                    if (mapEntry.isInternal()) {
+                        // internal redirect
+                        log.debug("resolve: Redirecting internally");
+                        break;
+                    }
+
+                    // external redirect
+                    log.debug("resolve: Returning external redirect");
+                    return new RedirectResource(this, absPath, mappedPath);
+                }
+            }
+
+            // if there is no virtual host based path mapping, abort
+            // and use the original realPath
+            if (mappedPath == null) {
+                log.debug(
+                    "resolve: Request path {} does not match any MapEntry",
+                    requestPath);
+                break;
+            }
+
+            // if the mapped path is not an URL, use this path to continue
+            if (!mappedPath.contains("://")) {
+                log.debug("resolve: Mapped path is for resource tree");
+                realPath = mappedPath;
+                break;
+            }
+
+            // otherwise the mapped path is an URI and we have to try to
+            // resolve that URI now, using the URI's path as the real path
+            try {
+                URI uri = new URI(mappedPath);
+                requestPath = getMapPath(uri.getScheme(), uri.getHost(),
+                    uri.getPort(), uri.getPath());
+                realPath = uri.getPath();
+
+                log.debug(
+                    "resolve: Mapped path is an URL, using new request path {}",
+                    requestPath);
+            } catch (URISyntaxException use) {
+                // TODO: log and fail
+                throw new ResourceNotFoundException(absPath);
+            }
+        }
+
+        // now we have the real path resolved from virtual host mapping
+        // this path may be absolute or relative, in which case we try
+        // to resolve it against the search path
+
+        // first check whether the requested resource is a StarResource
+        if (StarResource.appliesTo(realPath)) {
+
+            log.debug("resolve: Mapped path {} is a Star Resource", realPath);
+            return new StarResource(this, ensureAbsPath(realPath),
+                factory.getJcrResourceTypeProvider());
+        }
+
+        Resource res = null;
+        if (realPath.startsWith("/")) {
+
+            // let's check it with a direct access first
+            log.debug("resolve: Try absolute mapped path");
+            res = resolveInternal(realPath);
+
+        } else {
+
+            String[] searchPath = getSearchPath();
+            for (int i = 0; res == null && i < searchPath.length; i++) {
+                log.debug(
+                    "resolve: Try relative mapped path with search path entry {}",
+                    searchPath[i]);
+                res = resolveInternal(searchPath[i] + realPath);
+            }
+
+        }
+
+        if (res == null) {
+            log.debug("resolve: Resource {} does not exist", realPath);
+            res = new NonExistingResource(this, ensureAbsPath(realPath));
+        } else {
+            log.debug("resolve: Found resource {}", res);
+        }
+
+        return res;
+    }
+
+    public Resource resolve(HttpServletRequest request) {
+        return resolve(request, request.getPathInfo());
+    }
+
+    public Resource resolve(String absPath) {
+        return resolve(null, absPath);
+    }
+
+    // trivial implementation not taking into account any mappings in
+    // the content
+    public String map(String resourcePath) {
+        return resourcePath;
+    }
+
+    // trivial implementation not taking into account any mappings in
+    // the content and in /etc/map
+    public String map(HttpServletRequest request, String resourcePath) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(request.getScheme()).append("://");
+        sb.append(request.getServerName());
+        if (request.getServerPort() > 0) {
+            sb.append(':').append(request.getServerPort());
+        }
+        if (request.getContextPath() != null
+            && request.getContextPath().length() > 0) {
+            sb.append(request.getContextPath());
+        }
+        sb.append(resourcePath);
+        return sb.toString();
+    }
+
+    // ---------- search path for relative resoures
+
+    public String[] getSearchPath() {
+        return factory.getSearchPath().clone();
+    }
+
+    // ---------- direct resource access without resolution
+
+    public Resource getResource(String path) {
+
+        // if the path is absolute, normalize . and .. segements and get res
+        if (path.startsWith("/")) {
+            path = ResourceUtil.normalize(path);
+            return (path != null) ? getResourceInternal(path) : null;
+        }
+
+        // otherwise we have to apply the search path
+        // (don't use this.getSearchPath() to save a few cycle for not cloning)
+        for (String prefix : factory.getSearchPath()) {
+            Resource res = getResource(prefix + path);
+            if (res != null) {
+                return res;
+            }
+        }
+
+        // no resource found, if we get here
+        return null;
+    }
+
+    public Resource getResource(Resource base, String path) {
+
+        if (!path.startsWith("/") && base != null) {
+            path = base.getPath() + "/" + path;
+        }
+
+        return getResource(path);
+    }
+
+    public Iterator<Resource> listChildren(Resource parent) {
+        return rootProvider.listChildren(parent);
+    }
+
+    // ---------- Querying resources
+
+    public Iterator<Resource> findResources(String query, String language)
+            throws SlingException {
+        try {
+            QueryResult res = JcrResourceUtil.query(getSession(), query,
+                language);
+            return new JcrNodeResourceIterator(this, res.getNodes(),
+                rootProvider.getResourceTypeProviders());
+        } catch (javax.jcr.query.InvalidQueryException iqe) {
+            throw new QuerySyntaxException(iqe.getMessage(), query, language,
+                iqe);
+        } catch (RepositoryException re) {
+            throw new SlingException(re.getMessage(), re);
+        }
+    }
+
+    public Iterator<Map<String, Object>> queryResources(String query,
+            String language) throws SlingException {
+        try {
+            QueryResult result = JcrResourceUtil.query(getSession(), query,
+                language);
+            final String[] colNames = result.getColumnNames();
+            final RowIterator rows = result.getRows();
+            return new Iterator<Map<String, Object>>() {
+                public boolean hasNext() {
+                    return rows.hasNext();
+                };
+
+                public Map<String, Object> next() {
+                    Map<String, Object> row = new HashMap<String, Object>();
+                    try {
+                        Value[] values = rows.nextRow().getValues();
+                        for (int i = 0; i < values.length; i++) {
+                            Value v = values[i];
+                            if (v != null) {
+                                row.put(colNames[i],
+                                    JcrResourceUtil.toJavaObject(values[i]));
+                            }
+                        }
+                    } catch (RepositoryException re) {
+                        log.error(
+                            "queryResources$next: Problem accessing row values",
+                            re);
+                    }
+                    return row;
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException("remove");
+                }
+            };
+        } catch (javax.jcr.query.InvalidQueryException iqe) {
+            throw new QuerySyntaxException(iqe.getMessage(), query, language,
+                iqe);
+        } catch (RepositoryException re) {
+            throw new SlingException(re.getMessage(), re);
+        }
+    }
+
+    // ---------- Adaptable interface
+
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type == Session.class) {
+            return (AdapterType) getSession();
+        }
+
+        // fall back to default behaviour
+        return super.adaptTo(type);
+    }
+
+    // ---------- internal
+
+    /**
+     * Returns the JCR Session of the root resource provider which provides
+     * access to the repository.
+     */
+    private Session getSession() {
+        return rootProvider.getSession();
+    }
+
+    /**
+     * Returns a string used for matching map entries against the given request
+     * or URI parts.
+     * 
+     * @param scheme The URI scheme
+     * @param host The host name
+     * @param port The port number. If this is negative, the default value used
+     *            is 80 unless the scheme is "https" in which case the default
+     *            value is 443.
+     * @param path The (absolute) path
+     * @return The request path string {scheme}/{host}.{port}/{path}.
+     */
+    private String getMapPath(String scheme, String host, int port, String path) {
+        if (port < 0) {
+            port = ("https".equals(scheme)) ? 443 : 80;
+        }
+
+        return scheme + "/" + host + "." + port + path;
+    }
+
+    /**
+     * Internally resolves the absolute path. The will almost always contain
+     * request selectors and an extension. Therefore this method uses the
+     * {@link ResourcePathIterator} to cut off parts of the path to find the
+     * actual resource.
+     * <p>
+     * This method operates in two steps:
+     * <ol>
+     * <li>Check the path directly
+     * <li>Drill down the resource tree from the root down to the resource
+     * trying to get the child as per the respective path segment or finding a
+     * child whose <code>sling:alias</code> property is set to the respective
+     * name.
+     * </ol>
+     * <p>
+     * If neither mechanism (direct access and drill down) resolves to a
+     * resource this method returns <code>null</code>.
+     * 
+     * @param absPath The absolute path of the resource to return.
+     * @return The resource found or <code>null</code> if the resource could
+     *         not be found. The
+     *         {@link org.apache.sling.api.resource.ResourceMetadata#getResolutionPathInfo() resolution path info}
+     *         field of the resource returned is set to the part of the
+     *         <code>absPath</code> which has been cut off by the
+     *         {@link ResourcePathIterator} to resolve the resource.
+     */
+    private Resource resolveInternal(String absPath) {
+        Resource resource = null;
+        String curPath = absPath;
+        try {
+            final ResourcePathIterator it = new ResourcePathIterator(absPath);
+            while (it.hasNext() && resource == null) {
+                curPath = it.next();
+                resource = getResourceInternal(curPath);
+            }
+        } catch (Exception ex) {
+            throw new SlingException("Problem trying " + curPath
+                + " for request path " + absPath, ex);
+        }
+
+        // SLING-627: set the part cut off from the uriPath as
+        // sling.resolutionPathInfo property such that
+        // uriPath = curPath + sling.resolutionPathInfo
+        if (resource != null) {
+
+            String rpi = absPath.substring(curPath.length());
+            resource.getResourceMetadata().setResolutionPathInfo(rpi);
+
+        } else {
+
+            // no direct resource found, so we have to drill down into the
+            // resource tree to find a match
+            resource = getResourceInternal("/");
+            StringTokenizer tokener = new StringTokenizer(absPath, "/");
+            while (resource != null && tokener.hasMoreTokens()) {
+                String childNameRaw = tokener.nextToken();
+
+                Resource nextResource = getChildInternal(resource, childNameRaw);
+                if (nextResource != null) {
+
+                    resource = nextResource;
+
+                } else {
+
+                    String childName = null;
+                    ResourcePathIterator rpi = new ResourcePathIterator(
+                        childNameRaw);
+                    while (rpi.hasNext() && nextResource == null) {
+                        childName = rpi.next();
+                        nextResource = getChildInternal(resource, childName);
+                    }
+
+                    // switch the currentResource to the nextResource (may be
+                    // null)
+                    resource = nextResource;
+
+                    // SLING-627: set the part cut off from the uriPath as
+                    // sling.resolutionPathInfo property such that
+                    // uriPath = curPath + sling.resolutionPathInfo
+                    if (nextResource != null) {
+                        String path = ResourceUtil.normalize(ResourceUtil.getParent(
+                            nextResource).getPath()
+                            + "/" + childName);
+                        String pathInfo = absPath.substring(path.length());
+                        nextResource.getResourceMetadata().setResolutionPathInfo(
+                            pathInfo);
+                        break;
+                    }
+                }
+            }
+        }
+
+        return resource;
+    }
+
+    private Resource getChildInternal(Resource parent, String childName) {
+        Resource child = getResource(parent, childName);
+        if (child != null) {
+            String alias = getProperty(child, PROP_REDIRECT_INTERNAL);
+            if (alias != null) {
+                // TODO: might be a redirect ??
+            }
+
+            // we have the resource name, continue with the next level
+            return child;
+        }
+
+        // we do not have a child with the exact name, so we look for
+        // a child, whose alias matches the childName
+        Iterator<Resource> children = listChildren(parent);
+        while (children.hasNext()) {
+            child = children.next();
+            String alias = getProperty(child, PROP_ALIAS);
+            if (childName.equals(alias)) {
+                return child;
+            }
+        }
+
+        // no match for the childName found
+        return null;
+    }
+
+    /**
+     * Creates a JcrNodeResource with the given path if existing
+     */
+    protected Resource getResourceInternal(String path) {
+
+        Resource resource = rootProvider.getResource(this, path);
+        if (resource != null) {
+            resource.getResourceMetadata().setResolutionPath(path);
+            return resource;
+        }
+
+        log.debug("Cannot resolve path '{}' to a resource", path);
+        return null;
+    }
+
+    private String getProperty(Resource res, String propName) {
+        ValueMap props = res.adaptTo(ValueMap.class);
+        if (props != null) {
+            return props.get(propName, String.class);
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the <code>path</code> as an absolute path. If the path is
+     * already absolute it is returned unmodified (the same instance actually).
+     * If the path is relative it is made absolute by prepending the first entry
+     * of the {@link #getSearchPath() search path}.
+     * 
+     * @param path The path to ensure absolute
+     * @return The absolute path as explained above
+     */
+    private String ensureAbsPath(String path) {
+        if (!path.startsWith("/")) {
+            path = getSearchPath()[0] + path;
+        }
+        return path;
+    }
+
+    private List<MapEntry> getMap() {
+        List<MapEntry> entries = new ArrayList<MapEntry>();
+
+        // the standard map configuration
+        Resource res = getResourceInternal(MAP_ROOT);
+        if (res != null) {
+            gather(entries, res, "");
+        }
+
+        // backwards-compatible sling:vanityPath stuff
+        gatherVanityPaths(entries);
+        
+        return entries;
+    }
+
+    private void gather(List<MapEntry> entries, Resource parent,
+            String parentPath) {
+        // scheme list
+        Iterator<Resource> children = listChildren(parent);
+        while (children.hasNext()) {
+            Resource child = children.next();
+            String name = getProperty(child, PROP_REG_EXP);
+            if (name == null) {
+                name = ResourceUtil.getName(child);
+            }
+            String childPath = parentPath + name;
+
+            MapEntry mapEntry = MapEntry.create(childPath, child);
+            if (mapEntry != null) {
+                entries.add(mapEntry);
+            }
+
+            // add trailing slash to child path to append the child
+            childPath += "/";
+
+            // gather the children of this entry
+            gather(entries, child, childPath);
+        }
+    }
+    
+    private void gatherVanityPaths(List<MapEntry> entries) {
+        // sling:VanityPath (uppercase V) is the mixin name
+        // sling:vanityPath (lowercase) is the property name 
+        final String queryString = "SELECT sling:vanityPath, sling:redirect FROM sling:VanityPath WHERE sling:vanityPath IS NOT NULL ORDER BY sling:vanityOrder DESC";
+        final Iterator<Map<String, Object>> i = queryResources(queryString, Query.SQL);
+        while (i.hasNext()) {
+            Map<String, Object> row = i.next();
+            
+            // url is ignoring scheme and host.port and the path is
+            // what is stored in the sling:vanityPath property
+            Object pVanityPath = row.get("sling:vanityPath");
+            if (pVanityPath != null) {
+                String url = ".*/.*" + String.valueOf(pVanityPath);
+
+                // redirect target is the node providing the sling:vanityPath
+                // property (or its parent if the node is called jcr:content)
+                String redirect = String.valueOf(row.get("jcr:path"));
+                if (ResourceUtil.getName(redirect).equals("jcr:content")) {
+                    redirect = ResourceUtil.getParent(redirect);
+                }
+
+                // whether the target is attained by a 302/FOUND or by an
+                // internal redirect is defined by the sling:redirect property
+                boolean internal = true;
+                if (row.containsKey("sling:redirect")) {
+                    internal = !Boolean.valueOf(String.valueOf(row.get("sling:redirect")));
+                }
+
+                entries.add(new MapEntry(url, redirect, internal));
+            }
+        }
+    }
+
+}

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java Tue Nov 25 15:23:28 2008
@@ -85,6 +85,16 @@
     }
 
     /**
+     * @scr.property values.1="/apps" values.2="/libs"
+     */
+    public static final String PROP_PATH = "resource.resolver.searchpath";
+
+    /**
+     * @scr.property value="true" type="Boolean"
+     */
+    private static final String PROP_USE_NEW_RESOLVER = "resource.resolver.new";
+
+    /**
      * @scr.property value="true" type="Boolean"
      */
     private static final String PROP_ALLOW_DIRECT = "resource.resolver.allowDirect";
@@ -108,11 +118,6 @@
     private static final String PROP_MAPPING = "resource.resolver.mapping";
 
     /**
-     * @scr.property values.1="/apps" values.2="/libs"
-     */
-    public static final String PROP_PATH = "resource.resolver.searchpath";
-
-    /**
      * These regexps are executing during the resource resolving phase
      * before the mappings are applied.
      * @scr.property values.1="/_([^/]+?)_|/$1:"
@@ -126,7 +131,6 @@
      */
     private static final String PROP_MAPREGEXPS = "resource.resolver.mapregexps";
 
-
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -174,7 +178,16 @@
     private String[] searchPath;
 
     private ResourceProviderEntry rootProviderEntry;
-
+    
+    /**
+     * Temporary field to select which JcrResourceResolver implementation to
+     * use.
+     * 
+     * @see #PROP_USE_NEW_RESOLVER
+     * @see #getResourceResolver(Session)
+     */
+    private boolean useNewResourceResolver;
+    
     public JcrResourceResolverFactoryImpl() {
         this.rootProviderEntry = new ResourceProviderEntry("/", null, null);
     }
@@ -188,6 +201,11 @@
     public ResourceResolver getResourceResolver(Session session) {
         JcrResourceProviderEntry sessionRoot = new JcrResourceProviderEntry(
             session, rootProviderEntry, getJcrResourceTypeProvider());
+
+        if (useNewResourceResolver) {
+            return new JcrResourceResolver2(sessionRoot, this);
+        }
+        
         return new JcrResourceResolver(sessionRoot, this);
     }
 
@@ -260,6 +278,20 @@
 
         Dictionary<?, ?> properties = componentContext.getProperties();
 
+        // BEGIN Temporary solution to select old and new JcrResourceResolver
+        // select new or old resource resolver
+        String propNewRes = componentContext.getBundleContext().getProperty(
+            PROP_USE_NEW_RESOLVER);
+        boolean flagNewRes = !"false".equalsIgnoreCase(propNewRes);
+        useNewResourceResolver = OsgiUtil.toBoolean(properties.get(PROP_USE_NEW_RESOLVER), flagNewRes);
+        if (useNewResourceResolver) {
+            log.info("activate: Using new JCR ResourceResolver with extended Mapping");
+        } else {
+            log.info("activate: Using old JCR ResourceResolver");
+        }
+        // END Temporary solution to select old and new JcrResourceResolver
+        
+        
         BidiMap virtuals = new TreeBidiMap();
         String[] virtualList = (String[]) properties.get(PROP_VIRTUAL);
         for (int i = 0; virtualList != null && i < virtualList.length; i++) {

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java?rev=720647&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java Tue Nov 25 15:23:28 2008
@@ -0,0 +1,91 @@
+/*
+ * 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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.jcr.resource.internal.JcrResourceResolver2;
+
+/**
+ * The <code>MapEntry</code> class represents a mapping entry in the mapping
+ * configuration tree at <code>/etc/map</code>.
+ * <p>
+ * @see http://cwiki.apache.org/SLING/flexible-resource-resolution.html
+ */
+public class MapEntry {
+
+    private final Pattern urlPattern;
+
+    private final String redirect;
+
+    private final boolean isInternal;
+
+    public static MapEntry create(String url, Resource resource) {
+        ValueMap props = resource.adaptTo(ValueMap.class);
+        if (props != null) {
+            String redirect = props.get(
+                JcrResourceResolver2.PROP_REDIRECT_EXTERNAL, String.class);
+            if (redirect != null) {
+                return new MapEntry(url, redirect, false);
+            }
+            
+            redirect = props.get(
+                JcrResourceResolver2.PROP_REDIRECT_INTERNAL, String.class);
+            if (redirect != null) {
+                return new MapEntry(url, redirect, true);
+            }
+        }
+
+        return null;
+    }
+
+    public MapEntry(String url, String redirect, boolean isInternal) {
+        this.urlPattern = Pattern.compile(url);
+        this.redirect = redirect;
+        this.isInternal = isInternal;
+    }
+
+    public Matcher getMatcher(String value) {
+        return urlPattern.matcher(value);
+    }
+    
+    // Returns the replacement or null if the value does not match
+    public String replace(String value) {
+        Matcher m = urlPattern.matcher(value);
+        if (m.find()) {
+            StringBuffer buf = new StringBuffer();
+            m.appendReplacement(buf, getRedirect());
+            m.appendTail(buf);
+            return buf.toString();
+        }
+     
+        return null;
+    }
+
+    public String getRedirect() {
+        return redirect;
+    }
+
+    public boolean isInternal() {
+        return isInternal;
+    }
+}

Added: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/RedirectResource.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/RedirectResource.java?rev=720647&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/RedirectResource.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/RedirectResource.java Tue Nov 25 15:23:28 2008
@@ -0,0 +1,51 @@
+/*
+ * 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 java.util.Collections;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+public final class RedirectResource extends SyntheticResource {
+
+    final String target;
+
+    public RedirectResource(final ResourceResolver resolver, final String path,
+            final String target) {
+        super(resolver, path, "sling:redirect");
+        this.target = target;
+    }
+
+    /**
+     * @see org.apache.sling.api.adapter.Adaptable#adaptTo(java.lang.Class)
+     */
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        if (type == ValueMap.class) {
+            return (AdapterType) new ValueMapDecorator(
+                Collections.singletonMap("sling:target", (Object) this.target));
+        }
+
+        return super.adaptTo(type);
+    }
+
+}
\ No newline at end of file

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/starresource/StarResource.java Tue Nov 25 15:23:28 2008
@@ -51,8 +51,7 @@
 
     /** True if a StarResource should be used for the given request, if
      *  a real Resource was not found */
-    public static boolean appliesTo(HttpServletRequest request) {
-        String path = request.getPathInfo();
+    public static boolean appliesTo(String path) {
         return path.contains(SLASH_STAR) || path.endsWith(SLASH_STAR);
     }
 

Modified: incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties (original)
+++ incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Nov 25 15:23:28 2008
@@ -28,6 +28,15 @@
 resource.resolver.name = Resource Resolver
 resource.resolver.description =  Configures the Resource Resolver for request \
  URL and resource path rewriting.
+
+resource.resolver.new.name = Use New Resource Resolver
+resource.resolver.new.description = Whether to use the new or the traditional \
+ Resource Resolver. The new Resource Resolver ignores all mapping configurations \
+ in this configuration but uses a new mechanism for resource resolution as \
+ explained at http://cwiki.apache.org/SLING/flexible-resource-resolution.html. \
+ If unchecking this option, the traditional Resource Resolver is used which uses \
+ the mapping configuration defined here.
+ 
 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.

Added: incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd?rev=720647&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd (added)
+++ incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd Tue Nov 25 15:23:28 2008
@@ -0,0 +1,60 @@
+//
+//  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 302/FOUND response
+  - sling:redirect (string)
+  
+    // internal redirect for further processing
+  - sling:internalRedirect (string)
+
+
+//-----------------------------------------------------------------------------
+// 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
+  + * (nt:base) = sling:Mapping version
+  
\ No newline at end of file

Added: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java?rev=720647&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java (added)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java Tue Nov 25 15:23:28 2008
@@ -0,0 +1,663 @@
+/*
+ * 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.BufferedReader;
+import java.lang.reflect.Field;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.NonExistingResource;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
+import org.apache.sling.commons.testing.jcr.RepositoryUtil;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
+import org.apache.sling.jcr.resource.internal.helper.Mapping;
+import org.apache.sling.jcr.resource.internal.helper.RedirectResource;
+import org.apache.sling.jcr.resource.internal.helper.starresource.StarResource;
+
+public class JcrResourceResolver2Test extends RepositoryTestBase {
+
+    private String rootPath;
+
+    private Node rootNode;
+
+    private Node mapRoot;
+
+    private JcrResourceResolverFactoryImpl resFac;
+
+    private ResourceResolver resResolver;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        assertTrue(RepositoryUtil.registerNodeType(getSession(),
+            this.getClass().getResourceAsStream(
+                "/SLING-INF/nodetypes/folder.cnd")));
+        assertTrue(RepositoryUtil.registerNodeType(getSession(),
+            this.getClass().getResourceAsStream(
+                "/SLING-INF/nodetypes/resource.cnd")));
+        assertTrue(RepositoryUtil.registerNodeType(getSession(),
+            this.getClass().getResourceAsStream(
+                "/SLING-INF/nodetypes/vanitypath.cnd")));
+
+        resFac = new JcrResourceResolverFactoryImpl();
+
+        Field repoField = resFac.getClass().getDeclaredField("repository");
+        repoField.setAccessible(true);
+        repoField.set(resFac, getRepository());
+
+        Field mappingsField = resFac.getClass().getDeclaredField("mappings");
+        mappingsField.setAccessible(true);
+        mappingsField.set(resFac, new Mapping[] { Mapping.DIRECT });
+
+        Field patternsField = resFac.getClass().getDeclaredField("patterns");
+        patternsField.setAccessible(true);
+        patternsField.set(resFac,
+            new JcrResourceResolverFactoryImpl.ResourcePattern[0]);
+
+        // ensure using JcrResourceResolver2
+        Field unrrField = resFac.getClass().getDeclaredField("useNewResourceResolver");
+        unrrField.setAccessible(true);
+        unrrField.set(resFac, true);
+
+        try {
+            NamespaceRegistry nsr = session.getWorkspace().getNamespaceRegistry();
+            nsr.registerNamespace(SlingConstants.NAMESPACE_PREFIX,
+                JcrResourceConstants.SLING_NAMESPACE_URI);
+        } catch (Exception e) {
+            // don't care for now
+        }
+
+        // test data
+        rootPath = "/test" + System.currentTimeMillis();
+        rootNode = getSession().getRootNode().addNode(rootPath.substring(1),
+            "nt:unstructured");
+
+        // test mappings
+        mapRoot = getSession().getRootNode().addNode("etc", "nt:unstructured");
+        Node map = mapRoot.addNode("map", "nt:unstructured");
+        Node https = map.addNode("https", "nt:unstructured");
+        https.addNode("localhost.443", "nt:unstructured");
+        Node http = map.addNode("http", "nt:unstructured");
+        http.addNode("localhost.80", "nt:unstructured");
+
+        session.save();
+
+        resResolver = resFac.getResourceResolver(session);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (rootNode != null) {
+            rootNode.remove();
+        }
+
+        if (mapRoot != null) {
+            mapRoot.remove();
+        }
+
+        session.save();
+    }
+
+    public void testGetResource() throws Exception {
+        // existing resource
+        Resource res = resResolver.getResource(rootPath);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+
+        // missing resource
+        String path = rootPath + "/missing";
+        res = resResolver.getResource(path);
+        assertNull(res);
+    }
+
+    public void testResolveResource() throws Exception {
+        // existing resource
+        Resource res = resResolver.resolve(new ResourceResolverTestRequest(
+            rootPath), rootPath);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+
+        // missing resource below root should resolve "missing resource"
+        String path = rootPath + "/missing";
+        res = resResolver.resolve(new ResourceResolverTestRequest(path), path);
+        assertNotNull(res);
+        assertEquals(path, res.getPath());
+        assertEquals(Resource.RESOURCE_TYPE_NON_EXISTING, res.getResourceType());
+
+        assertNull(res.adaptTo(Node.class));
+
+        // root with selectors/ext should resolve root
+        path = rootPath + ".print.a4.html";
+        res = resResolver.resolve(new ResourceResolverTestRequest(path), path);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+
+        // missing resource should return NON_EXISTING Resource
+        path = rootPath + System.currentTimeMillis();
+        res = resResolver.resolve(new ResourceResolverTestRequest(path), path);
+        assertNotNull(res);
+        assertTrue(res instanceof NonExistingResource);
+        assertEquals(path, res.getPath());
+        assertEquals(Resource.RESOURCE_TYPE_NON_EXISTING, res.getResourceType());
+    }
+
+    public void testResolveResourceExternalRedirect() throws Exception {
+        HttpServletRequest request = new ResourceResolverTestRequest(rootPath) {
+            @Override
+            public String getScheme() {
+                return "https";
+            }
+
+            @Override
+            public String getServerName() {
+                return "localhost";
+            }
+
+            @Override
+            public int getServerPort() {
+                return -1;
+            }
+        };
+
+        Node localhost443 = mapRoot.getNode("map/https/localhost.443");
+        localhost443.setProperty(JcrResourceResolver2.PROP_REDIRECT_EXTERNAL,
+            "http://localhost");
+        session.save();
+        resResolver = resFac.getResourceResolver(session);
+
+        Resource res = resResolver.resolve(request, rootPath);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertTrue(res instanceof RedirectResource);
+        assertNotNull(res.adaptTo(ValueMap.class));
+        assertEquals("http://localhost" + rootPath,
+            res.adaptTo(ValueMap.class).get("sling:target", String.class));
+    }
+
+    public void testResolveResourceInternalRedirectUrl() throws Exception {
+        HttpServletRequest request = new ResourceResolverTestRequest(rootPath) {
+            @Override
+            public String getScheme() {
+                return "https";
+            }
+
+            @Override
+            public String getServerName() {
+                return "localhost";
+            }
+
+            @Override
+            public int getServerPort() {
+                return -1;
+            }
+        };
+
+        Node localhost443 = mapRoot.getNode("map/https/localhost.443");
+        localhost443.setProperty(JcrResourceResolver2.PROP_REDIRECT_INTERNAL,
+            "http://localhost");
+        session.save();
+        resResolver = resFac.getResourceResolver(session);
+
+        Resource res = resResolver.resolve(request, rootPath);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+    }
+
+    public void testResolveResourceInternalRedirectPath() throws Exception {
+        HttpServletRequest request = new ResourceResolverTestRequest(rootPath) {
+            @Override
+            public String getScheme() {
+                return "https";
+            }
+
+            @Override
+            public String getServerName() {
+                return "localhost";
+            }
+
+            @Override
+            public int getServerPort() {
+                return -1;
+            }
+        };
+
+        Node localhost443 = mapRoot.getNode("map/https/localhost.443");
+        Node toContent = localhost443.addNode("_playground_designground_",
+            "nt:unstructured");
+        toContent.setProperty(JcrResourceResolver2.PROP_REG_EXP,
+            "(playground|designground)");
+        toContent.setProperty(JcrResourceResolver2.PROP_REDIRECT_INTERNAL,
+            "/content/$1");
+        session.save();
+        resResolver = resFac.getResourceResolver(session);
+
+        Resource res = resResolver.resolve(request, "/playground.html");
+        assertNotNull(res);
+        assertEquals("/content/playground.html", res.getPath());
+
+        res = resResolver.resolve(request, "/playground/en.html");
+        assertNotNull(res);
+        assertEquals("/content/playground/en.html", res.getPath());
+
+        res = resResolver.resolve(request, "/libs/nt/folder.html");
+        assertNotNull(res);
+        assertEquals("/libs/nt/folder.html", res.getPath());
+    }
+
+    public void testResolveResourceAlias() throws Exception {
+        // define an alias for the rootPath
+        String alias = "testAlias";
+        rootNode.setProperty(JcrResourceResolver2.PROP_ALIAS, alias);
+        session.save();
+
+        String path = ResourceUtil.normalize(ResourceUtil.getParent(rootPath)
+            + "/" + alias + ".print.html");
+        
+        HttpServletRequest request = new ResourceResolverTestRequest(path);
+        Resource res = resResolver.resolve(request, path);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+        
+        assertEquals(".print.html",
+            res.getResourceMetadata().getResolutionPathInfo());
+        
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+
+        path = ResourceUtil.normalize(ResourceUtil.getParent(rootPath)
+            + "/" + alias + ".print.html/suffix.pdf");
+
+        request = new ResourceResolverTestRequest(path);
+        res = resResolver.resolve(request, path);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+        
+        assertEquals(".print.html/suffix.pdf",
+            res.getResourceMetadata().getResolutionPathInfo());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootNode.isSame(res.adaptTo(Node.class)));
+    }
+
+    public void testGetDoesNotGoUp() throws Exception {
+
+        final String path = rootPath + "/nothing";
+
+        {
+            final Resource res = resResolver.resolve(
+                new ResourceResolverTestRequest(path, "POST"), path);
+            assertNotNull(res);
+            assertEquals("POST request resolution does not go up the path",
+                Resource.RESOURCE_TYPE_NON_EXISTING, res.getResourceType());
+        }
+
+        {
+            final Resource res = resResolver.resolve(
+                new ResourceResolverTestRequest(path, "GET"), path);
+            assertNotNull(res);
+            assertEquals("GET request resolution does not go up the path",
+                Resource.RESOURCE_TYPE_NON_EXISTING, res.getResourceType());
+        }
+    }
+
+    public void testGetRemovesExtensionInResolution() throws Exception {
+        final String path = rootPath + ".whatever";
+        final Resource res = resResolver.resolve(
+            new ResourceResolverTestRequest(path, "GET"), path);
+        assertNotNull(res);
+        assertEquals(rootPath, res.getPath());
+        assertEquals(rootNode.getPrimaryNodeType().getName(),
+            res.getResourceType());
+    }
+
+    public void testStarResourcePlain() throws Exception {
+        final String path = rootPath + "/" + System.currentTimeMillis() + "/*";
+        testStarResourceHelper(path, "GET");
+        testStarResourceHelper(path, "POST");
+        testStarResourceHelper(path, "PUT");
+        testStarResourceHelper(path, "DELETE");
+    }
+
+    public void testStarResourceExtension() throws Exception {
+        final String path = rootPath + "/" + System.currentTimeMillis()
+            + "/*.html";
+        testStarResourceHelper(path, "GET");
+        testStarResourceHelper(path, "POST");
+        testStarResourceHelper(path, "PUT");
+        testStarResourceHelper(path, "DELETE");
+    }
+
+    public void testStarResourceSelectorExtension() throws Exception {
+        final String path = rootPath + "/" + System.currentTimeMillis()
+            + "/*.print.a4.html";
+        testStarResourceHelper(path, "GET");
+        testStarResourceHelper(path, "POST");
+        testStarResourceHelper(path, "PUT");
+        testStarResourceHelper(path, "DELETE");
+    }
+
+    public void testSlingFolder() throws Exception {
+
+        // create a folder
+        String folderPath = "folder";
+        Node folder = rootNode.addNode(folderPath, "sling:Folder");
+        rootNode.save();
+
+        // test default child node type
+        Node child = folder.addNode("child0");
+        folder.save();
+        assertEquals("sling:Folder", child.getPrimaryNodeType().getName());
+
+        // test explicit sling:Folder child
+        child = folder.addNode("child1", "sling:Folder");
+        folder.save();
+        assertEquals("sling:Folder", child.getPrimaryNodeType().getName());
+
+        // test explicit nt:folder child
+        child = folder.addNode("child2", "nt:folder");
+        folder.save();
+        assertEquals("nt:folder", child.getPrimaryNodeType().getName());
+
+        // test any child node -- use nt:unstructured here
+        child = folder.addNode("child3", "nt:unstructured");
+        folder.save();
+        assertEquals("nt:unstructured", child.getPrimaryNodeType().getName());
+    }
+
+    // ---------- internal
+
+    private void testStarResourceHelper(final String path, final String method) {
+        final Resource res = resResolver.resolve(
+            new ResourceResolverTestRequest(path, method), path);
+        assertNotNull(res);
+        assertTrue(ResourceUtil.isStarResource(res));
+        assertEquals(StarResource.class.getName(), res.getClass().getName());
+        assertEquals(StarResource.DEFAULT_RESOURCE_TYPE, res.getResourceType());
+    }
+
+    private static class ResourceResolverTestRequest implements
+            HttpServletRequest {
+
+        private final String pathInfo;
+
+        private final String method;
+
+        ResourceResolverTestRequest(String pathInfo) {
+            this(pathInfo, null);
+        }
+
+        ResourceResolverTestRequest(String pathInfo, String httpMethod) {
+            this.pathInfo = pathInfo;
+            this.method = httpMethod;
+        }
+
+        public String getPathInfo() {
+            return pathInfo;
+        }
+
+        public Object getAttribute(String name) {
+            return null;
+        }
+
+        public Enumeration<?> getAttributeNames() {
+            return null;
+        }
+
+        public String getCharacterEncoding() {
+            return null;
+        }
+
+        public int getContentLength() {
+            return 0;
+        }
+
+        public String getContentType() {
+            return null;
+        }
+
+        public ServletInputStream getInputStream() {
+            return null;
+        }
+
+        public String getLocalAddr() {
+            return null;
+        }
+
+        public String getLocalName() {
+            return null;
+        }
+
+        public int getLocalPort() {
+            return 0;
+        }
+
+        public Locale getLocale() {
+            return null;
+        }
+
+        public Enumeration<?> getLocales() {
+            return null;
+        }
+
+        public String getParameter(String name) {
+            return null;
+        }
+
+        public Map<?, ?> getParameterMap() {
+            return null;
+        }
+
+        public Enumeration<?> getParameterNames() {
+            return null;
+        }
+
+        public String[] getParameterValues(String name) {
+            return null;
+        }
+
+        public String getProtocol() {
+            return null;
+        }
+
+        public BufferedReader getReader() {
+            return null;
+        }
+
+        public String getRealPath(String path) {
+            return null;
+        }
+
+        public String getRemoteAddr() {
+            return null;
+        }
+
+        public String getRemoteHost() {
+            return null;
+        }
+
+        public int getRemotePort() {
+            return 0;
+        }
+
+        public RequestDispatcher getRequestDispatcher(String path) {
+            return null;
+        }
+
+        public String getScheme() {
+            return "http";
+        }
+
+        public String getServerName() {
+            return "localhost";
+        }
+
+        public int getServerPort() {
+            return -1;
+        }
+
+        public boolean isSecure() {
+            return false;
+        }
+
+        public void removeAttribute(String name) {
+        }
+
+        public void setAttribute(String name, Object o) {
+        }
+
+        public void setCharacterEncoding(String env) {
+        }
+
+        public String getAuthType() {
+            return null;
+        }
+
+        public String getContextPath() {
+            return null;
+        }
+
+        public Cookie[] getCookies() {
+            return null;
+        }
+
+        public long getDateHeader(String name) {
+            return 0;
+        }
+
+        public String getHeader(String name) {
+            return null;
+        }
+
+        public Enumeration<?> getHeaderNames() {
+            return null;
+        }
+
+        public Enumeration<?> getHeaders(String name) {
+            return null;
+        }
+
+        public int getIntHeader(String name) {
+            return 0;
+        }
+
+        public String getMethod() {
+            return method;
+        }
+
+        public String getPathTranslated() {
+            return null;
+        }
+
+        public String getQueryString() {
+            return null;
+        }
+
+        public String getRemoteUser() {
+            return null;
+        }
+
+        public String getRequestURI() {
+            return null;
+        }
+
+        public StringBuffer getRequestURL() {
+            return null;
+        }
+
+        public String getRequestedSessionId() {
+            return null;
+        }
+
+        public String getServletPath() {
+            return null;
+        }
+
+        public HttpSession getSession() {
+            return null;
+        }
+
+        public HttpSession getSession(boolean create) {
+            return null;
+        }
+
+        public Principal getUserPrincipal() {
+            return null;
+        }
+
+        public boolean isRequestedSessionIdFromCookie() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdFromURL() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdFromUrl() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdValid() {
+            return false;
+        }
+
+        public boolean isUserInRole(String role) {
+            return false;
+        }
+    }
+}

Modified: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java (original)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java Tue Nov 25 15:23:28 2008
@@ -76,6 +76,11 @@
         patternsField.setAccessible(true);
         patternsField.set(resFac, new JcrResourceResolverFactoryImpl.ResourcePattern[0]);
 
+        // ensure using JcrResourceResolver
+        Field unrrField = resFac.getClass().getDeclaredField("useNewResourceResolver");
+        unrrField.setAccessible(true);
+        unrrField.set(resFac, false);
+
         try {
             NamespaceRegistry nsr = session.getWorkspace().getNamespaceRegistry();
             nsr.registerNamespace(SlingConstants.NAMESPACE_PREFIX,

Modified: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java (original)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntryTest.java Tue Nov 25 15:23:28 2008
@@ -209,6 +209,10 @@
             return null;
         }
 
+        public String map(HttpServletRequest request, String resourcePath) {
+            return null;
+        }
+        
         public String map(String resourcePath) {
             return null;
         }
@@ -218,6 +222,10 @@
             return null;
         }
 
+        public Resource resolve(HttpServletRequest request, String absPath) {
+            return null;
+        }
+        
         public Resource resolve(HttpServletRequest request) {
             return null;
         }

Modified: incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java?rev=720647&r1=720646&r2=720647&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java (original)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java Tue Nov 25 15:23:28 2008
@@ -74,12 +74,20 @@
     public String map(String resourcePath) {
         return null;
     }
+    
+    public String map(HttpServletRequest request, String resourcePath) {
+        return null;
+    }
 
     public Iterator<Map<String, Object>> queryResources(String query,
             String language) {
         return null;
     }
 
+    public Resource resolve(HttpServletRequest request, String absPath) {
+        return null;
+    }
+    
     public Resource resolve(HttpServletRequest request) {
         return null;
     }