blob: cf54a3b88c18ce5d2bb5d1b506a06d39ae3019ec [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 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;
}
}
}