Author: justin
Date: Tue Apr 13 19:13:31 2010
New Revision: 933747

URL: http://svn.apache.org/viewvc?rev=933747&view=rev
Log:
SLING-1447 - committing first pass at workspace names in resource paths

Added:
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/WorkspaceDecoratedResource.java
    
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
    
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java
Modified:
    sling/trunk/bundles/jcr/resource/pom.xml
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceDecoratorTracker.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceIteratorDecorator.java
    
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
    
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java

Modified: sling/trunk/bundles/jcr/resource/pom.xml
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/pom.xml?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/pom.xml (original)
+++ sling/trunk/bundles/jcr/resource/pom.xml Tue Apr 13 19:13:31 2010
@@ -125,6 +125,7 @@
         <dependency>
             <groupId>javax.jcr</groupId>
             <artifactId>jcr</artifactId>
+            <version>2.0</version>
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
@@ -203,7 +204,7 @@
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-jcr-commons</artifactId>
-            <version>1.6.0</version>
+            <version>2.0.0</version>
             <scope>provided</scope>
         </dependency>
 
@@ -211,7 +212,25 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.commons.testing</artifactId>
-            <version>2.0.4-incubator</version>
+            <version>2.0.5-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+               <groupId>org.mockito</groupId>
+               <artifactId>mockito-all</artifactId>
+               <version>1.8.2</version>
+               <scope>test</scope>
+        </dependency>
+        <dependency>
+               <groupId>junit-addons</groupId>
+               <artifactId>junit-addons</artifactId>
+               <version>1.4</version>
+               <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-api</artifactId>
+            <version>2.0.0</version>
             <scope>test</scope>
         </dependency>
     </dependencies>

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrResourceConstants.java
 Tue Apr 13 19:13:31 2010
@@ -54,8 +54,9 @@ public class JcrResourceConstants {
     public static final String SLING_RESOURCE_SUPER_TYPE_PROPERTY = 
"sling:resourceSuperType";
 
     /**
-     * The name of the event property holding the workspace name.
+     * The name of the authentication info property containing the workspace 
name.
+     *
      * @since 2.0.8
      */
-    public static final String PROPERTY_WORKSPACE = "workspace";
+    public static final String AUTH_INFO_WORKSPACE = "user.jcr.workspace";
 }

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceListener.java
 Tue Apr 13 19:13:31 2010
@@ -19,8 +19,10 @@
 package org.apache.sling.jcr.resource.internal;
 
 import java.util.Dictionary;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Map;
 import java.util.Set;
 
 import javax.jcr.Node;
@@ -31,12 +33,13 @@ import javax.jcr.observation.EventIterat
 import javax.jcr.observation.EventListener;
 
 import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.resource.JcrResourceConstants;
-import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
 import org.osgi.service.event.EventAdmin;
 import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
@@ -82,14 +85,16 @@ public class JcrResourceListener impleme
      */
     public JcrResourceListener(final SlingRepository repository,
                                final String workspaceName,
-                               final JcrResourceResolverFactory factory,
+                               final ResourceResolverFactory factory,
                                final String startPath,
                                final String mountPrefix,
                                final ServiceTracker eventAdminTracker)
-    throws RepositoryException {
+    throws LoginException, RepositoryException {
         this.workspaceName = workspaceName;
-        this.session = repository.loginAdministrative(workspaceName);
-        this.resolver = factory.getResourceResolver(this.session);
+        Map<String,Object> authInfo = new HashMap<String,Object>();
+        authInfo.put(JcrResourceConstants.AUTH_INFO_WORKSPACE, workspaceName);
+        this.resolver = factory.getAdministrativeResourceResolver(authInfo);
+        this.session = resolver.adaptTo(Session.class);
         this.startPath = startPath;
         this.eventAdminTracker = eventAdminTracker;
         this.mountPrefix = (mountPrefix.equals("/") ? null : mountPrefix);
@@ -107,7 +112,7 @@ public class JcrResourceListener impleme
         } catch (RepositoryException e) {
             logger.warn("Unable to remove session listener: " + this, e);
         }
-        this.session.logout();
+        this.resolver.close();
     }
 
     /**
@@ -163,14 +168,23 @@ public class JcrResourceListener impleme
         // send events for removed
         for(final String path : removedPaths) {
             final Dictionary<String, String> properties = new 
Hashtable<String, String>();
-            properties.put(SlingConstants.PROPERTY_PATH, path);
+            properties.put(SlingConstants.PROPERTY_PATH, 
createWorkspacePath(path));
 
             localEA.postEvent(new 
org.osgi.service.event.Event(SlingConstants.TOPIC_RESOURCE_REMOVED, 
properties));
         }
     }
 
+    private String createWorkspacePath(String path) {
+        if (workspaceName == null) {
+            return path;
+        } else {
+            return workspaceName+":"+path;
+        }
+    }
+
     private void sendEvents(final Set<String> paths, final String topic, final 
EventAdmin localEA) {
-        for(final String path : paths) {
+        for(String path : paths) {
+            path = createWorkspacePath(path);
             Resource resource = this.resolver.getResource(path);
             if ( resource != null ) {
                 // check for nt:file nodes
@@ -191,9 +205,6 @@ public class JcrResourceListener impleme
                 }
                 final Dictionary<String, String> properties = new 
Hashtable<String, String>();
                 properties.put(SlingConstants.PROPERTY_PATH, 
resource.getPath());
-                if (workspaceName != null) {
-                    properties.put(JcrResourceConstants.PROPERTY_WORKSPACE, 
workspaceName);
-                }
                 final String resourceType = resource.getResourceType();
                 if ( resourceType != null ) {
                     properties.put(SlingConstants.PROPERTY_RESOURCE_TYPE, 
resource.getResourceType());

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolver.java
 Tue Apr 13 19:13:31 2010
@@ -27,6 +27,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.jcr.NamespaceException;
+import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.Value;
@@ -36,6 +37,7 @@ import javax.servlet.http.HttpServletReq
 
 import org.apache.sling.adapter.SlingAdaptable;
 import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.NonExistingResource;
 import org.apache.sling.api.resource.QuerySyntaxException;
 import org.apache.sling.api.resource.Resource;
@@ -43,6 +45,7 @@ import org.apache.sling.api.resource.Res
 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.JcrResourceConstants;
 import org.apache.sling.jcr.resource.JcrResourceUtil;
 import org.apache.sling.jcr.resource.internal.helper.MapEntries;
 import org.apache.sling.jcr.resource.internal.helper.MapEntry;
@@ -90,14 +93,27 @@ public class JcrResourceResolver extends
 
     private final MapEntries resourceMapper;
 
+    private boolean isAdmin;
+
+    private final Map<String, Object> originalAuthInfo;
+
+    private final String defaultWorkspaceName;
+
+    private final Map<String,ResourceResolver> createdResolvers = new 
HashMap<String,ResourceResolver>();
+
     /** Closed marker. */
     private volatile boolean closed = false;
 
     public JcrResourceResolver(JcrResourceProviderEntry rootProvider,
-            JcrResourceResolverFactoryImpl factory, MapEntries resourceMapper) 
{
+            JcrResourceResolverFactoryImpl factory, MapEntries resourceMapper,
+            boolean isAdmin, Map<String, Object> originalAuthInfo,
+            String defaultWorkspaceName) {
         this.rootProvider = rootProvider;
         this.factory = factory;
         this.resourceMapper = resourceMapper;
+        this.isAdmin = isAdmin;
+        this.originalAuthInfo = originalAuthInfo;
+        this.defaultWorkspaceName = defaultWorkspaceName;
     }
 
     /**
@@ -107,6 +123,9 @@ public class JcrResourceResolver extends
         if ( !this.closed ) {
             this.closed = true;
             getSession().logout();
+            for (ResourceResolver resolver : createdResolvers.values()) {
+                resolver.close();
+            }
         }
     }
 
@@ -186,7 +205,7 @@ public class JcrResourceResolver extends
                     log.debug("resolve: Returning external redirect");
                     return this.factory.getResourceDecoratorTracker().decorate(
                             new RedirectResource(this, absPath, mappedPath[0],
-                                   mapEntry.getStatus()),
+                                   mapEntry.getStatus()), null,
                              request);
                 }
             }
@@ -281,7 +300,7 @@ public class JcrResourceResolver extends
             log.debug("resolve: Path {} resolves to Resource {}", absPath, 
res);
         }
 
-        return this.factory.getResourceDecoratorTracker().decorate(res, 
request);
+        return this.factory.getResourceDecoratorTracker().decorate(res, null, 
request);
     }
 
     /**
@@ -475,22 +494,47 @@ public class JcrResourceResolver extends
     public Resource getResource(String path) {
         checkClosed();
 
+        if (path.contains(":")) {
+            String[] parts = path.split(":");
+            String workspaceName = parts[0];
+            if (workspaceName.equals(getSession().getWorkspace().getName())) {
+                path = parts[1];
+            } else {
+                try {
+                    ResourceResolver wsResolver = 
getResolverForWorkspace(workspaceName);
+                    return wsResolver.getResource(parts[1]);
+                } catch (LoginException e) {
+                    // requested a resource in a workspace I don't have access 
to.
+                    // TODO
+                }
+            }
+        }
+
         // if the path is absolute, normalize . and .. segements and get res
         if (path.startsWith("/")) {
             path = ResourceUtil.normalize(path);
-            final Resource result = (path != null) ? getResourceInternal(path) 
: null;
+            Resource result = (path != null) ? getResourceInternal(path) : 
null;
             if ( result != null ) {
-                return 
this.factory.getResourceDecoratorTracker().decorate(result, null);
+                String workspacePrefix = null;
+                if ( 
!getSession().getWorkspace().getName().equals(defaultWorkspaceName) ) {
+                    workspacePrefix = getSession().getWorkspace().getName();
+                }
+
+                result = 
this.factory.getResourceDecoratorTracker().decorate(result, workspacePrefix, 
null);
+                return result;
             }
             return 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;
+        String[] paths = factory.getSearchPath();
+        if (paths != null) {
+            for (String prefix : factory.getSearchPath()) {
+                Resource res = getResource(prefix + path);
+                if (res != null) {
+                    return res;
+                }
             }
         }
 
@@ -516,7 +560,29 @@ public class JcrResourceResolver extends
      */
     public Iterator<Resource> listChildren(Resource parent) {
         checkClosed();
-        return new 
ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
+        String path = parent.getPath();
+        if (path.contains(":")) {
+            String[] parts = path.split(":");
+            String workspaceName = parts[0];
+            if (workspaceName.equals(getSession().getWorkspace().getName())) {
+                path = parts[1];
+            } else {
+                try {
+                    ResourceResolver wsResolver = 
getResolverForWorkspace(workspaceName);
+                    return wsResolver.listChildren(parent);
+                } catch (LoginException e) {
+                    // requested a resource in a workspace I don't have access 
to.
+                    // TODO
+                }
+            }
+        }
+
+        String workspacePrefix = null;
+        if ( 
!getSession().getWorkspace().getName().equals(defaultWorkspaceName) ) {
+            workspacePrefix = getSession().getWorkspace().getName();
+        }
+
+        return new 
ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(), 
workspacePrefix,
                 rootProvider.listChildren(parent));
     }
 
@@ -531,7 +597,7 @@ public class JcrResourceResolver extends
         try {
             QueryResult res = JcrResourceUtil.query(getSession(), query,
                 language);
-            return new 
ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
+            return new 
ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(), null,
                     new JcrNodeResourceIterator(this, res.getNodes(),
                      factory.getDynamicClassLoader()));
         } catch (javax.jcr.query.InvalidQueryException iqe) {
@@ -615,6 +681,24 @@ public class JcrResourceResolver extends
         return rootProvider.getSession();
     }
 
+    // TODO - add some double-checked locking here.
+    private synchronized ResourceResolver getResolverForWorkspace(String 
workspaceName) throws LoginException {
+        ResourceResolver wsResolver = createdResolvers.get(workspaceName);
+        if (wsResolver == null) {
+            if (isAdmin) {
+                Map<String,Object> newAuthInfo = new HashMap<String,Object>();
+                newAuthInfo.put(JcrResourceConstants.AUTH_INFO_WORKSPACE, 
workspaceName);
+                wsResolver = 
factory.getAdministrativeResourceResolver(newAuthInfo);
+            } else {
+                Map<String,Object> newAuthInfo = new 
HashMap<String,Object>(originalAuthInfo);
+                newAuthInfo.put(JcrResourceConstants.AUTH_INFO_WORKSPACE, 
workspaceName);
+                wsResolver = factory.getResourceResolver(newAuthInfo);
+            }
+            createdResolvers.put(workspaceName, wsResolver);
+        }
+        return wsResolver;
+    }
+
     /**
      * Returns a string used for matching map entries against the given request
      * or URI parts.

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverFactoryImpl.java
 Tue Apr 13 19:13:31 2010
@@ -44,6 +44,7 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.commons.classloader.DynamicClassLoaderManager;
 import org.apache.sling.commons.osgi.OsgiUtil;
 import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.resource.JcrResourceConstants;
 import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
 import org.apache.sling.jcr.resource.internal.helper.MapEntries;
 import org.apache.sling.jcr.resource.internal.helper.Mapping;
@@ -231,11 +232,7 @@ public class JcrResourceResolverFactoryI
      * @see 
org.apache.sling.jcr.resource.JcrResourceResolverFactory#getResourceResolver(javax.jcr.Session)
      */
     public ResourceResolver getResourceResolver(Session session) {
-        JcrResourceProviderEntry sessionRoot = new JcrResourceProviderEntry(
-            session, rootProviderEntry,
-            this.getDynamicClassLoader());
-
-        return new JcrResourceResolver(sessionRoot, this, mapEntries);
+        return getResourceResolver(session, true, null);
     }
 
     // ---------- Implementation helpers --------------------------------------
@@ -375,22 +372,27 @@ public class JcrResourceResolverFactoryI
                 e);
         }
 
-        String[] listenerWorkspaces = 
OsgiUtil.toStringArray(properties.get(PROP_LISTENER_WORKSPACES));
 
         // start observation listener
         try {
-            if (listenerWorkspaces == null) {
-                this.resourceListeners =
-                    Collections.singleton(new 
JcrResourceListener(this.repository, null, this, "/", "/", 
this.eventAdminTracker));
-            } else {
-                if 
(Arrays.asList(listenerWorkspaces).contains(ALL_WORKSPACES)) {
-                    listenerWorkspaces = getAllWorkspaces();
-                }
+            this.resourceListeners = new HashSet<JcrResourceListener>();
+
+            // first - add a listener for the default workspace
+            this.resourceListeners.add(new 
JcrResourceListener(this.repository, null, this, "/", "/", 
this.eventAdminTracker));
+
+            // then, iterate through any workspaces which are configured
+            String[] listenerWorkspaces = 
OsgiUtil.toStringArray(properties.get(PROP_LISTENER_WORKSPACES));
+            if (Arrays.asList(listenerWorkspaces).contains(ALL_WORKSPACES)) {
+                listenerWorkspaces = getAllWorkspaces();
+            }
 
+            if (listenerWorkspaces != null) {
                 this.resourceListeners = new 
HashSet<JcrResourceListener>(listenerWorkspaces.length);
                 for (String wspName : listenerWorkspaces) {
-                    this.resourceListeners.add(
+                    if 
(!wspName.equals(this.repository.getDefaultWorkspace())) {
+                        this.resourceListeners.add(
                             new JcrResourceListener(this.repository, wspName, 
this, "/", "/", this.eventAdminTracker));
+                    }
                 }
             }
         } catch (Exception e) {
@@ -471,7 +473,19 @@ public class JcrResourceResolverFactoryI
         } catch (RepositoryException re) {
             throw getLoginException(re);
         }
-        return this.getResourceResolver(handleSudo(session, 
authenticationInfo));
+        return this.getResourceResolver(handleSudo(session, 
authenticationInfo), true, authenticationInfo);
+    }
+
+    /**
+     * Create a new ResourceResolver wrapping a Session object. Carries map of
+     * authentication info in order to create a new resolver as needed.
+     */
+    private ResourceResolver getResourceResolver(Session session, boolean 
isAdmin, Map<String, Object> authenticationInfo) {
+        JcrResourceProviderEntry sessionRoot = new JcrResourceProviderEntry(
+            session, rootProviderEntry,
+            this.getDynamicClassLoader());
+
+        return new JcrResourceResolver(sessionRoot, this, mapEntries, isAdmin, 
authenticationInfo, repository.getDefaultWorkspace());
     }
 
     /**
@@ -491,7 +505,7 @@ public class JcrResourceResolverFactoryI
         } catch (RepositoryException re) {
             throw getLoginException(re);
         }
-        return this.getResourceResolver(handleSudo(session, 
authenticationInfo));
+        return this.getResourceResolver(handleSudo(session, 
authenticationInfo), false, authenticationInfo);
     }
 
     /**
@@ -534,7 +548,7 @@ public class JcrResourceResolverFactoryI
      */
     private String getWorkspace(final Map<String, Object> authenticationInfo) {
         if ( authenticationInfo != null ) {
-            return (String) authenticationInfo.get("user.jcr.workspace");
+            return (String) 
authenticationInfo.get(JcrResourceConstants.AUTH_INFO_WORKSPACE);
         }
         return null;
     }

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceDecoratorTracker.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceDecoratorTracker.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceDecoratorTracker.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceDecoratorTracker.java
 Tue Apr 13 19:13:31 2010
@@ -55,8 +55,8 @@ public class ResourceDecoratorTracker {
         }
     }
 
-    /** Decorate a resource. */
-    public Resource decorate(final Resource resource, final HttpServletRequest 
request) {
+    /** Decorate a resource.  */
+    public Resource decorate(final Resource resource, String workspaceName, 
final HttpServletRequest request) {
         Resource result = resource;
         final ResourceDecorator[] decorators = this.resourceDecoratorsArray;
         for(final ResourceDecorator decorator : decorators) {
@@ -70,6 +70,9 @@ public class ResourceDecoratorTracker {
                 result = original;
             }
         }
+        if (workspaceName != null) {
+            result = new WorkspaceDecoratedResource(result, workspaceName);
+        }
         return result;
     }
 

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceIteratorDecorator.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceIteratorDecorator.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceIteratorDecorator.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/ResourceIteratorDecorator.java
 Tue Apr 13 19:13:31 2010
@@ -29,12 +29,16 @@ public class ResourceIteratorDecorator i
 
     private final ResourceDecoratorTracker tracker;
 
+    private final String workspaceName;
+
     private final Iterator<Resource> iterator;
 
     public ResourceIteratorDecorator(final ResourceDecoratorTracker tracker,
+            final String workspaceName,
             final Iterator<Resource> iterator) {
         this.tracker = tracker;
         this.iterator = iterator;
+        this.workspaceName = workspaceName;
     }
 
     public boolean hasNext() {
@@ -42,7 +46,7 @@ public class ResourceIteratorDecorator i
     }
 
     public Resource next() {
-        return this.tracker.decorate(this.iterator.next(), null);
+        return this.tracker.decorate(this.iterator.next(), workspaceName, 
null);
     }
 
     public void remove() {

Added: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/WorkspaceDecoratedResource.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/WorkspaceDecoratedResource.java?rev=933747&view=auto
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/WorkspaceDecoratedResource.java
 (added)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/WorkspaceDecoratedResource.java
 Tue Apr 13 19:13:31 2010
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * Decorated resource which prepends the workspace name to
+ * a delegate resource's path.
+ */
+class WorkspaceDecoratedResource implements Resource {
+
+    private final Resource delegate;
+    private final String workspaceName;
+
+    WorkspaceDecoratedResource(Resource resource, String workspaceName) {
+        this.delegate = resource;
+        this.workspaceName = workspaceName;
+    }
+
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return delegate.adaptTo(type);
+    }
+
+    public String getPath() {
+        if (workspaceName != null) {
+            return workspaceName + ":" + delegate.getPath();
+        } else {
+            return delegate.getPath();
+        }
+    }
+
+    public ResourceMetadata getResourceMetadata() {
+        return delegate.getResourceMetadata();
+    }
+
+    public ResourceResolver getResourceResolver() {
+        return delegate.getResourceResolver();
+    }
+
+    public String getResourceSuperType() {
+        return delegate.getResourceSuperType();
+    }
+
+    public String getResourceType() {
+        return delegate.getResourceType();
+    }
+
+    public String toString() {
+        return delegate.toString();
+    }
+
+}

Modified: 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/ResourceProviderEntry.java
 Tue Apr 13 19:13:31 2010
@@ -114,7 +114,7 @@ public class ResourceProviderEntry imple
 
         // this will consume slightly more memory but ensures read is fast.
         storageMap.setFast(true);
-        
+
     }
 
     /**

Added: 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java?rev=933747&view=auto
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
 (added)
+++ 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
 Tue Apr 13 19:13:31 2010
@@ -0,0 +1,166 @@
+/*
+ * 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 static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import junitx.util.PrivateAccessor;
+
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.commons.testing.jcr.EventHelper;
+import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * Test of JcrResourceListener.
+ */
+public class JcrResourceListenerTest extends RepositoryTestBase {
+    private String createdPath;
+
+    private String pathToDelete;
+
+    private String pathToModify;
+
+    public void testDefaultWorkspace() throws Exception {
+        List<Event> events = generateEvents(null);
+
+        Event event = events.get(0);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_ADDED, event.getTopic());
+        assertEquals(createdPath, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+        event = events.get(1);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_CHANGED, event.getTopic());
+        assertEquals(pathToModify, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+        event = events.get(2);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_REMOVED, event.getTopic());
+        assertEquals(pathToDelete, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+    }
+
+    public void testInWs2() throws Exception {
+        List<Event> events = generateEvents("ws2");
+
+        assertEquals(3, events.size());
+        Event event = events.get(0);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_ADDED, event.getTopic());
+        assertEquals("ws2:" + createdPath, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+        event = events.get(1);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_CHANGED, event.getTopic());
+        assertEquals("ws2:" + pathToModify, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+        event = events.get(2);
+        assertEquals(SlingConstants.TOPIC_RESOURCE_REMOVED, event.getTopic());
+        assertEquals("ws2:" + pathToDelete, 
event.getProperty(SlingConstants.PROPERTY_PATH));
+
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        try {
+            getSession().getWorkspace().createWorkspace("ws2");
+        } catch (Exception e) {
+            if (!e.getMessage().equals("workspace 'ws2' already exists.")) {
+                throw e;
+            }
+        }
+
+    }
+
+    private static void createNode(Session session, String path) throws 
RepositoryException {
+        session.getRootNode().addNode(path.substring(1), "nt:unstructured");
+        session.save();
+    }
+
+
+    private void addNodeToDelete(Session session) throws RepositoryException {
+        pathToDelete = createTestPath();
+        createNode(session, pathToDelete);
+
+    }
+
+    private void addNodeToModify(Session session) throws RepositoryException {
+        pathToModify = createTestPath();
+        createNode(session, pathToModify);
+
+    }
+
+    private String createTestPath() {
+        return "/test" + System.currentTimeMillis();
+    }
+
+    private List<Event> generateEvents(String workspaceName) throws Exception {
+        final Session session = 
getRepository().loginAdministrative(workspaceName);
+
+        final List<Event> events = new ArrayList<Event>();
+
+        addNodeToModify(session);
+        addNodeToDelete(session);
+
+        JcrResourceResolverFactoryImpl factory = new 
JcrResourceResolverFactoryImpl();
+        PrivateAccessor.setField(factory, "repository", getRepository());
+
+        final EventAdmin mockEA = new EventAdmin() {
+
+            public void postEvent(Event event) {
+                events.add(event);
+            }
+
+            public void sendEvent(Event event) {
+                events.add(event);
+            }
+        };
+        final ServiceTracker tracker = mock(ServiceTracker.class);
+        when(tracker.getService()).thenReturn(mockEA);
+
+        JcrResourceListener listener = new 
SynchronousJcrResourceListener(getRepository(), workspaceName, factory, "/",
+                "/", tracker);
+
+        createdPath = createTestPath();
+        createNode(session, createdPath);
+
+        Node modified = session.getNode(pathToModify);
+        modified.setProperty("foo", "bar");
+        session.save();
+
+        Node deleted = session.getNode(pathToDelete);
+        deleted.remove();
+        session.save();
+
+        listener.dispose();
+
+        Session newSession = 
getRepository().loginAdministrative(workspaceName);
+        EventHelper helper = new EventHelper(newSession);
+        helper.waitForEvents(5000);
+        helper.dispose();
+        newSession.logout();
+        session.logout();
+
+        return events;
+    }
+}

Modified: 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java?rev=933747&r1=933746&r2=933747&view=diff
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java
 (original)
+++ 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceResolverTest.java
 Tue Apr 13 19:13:31 2010
@@ -21,12 +21,14 @@ package org.apache.sling.jcr.resource.in
 import java.io.BufferedReader;
 import java.lang.reflect.Field;
 import java.security.Principal;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Locale;
 import java.util.Map;
 
 import javax.jcr.NamespaceRegistry;
 import javax.jcr.Node;
+import javax.jcr.Session;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.Cookie;
@@ -61,6 +63,12 @@ public class JcrResourceResolverTest ext
 
     private MapEntries mapEntries;
 
+    private Session ws2Session;
+
+    private ResourceResolver ws2Resolver;
+
+    private Node rootWs2Node;
+
     protected void setUp() throws Exception {
         super.setUp();
         assertTrue(RepositoryUtil.registerNodeType(getSession(),
@@ -128,6 +136,15 @@ public class JcrResourceResolverTest ext
         }
 
         resResolver = resFac.getResourceResolver(session);
+
+        ws2Session = getRepository().loginAdministrative("ws2");
+
+        rootWs2Node = ws2Session.getRootNode().addNode(rootPath.substring(1), 
"nt:unstructured");
+        ws2Session.save();
+
+        ws2Resolver = 
resFac.getAdministrativeResourceResolver(Collections.singletonMap(JcrResourceConstants.AUTH_INFO_WORKSPACE,
 (Object) "ws2"));
+
+
     }
 
     @Override
@@ -140,11 +157,18 @@ public class JcrResourceResolverTest ext
             rootNode.remove();
         }
 
+        if (rootWs2Node != null) {
+            rootWs2Node.remove();
+        }
+
         if (mapRoot != null) {
             mapRoot.remove();
         }
 
         session.save();
+        ws2Session.save();
+
+        ws2Resolver.close();
 
         super.tearDown();
     }
@@ -223,6 +247,40 @@ public class JcrResourceResolverTest ext
         assertNull(res);
     }
 
+    public void testGetResourceFromWs2ViaWs2Resolver() throws Exception {
+        // existing resource
+        Resource res = ws2Resolver.getResource("ws2:" + rootPath);
+        assertNotNull(res);
+        assertEquals("ws2:" + rootPath, res.getPath());
+        assertEquals(rootWs2Node.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootWs2Node.isSame(res.adaptTo(Node.class)));
+
+        // missing resource
+        String path = "ws2:" + rootPath + "/missing";
+        res = resResolver.getResource(path);
+        assertNull(res);
+    }
+
+    public void testGetResourceFromWs2ViaDefaultResolver() throws Exception {
+        // existing resource
+        Resource res = resResolver.getResource("ws2:" + rootPath);
+        assertNotNull(res);
+        assertEquals("ws2:" + rootPath, res.getPath());
+        assertEquals(rootWs2Node.getPrimaryNodeType().getName(),
+            res.getResourceType());
+
+        assertNotNull(res.adaptTo(Node.class));
+        assertTrue(rootWs2Node.isSame(res.adaptTo(Node.class)));
+
+        // missing resource
+        String path = "ws2:" + rootPath + "/missing";
+        res = resResolver.getResource(path);
+        assertNull(res);
+    }
+
     public void testResolveResource() throws Exception {
         // existing resource
         HttpServletRequest request = new ResourceResolverTestRequest(rootPath);
@@ -903,12 +961,12 @@ public class JcrResourceResolverTest ext
         mapped = resResolver.map(child.getPath());
         assertEquals(path, mapped);
     }
-    
+
     public void testMapURLEscaping() throws Exception {
 
         final String mapHostInternal = "internal.host.com";
         final String mapRootInternal = "/content/internal";
-        
+
         Node internalRedirect = mapRoot.getNode("map/http").addNode(
             mapHostInternal + ".80", "sling:Mapping");
         
internalRedirect.setProperty(JcrResourceResolver.PROP_REDIRECT_INTERNAL,
@@ -919,7 +977,7 @@ public class JcrResourceResolverTest ext
 
         final String path = "/sample with spaces";
         final String escapedPath = "/sample%20with%20spaces";
-        
+
         // 
---------------------------------------------------------------------
         // internal redirect
 
@@ -937,11 +995,11 @@ public class JcrResourceResolverTest ext
         // => only return path, escaped, because request host/port matches 
(cut off host part)
         mapped = resResolver.map(new ResourceResolverTestRequest(null, 
mapHostInternal, -1, rootPath), mapRootInternal + path);
         assertEquals(escapedPath, mapped);
-        
+
         // 
---------------------------------------------------------------------
         // no mapping config
         // => return only escaped path
-        
+
         final String unmappedRoot = "/unmappedRoot";
 
         // a) test map(String)
@@ -953,7 +1011,7 @@ public class JcrResourceResolverTest ext
         assertEquals(unmappedRoot + escapedPath, mapped);
 
     }
-    
+
     public void testMapNamespaceMangling() throws Exception {
 
         final String mapHost = "virtual.host.com";

Added: 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java?rev=933747&view=auto
==============================================================================
--- 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java
 (added)
+++ 
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/SynchronousJcrResourceListener.java
 Tue Apr 13 19:13:31 2010
@@ -0,0 +1,42 @@
+/*
+ * 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 javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.observation.SynchronousEventListener;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.util.tracker.ServiceTracker;
+
+/**
+ * This class is used to ensure that events are handled during the test.
+ *
+ * TODO - Ideally, this wouldn't be necessary, but EventHelper doesn't seem
+ * to be working 100% of the time.
+ *
+ */
+public class SynchronousJcrResourceListener extends JcrResourceListener 
implements SynchronousEventListener {
+
+    public SynchronousJcrResourceListener(SlingRepository repository, String 
workspaceName,
+            ResourceResolverFactory factory, String startPath, String 
mountPrefix, ServiceTracker eventAdminTracker)
+            throws LoginException, RepositoryException {
+        super(repository, workspaceName, factory, startPath, mountPrefix, 
eventAdminTracker);
+    }
+
+}


Reply via email to