blob: 915591bcb3695b445e85f3649f4db6548f9d0db5 [file] [log] [blame]
/*
* 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 SF 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.resourceresolver.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.QueriableResourceProvider;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceProviderFactory;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.EventAdmin;
/**
* This tests the ResourceResolver using mocks. The Unit test is in addition to
* ResourceResolverImplTest which covers API conformance more than it covers all
* code paths.
*/
// TODO: Configure mapping to react correctly.
// TODO: test external redirect.
// TODO: Map to URI
// TODO: Statresource
public class MockedResourceResolverImplTest {
private static final List<Resource> EMPTY_RESOURCE_LIST = new ArrayList<Resource>();
private static final String FAKE_QUERY_LANGUAGE = "fake";
private static final String PATH = "path";
private ResourceResolverFactoryActivator activator;
@Mock
private ComponentContext componentContext;
@Mock
private EventAdmin eventAdmin;
@Mock
private BundleContext bundleContext;
@Mock
private Bundle usingBundle;
@Mock
private BundleContext usingBundleContext;
private Map<String, Object> services = new HashMap<String, Object>();
private Map<String, Object> serviceProperties = new HashMap<String, Object>();
private ResourceResolverFactoryImpl resourceResolverFactory;
@Mock
private ResourceProvider resourceProvider;
@Mock
private ResourceProviderFactory resourceProviderFactory;
/**
* the factory creates these.
*/
@Mock
private ResourceProvider factoryResourceProvider;
@Mock
private ResourceProvider factoryAdministrativeResourceProvider;
/**
* deals with /etc resolution.
*/
@Mock
private ResourceProvider mappingResourceProvider;
/**
* deals with /apps and /libs resolution.
*/
@Mock
private ResourceProvider appsResourceProvider;
/**
* QueriableResourceProviders
*/
@Mock
private QueriableResourceProvider queriableResourceProviderA;
public MockedResourceResolverImplTest() {
MockitoAnnotations.initMocks(this);
}
@SuppressWarnings("unchecked")
@Before
public void before() throws LoginException {
activator = new ResourceResolverFactoryActivator();
Mockito.when(componentContext.getProperties()).thenReturn(buildBundleProperties());
Mockito.when(componentContext.getBundleContext()).thenReturn(
bundleContext);
activator.eventAdmin = eventAdmin;
activator.resourceAccessSecurityTracker = new ResourceAccessSecurityTracker();
activator.bindResourceProvider(resourceProvider,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.DummyTestProvider",
10L,
new String[] { "/single" }));
// setup mapping resources at /etc/map to exercise vanity etc.
// hmm, can't provide the resolver since its not up and ready.
// mapping almost certainly work properly until this can be setup correctly.
buildMappingResource("/etc/map", mappingResourceProvider, null);
activator.bindResourceProvider(mappingResourceProvider,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.MapProvider",
11L,
new String[] { "/etc" }));
// bind a ResourceProviderFactory to satidy the pre-requirements
Mockito.when(resourceProviderFactory.getResourceProvider(null)).thenReturn(
factoryResourceProvider);
Mockito.when(
resourceProviderFactory.getResourceProvider(Mockito.anyMap())).thenReturn(
factoryResourceProvider);
Mockito.when(
resourceProviderFactory.getAdministrativeResourceProvider(null)).thenReturn(
factoryAdministrativeResourceProvider);
Mockito.when(
resourceProviderFactory.getAdministrativeResourceProvider(Mockito.anyMap())).thenReturn(
factoryAdministrativeResourceProvider);
activator.bindResourceProviderFactory(resourceProviderFactory,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.DummyTestProviderFactory",
12L,
new String[] { "/factory" } ));
activator.bindResourceProvider(appsResourceProvider,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.AppsProvider",
13L,
new String[] { "/apps", "/libs" }));
activator.bindResourceProvider(queriableResourceProviderA,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.QueriableResourceProviderA",
14L,
new String[] { "/searchA" }));
// activate the components.
activator.activate(componentContext);
// configure using Bundle
Mockito.when(usingBundle.getBundleContext()).thenReturn(usingBundleContext);
Mockito.when(usingBundleContext.getBundle()).thenReturn(usingBundle);
// extract any services that were registered into a map.
ArgumentCaptor<String> classesCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Object> serviceCaptor = ArgumentCaptor.forClass(Object.class);
@SuppressWarnings("rawtypes")
ArgumentCaptor<Dictionary> propertiesCaptor = ArgumentCaptor.forClass(Dictionary.class);
Mockito.verify(bundleContext, Mockito.atLeastOnce()).registerService(
classesCaptor.capture(), serviceCaptor.capture(),
propertiesCaptor.capture());
int si = 0;
List<Object> serviceList = serviceCaptor.getAllValues();
@SuppressWarnings({ "unused", "rawtypes" })
List<Dictionary> servicePropertiesList = propertiesCaptor.getAllValues();
for (String serviceName : classesCaptor.getAllValues()) {
services.put(serviceName, serviceList.get(si));
serviceProperties.put(serviceName, serviceProperties.get(si));
si++;
}
// verify that a ResourceResolverFactoryImpl was created and registered.
Assert.assertNotNull(services.get(ResourceResolverFactory.class.getName()));
Object rrf = services.get(ResourceResolverFactory.class.getName());
if (rrf instanceof ServiceFactory) {
rrf = ((ServiceFactory) rrf).getService(usingBundle, null);
}
Assert.assertTrue(rrf instanceof ResourceResolverFactoryImpl);
resourceResolverFactory = (ResourceResolverFactoryImpl) rrf;
}
private Resource buildMappingResource(String path,
ResourceProvider provider, ResourceResolver resourceResolver) {
List<Resource> localHostAnyList = new ArrayList<Resource>();
localHostAnyList.add(buildResource(path+"/http/example.com.80/cgi-bin", EMPTY_RESOURCE_LIST, resourceResolver, provider, "sling:internalRedirect", "/scripts" ));
localHostAnyList.add(buildResource(path+"/http/example.com.80/gateway", EMPTY_RESOURCE_LIST, resourceResolver, provider,"sling:internalRedirect", "http://gbiv.com"));
localHostAnyList.add(buildResource(path+"/http/example.com.80/stories", EMPTY_RESOURCE_LIST, resourceResolver, provider,"sling:internalRedirect", "/anecdotes/$1"));
List<Resource> mappingChildren = new ArrayList<Resource>();
mappingChildren.add(buildResource(path+"/http/example.com.80", EMPTY_RESOURCE_LIST, resourceResolver, provider,"sling:redirect", "http://www.example.com/"));
mappingChildren.add(buildResource(path+"/http/www.example.com.80", EMPTY_RESOURCE_LIST, resourceResolver, provider,"sling:internalRedirect", "/example"));
mappingChildren.add(buildResource(path+"/http/any_example.com.80", EMPTY_RESOURCE_LIST, resourceResolver, provider,"sling:match", ".+\\.example\\.com\\.80", "sling:redirect", "http://www.example.com/"));
mappingChildren.add(buildResource(path+"/http/localhost_any", localHostAnyList, resourceResolver, provider,"sling:match", "localhost\\.\\d*", "sling:internalRedirect", "/content"));
Resource etcMapResource = buildResource(path+"/http", mappingChildren);
Mockito.when(provider.getResource(Mockito.any(ResourceResolver.class), Mockito.eq(path))).thenReturn(etcMapResource);
return etcMapResource;
}
@After
public void after() {
activator.unbindResourceProvider(resourceProvider,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.DummyTestProvider",
10L,
new String[] { "/single" }));
activator.unbindResourceProvider(mappingResourceProvider,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.MapProvider",
11L,
new String[] { "/etc" }));
activator.bindResourceProviderFactory(resourceProviderFactory,
buildResourceProviderProperties("org.apache.sling.resourceresolver.impl.DummyTestProviderFactory",
12L,
new String[] { "/factory" } ));
}
/**
* build child resources as an iterable of resources.
* @param parent
* @return
*/
private Iterable<Resource> buildChildResources(String parent) {
List<Resource> mappingResources = new ArrayList<Resource>();
for ( int i = 0; i < 5; i++ ) {
mappingResources.add(buildResource(parent+"/m"+i, EMPTY_RESOURCE_LIST));
}
return mappingResources;
}
/**
* Build a resource based on path and children.
* @param fullpath
* @param children
* @return
*/
private Resource buildResource(String fullpath, Iterable<Resource> children) {
return buildResource(fullpath, children, null, null, null);
}
/** Build a List of ValueMap */
private List<ValueMap> buildValueMapCollection(int howMany, String pathPrefix) {
final List<ValueMap> result = new ArrayList<ValueMap>();
for(int i=0; i < howMany; i++) {
final Map<String, Object> m = new HashMap<String, Object>();
m.put(PATH, pathPrefix + i);
result.add(new ValueMapDecorator(m));
}
return result;
}
/**
* Build a resource with path, children and resource resolver.
* @param fullpath
* @param children
* @param resourceResolver
* @return
*/
private Resource buildResource(String fullpath, Iterable<Resource> children, ResourceResolver resourceResolver, ResourceProvider provider, String ... properties) {
Resource resource = Mockito.mock(Resource.class);
Mockito.when(resource.getName()).thenReturn(getName(fullpath));
Mockito.when(resource.getPath()).thenReturn(fullpath);
ResourceMetadata resourceMetadata = new ResourceMetadata();
Mockito.when(resource.getResourceMetadata()).thenReturn(resourceMetadata);
Mockito.when(resource.listChildren()).thenReturn(children.iterator());
Mockito.when(resource.getResourceResolver()).thenReturn(resourceResolver);
// register the resource with the provider
if ( provider != null ) {
Mockito.when(provider.listChildren(resource)).thenReturn(children.iterator());
if ( resourceResolver != null) {
Mockito.when(provider.getResource(Mockito.eq(resourceResolver), Mockito.eq(fullpath))).thenReturn(resource);
Mockito.when(provider.getResource(Mockito.eq(resourceResolver), Mockito.any(HttpServletRequest.class), Mockito.eq(fullpath))).thenReturn(resource);
} else {
Mockito.when(provider.getResource(Mockito.any(ResourceResolver.class), Mockito.eq(fullpath))).thenReturn(resource);
Mockito.when(provider.getResource(Mockito.any(ResourceResolver.class), Mockito.any(HttpServletRequest.class), Mockito.eq(fullpath))).thenReturn(resource);
}
}
if ( properties != null ) {
ValueMap vm = new SimpleValueMapImpl();
for ( int i=0; i < properties.length; i+=2) {
resourceMetadata.put(properties[i], properties[i+1]);
vm.put(properties[i], properties[i+1]);
}
Mockito.when(resource.getValueMap()).thenReturn(vm);
Mockito.when(resource.adaptTo(Mockito.eq(ValueMap.class))).thenReturn(vm);
} else {
Mockito.when(resource.getValueMap()).thenReturn(ValueMapDecorator.EMPTY);
Mockito.when(resource.adaptTo(Mockito.eq(ValueMap.class))).thenReturn(ValueMapDecorator.EMPTY);
}
return resource;
}
/**
* extract the name from a path.
* @param fullpath
* @return
*/
private String getName(String fullpath) {
int n = fullpath.lastIndexOf("/");
return fullpath.substring(n+1);
}
/**
* Build properties for a resource provider.
* @param servicePID
* @param serviceID
* @param roots
* @return
*/
private Map<String, Object> buildResourceProviderProperties(String servicePID, long serviceID, String[] roots) {
Map<String, Object> resourceProviderProperties = new HashMap<String, Object>();
resourceProviderProperties.put(Constants.SERVICE_PID, servicePID);
resourceProviderProperties.put(Constants.SERVICE_ID, serviceID);
resourceProviderProperties.put(Constants.SERVICE_VENDOR, "Apache");
resourceProviderProperties.put(Constants.SERVICE_DESCRIPTION,
"Dummy Provider");
resourceProviderProperties.put(QueriableResourceProvider.LANGUAGES,
new String[] { FAKE_QUERY_LANGUAGE } );
resourceProviderProperties.put(ResourceProvider.ROOTS, roots);
return resourceProviderProperties;
}
/**
* build a properties for a resource resolver bundle.
* @return
*/
private Dictionary<String, Object> buildBundleProperties() {
Dictionary<String, Object> properties = new Hashtable<String, Object>();
properties.put("resource.resolver.virtual", new String[] { "/:/" });
properties.put("resource.resolver.mapping", new String[] { "/:/",
"/content/:/", "/system/docroot/:/", "/content.html-/$" });
properties.put("resource.resolver.allowDirect", true);
properties.put("resource.resolver.searchpath", new String[] { "/apps",
"/libs" });
properties.put("resource.resolver.manglenamespaces", true);
properties.put("resource.resolver.map.location", "/etc/map");
properties.put("resource.resolver.default.vanity.redirect.status", 302);
properties.put(
"resource.resolver.required.providers",
new String[] { "org.apache.sling.resourceresolver.impl.DummyTestProvider" });
properties.put(Constants.SERVICE_VENDOR, "Apache");
properties.put(Constants.SERVICE_DESCRIPTION, "Testing");
return properties;
}
/**
* Test getting a resolver.
* @throws LoginException
*/
@Test
public void testGetResolver() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Map<String, Object> authenticationInfo = new HashMap<String, Object>();
resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(authenticationInfo);
Assert.assertNotNull(resourceResolver);
}
/**
* Misceleneous coverage.
* @throws LoginException
*/
@Test
public void testResolverMisc() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
try {
resourceResolver.getAttribute(null);
Assert.fail("Should have thrown a NPE");
} catch ( NullPointerException e) {
// this is expected.
}
Assert.assertArrayEquals(new String[]{"/apps/","/libs/"}, resourceResolver.getSearchPath());
}
/**
* Test various administrative resource resolvers.
* @throws LoginException
*/
@Test
public void testGetAuthenticatedResolve() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Map<String, Object> authenticationInfo = new HashMap<String, Object>();
resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(authenticationInfo);
Assert.assertNotNull(resourceResolver);
}
/**
* Test getResource for a resource provided by a resource provider.
* @throws LoginException
*/
@Test
public void testGetResource() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Resource singleResource = buildResource("/single/test", EMPTY_RESOURCE_LIST, resourceResolver, resourceProvider);
Resource resource = resourceResolver.getResource("/single/test");
Assert.assertEquals(singleResource, resource);
}
/**
* Test getResource where path contains intermediate . verifying fix for SLING-864
* @throws LoginException
*/
@Test
public void testGetResourceSLING864() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Resource singleResource = buildResource("/single/test.with/extra.dots/inthepath", EMPTY_RESOURCE_LIST,resourceResolver, resourceProvider);
Resource resource = resourceResolver.getResource("/single/test.with/extra.dots/inthepath");
Assert.assertEquals(singleResource, resource);
}
/**
* Test search paths
* @throws LoginException
*/
@Test
public void testRelativeResource() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Resource appResource = buildResource("/apps/store/inventory", EMPTY_RESOURCE_LIST, resourceResolver, appsResourceProvider);
Resource libResource = buildResource("/libs/store/catalog", EMPTY_RESOURCE_LIST, resourceResolver, appsResourceProvider);
Resource testResource = resourceResolver.getResource("store/inventory");
Assert.assertEquals(appResource, testResource);
testResource = resourceResolver.getResource("store/catalog");
Assert.assertEquals(libResource, testResource);
}
/**
* Test getResource for a resource provided by a factory provider.
* @throws LoginException
*/
@Test
public void testGetFactoryResource() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Assert.assertNotNull(resourceResolver);
Resource factoryResource = buildResource("/factory/test", EMPTY_RESOURCE_LIST, resourceResolver, factoryResourceProvider);
Resource resource = resourceResolver.getResource("/factory/test");
Assert.assertEquals(factoryResource, resource);
}
/**
* Basic test of mapping functionality, at the moment needs more
* configuration in the virtual /etc/map.
*
* @throws LoginException
*/
@Test
public void testMapping() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
buildResource("/factory/test", EMPTY_RESOURCE_LIST, resourceResolver, factoryResourceProvider);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getScheme()).thenReturn("http");
Mockito.when(request.getServerPort()).thenReturn(80);
Mockito.when(request.getServerName()).thenReturn("localhost");
String path = resourceResolver.map(request,"/factory/test?q=123123");
Assert.assertEquals("/factory/test?q=123123", path);
path = resourceResolver.map(request,"/factory/test");
Assert.assertEquals("/factory/test", path);
// test path mapping without a request.
path = resourceResolver.map("/factory/test");
Assert.assertEquals("/factory/test", path);
buildResource("/content", EMPTY_RESOURCE_LIST, resourceResolver, factoryResourceProvider);
path = resourceResolver.map("/content.html");
Assert.assertEquals("/content.html", path);
}
/**
* Tests list children via the resource (NB, this doesn't really test the
* resource resolver at all, but validates this unit test.)
*
* @throws LoginException
*/
@Test
public void testListChildren() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
buildResource("/single/test/withchildren", buildChildResources("/single/test/withchildren"), resourceResolver, resourceProvider );
Resource resource = resourceResolver.getResource("/single/test/withchildren");
Assert.assertNotNull(resource);
// test via the resource list children itself, this really just tests this test case.
Iterator<Resource> resourceIterator = resource.listChildren();
Assert.assertNotNull(resourceResolver);
int i = 0;
while(resourceIterator.hasNext()) {
Assert.assertEquals("m"+i, resourceIterator.next().getName());
i++;
}
Assert.assertEquals(5, i);
}
/**
* Test listing children via the resource resolver listChildren call.
* @throws LoginException
*/
@Test
public void testResourceResolverListChildren() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
buildResource("/single/test/withchildren", buildChildResources("/single/test/withchildren"), resourceResolver, resourceProvider);
Resource resource = resourceResolver.getResource("/single/test/withchildren");
Assert.assertNotNull(resource);
// test via the resource list children itself, this really just tests this test case.
Iterator<Resource> resourceIterator = resourceResolver.listChildren(resource);
Assert.assertNotNull(resourceResolver);
int i = 0;
while(resourceIterator.hasNext()) {
Assert.assertEquals("m"+i, resourceIterator.next().getName());
i++;
}
Assert.assertEquals(5,i);
}
/**
* Tests listing children via the resource resolver getChildren call.
* @throws LoginException
*/
@Test
public void testResourceResolverGetChildren() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
buildResource("/single/test/withchildren", buildChildResources("/single/test/withchildren"), resourceResolver, resourceProvider);
Resource resource = resourceResolver.getResource("/single/test/withchildren");
Assert.assertNotNull(resource);
// test via the resource list children itself, this really just tests this test case.
Iterable<Resource> resourceIterator = resourceResolver.getChildren(resource);
Assert.assertNotNull(resourceResolver);
int i = 0;
for(Resource r : resourceIterator) {
Assert.assertEquals("m"+i, r.getName());
i++;
}
Assert.assertEquals(5,i);
}
@Test
public void testQueryResources() throws LoginException {
final int n = 3;
Mockito.when(queriableResourceProviderA.queryResources(Mockito.any(ResourceResolver.class), Mockito.any(String.class), Mockito.any(String.class)))
.thenReturn(buildValueMapCollection(n, "A_").iterator());
final ResourceResolver rr = resourceResolverFactory.getResourceResolver(null);
buildResource("/search/test/withchildren", buildChildResources("/search/test/withchildren"), rr, resourceProvider);
final Iterator<Map<String, Object>> it = rr.queryResources("/search", FAKE_QUERY_LANGUAGE);
final Set<String> toFind = new HashSet<String>();
for(int i=0; i < n; i++) {
toFind.add("A_" + i);
}
assertTrue("Expecting non-empty result (" + n + ")", it.hasNext());
while(it.hasNext()) {
final Map<String, Object> m = it.next();
toFind.remove(m.get(PATH));
}
assertTrue("Expecting no leftovers (" + n + ") in" + toFind, toFind.isEmpty());
}
@Test public void test_versions() throws LoginException {
ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
Resource resource = resourceResolver.resolve("/content/test.html;v=1.0");
Map<String, String> parameters = resource.getResourceMetadata().getParameterMap();
assertEquals("/content/test.html", resource.getPath());
assertEquals("test.html", resource.getName());
assertEquals(Collections.singletonMap("v", "1.0"), parameters);
resource = resourceResolver.resolve("/content/test;v='1.0'.html");
parameters = resource.getResourceMetadata().getParameterMap();
assertEquals("/content/test.html", resource.getPath());
assertEquals("test.html", resource.getName());
assertEquals(Collections.singletonMap("v", "1.0"), parameters);
buildResource("/single/test/withchildren", buildChildResources("/single/test/withchildren"), resourceResolver, resourceProvider);
resource = resourceResolver.getResource("/single/test/withchildren;v='1.0'");
assertNotNull(resource);
assertEquals("/single/test/withchildren", resource.getPath());
assertEquals("withchildren", resource.getName());
}
}