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); + } + +}