This is an automated email from the ASF dual-hosted git repository. andysch pushed a commit to branch feature/SLING-7768 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git
commit 903cbd84840ff76f899f02f763dc502412a846c7 Merge: f4aae98 5dcaacc Author: Andreas Schaefer <[email protected]> AuthorDate: Mon Jul 29 08:48:15 2019 -0700 Merge branch 'master' into feature/SLING-7768 # Conflicts: # pom.xml # src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java # src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java # src/test/java/org/apache/sling/resourceresolver/impl/SimpleValueMapImpl.java # src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java # src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java # src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java # src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java CODE_OF_CONDUCT.md | 22 ++ CONTRIBUTING.md | 24 ++ Jenkinsfile | 20 ++ README.md | 4 +- bnd.bnd | 17 + pom.xml | 27 +- .../impl/CommonResourceResolverFactoryImpl.java | 20 +- .../impl/ResourceResolverFactoryActivator.java | 5 - .../impl/ResourceResolverFactoryConfig.java | 6 - .../impl/ResourceResolverImpl.java | 236 ++----------- .../resourceresolver/impl/ResourceTypeUtil.java | 8 +- .../console/ResourceResolverWebConsolePlugin.java | 34 +- .../impl/helper/ResourceResolverContext.java | 4 +- .../impl/helper/ResourceResolverControl.java | 20 +- .../impl/legacy/LegacyResourceProviderAdapter.java | 28 +- .../LegacyResourceProviderFactoryAdapter.java | 36 +- .../impl/mapping/MapConfigurationProvider.java | 4 - .../resourceresolver/impl/mapping/MapEntries.java | 38 +- .../impl/mapping/MapEntriesHandler.java | 7 - .../resourceresolver/impl/mapping/MapEntry.java | 20 +- .../impl/mapping/ResourceMapperImpl.java | 382 +++++++++++++++++++++ .../impl/providers/ResourceProviderTracker.java | 13 +- .../stateful/AuthenticatedResourceProvider.java | 14 +- .../providers/stateful/BasicResolveContext.java | 14 +- .../impl/providers/stateful/ProviderManager.java | 28 +- .../impl/EtcMappingResourceResolverTest.java | 63 ++++ .../sling/resourceresolver/impl/Fixture.java | 17 +- .../impl/MockedResourceResolverImplTest.java | 16 +- .../impl/ResourceResolverMangleNamespacesTest.java | 8 +- .../resourceresolver/impl/SimpleValueMapImpl.java | 7 +- .../mapping/AbstractMappingMapEntriesTest.java | 21 ++ .../impl/mapping/EtcMappingMapEntriesTest.java | 13 + .../impl/mapping/InMemoryResource.java | 96 ++++++ .../impl/mapping/InMemoryResourceProvider.java | 79 +++++ .../impl/mapping/MapEntriesTest.java | 292 +++++----------- .../impl/mapping/MapEntryTest.java | 60 ++++ .../impl/mapping/ResourceMapperImplTest.java | 284 +++++++++++++++ .../providers/ResourceProviderTrackerTest.java | 51 ++- .../sling/resourceresolver/util/MockTestUtil.java | 155 +++++++++ 39 files changed, 1571 insertions(+), 622 deletions(-) diff --cc pom.xml index 5466e8a,0b35127..cea7528 --- a/pom.xml +++ b/pom.xml @@@ -63,29 -62,14 +62,40 @@@ </execution> </executions> </plugin> ++<!-- + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package> + javax.jcr;resolution:=optional, + * + </Import-Package> ++ <!- - Check if that is still needed - -> + <Export-Package> + org.apache.sling.resourceresolver.impl.mapping + </Export-Package> + <Provide-Capability> + osgi.service;objectClass=javax.servlet.Servlet, + osgi.service;objectClass=org.apache.sling.api.resource.ResourceResolverFactory, + osgi.service;objectClass=org.apache.sling.api.resource.observation.ResourceChangeListener, + osgi.service;objectClass=org.apache.sling.api.resource.runtime.RuntimeService, + osgi.service;objectClass=org.apache.sling.spi.resource.provider.ResourceProvider + </Provide-Capability> + </instructions> + </configuration> + </plugin> ++--> + <plugin> + <groupId>biz.aQute.bnd</groupId> + <artifactId>bnd-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>biz.aQute.bnd</groupId> + <artifactId>bnd-baseline-maven-plugin</artifactId> + </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javadoc-plugin</artifactId> diff --cc src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java index 478be4a,ffbcb2d..35196a2 --- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java +++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java @@@ -37,9 -37,7 +37,8 @@@ import org.apache.sling.api.resource.Re import org.apache.sling.api.resource.path.Path; import org.apache.sling.api.resource.runtime.RuntimeService; import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker; - import org.apache.sling.resourceresolver.impl.mapping.MapEntries; import org.apache.sling.resourceresolver.impl.mapping.Mapping; +import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider; import org.apache.sling.resourceresolver.impl.observation.ResourceChangeListenerWhiteboard; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker.ChangeListener; diff --cc src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java index 89fa39b,9779b3b..2efa826 --- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java +++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java @@@ -53,8 -52,7 +53,9 @@@ import java.util.concurrent.locks.Reent import javax.servlet.http.HttpServletResponse; import org.apache.sling.api.SlingConstants; +import org.apache.sling.api.SlingException; import org.apache.sling.api.resource.LoginException; ++import org.apache.sling.api.resource.QuerySyntaxException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; @@@ -164,8 -151,7 +163,7 @@@ public class MapEntries implement this.resolveMapsMap = Collections.singletonMap(GLOBAL_LIST_KEY, (List<MapEntry>)Collections.EMPTY_LIST); this.mapMaps = Collections.<MapEntry> emptyList(); this.vanityTargets = Collections.<String,List <String>>emptyMap(); - this.aliasMap = Collections.<String, Map<String, String>>emptyMap(); + this.aliasMap = Collections.emptyMap(); - this.stringInterpolationProvider = stringInterpolationProvider; doInit(); diff --cc src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java index 0fb93fc,b8f73e1..9ae3f7d --- a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java +++ b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java @@@ -23,8 -23,6 +23,11 @@@ import org.apache.sling.api.resource.ob import org.apache.sling.api.resource.path.Path; import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider; import org.apache.sling.resourceresolver.impl.mapping.MapEntries; ++<<<<<<< HEAD +import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration; +import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderImpl; ++======= ++>>>>>>> master import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker; @@@ -55,7 -53,6 +58,10 @@@ import static org.apache.sling.resource import static org.apache.sling.resourceresolver.util.MockTestUtil.checkRedirectResource; import static org.apache.sling.resourceresolver.util.MockTestUtil.createRequestFromUrl; import static org.apache.sling.resourceresolver.util.MockTestUtil.setInaccessibleField; ++<<<<<<< HEAD +import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider; ++======= ++>>>>>>> master import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; @@@ -90,10 -87,6 +96,13 @@@ public class EtcMappingResourceResolver @Mock ResourceProvider<?> resourceProvider; ++<<<<<<< HEAD + @Mock + StringInterpolationProviderConfiguration stringInterpolationProviderConfiguration; + + StringInterpolationProviderImpl stringInterpolationProvider = new StringInterpolationProviderImpl(); ++======= ++>>>>>>> master MapEntries mapEntries; File vanityBloomFilterFile; @@@ -122,7 -115,6 +131,10 @@@ setInaccessibleField("resourceProviderTracker", activator, resourceProviderTracker); setInaccessibleField("resourceAccessSecurityTracker", activator, new ResourceAccessSecurityTracker()); setInaccessibleField("bundleContext", activator, bundleContext); ++<<<<<<< HEAD + setInaccessibleField("stringInterpolationProvider", activator, stringInterpolationProvider); ++======= ++>>>>>>> master setInaccessibleField("mapRoot", activator, "/etc/map"); setInaccessibleField("mapRootPrefix", activator, "/etc/map"); setInaccessibleField("observationPaths", activator, new Path[] {new Path("/")}); @@@ -133,7 -125,7 +145,11 @@@ when(bundleContext.getDataFile("vanityBloomFilter.txt")).thenReturn(vanityBloomFilterFile); when(serviceUserMapper.getServiceUserID(any(Bundle.class),anyString())).thenReturn("mapping"); // Activate method is package private so we use reflection to to call it ++<<<<<<< HEAD + callInaccessibleMethod("activate", commonFactory, BundleContext.class, bundleContext); ++======= + callInaccessibleMethod("activate", null, commonFactory, BundleContext.class, bundleContext); ++>>>>>>> master final Bundle usingBundle = mock(Bundle.class); resourceResolverFactory = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null); resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null); @@@ -147,6 -139,15 +163,18 @@@ return new ArrayList<>(); } ++<<<<<<< HEAD ++======= + /** + * Changes to the /etc/map in our tests are not taking effect until there is an Change Event issued + * + * ATTENTION: this method can only be issued once. After that the Resource Metadata is locked and + * hence updates will fail + * + * @param path Path to the resource root to be refreshed + * @param isExternal External flag of the ResourceChange event + */ ++>>>>>>> master void refreshMapEntries(String path, boolean isExternal) { ((MapEntries) commonFactory.getMapEntries()).onChange( asList( @@@ -272,39 -273,37 +300,74 @@@ checkInternalResource(resolvedResource, "/anecdotes/stories"); } ++<<<<<<< HEAD + @Test + public void simple_node_string_interpolation() throws Exception { + buildResource("${siv.one}", http, resourceResolver, resourceProvider,PROP_REDIRECT_EXTERNAL, "/content/simple-node"); + setupStringInterpolationProvider(stringInterpolationProvider, stringInterpolationProviderConfiguration, bundleContext, new String[] {"siv.one=test-simple-node.80"}); + + refreshMapEntries("/etc/map", true); + + ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/test-simple-node.80/", "/content/simple-node/"); + expectedEtcMapping.assertEtcMap("String Interpolation for simple match", commonFactory.getMapEntries().getResolveMaps()); + + Resource content = buildResource("/content", null, resourceResolver, resourceProvider); + Resource simpleNode = buildResource("/content/simple-node", content, resourceResolver, resourceProvider); + + HttpServletRequest request = createRequestFromUrl("http://test-simple-node:80/"); + Resource resolvedResource = resourceResolver.resolve(request, "/"); + checkRedirectResource(resolvedResource, "/content/simple-node/", 302); + } + + @Test + public void simple_match_string_interpolation() throws Exception { + buildResource("test-node", http, resourceResolver, resourceProvider, + PROP_REG_EXP, "${siv.one}/", + PROP_REDIRECT_EXTERNAL, "/content/simple-match/" + ); + setupStringInterpolationProvider(stringInterpolationProvider, stringInterpolationProviderConfiguration, bundleContext, new String[] {"siv.one=test-simple-match.80"}); + + refreshMapEntries("/etc/map", true); + + ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/test-simple-match.80/", "/content/simple-match/"); + expectedEtcMapping.assertEtcMap("String Interpolation for simple match", commonFactory.getMapEntries().getResolveMaps()); + + HttpServletRequest request = createRequestFromUrl("http://test-simple-match:80/"); + Resource resolvedResource = resourceResolver.resolve(request, "/"); + checkRedirectResource(resolvedResource, "/content/simple-match/", 302); ++======= + /** + * ATTENTION: this tests showcases an erroneous condition of an endless circular mapping in the /etc/map. When + * this test passes this condition is present. After a fix this test must be adjusted. + * + * This confirms an issue with the Etc Mapping where a mapping from a node to a child node (here / to /content) + * ends up in a endless circular mapping. + * The only way to recover from this is to go to the OSGi console and change the /etc/map path in the Resource + * Resolver factory. + * Either the Etc Mapping discovers this condition and stops it or at least ignores mapping for Composum to allow + * the /etc/map to be edited. + */ + @Test + public void endless_circular_mapping() throws Exception { + buildResource(http.getPath() + "/localhost.8080", http, resourceResolver, resourceProvider, PROP_REDIRECT_EXTERNAL, "/content"); + refreshMapEntries("/etc/map", true); + + ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping("^http/localhost.8080/", "/content/"); + expectedEtcMapping.assertEtcMap("Etc Mapping for root node to content", commonFactory.getMapEntries().getResolveMaps()); + + buildResource("/content/test", null, resourceResolver, resourceProvider); + buildResource("/content/content/test", null, resourceResolver, resourceProvider); + buildResource("/content/content/content/test", null, resourceResolver, resourceProvider); + + HttpServletRequest request = createRequestFromUrl("http://localhost:8080/"); + Resource resolvedResource = resourceResolver.resolve(request, "/test.html"); + checkRedirectResource(resolvedResource, "/content/test.html", 302); + + resolvedResource = resourceResolver.resolve(request, "/content/test.html"); + checkRedirectResource(resolvedResource, "/content/content/test.html", 302); + + resolvedResource = resourceResolver.resolve(request, "/content/content/test.html"); + checkRedirectResource(resolvedResource, "/content/content/content/test.html", 302); ++>>>>>>> master } } diff --cc src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java index 3e1ac90,6a5f9e2..e7129da --- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java +++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java @@@ -46,7 -46,6 +46,10 @@@ import java.util.concurrent.ExecutorSer import java.util.concurrent.Future; import java.util.concurrent.Semaphore; ++<<<<<<< HEAD +import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider; ++======= ++>>>>>>> master import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyMap; @@@ -79,10 -78,6 +82,13 @@@ public abstract class AbstractMappingMa @Mock ResourceResolver resourceResolver; ++<<<<<<< HEAD + @Mock + StringInterpolationProviderConfiguration stringInterpolationProviderConfiguration; + + StringInterpolationProviderImpl stringInterpolationProvider = new StringInterpolationProviderImpl(); ++======= ++>>>>>>> master MapEntries mapEntries; File vanityBloomFilterFile; @@@ -106,7 -101,6 +112,10 @@@ when(resourceResolverFactory.isVanityPathEnabled()).thenReturn(true); when(resourceResolverFactory.getVanityPathConfig()).thenReturn(configs); when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(true); ++<<<<<<< HEAD + when(resourceResolverFactory.isForceNoAliasTraversal()).thenReturn(true); ++======= ++>>>>>>> master when(resourceResolverFactory.getObservationPaths()).thenReturn(new Path[] {new Path("/")}); when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT); when(resourceResolverFactory.getMaxCachedVanityPathEntries()).thenReturn(-1L); @@@ -117,8 -111,7 +126,12 @@@ map = setupEtcMapResource("/etc", "map"); http = setupEtcMapResource("http", map); ++<<<<<<< HEAD + setupStringInterpolationProvider(stringInterpolationProvider, stringInterpolationProviderConfiguration, bundleContext, new String[] {}); + mapEntries = new MapEntries(resourceResolverFactory, bundleContext, eventAdmin, stringInterpolationProvider); ++======= + mapEntries = new MapEntries(resourceResolverFactory, bundleContext, eventAdmin); ++>>>>>>> master final Field aliasMapField = MapEntries.class.getDeclaredField("aliasMap"); aliasMapField.setAccessible(true); @@@ -191,7 -184,7 +204,11 @@@ return resource; } ++<<<<<<< HEAD + MapEntriesTest.DataFuture createDataFuture(ExecutorService pool, final MapEntries mapEntries) { ++======= + DataFuture createDataFuture(ExecutorService pool, final MapEntries mapEntries) { ++>>>>>>> master Future<Iterator<?>> future = pool.submit(new Callable<Iterator<?>>() { @Override @@@ -199,7 -192,7 +216,11 @@@ return mapEntries.getResolveMapsIterator("http/localhost.8080/target/justVanityPath"); } }); ++<<<<<<< HEAD + return new MapEntriesTest.DataFuture(future); ++======= + return new DataFuture(future); ++>>>>>>> master } void simulateSomewhatSlowSessionOperation(final Semaphore sessionLock) throws InterruptedException { diff --cc src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java index a98ca6e,a7d09ca..bfede24 --- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java +++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java @@@ -16,40 -16,12 +16,49 @@@ */ package org.apache.sling.resourceresolver.impl.mapping; ++<<<<<<< HEAD +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.path.Path; +import org.apache.sling.resourceresolver.impl.CommonResourceResolverFactoryImpl; +import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker; +import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryActivator; +import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl; +import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler; +import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage; +import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker; +import org.apache.sling.serviceusermapping.ServiceUserMapper; +import org.apache.sling.spi.resource.provider.ResolveContext; +import org.apache.sling.spi.resource.provider.ResourceContext; +import org.apache.sling.spi.resource.provider.ResourceProvider; +import org.junit.Test; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.Iterator; +import java.util.List; + +import static java.util.Arrays.asList; +import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler; +import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL; +import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL; +import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping; +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; ++======= + import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL; + import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL; + + import org.apache.sling.api.resource.Resource; + import org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping; + import org.junit.Test; ++>>>>>>> master /** * These tests are for the /etc/map setup of the Map Entries when @@@ -127,125 -99,5 +136,129 @@@ public class EtcMappingMapEntriesTest e .addEtcMapEntry("^http/localhost\\.\\d*/gateway/", true, "http://gbiv.com/") .addEtcMapEntry("^http/localhost\\.\\d*/(stories)/", true, "/anecdotes/$1/"); expectedEtcMapping.assertEtcMap("Etc Mapping for nested internal mixed mapping", mapEntries.getResolveMaps()); ++<<<<<<< HEAD + + // Not really an etc-map resource but it is good for now + final Resource test = setupEtcMapResource("/scripts", "test"); + ResourceProvider<?> rp = new ResourceProvider<Object>() { + @Override + public Resource getResource(ResolveContext<Object> ctx, String path, ResourceContext rCtx, Resource parent) { + if(path.equals("/scripts/test")) { + return test; + } + if(path.startsWith(map.getPath())) { + return findMapping(map, path); + } + return null; + } + + private Resource findMapping(Resource parent, String path) { + if(parent.getPath().equals(path)) { + return parent; + } + Iterator<Resource> i = parent.listChildren(); + while(i.hasNext()) { + Resource child = i.next(); + if(path.equals(child.getPath())) { + return child; + } else { + return findMapping(child, path); + } + } + return null; + } + + @Override + public Iterator<Resource> listChildren(ResolveContext<Object> ctx, Resource parent) { + if(parent.getPath().startsWith(map.getPath())) { + return parent.listChildren(); + } + return null; + } + }; + + List<ResourceProviderHandler> handlers = asList(createRPHandler(rp, "rp1", 0, "/")); + ResourceProviderTracker resourceProviderTracker = mock(ResourceProviderTracker.class); + ResourceProviderStorage storage = new ResourceProviderStorage(handlers); + when(resourceProviderTracker.getResourceProviderStorage()).thenReturn(storage); + ResourceResolverFactoryActivator activator = spy(new ResourceResolverFactoryActivator()); + // Both 'resourceProviderTracker' and 'resourceAccessSecurityTracker' are package private and so we cannot + // set them here. Intercept the call to obtain them and provide the desired value + when(activator.getResourceProviderTracker()).thenReturn(resourceProviderTracker); + when(activator.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker()); + when(activator.getBundleContext()).thenReturn(bundleContext); + when(activator.getStringInterpolationProvider()).thenReturn(stringInterpolationProvider); + when(activator.getMapRoot()).thenReturn("/etc/map"); + when(activator.getObservationPaths()).thenReturn(new Path[] {new Path("/")}); + CommonResourceResolverFactoryImpl commonFactory = spy(new CommonResourceResolverFactoryImpl(activator)); + when(bundleContext.getBundle()).thenReturn(bundle); + ServiceUserMapper serviceUserMapper = mock(ServiceUserMapper.class); + when(activator.getServiceUserMapper()).thenReturn(serviceUserMapper); + when(serviceUserMapper.getServiceUserID(any(Bundle.class),anyString())).thenReturn("mapping"); + Method method = CommonResourceResolverFactoryImpl.class.getDeclaredMethod("activate", BundleContext.class); + method.setAccessible(true); + method.invoke(commonFactory, bundleContext); + final Bundle usingBundle = mock(Bundle.class); + ResourceResolverFactoryImpl resFac = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null); + ResourceResolver resResolver = resFac.getAdministrativeResourceResolver(null); + + HttpServletRequest request = mock(HttpServletRequest.class); + when(request.getScheme()).thenReturn("http"); + when(request.getServerName()).thenReturn("localhost"); + when(request.getServerPort()).thenReturn(80); + Resource mappedResource = resResolver.resolve(request, "/cgi-bin/test.html"); + String path = mappedResource.getPath(); + assertEquals("Wrong Resolved Path", "/scripts/test", path); + } + +// @Test +// public void regex_map_internal_mapping() throws Exception { +// setupEtcMapResource("regexmap", http, +// PROP_REG_EXP, "$1.example.com/$2", +// PROP_REDIRECT_INTERNAL, "/content/([^/]+)/(.*)" +// ); +// +// mapEntries.doInit(); +// // Regex Mappings are ignored for the Resolve Map +// ExpectedEtcMapping expectedEtcMapping = new ExpectedEtcMapping(); +//// .addEtcMapEntry("^http/$1.example.com/$2", true, "/content/([^/]+)/(.*)"); +// expectedEtcMapping.assertEtcMap("Etc Mapping for regex map internal mapping", mapEntries.getResolveMaps()); +// +// ResourceProvider<?> rp = new ResourceProvider<Object>() { +// +// @Override +// public Resource getResource(ResolveContext<Object> ctx, String path, ResourceContext rCtx, Resource parent) { +// return null; +// } +// +// @Override +// public Iterator<Resource> listChildren(ResolveContext<Object> ctx, Resource parent) { +// return null; +// } +// }; +// +// List<ResourceProviderHandler> handlers = asList(createRPHandler(rp, "rp1", 0, "/")); +// ResourceProviderTracker resourceProviderTracker = mock(ResourceProviderTracker.class); +// ResourceProviderStorage storage = new ResourceProviderStorage(handlers); +// when(resourceProviderTracker.getResourceProviderStorage()).thenReturn(storage); +// ResourceResolverFactoryActivator activator = spy(new ResourceResolverFactoryActivator()); +// when(activator.getResourceProviderTracker()).thenReturn(resourceProviderTracker); +//// activator.resourceProviderTracker = resourceProviderTracker; +// when(activator.getResourceAccessSecurityTracker()).thenReturn(new ResourceAccessSecurityTracker()); +//// activator.resourceAccessSecurityTracker = new ResourceAccessSecurityTracker(); +// CommonResourceResolverFactoryImpl commonFactory = new CommonResourceResolverFactoryImpl(activator); +// final Bundle usingBundle = mock(Bundle.class); +// ResourceResolverFactoryImpl resFac = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null); +// ResourceResolver resResolver = resFac.getAdministrativeResourceResolver(null); +// +// HttpServletRequest request = mock(HttpServletRequest.class); +// when(request.getScheme()).thenReturn("http"); +// when(request.getServerName()).thenReturn("a.example.com"); +// when(request.getServerPort()).thenReturn(80); +// Resource mappedResource = resResolver.resolve(request, "/b.html"); +// String path = mappedResource.getPath(); +// } ++======= + } ++>>>>>>> master } diff --cc src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java index b61ac20,9b11f48..db072c3 --- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java +++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java @@@ -16,19 -16,18 +16,20 @@@ */ package org.apache.sling.resourceresolver.impl.mapping; - import org.apache.sling.api.SlingException; - import org.apache.sling.api.resource.Resource; - import org.apache.sling.api.resource.observation.ResourceChange; - import org.apache.sling.api.resource.observation.ResourceChange.ChangeType; - import org.apache.sling.api.wrappers.ValueMapDecorator; - import org.apache.sling.resourceresolver.impl.ResourceResolverImpl; - import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider.VanityPathConfig; - import org.junit.Test; - import org.mockito.ArgumentCaptor; - import org.mockito.Mockito; - import org.mockito.invocation.InvocationOnMock; - import org.mockito.stubbing.Answer; + import static org.junit.Assert.assertEquals; + import static org.junit.Assert.assertFalse; + import static org.junit.Assert.assertNotNull; + import static org.junit.Assert.assertNull; + import static org.junit.Assert.assertTrue; ++import static org.junit.Assert.fail; + import static org.mockito.Matchers.any; + import static org.mockito.Matchers.anyString; + import static org.mockito.Matchers.eq; ++import static org.mockito.Mockito.doReturn; + import static org.mockito.Mockito.mock; + import static org.mockito.Mockito.when; + import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; @@@ -36,6 -34,6 +37,7 @@@ import java.text.ParseException import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; ++import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; @@@ -43,30 -41,62 +45,69 @@@ import java.util.List import java.util.Map; import java.util.Random; import java.util.Set; ++import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; ++import java.util.concurrent.Future; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; - import static org.junit.Assert.assertEquals; - import static org.junit.Assert.assertFalse; - import static org.junit.Assert.assertNotNull; - import static org.junit.Assert.assertNull; - import static org.junit.Assert.assertTrue; - import static org.mockito.Matchers.any; - import static org.mockito.Matchers.anyString; - import static org.mockito.Matchers.eq; - import static org.mockito.Mockito.doReturn; - import static org.mockito.Mockito.mock; - import static org.mockito.Mockito.when; ++import org.apache.sling.api.SlingException; + import org.apache.sling.api.resource.Resource; + 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.api.resource.observation.ResourceChange; + import org.apache.sling.api.resource.observation.ResourceChange.ChangeType; + import org.apache.sling.api.resource.path.Path; + import org.apache.sling.api.wrappers.ValueMapDecorator; + import org.apache.sling.resourceresolver.impl.ResourceResolverImpl; + import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider.VanityPathConfig; + import org.junit.After; + 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.mockito.invocation.InvocationOnMock; + import org.mockito.stubbing.Answer; + import org.osgi.framework.Bundle; + import org.osgi.framework.BundleContext; + import org.osgi.service.event.EventAdmin; + -public class MapEntriesTest extends AbstractMappingMapEntriesTest { ++public class MapEntriesTest { + + private MapEntries mapEntries; + + File vanityBloomFilterFile; + + @Mock + private MapConfigurationProvider resourceResolverFactory; + + @Mock + private BundleContext bundleContext; - public class MapEntriesTest extends AbstractMappingMapEntriesTest { + @Mock + private Bundle bundle; + + @Mock + private ResourceResolver resourceResolver; + + @Mock + private EventAdmin eventAdmin; + + private Map<String, Map<String, String>> aliasMap; + + @SuppressWarnings({ "unchecked" }) + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); - List<MapConfigurationProvider.VanityPathConfig> getVanityPathConfigs() { final List<VanityPathConfig> configs = new ArrayList<>(); configs.add(new VanityPathConfig("/libs/", false)); configs.add(new VanityPathConfig("/libs/denied", true)); @@@ -80,12 -110,36 +121,37 @@@ configs.add(new VanityPathConfig("/vanityPathOnJcrContent", false)); Collections.sort(configs); + vanityBloomFilterFile = new File("src/main/resourcesvanityBloomFilter.txt"); + when(bundle.getSymbolicName()).thenReturn("TESTBUNDLE"); + when(bundleContext.getBundle()).thenReturn(bundle); + when(bundleContext.getDataFile("vanityBloomFilter.txt")).thenReturn(vanityBloomFilterFile); + when(resourceResolverFactory.getServiceResourceResolver(any(Map.class))).thenReturn(resourceResolver); + when(resourceResolverFactory.isVanityPathEnabled()).thenReturn(true); + when(resourceResolverFactory.getVanityPathConfig()).thenReturn(configs); + when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(true); ++ when(resourceResolverFactory.isForceNoAliasTraversal()).thenReturn(true); + when(resourceResolverFactory.getObservationPaths()).thenReturn(new Path[] {new Path("/")}); + when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT); + when(resourceResolverFactory.getMaxCachedVanityPathEntries()).thenReturn(-1L); + when(resourceResolverFactory.isMaxCachedVanityPathEntriesStartup()).thenReturn(true); + when(resourceResolver.findResources(anyString(), eq("sql"))).thenReturn( + Collections.<Resource> emptySet().iterator()); + + mapEntries = new MapEntries(resourceResolverFactory, bundleContext, eventAdmin); + final Field aliasMapField = MapEntries.class.getDeclaredField("aliasMap"); + aliasMapField.setAccessible(true); + + this.aliasMap = ( Map<String, Map<String, String>>) aliasMapField.get(mapEntries); + } - return configs; + @After + public void tearDown() throws Exception { + vanityBloomFilterFile.delete(); } + - @Test - public void test_simple_alias_support() { + @Test(timeout = 1000) + public void test_simple_alias_support() throws InterruptedException { Resource parent = mock(Resource.class); when(parent.getPath()).thenReturn("/parent"); diff --cc src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java index 1c56a47,1480397..3f36327 --- a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java +++ b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java @@@ -26,15 -26,12 +26,21 @@@ import org.apache.sling.api.wrappers.Va import org.apache.sling.resourceresolver.impl.SimpleValueMapImpl; import org.apache.sling.resourceresolver.impl.helper.RedirectResource; import org.apache.sling.resourceresolver.impl.mapping.MapEntry; ++<<<<<<< HEAD +import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider; +import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration; ++======= ++>>>>>>> master import org.apache.sling.spi.resource.provider.ResolveContext; import org.apache.sling.spi.resource.provider.ResourceContext; import org.apache.sling.spi.resource.provider.ResourceProvider; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; ++<<<<<<< HEAD +import org.osgi.framework.BundleContext; ++======= ++>>>>>>> master import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Field; @@@ -75,9 -72,6 +81,12 @@@ public class MockTestUtil } public static void checkInternalResource(Resource internal, String path) { ++<<<<<<< HEAD +// assertThat("Not a Non Existing Resource", redirect, instanceOf(NonExistingResource.class)); +// NonExistingResource nonExistingResource = (NonExistingResource) redirect; +// if(path != null) { ++======= ++>>>>>>> master assertEquals("Wrong Path for Resource", path, internal.getPath()); } @@@ -132,7 -126,7 +141,11 @@@ * @param resourceResolver Resource Resolver of this resource * @param provider Resource Provider Instance * @param properties Key / Value pair for resource properties (the number of strings must be even) ++<<<<<<< HEAD + * @return ++======= + * @return Mock Resource able to handle addition of children later on ++>>>>>>> master */ @SuppressWarnings("unchecked") public static Resource buildResource(String fullPath, Resource parent, ResourceResolver resourceResolver, ResourceProvider<?> provider, String... properties) { @@@ -188,23 -182,57 +201,77 @@@ return resource; } ++<<<<<<< HEAD + public static Object callInaccessibleMethod(String methodName, Object target, Class paramsType, Object param) throws NoSuchMethodException { + return callInaccessibleMethod(methodName, target, new Class[] {paramsType}, new Object[] {param}); + } + + public static Object callInaccessibleMethod(String methodName, Object target, Class[] paramsTypes, Object[] params) throws NoSuchMethodException { + if(paramsTypes != null && params != null) { + if(params.length != paramsTypes.length) { throw new IllegalArgumentException("Number of Parameter Types and Values were not the same"); } + } else { + paramsTypes = null; + params = null; + } + try { + Method method = target.getClass().getDeclaredMethod(methodName, paramsTypes); + method.setAccessible(true); + return method.invoke(target, params); + } catch(NoSuchMethodException e) { + throw new UnsupportedOperationException("Failed to find method: " + methodName, e); ++======= + /** + * Calls a private method that has no parameter like a getter + * + * @param methodName Name of the method + * @param target Target instance + * @return Object that is returned from the method call + * + * @throws UnsupportedOperationException If the call failed because it method is not found, has no access or invocation failed + */ + public static <T> T callInaccessibleMethod(String methodName, Class<T> returnType, Object target) { + return callInaccessibleMethod(methodName, returnType, target, new Class[] {}, new Object[] {}); + } + + /** + * Calls a private method that has one parameter like a setter method + * + * @param methodName Name of the method + * @param target Target instance + * @param paramsType Parameter Type which cannot be null + * @param param Parameter Value + * @return Object that is returned from the method call + * + * @throws UnsupportedOperationException If the call failed because it method is not found, has no access or invocation failed + */ + public static <T> T callInaccessibleMethod(String methodName, Class<T> returnType, Object target, Class paramsType, Object param) { + return callInaccessibleMethod(methodName, returnType, target, new Class[] {paramsType}, new Object[] {param}); + } + + /** + * Calls a private method that has none or one parameter like a setter method + * + * ATTENTION: If parameter types of values is null then both are set to null. Also the length of the arrays must + * be the same + * + * @param methodName Name of the method + * @param target Target instance + * @param parameterTypes Parameter Types which must not be null + * @param parameters Parameter Values which must not be null + * @return Object that is returned from the method call + * + * @throws IllegalArgumentException If the parameter types and values do not match + * @throws UnsupportedOperationException If the call failed because it method is not found, has no access or invocation failed + */ + public static <T> T callInaccessibleMethod(String methodName, Class<T> returnType, Object target, Class[] parameterTypes, Object[] parameters) { + if(parameterTypes != null && parameters != null) { + if(parameters.length != parameterTypes.length) { throw new IllegalArgumentException("Number of Parameter Types and Values were not the same"); } + } else { + throw new IllegalArgumentException("Parameter Type and Value Array cannot be null"); + } + try { + return getInaccessibleMethod(methodName, returnType, target, parameterTypes).call(parameters); ++>>>>>>> master } catch (IllegalAccessException e) { throw new UnsupportedOperationException("Failed to access method: " + methodName, e); } catch (InvocationTargetException e) { @@@ -212,26 -240,85 +279,108 @@@ } } ++<<<<<<< HEAD + public static void setInaccessibleField(String fieldName, Object target, Object fieldValue) throws NoSuchMethodException { + try { + Field field = target.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, fieldValue); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException("Failed to access field: " + fieldName, e); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } + + public static void setupStringInterpolationProvider( + StringInterpolationProvider provider, StringInterpolationProviderConfiguration configuration, BundleContext bundleContext, final String[] placeholderValues + ) throws NoSuchMethodException { + when(configuration.place_holder_key_value_pairs()).thenReturn(placeholderValues); + callInaccessibleMethod("activate", provider, + new Class[] {BundleContext.class, StringInterpolationProviderConfiguration.class}, + new Object[] {bundleContext, configuration} + ); ++======= + public static <T> MethodWrapper<T> getInaccessibleMethod(String methodName, Class<T> returnType, Object target, Class...parameterTypes) { + return new MethodWrapper(methodName, returnType, target, parameterTypes); + } + + public static class MethodWrapper<T> { + private Method method; + private Object target; + + public MethodWrapper(String methodName, Class<T> returnType, Object target, Class[] parameterTypes) { + try { + this.method = target.getClass().getDeclaredMethod(methodName, parameterTypes); + this.method.setAccessible(true); + this.target = target; + if(returnType == null && !this.method.getReturnType().equals(Void.TYPE)) { + throw new IllegalArgumentException("Return Type is null but method does not return Void but: " + this.method.getReturnType()); + } + if(returnType != null && !returnType.isAssignableFrom(this.method.getReturnType())) { + throw new IllegalArgumentException("Return Type is not assignable to: " + returnType + ", it returns this: " + this.method.getReturnType()); + } + } catch (NoSuchMethodException e) { + throw new UnsupportedOperationException("Failed to find method: " + methodName, e); + } + } + + public T call(Object...parameters) throws InvocationTargetException, IllegalAccessException { + return (T) method.invoke(target, parameters); + } + } + + /** + * Sets the value of a private field + * + * @param fieldName Name of the field to be set + * @param target Target instance + * @param fieldValue Value to be set + * + * @throws UnsupportedOperationException If the call failed because it field is not found or has no access + */ + public static void setInaccessibleField(String fieldName, Object target, Object fieldValue) throws NoSuchMethodException { + try { + getInaccessibleFieldWrapper(fieldName, target, Object.class).set(fieldValue); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException("Failed to access field: " + fieldName, e); + } + } + + public static <T> T getInaccessibleField(String fieldName, Object target, Class<T> type) { + try { + return getInaccessibleFieldWrapper(fieldName, target, type).get(); + } catch (IllegalAccessException e) { + throw new UnsupportedOperationException("Failed to access field: " + fieldName, e); + } + } + + public static <T> FieldWrapper<T> getInaccessibleFieldWrapper(String fieldName, Object target, Class<T> type) { + try { + return new FieldWrapper(fieldName, target, type); + } catch (NoSuchFieldException e) { + throw new UnsupportedOperationException("Failed to find field: " + fieldName, e); + } + } + + public static class FieldWrapper<T> { + private Field field; + private Object target; + + public FieldWrapper(String fieldName, Object target, Class<T> type) throws NoSuchFieldException { + this.field = target.getClass().getDeclaredField(fieldName); + this.field.setAccessible(true); + this.target = target; + } + + public void set(T parameter) throws IllegalAccessException { + field.set(target, parameter); + } + + public T get() throws IllegalAccessException { + return (T) field.get(target); + } ++>>>>>>> master } /** @@@ -243,6 -330,9 +392,12 @@@ public List<Resource> getChildrenList(); } ++<<<<<<< HEAD ++======= + /** + * Defines the Result of the Etc Mapping for easy testing + */ ++>>>>>>> master public static class ExpectedEtcMapping { List<ExpectedEtcMapEntry> expectedEtcMapEntries = new ArrayList<>();
