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 2007/11/13 23:04:19 UTC
svn commit: r594664 - in /incubator/sling/trunk/jcr/resource/src/main:
java/org/apache/sling/jcr/resource/internal/
java/org/apache/sling/jcr/resource/internal/helper/ resources/OSGI-INF/
resources/OSGI-INF/metatype/
Author: fmeschbe
Date: Tue Nov 13 14:04:18 2007
New Revision: 594664
URL: http://svn.apache.org/viewvc?rev=594664&view=rev
Log:
SLING-93 Add request URL mapping functionality to the JCRResourceManager from
the former sling/core ContentResolverFilter. Configuration for the mapping
is done on the JcrResourceManagerFactory.
Added:
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/OSGI-INF/
incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/
incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties
Modified:
incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java
incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java
Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java?rev=594664&r1=594663&r2=594664&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManager.java Tue Nov 13 14:04:18 2007
@@ -18,6 +18,9 @@
*/
package org.apache.sling.jcr.resource.internal;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.security.AccessControlException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
@@ -45,8 +48,10 @@
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.jcr.resource.DefaultMappedObject;
import org.apache.sling.jcr.resource.JcrResourceUtil;
+import org.apache.sling.jcr.resource.PathResolver;
import org.apache.sling.jcr.resource.internal.helper.JcrNodeResource;
import org.apache.sling.jcr.resource.internal.helper.JcrNodeResourceIterator;
+import org.apache.sling.jcr.resource.internal.helper.Mapping;
import org.apache.sling.jcr.resource.internal.helper.ResourcePathIterator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,7 +59,7 @@
/**
* The <code>JcrResourceManager</code> TODO
*/
-public class JcrResourceManager implements ResourceManager {
+public class JcrResourceManager implements ResourceManager, PathResolver {
/** default log */
private final Logger log = LoggerFactory.getLogger(getClass());
@@ -101,17 +106,8 @@
// ---------- ResourceResolver interface ----------------------------------
public Resource resolve(ServletRequest request) throws SlingException {
- Resource result = null;
- String path = null;
String pathInfo = ((HttpServletRequest) request).getPathInfo();
- try {
- final ResourcePathIterator it = new ResourcePathIterator(pathInfo);
- while (it.hasNext() && result == null) {
- result = getResourceInternal(it.next(), null);
- }
- } catch (RepositoryException re) {
- throw new SlingException("RepositoryException for path=" + path, re);
- }
+ Resource result = resolve(pathInfo);
if (result == null) {
result = new NonExistingResource(pathInfo);
@@ -225,6 +221,88 @@
}
}
+ // ---------- PathResolver interface --------------------------------------
+
+ /**
+ * @throws AccessControlException If an item would exist but is not readable
+ * to this manager's session.
+ */
+ public Resource resolve(String url) throws SlingException {
+
+ // decode the request URI (required as the servlet container does not
+ try {
+ url = URLDecoder.decode(url, "UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ log.error("Cannot decode request URI using UTF-8", uee);
+ } catch (Exception e) {
+ log.error("Failed to decode request URI " + url, e);
+ }
+
+
+ // convert fake urls
+ String realUrl = (String) factory.getVirtualURLMap().get(url);
+ if (realUrl != null) {
+ log.debug("resolve: Using real url '{}' for virtual url '{}'",
+ realUrl, url);
+ url = realUrl;
+ }
+
+ try {
+
+ // translate url to a mapped url structure
+ return transformURL(url);
+
+ } catch (AccessControlException ace) {
+ // rethrow AccessControlExceptions to be handled
+ throw ace;
+
+ } catch (SlingException se) {
+ // rethrow SlingException as it is declared
+ throw se;
+
+ } catch (Throwable t) {
+ // wrap any other issue into a SlingException
+ throw new SlingException("Problem resolving " + url, t);
+ }
+ }
+
+ public String pathToURL(String path) {
+ return pathToURL(null, path, null);
+ }
+
+ public String pathToURL(String prefix, String path, String suffix) {
+ String href = null;
+
+ // get first map
+ Mapping[] mappings = factory.getMappings();
+ for (int i = 0; i < mappings.length && href == null; i++) {
+ href = mappings[i].mapHandle(path);
+ }
+
+ // if no mapping's to prefix matches the handle, use the handle itself
+ if (href == null) {
+ href = path;
+ }
+
+ // check virtual mappings
+ String virtual = (String) factory.getVirtualURLMap().getKey(href);
+ if (virtual != null) {
+ href = virtual;
+ }
+
+ // handle prefix and suffix
+ if (prefix != null && !prefix.equals("") && !prefix.equals("/")) {
+ href = prefix + href;
+ }
+ if (suffix != null) {
+ href += suffix;
+ }
+
+ log.debug("MapHandle: {} + {} + {} -> {}", new Object[] { prefix, path,
+ suffix, href });
+ return href;
+ }
+
// ---------- ResourceManager interface -----------------------------------
public Resource getResource(String path, Class<?> type)
@@ -408,6 +486,46 @@
}
// ---------- implementation helper ----------------------------------------
+
+ private Resource transformURL(String url) throws SlingException {
+ Mapping[] mappings = factory.getMappings();
+ for (int i = 0; i < mappings.length; i++) {
+ // exchange the 'to'-portion with the 'from' portion and check
+ String href = mappings[i].mapUri(url);
+ if (href == null) {
+ log.debug("Mapping {} cannot map {}", mappings[i], url);
+ continue;
+ }
+
+ Resource resource = scanPath(href);
+ if (resource != null) {
+ return resource;
+ }
+
+ log.debug("Cannot resolve {} to resource", href);
+ }
+
+ log.error("Could not resolve URL {} to a Content object", url);
+ return null;
+
+ }
+
+ private Resource scanPath(String uriPath) throws SlingException {
+ Resource resource = null;
+ String curPath = uriPath;
+ try {
+ final ResourcePathIterator it = new ResourcePathIterator(uriPath);
+ while (it.hasNext() && resource == null) {
+ curPath = it.next();
+ resource = getResourceInternal(curPath, null);
+ }
+ } catch (RepositoryException re) {
+ throw new SlingException("Problem trying " + curPath
+ + " for request path " + uriPath, re);
+ }
+
+ return resource;
+ }
/** Creates a JcrNodeResource with the given path if existing */
protected Resource getResourceInternal(String path, Class<?> type)
Modified: incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java?rev=594664&r1=594663&r2=594664&view=diff
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java (original)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceManagerFactoryImpl.java Tue Nov 13 14:04:18 2007
@@ -18,19 +18,24 @@
*/
package org.apache.sling.jcr.resource.internal;
+import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import org.apache.commons.collections.BidiMap;
+import org.apache.commons.collections.bidimap.TreeBidiMap;
import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
import org.apache.sling.api.resource.ResourceManager;
import org.apache.sling.commons.mime.MimeTypeService;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.resource.JcrResourceManagerFactory;
+import org.apache.sling.jcr.resource.internal.helper.Mapping;
import org.apache.sling.jcr.resource.internal.loader.Loader;
import org.apache.sling.jcr.resource.internal.mapping.ObjectContentManagerFactory;
import org.osgi.framework.Bundle;
@@ -53,10 +58,10 @@
* <li>Fires OSGi EventAdmin events on behalf of internal helper objects
* </ul>
*
- * @scr.component immediate="true" label="%content.name"
- * description="%content.description" metatype="false"
- * @scr.property name="service.description" value="Sling
- * JcrResourceManagerFactory Implementation"
+ * @scr.component immediate="true" label="%resource.resolver.name"
+ * description="%resource.resolver.description"
+ * @scr.property name="service.description"
+ * value="Sling JcrResourceManagerFactory Implementation"
* @scr.property name="service.vendor" value="The Apache Software Foundation"
* @scr.service interface="org.apache.sling.jcr.resource.JcrResourceManagerFactory"
*/
@@ -64,6 +69,28 @@
JcrResourceManagerFactory, SynchronousBundleListener {
/**
+ * @scr.property value="true" type="Boolean"
+ */
+ private static final String PROP_ALLOW_DIRECT = "resource.resolver.allowDirect";
+
+ /**
+ * The resolver.virtual property has no default configuration. But the sling
+ * 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";
+
+ /**
+ * @scr.property values.1="/-/" values.2="/content/-/"
+ * Cvalues.3="/apps/×/docroot/-/"
+ * Cvalues.4="/libs/×/docroot/-/"
+ * values.5="/system/docroot/-/"
+ */
+ private static final String PROP_MAPPING = "resource.resolver.mapping";
+
+ /**
* The JCR Repository we access to resolve resources
*
* @scr.reference
@@ -88,6 +115,15 @@
/** The OSGi Component Context */
private ComponentContext componentContext;
+ /** all mappings */
+ private Mapping[] mappings;
+
+ /** The fake urls */
+ private BidiMap virtualURLMap;
+
+ /** <code>true</code>, if direct mappings from URI to handle are allowed */
+ private boolean allowDirect = false;
+
/**
* Map of administrative sessions used to check item existence. Indexed by
* workspace name. The map is filled on-demand. The sessions are closed when
@@ -258,6 +294,15 @@
return (mts != null) ? mts.getMimeType(name) : null;
}
+ /** If url is a virtual URL returns the real URL, otherwise returns url */
+ BidiMap getVirtualURLMap() {
+ return virtualURLMap;
+ }
+
+ Mapping[] getMappings() {
+ return mappings;
+ }
+
// ---------- SCR Integration
protected void activate(ComponentContext componentContext) {
@@ -287,6 +332,36 @@
objectContentManagerFactory.registerMapperClient(bundles[i]);
}
}
+
+ Dictionary<?, ?> properties = componentContext.getProperties();
+
+ BidiMap virtuals = new TreeBidiMap();
+ String[] virtualList = (String[]) properties.get(PROP_VIRTUAL);
+ for (int i=0; virtualList != null && i < virtualList.length; i++) {
+ String[] parts = Mapping.split(virtualList[i]);
+ virtuals.put(parts[0], parts[2]);
+ }
+ virtualURLMap = virtuals;
+
+ List<Mapping> maps = new ArrayList<Mapping>();
+ String[] mappingList = (String[]) properties.get(PROP_MAPPING);
+ for (int i=0; mappingList != null && i < mappingList.length; i++) {
+ maps.add(new Mapping(mappingList[i]));
+ }
+ Mapping[] tmp = maps.toArray(new Mapping[maps.size()]);
+
+ // check whether direct mappings are allowed
+ Boolean directProp = (Boolean) properties.get(PROP_ALLOW_DIRECT);
+ allowDirect = (directProp != null) ? directProp.booleanValue() : true;
+ if (allowDirect) {
+ Mapping[] tmp2 = new Mapping[tmp.length + 1];
+ tmp2[0] = Mapping.DIRECT;
+ System.arraycopy(tmp, 0, tmp2, 1, tmp.length);
+ mappings = tmp2;
+ } else {
+ mappings = tmp;
+ }
+
}
protected void deactivate(ComponentContext oldContext) {
Added: 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=594664&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java (added)
+++ incubator/sling/trunk/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/Mapping.java Tue Nov 13 14:04:18 2007
@@ -0,0 +1,204 @@
+/*
+ * 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.StringTokenizer;
+
+/**
+ * The <code>Mapping</code> class conveys the mapping configuration used by
+ * the
+ * {@link org.apache.sling.jcr.resource.internal.JcrResourceManagerFactoryImpl}.
+ */
+public class Mapping {
+
+ /**
+ * defines the 'inbound' direction, that is mapping request path to item
+ * path
+ */
+ public static final int INBOUND = 1;
+
+ /** defined the 'outbound' direction, that is mapping item path to URL path */
+ public static final int OUTBOUND = 2;
+
+ /** defines the 'both' direction */
+ public static final int BOTH = 3;
+
+ /** Simple mapper instance mapping path to URLs 1:1 in both directions */
+ public static final Mapping DIRECT = new Mapping("", "", BOTH) {
+
+ @Override
+ public String mapHandle(String handle) {
+ return handle;
+ }
+
+ @Override
+ public boolean mapsInbound() {
+ return true;
+ }
+
+ @Override
+ public boolean mapsOutbound() {
+ return true;
+ }
+
+ @Override
+ public String mapUri(String uriPath) {
+ return uriPath;
+ }
+ };
+
+ /** the 'from' (inside, repository) mapping */
+ private final String from;
+
+ /** the 'to' (outside, URL) mapping */
+ private final String to;
+
+ /** the length of the 'from' field */
+ private final int fromLength;
+
+ /** the length of the 'to' field */
+ private final int toLength;
+
+ /** the mapping direction */
+ private final int direction;
+
+ /**
+ * Creates a new instance of a mapping.
+ *
+ * @param from Handle prefix possible valid in the ContentBus.
+ * @param to URI path prefix to be replaced by from to get a possibly valid
+ * handle.
+ * @param dir the direction of the mapping. either "inwards", "outwards" or
+ * "both".
+ * @throws NullPointerException if either <code>from</code> or
+ * <code>to</code> is <code>null</code>.
+ */
+ public Mapping(String from, String to, String dir) {
+ this(from, to, "in".equals(dir) ? Mapping.INBOUND : ("out".equals(dir)
+ ? Mapping.OUTBOUND
+ : Mapping.BOTH));
+ }
+
+ /**
+ * Creates a new instance of a mapping.
+ *
+ * @param from Handle prefix possible valid in the ContentBus.
+ * @param to URI path prefix to be replaced by from to get a possibly valid
+ * handle.
+ * @throws NullPointerException if either <code>from</code> or
+ * <code>to</code> is <code>null</code>.
+ */
+ public Mapping(String from, String to) {
+ this(from, to, Mapping.BOTH);
+ }
+
+ public Mapping(String config) {
+ this(split(config));
+ }
+
+ public Mapping(String[] parts) {
+ this.from = parts[0];
+ this.to = parts[2];
+ this.fromLength = this.from.length();
+ this.toLength = this.to.length();
+
+ this.direction = ">".equals(parts[1])
+ ? Mapping.INBOUND
+ : ("<".equals(parts[1]) ? Mapping.OUTBOUND : Mapping.BOTH);
+ }
+
+ /**
+ * Replaces the prefix <em>to</em> by the new prefix <em>from</em>, if
+ * and only if <code>uriPath</code> starts with the <em>to</em> prefix.
+ * If <code>uriPath</code> does not start with the <em>to</em> prefix,
+ * or if this mapping is not defined as a 'inward' mapping,
+ * <code>null</code> is returned.
+ *
+ * @param uriPath The URI path for which to replace the <em>to</em> prefix
+ * by the <em>from</em> prefix.
+ * @return The string after replacement or <code>null</code> if the
+ * <code>uriPath</code> does not start with the <em>to</em>
+ * prefix, or {@link #mapsInwards} returns <code>false</code>.
+ */
+ public String mapUri(String uriPath) {
+ return (this.mapsInbound() && uriPath.startsWith(this.to)) ? this.from
+ + uriPath.substring(this.toLength) : null;
+ }
+
+ /**
+ * Replaces the prefix <em>from</em> by the new prefix <em>to</em>, if
+ * and only if <code>handle</code> starts with the <em>from</em> prefix.
+ * If <code>uriPath</code> does not start with the <em>from</em> prefix,
+ * or if this mapping is not defined as a 'outward' mapping,
+ * <code>null</code> is returned.
+ *
+ * @param handle The URI path for which to replace the <em>from</em>
+ * prefix by the <em>to</em> prefix.
+ * @return The string after replacement or <code>null</code> if the
+ * <code>handle</code> does not start with the <em>from</em>
+ * prefix, or {@link #mapsOutwards} returns <code>false</code>.
+ */
+ public String mapHandle(String handle) {
+ return (this.mapsOutbound() && handle.startsWith(this.from)) ? this.to
+ + handle.substring(this.fromLength) : null;
+ }
+
+ /**
+ * Checks, if this mapping is defined for inbound mapping.
+ *
+ * @return <code>true</code> if this mapping is defined for inbound
+ * mapping; <code>false</code> otherwise
+ */
+ public boolean mapsInbound() {
+ return (this.direction & Mapping.INBOUND) > 0;
+ }
+
+ /**
+ * Checks, if this mapping is defined for outbound mapping.
+ *
+ * @return <code>true</code> if this mapping is defined for outbound
+ * mapping; <code>false</code> otherwise
+ */
+ public boolean mapsOutbound() {
+ return (this.direction & Mapping.OUTBOUND) > 0;
+ }
+
+ /**
+ * Constructs a new mapping with the given mapping string and the direction
+ */
+ private Mapping(String from, String to, int dir) {
+ this.from = from;
+ this.to = to;
+ this.fromLength = from.length();
+ this.toLength = to.length();
+ this.direction = dir;
+ }
+
+ public static String[] split(String map) {
+ String[] res = new String[3]; // src, op, dst
+ StringTokenizer st = new StringTokenizer(map, "-<>", true);
+
+ for (int i = 0; i < res.length; i++) {
+ res[i] = st.hasMoreTokens() ? st.nextToken() : "";
+ }
+
+ return res;
+ }
+
+}
Added: 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=594664&view=auto
==============================================================================
--- incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties (added)
+++ incubator/sling/trunk/jcr/resource/src/main/resources/OSGI-INF/metatype/metatype.properties Tue Nov 13 14:04:18 2007
@@ -0,0 +1,45 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+
+#
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the SCR plugin
+
+#
+# Localizations for JcrResourceManagerFactoryImpl configuration
+resource.resolver.name = Content Resolver
+resource.resolver.description = Configures the Content Resolver for request \
+ URL and content path rewriting.
+resource.resolver.allowDirect.name = Allow Direct Mapping
+resource.resolver.allowDirect.description = Whether to add a direct URL \
+ mapping to the front of the mapping list.
+resource.resolver.virtual.name = Virtual URLs
+resource.resolver.virtual.description = List of virtual URLs and there \
+ mappings to real URLs. Format is <externalURL>-<internalURL>. Mappings are \
+ applied on the complete request URL only.
+resource.resolver.mapping.name = URL Mappings
+resource.resolver.mapping.description = List of mappings to apply to URLs. \
+ Incoming mappings are applied to request URLs to map to Content paths, \
+ outgoing mappings are applied to map Content paths to URLs used on subsequent \
+ requests. Form ist <externalURLPrefix><op><internalURLPrefix> where <op> is \
+ ">" for incoming mappings, "<" for outgoing mappings and "-" for mappings \
+ applied in both directions. Mappings are applied in configuration order by \
+ comparing and replacing URL prefixes.