| /* |
| * 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.resourceresolver.impl.mapping; |
| |
| import static org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration; |
| import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider; |
| import static org.junit.Assert.fail; |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Matchers.anyMap; |
| import static org.mockito.Matchers.anyString; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| import static org.mockito.Mockito.withSettings; |
| |
| import java.io.File; |
| import java.lang.reflect.Field; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.Semaphore; |
| |
| import org.apache.sling.api.resource.Resource; |
| import org.apache.sling.api.resource.ResourceMetadata; |
| 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.path.Path; |
| import org.apache.sling.api.wrappers.ValueMapDecorator; |
| import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.mockito.Mock; |
| 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; |
| |
| /** |
| * These are tests that are testing the Sling Interpolation Feature (SLING-7768) |
| * on the MapEntries level |
| */ |
| public abstract class AbstractMappingMapEntriesTest { |
| static final String PROP_REG_EXP = "sling:match"; |
| |
| @Mock |
| MapConfigurationProvider resourceResolverFactory; |
| |
| @Mock |
| BundleContext bundleContext; |
| |
| @Mock |
| Bundle bundle; |
| |
| @Mock |
| EventAdmin eventAdmin; |
| |
| @Mock |
| ResourceResolver resourceResolver; |
| |
| StringInterpolationProviderConfiguration stringInterpolationProviderConfiguration; |
| |
| StringInterpolationProviderImpl stringInterpolationProvider = new StringInterpolationProviderImpl(); |
| MapEntries mapEntries; |
| |
| File vanityBloomFilterFile; |
| |
| Resource map; |
| Resource http; |
| |
| Map<String, Map<String, String>> aliasMap; |
| |
| @SuppressWarnings({"unchecked"}) |
| @Before |
| public void setup() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| |
| List<MapConfigurationProvider.VanityPathConfig> configs = getVanityPathConfigs(); |
| vanityBloomFilterFile = new File("target/test-classes/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.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()); |
| |
| map = setupEtcMapResource("/etc", "map"); |
| http = setupEtcMapResource("http", map); |
| |
| stringInterpolationProviderConfiguration = createStringInterpolationProviderConfiguration(); |
| setupStringInterpolationProvider(stringInterpolationProvider, stringInterpolationProviderConfiguration, new String[] {}); |
| mapEntries = new MapEntries(resourceResolverFactory, bundleContext, eventAdmin, stringInterpolationProvider); |
| |
| final Field aliasMapField = MapEntries.class.getDeclaredField("aliasMap"); |
| aliasMapField.setAccessible(true); |
| this.aliasMap = ( Map<String, Map<String, String>>) aliasMapField.get(mapEntries); |
| } |
| |
| List<MapConfigurationProvider.VanityPathConfig> getVanityPathConfigs() { |
| return new ArrayList<>(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| vanityBloomFilterFile.delete(); |
| } |
| |
| |
| // -------------------------- private methods ---------- |
| |
| ValueMap buildValueMap(Object... string) { |
| final Map<String, Object> data = new HashMap<>(); |
| for (int i = 0; i < string.length; i = i + 2) { |
| data.put((String) string[i], string[i+1]); |
| } |
| return new ValueMapDecorator(data); |
| } |
| |
| Resource getVanityPathResource(final String path) { |
| Resource rsrc = mock(Resource.class); |
| when(rsrc.getPath()).thenReturn(path); |
| when(rsrc.getName()).thenReturn(ResourceUtil.getName(path)); |
| when(rsrc.getValueMap()).thenReturn(buildValueMap("sling:vanityPath", "/vanity" + path)); |
| return rsrc; |
| } |
| |
| Resource setupEtcMapResource(String parentPath, String name, String...valueMapPairs) { |
| return setupEtcMapResource0(parentPath, name, null, valueMapPairs); |
| } |
| Resource setupEtcMapResource(String name, Resource parent, String...valueMapPairs) { |
| return setupEtcMapResource0(null, name, parent, valueMapPairs); |
| } |
| private Resource setupEtcMapResource0(String parentPath, String name, Resource parent, String...valueMapPairs) { |
| Resource resource = mock(Resource.class, withSettings().name(name).extraInterfaces(ResourceDecorator.class)); |
| String path = (parent == null ? parentPath : parent.getPath()) + "/" + name; |
| when(resource.getPath()).thenReturn(path); |
| when(resource.getName()).thenReturn(name); |
| ValueMap valueMap = buildValueMap(valueMapPairs); |
| when(resource.getValueMap()).thenReturn(valueMap); |
| when(resource.adaptTo(ValueMap.class)).thenReturn(valueMap); |
| when(resourceResolver.getResource(resource.getPath())).thenReturn(resource); |
| if(parent != null) { |
| List<Resource> childList = ((ResourceDecorator) parent).getChildrenList(); |
| childList.add(resource); |
| } |
| final List<Resource> childrenList = new ArrayList<>(); |
| when(((ResourceDecorator) resource).getChildrenList()).thenReturn(childrenList); |
| // Delay the children list iterator to make sure all children are added beforehand |
| // Iterators have a modCount that is set when created. Any changes to the underlying list will |
| // change that modCount and the usage of the iterator will fail due to Concurrent Modification Exception |
| when(resource.listChildren()).thenAnswer(new Answer<Iterator<Resource>>() { |
| @Override |
| public Iterator<Resource> answer(InvocationOnMock invocation) throws Throwable { |
| return childrenList.iterator(); |
| } |
| }); |
| ResourceMetadata resourceMetadata = mock(ResourceMetadata.class); |
| when(resource.getResourceMetadata()).thenReturn(resourceMetadata); |
| doNothing().when(resourceMetadata).setResolutionPath(anyString()); |
| doNothing().when(resourceMetadata).setParameterMap(anyMap()); |
| |
| return resource; |
| } |
| |
| DataFuture createDataFuture(ExecutorService pool, final MapEntries mapEntries) { |
| |
| Future<Iterator<?>> future = pool.submit(new Callable<Iterator<?>>() { |
| @Override |
| public Iterator<MapEntry> call() throws Exception { |
| return mapEntries.getResolveMapsIterator("http/localhost.8080/target/justVanityPath"); |
| } |
| }); |
| return new DataFuture(future); |
| } |
| |
| void simulateSomewhatSlowSessionOperation(final Semaphore sessionLock) throws InterruptedException { |
| if (!sessionLock.tryAcquire()) { |
| fail("concurrent session access detected"); |
| } |
| try{ |
| Thread.sleep(1); |
| } finally { |
| sessionLock.release(); |
| } |
| } |
| |
| /** |
| * Iterator to piggyback the list of Resources onto a Resource Mock |
| * so that we can add children to them and create the iterators after |
| * everything is setup |
| */ |
| static interface ResourceDecorator { |
| public List<Resource> getChildrenList(); |
| } |
| |
| static class DataFuture { |
| public Future<Iterator<?>> future; |
| |
| public DataFuture(Future<Iterator<?>> future) { |
| super(); |
| this.future = future; |
| } |
| } |
| |
| } |