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/28 13:14:53 UTC

svn commit: r721447 - in /incubator/sling/trunk/jcr/resource: ./ src/main/java/org/apache/sling/jcr/resource/internal/ src/main/java/org/apache/sling/jcr/resource/internal/helper/ src/main/resources/SLING-INF/nodetypes/ src/test/java/org/apache/sling/j...

Author: fmeschbe
Date: Fri Nov 28 04:14:52 2008
New Revision: 721447

URL: http://svn.apache.org/viewvc?rev=721447&view=rev
Log:
SLING-249 Add support for old Virtual URLs and URL Mappings,
add support for multiple internal redirect options, add
support for setting the external redirect status, add
namespace mangling

Modified:
    incubator/sling/trunk/jcr/resource/pom.xml
    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/JcrResourceResolverFactoryImpl.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/Mapping.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/pom.xml
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/pom.xml?rev=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/pom.xml (original)
+++ incubator/sling/trunk/jcr/resource/pom.xml Fri Nov 28 04:14:52 2008
@@ -67,9 +67,9 @@
 
                         <Sling-Nodetypes>
                             SLING-INF/nodetypes/folder.cnd,
-                            SLING-INF/nodetypes/mapping.cnd,
                             SLING-INF/nodetypes/resource.cnd,
-                            SLING-INF/nodetypes/vanitypath.cnd
+                            SLING-INF/nodetypes/vanitypath.cnd,
+                            SLING-INF/nodetypes/mapping.cnd
                         </Sling-Nodetypes>
 
                     </instructions>

Modified: 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=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2.java Fri Nov 28 04:14:52 2008
@@ -21,11 +21,17 @@
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.StringTokenizer;
+import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -34,6 +40,7 @@
 import javax.jcr.query.QueryResult;
 import javax.jcr.query.RowIterator;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.adapter.SlingAdaptable;
 import org.apache.sling.api.SlingException;
@@ -46,6 +53,7 @@
 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.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;
@@ -57,6 +65,20 @@
 public class JcrResourceResolver2 extends SlingAdaptable implements
         ResourceResolver {
 
+    private static final String MANGLE_NAMESPACE_IN_SUFFIX = "_";
+
+    private static final String MANGLE_NAMESPACE_IN_PREFIX = "/_";
+
+    private static final String MANGLE_NAMESPACE_IN = "/_([^_]+)_";
+
+    private static final String MANGLE_NAMESPACE_OUT_SUFFIX = ":";
+
+    private static final String MANGLE_NAMESPACE_OUT_PREFIX = "/";
+
+    private static final String MANGLE_NAMESPACE_OUT = "/([^:]+):";
+
+    private static final String ANY_SCHEME_HOST = ".*/.*";
+
     private static final String MAP_ROOT = "/etc/map";
 
     public static final String PROP_REG_EXP = "sling:match";
@@ -66,6 +88,8 @@
     public static final String PROP_ALIAS = "sling:alias";
 
     public static final String PROP_REDIRECT_EXTERNAL = "sling:redirect";
+    
+    public static final String PROP_REDIRECT_EXTERNAL_STATUS = "sling:status";
 
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
@@ -75,6 +99,8 @@
     private final JcrResourceResolverFactoryImpl factory;
 
     private final List<MapEntry> maps;
+    
+    private Set<String> namespaces;
 
     public JcrResourceResolver2(JcrResourceProviderEntry rootProvider,
             JcrResourceResolverFactoryImpl factory) {
@@ -93,8 +119,11 @@
             absPath = "/";
         }
 
+        // check for special namespace prefix treatment
+        absPath = unmangleNamespaces(absPath);
+        
         // Assume http://localhost:80 if request is null
-        String realPath = absPath;
+        String[] realPathList = { absPath };
         String requestPath;
         if (request != null) {
             requestPath = getMapPath(request.getScheme(),
@@ -111,7 +140,7 @@
         // 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;
+            String[] mappedPath = null;
             for (MapEntry mapEntry : maps) {
                 mappedPath = mapEntry.replace(requestPath);
                 if (mappedPath != null) {
@@ -127,7 +156,7 @@
 
                     // external redirect
                     log.debug("resolve: Returning external redirect");
-                    return new RedirectResource(this, absPath, mappedPath);
+                    return new RedirectResource(this, absPath, mappedPath[0]);
                 }
             }
 
@@ -141,19 +170,19 @@
             }
 
             // if the mapped path is not an URL, use this path to continue
-            if (!mappedPath.contains("://")) {
+            if (!mappedPath[0].contains("://")) {
                 log.debug("resolve: Mapped path is for resource tree");
-                realPath = mappedPath;
+                realPathList = 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);
+                URI uri = new URI(mappedPath[0]);
                 requestPath = getMapPath(uri.getScheme(), uri.getHost(),
                     uri.getPort(), uri.getPath());
-                realPath = uri.getPath();
+                realPathList = new String[] { uri.getPath() };
 
                 log.debug(
                     "resolve: Mapped path is an URL, using new request path {}",
@@ -168,36 +197,43 @@
         // 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)) {
+        Resource res = null;
+        for (int i = 0; res == null && i < realPathList.length; i++) {
+            String realPath = realPathList[i];
 
-            log.debug("resolve: Mapped path {} is a Star Resource", realPath);
-            return new StarResource(this, ensureAbsPath(realPath),
-                factory.getJcrResourceTypeProvider());
-        }
+            // first check whether the requested resource is a StarResource
+            if (StarResource.appliesTo(realPath)) {
 
-        Resource res = null;
-        if (realPath.startsWith("/")) {
+                log.debug("resolve: Mapped path {} is a Star Resource",
+                    realPath);
+                res = new StarResource(this, ensureAbsPath(realPath),
+                    factory.getJcrResourceTypeProvider());
 
-            // let's check it with a direct access first
-            log.debug("resolve: Try absolute mapped path");
-            res = resolveInternal(realPath);
+            } else
 
-        } else {
+            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 spi = 0; res == null && spi < searchPath.length; spi++) {
+                    log.debug(
+                        "resolve: Try relative mapped path with search path entry {}",
+                        searchPath[spi]);
+                    res = resolveInternal(searchPath[spi] + realPath);
+                }
 
-            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));
+            log.debug("resolve: Resource {} does not exist", realPathList[0]);
+            res = new NonExistingResource(this, ensureAbsPath(realPathList[0]));
         } else {
             log.debug("resolve: Found resource {}", res);
         }
@@ -216,23 +252,30 @@
     // trivial implementation not taking into account any mappings in
     // the content
     public String map(String resourcePath) {
-        return resourcePath;
+        return map(null, 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());
+        
+        if (request != null) {
+            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);
+
+        // mangle the namespaces
+        sb.append(mangleNamespaces(resourcePath));
+        
         return sb.toString();
     }
 
@@ -359,6 +402,23 @@
     private Session getSession() {
         return rootProvider.getSession();
     }
+    
+    private Set<String> getNamespaces() {
+        if (namespaces == null) {
+            
+            // get the current set of namespaces, we cache throughout our
+            // life time..
+            String[] namespaceList;
+            try {
+                namespaceList = getSession().getNamespacePrefixes();
+            } catch (RepositoryException re) {
+                namespaceList = new String[0];
+            }
+
+            namespaces = new HashSet<String>(Arrays.asList(namespaceList));
+        }
+        return namespaces;
+    }
 
     /**
      * Returns a string used for matching map entries against the given request
@@ -562,6 +622,9 @@
             gather(entries, res, "");
         }
 
+        // backwards-compatibility: read current configuration
+        gatherConfiguration(entries);
+        
         // backwards-compatible sling:vanityPath stuff
         gatherVanityPaths(entries);
         
@@ -605,7 +668,7 @@
             // what is stored in the sling:vanityPath property
             Object pVanityPath = row.get("sling:vanityPath");
             if (pVanityPath != null) {
-                String url = ".*/.*" + String.valueOf(pVanityPath);
+                String url = ANY_SCHEME_HOST + String.valueOf(pVanityPath);
 
                 // redirect target is the node providing the sling:vanityPath
                 // property (or its parent if the node is called jcr:content)
@@ -616,14 +679,86 @@
 
                 // 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")));
+                int status = -1;
+                if (row.containsKey("sling:redirect")
+                    && Boolean.valueOf(String.valueOf(row.get("sling:redirect")))) {
+                    status = HttpServletResponse.SC_FOUND;
                 }
 
-                entries.add(new MapEntry(url, redirect, internal));
+                entries.add(new MapEntry(url, redirect, status));
             }
         }
     }
+    
+    private void gatherConfiguration(List<MapEntry> entries) {
+        // virtual uris
+        Map<?, ?> virtuals = factory.getVirtualURLMap();
+        if (virtuals != null) {
+            for (Entry<?, ?> virtualEntry : virtuals.entrySet()) {
+                String url = ANY_SCHEME_HOST + virtualEntry.getKey();
+                String redirect = (String) virtualEntry.getValue();
+                entries.add(new MapEntry(url, redirect, -1));
+            }
+        }
+        
+        // URL Mappings
+        Mapping[] mappings = factory.getMappings();
+        if (mappings != null) {
+            Map<String, List<String>> map = new HashMap<String, List<String>>();
+            for (Mapping mapping : mappings) {
+                if (mapping.mapsInbound()) {
+                    String url = mapping.getFrom();
+                    String alias = mapping.getTo();
+                    if (url.length() > 0) {
+                        List<String> aliasList = map.get(url);
+                        if (aliasList == null) {
+                            aliasList = new ArrayList<String>();
+                            map.put(url, aliasList);
+                        }
+                        aliasList.add(alias);
+                    }
+                }
+            }
+            for (Entry<String, List<String>> entry : map.entrySet()) {
+                entries.add(new MapEntry(ANY_SCHEME_HOST + entry.getKey(),
+                    entry.getValue().toArray(new String[0]), -1));
+            }
+        }
+    }
+
+    private String mangleNamespaces(String absPath) {
+        if (factory.isMangleNamespacePrefixes() && absPath.contains(MANGLE_NAMESPACE_OUT_SUFFIX)) {
+            Pattern p = Pattern.compile(MANGLE_NAMESPACE_OUT);
+            Matcher m = p.matcher(absPath);
+            StringBuffer buf = new StringBuffer();
+            while (m.find()) {
+                String replacement = MANGLE_NAMESPACE_IN_PREFIX + m.group(1) + MANGLE_NAMESPACE_IN_SUFFIX;
+                m.appendReplacement(buf, replacement);
+            }
+            m.appendTail(buf);
+            absPath = buf.toString();
+        }
+        
+        return absPath;
+    }
 
+    private String unmangleNamespaces(String absPath) {
+        if (factory.isMangleNamespacePrefixes() && absPath.contains(MANGLE_NAMESPACE_IN_PREFIX)) {
+            Set<String> namespaces = getNamespaces();
+            Pattern p = Pattern.compile(MANGLE_NAMESPACE_IN);
+            Matcher m = p.matcher(absPath);
+            StringBuffer buf = new StringBuffer();
+            while (m.find()) {
+                String namespace = m.group(1);
+                if (namespaces.contains(namespace)) {
+                    String replacement = MANGLE_NAMESPACE_OUT_PREFIX + namespace + MANGLE_NAMESPACE_OUT_SUFFIX;
+                    m.appendReplacement(buf, replacement);
+                }
+            }
+            m.appendTail(buf);
+            absPath = buf.toString();
+        }
+        
+        return absPath;
+    }
 }

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=721447&r1=721446&r2=721447&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 Fri Nov 28 04:14:52 2008
@@ -56,7 +56,7 @@
  * descriptors provided by bundles.
  * <li>Fires OSGi EventAdmin events on behalf of internal helper objects
  * </ul>
- *
+ * 
  * @scr.component immediate="true" label="%resource.resolver.name"
  *                description="%resource.resolver.description"
  * @scr.property name="service.description"
@@ -69,14 +69,14 @@
  * @scr.reference name="JcrResourceTypeProvider"
  *                interface="org.apache.sling.jcr.resource.JcrResourceTypeProvider"
  *                cardinality="0..n" policy="dynamic"
-
  */
-public class JcrResourceResolverFactoryImpl
-    implements JcrResourceResolverFactory {
+public class JcrResourceResolverFactoryImpl implements
+        JcrResourceResolverFactory {
 
     public final static class ResourcePattern {
         public final Pattern pattern;
-        public final String  replacement;
+
+        public final String replacement;
 
         public ResourcePattern(final Pattern p, final String r) {
             this.pattern = p;
@@ -104,7 +104,7 @@
      * maven plugin and the sling management console cannot handle empty
      * multivalue properties at the moment. So we just add a dummy direct
      * mapping.
-     *
+     * 
      * @scr.property values.1="/-/"
      */
     private static final String PROP_VIRTUAL = "resource.resolver.virtual";
@@ -118,8 +118,9 @@
     private static final String PROP_MAPPING = "resource.resolver.mapping";
 
     /**
-     * These regexps are executing during the resource resolving phase
-     * before the mappings are applied.
+     * These regexps are executing during the resource resolving phase before
+     * the mappings are applied.
+     * 
      * @scr.property values.1="/_([^/]+?)_|/$1:"
      */
     private static final String PROP_REGEXPS = "resource.resolver.regexps";
@@ -127,21 +128,47 @@
     /**
      * These regexps are executed during a map operation as the back conversion
      * of the {@link #PROP_REGEXPS}
+     * 
      * @scr.property values.1="/([^/]+?):([^/]+)|/_$1_$2"
      */
     private static final String PROP_MAPREGEXPS = "resource.resolver.mapregexps";
 
+    /**
+     * Defines whether namespace prefixes of resource names inside the path
+     * (e.g. <code>jcr:</code> in <code>/home/path/jcr:content</code>) are
+     * mangled or not.
+     * <p>
+     * Mangling means the any namespace prefix contained in the path is replaced
+     * as per the generic substitution pattern <code>/([^:]+):/_$1_/</code>
+     * when calling the <code>map</code> method of the resource resolver.
+     * Likewise the <code>resolve</code> methods will unmangle such namespace
+     * prefixes according to the substituation pattern
+     * <code>/_([^_]+)_/$1:/</code>.
+     * <p>
+     * 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.
+     * <p>
+     * The default value of this property if no configuration is provided is
+     * <code>false</code>.
+     * 
+     * @scr.property value="false" type="Boolean"
+     */
+    private static final String PROP_MANGLE_NAMESPACES = "resource.resolver.manglenamespaces";
+
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     /**
      * The JCR Repository we access to resolve resources
-     *
+     * 
      * @scr.reference
      */
     private SlingRepository repository;
 
-    /** The (optional) resource type providers.
+    /**
+     * The (optional) resource type providers.
      */
     protected final List<JcrResourceTypeProviderEntry> jcrResourceTypeProviders = new ArrayList<JcrResourceTypeProviderEntry>();
 
@@ -178,7 +205,7 @@
     private String[] searchPath;
 
     private ResourceProviderEntry rootProviderEntry;
-    
+
     /**
      * Temporary field to select which JcrResourceResolver implementation to
      * use.
@@ -187,6 +214,9 @@
      * @see #getResourceResolver(Session)
      */
     private boolean useNewResourceResolver;
+
+    // whether to mangle paths with namespaces or not
+    private boolean mangleNamespacePrefixes;
     
     public JcrResourceResolverFactoryImpl() {
         this.rootProviderEntry = new ResourceProviderEntry("/", null, null);
@@ -205,18 +235,18 @@
         if (useNewResourceResolver) {
             return new JcrResourceResolver2(sessionRoot, this);
         }
-        
+
         return new JcrResourceResolver(sessionRoot, this);
     }
 
     protected JcrResourceTypeProvider[] getJcrResourceTypeProvider() {
         JcrResourceTypeProvider[] providers = null;
-        synchronized ( this.jcrResourceTypeProviders ) {
-            if ( this.jcrResourceTypeProviders.size() > 0 ) {
+        synchronized (this.jcrResourceTypeProviders) {
+            if (this.jcrResourceTypeProviders.size() > 0) {
                 providers = new JcrResourceTypeProvider[this.jcrResourceTypeProviders.size()];
                 int index = 0;
                 final Iterator<JcrResourceTypeProviderEntry> i = this.jcrResourceTypeProviders.iterator();
-                while ( i.hasNext() ) {
+                while (i.hasNext()) {
                     providers[index] = i.next().provider;
                 }
             }
@@ -243,6 +273,10 @@
                 : null;
     }
 
+    BidiMap getVirtualURLMap() {
+        return virtualURLMap;
+    }
+
     Mapping[] getMappings() {
         return mappings;
     }
@@ -259,6 +293,10 @@
         return backPatterns;
     }
 
+    boolean isMangleNamespacePrefixes() {
+        return mangleNamespacePrefixes;
+        
+    }
     /**
      * Getter for rootProviderEntry, making it easier to extend
      * JcrResourceResolverFactoryImpl. See <a
@@ -283,15 +321,15 @@
         String propNewRes = componentContext.getBundleContext().getProperty(
             PROP_USE_NEW_RESOLVER);
         boolean flagNewRes = !"false".equalsIgnoreCase(propNewRes);
-        useNewResourceResolver = OsgiUtil.toBoolean(properties.get(PROP_USE_NEW_RESOLVER), flagNewRes);
+        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++) {
@@ -341,6 +379,10 @@
             searchPath = new String[] { "/" };
         }
 
+        // namespace mangling
+        mangleNamespacePrefixes = OsgiUtil.toBoolean(
+            properties.get(PROP_MANGLE_NAMESPACES), false);
+
         // bind resource providers not bound yet
         for (ServiceReference reference : delayedResourceProviders) {
             bindResourceProvider(reference);
@@ -352,10 +394,10 @@
     private ResourcePattern[] getResourcePatterns(String[] patternList) {
         // regexps
         List<ResourcePattern> patterns = new ArrayList<ResourcePattern>();
-        if ( patternList != null ) {
-            for(final String p : patternList) {
+        if (patternList != null) {
+            for (final String p : patternList) {
                 int pos = p.lastIndexOf('|');
-                if ( pos == -1 ) {
+                if (pos == -1) {
                     log.error("Invalid regexp: {}", p);
                 } else {
                     final String replString = p.substring(pos + 1);
@@ -368,8 +410,8 @@
     }
 
     protected void processDelayedJcrResourceTypeProviders() {
-        synchronized ( this.jcrResourceTypeProviders ) {
-            for(ServiceReference reference : delayedJcrResourceTypeProviders ) {
+        synchronized (this.jcrResourceTypeProviders) {
+            for (ServiceReference reference : delayedJcrResourceTypeProviders) {
                 this.addJcrResourceTypeProvider(reference);
             }
             delayedJcrResourceTypeProviders.clear();
@@ -377,32 +419,34 @@
     }
 
     protected void addJcrResourceTypeProvider(final ServiceReference reference) {
-        final Long id = (Long)reference.getProperty(Constants.SERVICE_ID);
+        final Long id = (Long) reference.getProperty(Constants.SERVICE_ID);
         long ranking = -1;
-        if ( reference.getProperty(Constants.SERVICE_RANKING) != null ) {
-            ranking = (Long)reference.getProperty(Constants.SERVICE_RANKING);
+        if (reference.getProperty(Constants.SERVICE_RANKING) != null) {
+            ranking = (Long) reference.getProperty(Constants.SERVICE_RANKING);
         }
         this.jcrResourceTypeProviders.add(new JcrResourceTypeProviderEntry(id,
-                 ranking,
-                 (JcrResourceTypeProvider)this.componentContext.locateService("JcrResourceTypeProvider", reference)));
-        Collections.sort(this.jcrResourceTypeProviders, new Comparator<JcrResourceTypeProviderEntry>() {
-
-            public int compare(JcrResourceTypeProviderEntry o1,
-                               JcrResourceTypeProviderEntry o2) {
-                if ( o1.ranking < o2.ranking ) {
-                    return 1;
-                } else if ( o1.ranking > o2.ranking ) {
-                    return -1;
-                } else {
-                    if ( o1.serviceId < o2.serviceId ) {
-                        return -1;
-                    } else if ( o1.serviceId > o2.serviceId ) {
+            ranking,
+            (JcrResourceTypeProvider) this.componentContext.locateService(
+                "JcrResourceTypeProvider", reference)));
+        Collections.sort(this.jcrResourceTypeProviders,
+            new Comparator<JcrResourceTypeProviderEntry>() {
+
+                public int compare(JcrResourceTypeProviderEntry o1,
+                        JcrResourceTypeProviderEntry o2) {
+                    if (o1.ranking < o2.ranking) {
                         return 1;
+                    } else if (o1.ranking > o2.ranking) {
+                        return -1;
+                    } else {
+                        if (o1.serviceId < o2.serviceId) {
+                            return -1;
+                        } else if (o1.serviceId > o2.serviceId) {
+                            return 1;
+                        }
                     }
+                    return 0;
                 }
-                return 0;
-            }
-        });
+            });
 
     }
 
@@ -460,7 +504,7 @@
     }
 
     protected void bindJcrResourceTypeProvider(ServiceReference reference) {
-        synchronized ( this.jcrResourceTypeProviders ) {
+        synchronized (this.jcrResourceTypeProviders) {
             if (componentContext == null) {
                 delayedJcrResourceTypeProviders.add(reference);
             } else {
@@ -470,13 +514,13 @@
     }
 
     protected void unbindJcrResourceTypeProvider(ServiceReference reference) {
-        synchronized ( this.jcrResourceTypeProviders ) {
+        synchronized (this.jcrResourceTypeProviders) {
             delayedJcrResourceTypeProviders.remove(reference);
-            final long id = (Long)reference.getProperty(Constants.SERVICE_ID);
+            final long id = (Long) reference.getProperty(Constants.SERVICE_ID);
             final Iterator<JcrResourceTypeProviderEntry> i = this.jcrResourceTypeProviders.iterator();
-            while ( i.hasNext() ) {
+            while (i.hasNext()) {
                 final JcrResourceTypeProviderEntry current = i.next();
-                if ( current.serviceId == id ) {
+                if (current.serviceId == id) {
                     i.remove();
                 }
             }
@@ -492,12 +536,13 @@
 
     protected static final class JcrResourceTypeProviderEntry {
         final long serviceId;
+
         final long ranking;
+
         final JcrResourceTypeProvider provider;
 
-        public JcrResourceTypeProviderEntry(final long id,
-                                            final long ranking,
-                                            final JcrResourceTypeProvider p) {
+        public JcrResourceTypeProviderEntry(final long id, final long ranking,
+                final JcrResourceTypeProvider p) {
             this.serviceId = id;
             this.ranking = ranking;
             this.provider = p;

Modified: 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=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/MapEntry.java Fri Nov 28 04:14:52 2008
@@ -35,9 +35,9 @@
 
     private final Pattern urlPattern;
 
-    private final String redirect;
+    private final String[] redirect;
 
-    private final boolean isInternal;
+    private final int status;
 
     public static MapEntry create(String url, Resource resource) {
         ValueMap props = resource.adaptTo(ValueMap.class);
@@ -45,23 +45,29 @@
             String redirect = props.get(
                 JcrResourceResolver2.PROP_REDIRECT_EXTERNAL, String.class);
             if (redirect != null) {
-                return new MapEntry(url, redirect, false);
+                int status = props.get(
+                    JcrResourceResolver2.PROP_REDIRECT_EXTERNAL_STATUS, 302);
+                return new MapEntry(url, redirect, status);
             }
             
-            redirect = props.get(
-                JcrResourceResolver2.PROP_REDIRECT_INTERNAL, String.class);
-            if (redirect != null) {
-                return new MapEntry(url, redirect, true);
+            String[] internalRedirect = props.get(
+                JcrResourceResolver2.PROP_REDIRECT_INTERNAL, String[].class);
+            if (internalRedirect != null) {
+                return new MapEntry(url, internalRedirect, -1);
             }
         }
 
         return null;
     }
 
-    public MapEntry(String url, String redirect, boolean isInternal) {
+    public MapEntry(String url, String redirect, int status) {
+        this(url, new String[]{ redirect }, status);
+    }
+    
+    public MapEntry(String url, String[] redirect, int status) {
         this.urlPattern = Pattern.compile(url);
         this.redirect = redirect;
-        this.isInternal = isInternal;
+        this.status = status;
     }
 
     public Matcher getMatcher(String value) {
@@ -69,23 +75,32 @@
     }
     
     // Returns the replacement or null if the value does not match
-    public String replace(String value) {
+    public String[] replace(String value) {
         Matcher m = urlPattern.matcher(value);
         if (m.find()) {
+            String[] redirects = getRedirect();
+            String[] results = new String[redirects.length];
             StringBuffer buf = new StringBuffer();
-            m.appendReplacement(buf, getRedirect());
-            m.appendTail(buf);
-            return buf.toString();
+            for (int i=0; i < redirects.length; i++) {
+                m.appendReplacement(buf, redirects[i]);
+                m.appendTail(buf);
+                results[i] = buf.toString();
+            }
+            return results;
         }
      
         return null;
     }
 
-    public String getRedirect() {
+    public String[] getRedirect() {
         return redirect;
     }
 
     public boolean isInternal() {
-        return isInternal;
+        return getStatus() < 0;
+    }
+    
+    public int getStatus() {
+        return status;
     }
 }

Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java?rev=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java Fri Nov 28 04:14:52 2008
@@ -165,6 +165,16 @@
             + handle.substring(this.fromLength) : null;
     }
 
+    // TODO: temporary
+    public String getFrom() {
+        return from;
+    }
+    
+    // TODO: temporary
+    public String getTo() {
+        return to;
+    }
+    
     /**
      * Checks, if this mapping is defined for inbound mapping.
      *

Modified: 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=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd (original)
+++ incubator/sling/trunk/jcr/resource/src/main/resources/SLING-INF/nodetypes/mapping.cnd Fri Nov 28 04:14:52 2008
@@ -43,11 +43,15 @@
     // segment, thus it should not contain any slashes 
   - sling:match (string)
   
-    // Location header value for 302/FOUND response
+    // 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
 
 
 //-----------------------------------------------------------------------------

Modified: 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=721447&r1=721446&r2=721447&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java (original)
+++ incubator/sling/trunk/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver2Test.java Fri Nov 28 04:14:52 2008
@@ -66,13 +66,13 @@
                 "/SLING-INF/nodetypes/folder.cnd")));
         assertTrue(RepositoryUtil.registerNodeType(getSession(),
             this.getClass().getResourceAsStream(
-                "/SLING-INF/nodetypes/mapping.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")));
+        assertTrue(RepositoryUtil.registerNodeType(getSession(),
+            this.getClass().getResourceAsStream(
+                "/SLING-INF/nodetypes/mapping.cnd")));
 
         resFac = new JcrResourceResolverFactoryImpl();