/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gwc.layer;
import static org.geoserver.gwc.GWC.tileLayerName;
import static org.geoserver.gwc.GWCTestHelpers.mockGroup;
import static org.geoserver.gwc.GWCTestHelpers.mockLayer;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
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.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import com.google.common.collect.Iterators;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.PublishedType;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.WorkspaceInfoImpl;
import org.geoserver.gwc.GWC;
import org.geoserver.gwc.config.GWCConfig;
import org.geoserver.ows.LocalWorkspace;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.layer.TileLayer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Test class for the GWCCatalogListener
*
* @author groldan
*/
public class CatalogConfigurationTest {
private Catalog catalog;
private TileLayerCatalog tileLayerCatalog;
private GridSetBroker gridSetBroker;
private CatalogConfiguration config;
private LayerInfo layer1, layer2, layerWithNoTileLayer;
private LayerGroupInfo group1, group2, groupWithNoTileLayer;
private GeoServerTileLayerInfo layerInfo1, layerInfo2, groupInfo1, groupInfo2;
private GWC mockMediator;
private GWCConfig defaults;
@Before
public void setUp() throws Exception {
defaults = GWCConfig.getOldDefaults();
defaults.getDefaultVectorCacheFormats().clear();
defaults.getDefaultVectorCacheFormats().add("image/png8");
defaults.getDefaultOtherCacheFormats().clear();
defaults.getDefaultOtherCacheFormats().add("image/jpeg");
defaults.setCacheLayersByDefault(false);
defaults.setCacheNonDefaultStyles(true);
layer1 = mockLayer("layer1", "test", new String[]{}, PublishedType.RASTER);
layer2 = mockLayer("layer2", "test", new String[]{}, PublishedType.RASTER);
layerWithNoTileLayer = mockLayer("layerWithNoTileLayer", new String[]{}, PublishedType.RASTER);
group1 = mockGroup("group1", layer1, layer2);
group2 = mockGroup("group2", layer2, layer1);
groupWithNoTileLayer = mockGroup("groupWithNoTileLayer", layerWithNoTileLayer, layer1,
layer2);
// set tile layer settings to layer1, layer2, group1, and group2
layerInfo1 = TileLayerInfoUtil.loadOrCreate(layer1, defaults);
layerInfo1.setMetaTilingX(1);
layerInfo1.setMetaTilingY(1);
layerInfo2 = TileLayerInfoUtil.loadOrCreate(layer2, defaults);
layerInfo2.setMetaTilingX(2);
layerInfo2.setMetaTilingY(2);
groupInfo1 = TileLayerInfoUtil.loadOrCreate(group1, defaults);
groupInfo1.setMetaTilingX(3);
groupInfo1.setMetaTilingY(3);
groupInfo2 = TileLayerInfoUtil.loadOrCreate(group2, defaults);
groupInfo2.setMetaTilingX(4);
groupInfo2.setMetaTilingY(4);
catalog = mock(Catalog.class);
// catalog returns the three layers, two with tile layer and one without
when(catalog.getLayers())
.thenReturn(ImmutableList.of(layer1, layerWithNoTileLayer, layer2));
when(catalog.getLayer(layer1.getId())).thenReturn(layer1);
when(catalog.getLayer(layer2.getId())).thenReturn(layer2);
when(catalog.getLayerGroup(group1.getId())).thenReturn(group1);
when(catalog.getLayerGroup(group2.getId())).thenReturn(group2);
// catalog returns the three layer groups, two with tile layer and one without
when(catalog.getLayerGroups()).thenReturn(
ImmutableList.of(group1, groupWithNoTileLayer, group2));
when(catalog.getLayerByName(eq(tileLayerName(layer1)))).thenReturn(layer1);
when(catalog.getLayerByName(eq(tileLayerName(layer2)))).thenReturn(layer2);
when(catalog.getLayerByName(eq(tileLayerName(layerWithNoTileLayer)))).thenReturn(
layerWithNoTileLayer);
when(catalog.getLayerGroupByName(eq(tileLayerName(group1)))).thenReturn(group1);
when(catalog.getLayerGroupByName(eq(tileLayerName(group2)))).thenReturn(group2);
when(catalog.getLayerGroupByName(eq(tileLayerName(groupWithNoTileLayer)))).thenReturn(
groupWithNoTileLayer);
gridSetBroker = new GridSetBroker(true, true);
Set<String> layerNames = ImmutableSet.of(tileLayerName(layer1), tileLayerName(layer2),
tileLayerName(group1), tileLayerName(group2));
tileLayerCatalog = mock(TileLayerCatalog.class);
when(tileLayerCatalog.getLayerIds()).thenReturn(
ImmutableSet.of(layer1.getId(), layer2.getId(), group1.getId(), group2.getId()));
when(tileLayerCatalog.getLayerNames()).thenReturn(layerNames);
when(tileLayerCatalog.getLayerById(layer1.getId())).thenReturn(layerInfo1);
when(tileLayerCatalog.getLayerById(layer2.getId())).thenReturn(layerInfo2);
when(tileLayerCatalog.getLayerById(group1.getId())).thenReturn(groupInfo1);
when(tileLayerCatalog.getLayerById(group2.getId())).thenReturn(groupInfo2);
when(tileLayerCatalog.exists(layer1.getId())).thenReturn(true);
when(tileLayerCatalog.exists(layer2.getId())).thenReturn(true);
when(tileLayerCatalog.exists(group1.getId())).thenReturn(true);
when(tileLayerCatalog.exists(group2.getId())).thenReturn(true);
when(tileLayerCatalog.getLayerByName(tileLayerName(layer1))).thenReturn(layerInfo1);
when(tileLayerCatalog.getLayerByName(tileLayerName(layer2))).thenReturn(layerInfo2);
when(tileLayerCatalog.getLayerByName(tileLayerName(group1))).thenReturn(groupInfo1);
when(tileLayerCatalog.getLayerByName(tileLayerName(group2))).thenReturn(groupInfo2);
when(tileLayerCatalog.getLayerId(tileLayerName(layer1))).thenReturn(layer1.getId());
when(tileLayerCatalog.getLayerId(tileLayerName(layer2))).thenReturn(layer2.getId());
when(tileLayerCatalog.getLayerId(tileLayerName(group1))).thenReturn(group1.getId());
when(tileLayerCatalog.getLayerId(tileLayerName(group2))).thenReturn(group2.getId());
config = new CatalogConfiguration(catalog, tileLayerCatalog, gridSetBroker);
mockMediator = mock(GWC.class);
GWC.set(mockMediator);
when(mockMediator.getConfig()).thenReturn(defaults);
when(mockMediator.getCatalog()).thenReturn(catalog);
}
@After
public void tearDown() {
GWC.set(null);
}
@Test public void testGoofyMethods() {
assertEquals("GeoServer Catalog Configuration", config.getIdentifier());
assertNull(config.getServiceInformation());
assertTrue(config.isRuntimeStatsEnabled());
}
@Test public void testInitialize() {
assertEquals(4, config.initialize(gridSetBroker));
}
@Test public void testGetTileLayerCount() {
assertEquals(4, config.getTileLayerCount());
}
@Test public void testGetTileLayerNames() {
Set<String> expected = ImmutableSet.of(tileLayerName(layer1), tileLayerName(layer2),
tileLayerName(group1), tileLayerName(group2));
Set<String> actual = config.getTileLayerNames();
assertEquals(expected, actual);
}
@Test public void testGetLayers() {
Iterable<GeoServerTileLayer> layers = config.getLayers();
testGetLayers(layers);
}
@Test public void testDeprecatedGetTileLayers() {
@SuppressWarnings("deprecation")
List<GeoServerTileLayer> layers = config.getTileLayers();
testGetLayers(layers);
}
private void testGetLayers(Iterable<GeoServerTileLayer> layers) {
assertEquals(3, catalog.getLayers().size());
assertEquals(3, catalog.getLayerGroups().size());
assertEquals(4, Iterables.size(layers));
Set<GeoServerTileLayerInfo> expected = ImmutableSet.of(layerInfo1, layerInfo2, groupInfo1,
groupInfo2);
Set<GeoServerTileLayerInfo> actual = new HashSet<GeoServerTileLayerInfo>();
for (GeoServerTileLayer layer : layers) {
actual.add(layer.getInfo());
}
assertEquals(4, actual.size());
assertEquals(expected, actual);
}
@Test public void testGetTileLayer() {
String layerName = tileLayerName(layerWithNoTileLayer);
assertNull(config.getTileLayer(layerName));
assertNull(config.getTileLayer(tileLayerName(groupWithNoTileLayer)));
assertNotNull(config.getTileLayer(tileLayerName(layer1)));
assertNotNull(config.getTileLayer(tileLayerName(layer2)));
assertNotNull(config.getTileLayer(tileLayerName(group1)));
assertNotNull(config.getTileLayer(tileLayerName(group2)));
assertNull(config.getTileLayer("anythingElse"));
}
@Test public void testModifyLayer() {
try {
config.modifyLayer(null);
fail("expected precondition exception");
} catch (RuntimeException e) {
assertTrue(e.getMessage().contains("is null"));
}
try {
config.modifyLayer(mock(TileLayer.class));
fail("expected precondition exception");
} catch (RuntimeException e) {
assertTrue(e.getMessage().contains("Can't save TileLayer of type"));
}
GeoServerTileLayer tileLayer1 = config.getTileLayer(tileLayerName(layer1));
GeoServerTileLayer tileLayer2 = config.getTileLayer(tileLayerName(group1));
testModifyLayer(tileLayer1);
testModifyLayer(tileLayer2);
}
private void testModifyLayer(GeoServerTileLayer orig) {
GeoServerTileLayerInfo newState = TileLayerInfoUtil.create(defaults);
newState.setId(orig.getInfo().getId());
newState.setName(orig.getInfo().getName());
assertFalse(orig.equals(newState));
final GeoServerTileLayer modified;
if (orig.getLayerInfo() != null) {
modified = new GeoServerTileLayer(orig.getLayerInfo(), gridSetBroker, newState);
} else {
modified = new GeoServerTileLayer(orig.getLayerGroupInfo(), gridSetBroker, newState);
}
assertEquals(orig.getInfo(), config.getTileLayer(orig.getName()).getInfo());
config.modifyLayer(modified);
assertEquals(newState, config.getTileLayer(orig.getName()).getInfo());
final String origName = orig.getName();
modified.getInfo().setName("changed");
config.modifyLayer(modified);
assertNull(config.getTileLayer(origName));
assertFalse(config.getTileLayerNames().contains(origName));
}
@Test public void testRemoveLayer() {
try {
config.removeLayer(null);
fail("expected precondition violation exception");
} catch (RuntimeException e) {
assertTrue(true);
}
assertFalse(config.removeLayer(GWC.tileLayerName(layerWithNoTileLayer)));
assertFalse(config.removeLayer(GWC.tileLayerName(groupWithNoTileLayer)));
String layerName;
layerName = tileLayerName(layer1);
assertNotNull(config.getTileLayer(layerName));
final int initialCount = config.getTileLayerCount();
assertTrue(config.removeLayer(layerName));
assertNull(config.getTileLayer(layerName));
assertFalse(config.getTileLayerNames().contains(layerName));
assertEquals(initialCount - 1, config.getTileLayerCount());
layerName = GWC.tileLayerName(group1);
assertNotNull(config.getTileLayer(layerName));
assertTrue(config.removeLayer(layerName));
assertNull(config.getTileLayer(layerName));
assertEquals(initialCount - 2, config.getTileLayerCount());
}
@Test public void testSaveRename() {
GeoServerTileLayerInfo originalState = layerInfo1;
GeoServerTileLayerInfo forceState1 = TileLayerInfoUtil.loadOrCreate(layer1, defaults);
when(tileLayerCatalog.save(same(forceState1))).thenReturn(originalState);
forceState1.setName("newName");
config.modifyLayer(new GeoServerTileLayer(layer1, gridSetBroker, forceState1));
verify(mockMediator, never()).layerRemoved(anyString());
verify(mockMediator, never()).layerRenamed(anyString(), anyString());
config.save();
verify(tileLayerCatalog, times(1)).save(same(forceState1));
// and gwc has been instructed on the changes
verify(mockMediator, times(1)).layerRenamed(eq(layerInfo1.getName()), eq("newName"));
}
@Test public void testSave() {
// add a pending delete
when(tileLayerCatalog.delete(eq(layerInfo2.getId()))).thenReturn(layerInfo2);
assertTrue(config.removeLayer(layerInfo2.getName()));
// and a failed one at save
when(tileLayerCatalog.delete(eq(groupInfo1.getId()))).thenReturn(groupInfo1);
assertTrue(config.removeLayer(groupInfo1.getName()));
doThrow(new IllegalArgumentException("failedDelete")).when(tileLayerCatalog).delete(
eq(group1.getId()));
// add two pending modifications
GeoServerTileLayerInfo forceState1 = TileLayerInfoUtil.loadOrCreate(layer1, defaults);
forceState1.setName("newName");
GeoServerTileLayerInfo forceState2 = TileLayerInfoUtil.loadOrCreate(group2, defaults);
when(tileLayerCatalog.save(same(forceState1))).thenReturn(layerInfo1);
config.modifyLayer(new GeoServerTileLayer(layer1, gridSetBroker, forceState1));
config.modifyLayer(new GeoServerTileLayer(group2, gridSetBroker, forceState2));
// make this last one fail
doThrow(new IllegalArgumentException("failedSave")).when(tileLayerCatalog).save(
eq(forceState2));
verify(mockMediator, times(1)).layerRemoved(layerInfo2.getName());
verify(mockMediator, never()).layerRenamed(anyString(), anyString());
GeoServerTileLayerInfo addedState1 = TileLayerInfoUtil.loadOrCreate(layerWithNoTileLayer,
defaults);
config.addLayer(new GeoServerTileLayer(layerWithNoTileLayer, gridSetBroker, addedState1));
doThrow(new IllegalArgumentException("callback exception")).when(mockMediator).layerAdded(
eq(addedState1.getName()));
GeoServerTileLayerInfo addedState2 = TileLayerInfoUtil.loadOrCreate(groupWithNoTileLayer,
defaults);
config.addLayer(new GeoServerTileLayer(groupWithNoTileLayer, gridSetBroker, addedState2));
config.save();
verify(tileLayerCatalog, times(1)).delete(eq(group1.getId()));
verify(tileLayerCatalog, times(1)).delete(eq(layer2.getId()));
verify(tileLayerCatalog, times(1)).save(same(forceState1));
verify(tileLayerCatalog, times(1)).save(same(forceState2));
verify(tileLayerCatalog, times(1)).save(same(addedState1));
verify(tileLayerCatalog, times(1)).save(same(addedState2));
// and gwc has been instructed on the changes
verify(mockMediator, times(1)).layerRemoved(eq(layerInfo2.getName()));
verify(mockMediator, times(1)).layerRenamed(eq(layerInfo1.getName()), eq("newName"));
verify(mockMediator, times(1)).layerAdded(eq(addedState1.getName()));
verify(mockMediator, times(1)).layerAdded(eq(addedState2.getName()));
}
@Test public void testCanSave() {
// Create mock layer not transient and ensure that the Layer cannot be saved
GeoServerTileLayer l = mock(GeoServerTileLayer.class);
when(l.isTransientLayer()).thenReturn(true);
assertFalse(config.canSave(l));
}
@Test public void testNoGeometry() throws Exception {
org.opengis.feature.type.FeatureType featureTypeWithNoGeometry = mock(org.opengis.feature.type.FeatureType.class);
when(featureTypeWithNoGeometry.getGeometryDescriptor()).thenReturn(null);
org.geoserver.catalog.FeatureTypeInfo resourceWithNoGeometry = mock(org.geoserver.catalog.FeatureTypeInfo.class);
when(resourceWithNoGeometry.getFeatureType()).thenReturn(featureTypeWithNoGeometry);
LayerInfo layerWithNoGeometry = mockLayer("layerWithNoGeometry", new String[]{}, PublishedType.VECTOR);
layerWithNoGeometry.setResource(resourceWithNoGeometry);
GeoServerTileLayer tl = mock(GeoServerTileLayer.class);
GeoServerTileLayerInfo info = new GeoServerTileLayerInfoImpl();
info.setId("layerWithNoGeometry");
info.setName("layerWithNoGeometry");
when(tl.getId()).thenReturn("layerWithNoGeometry");
when(tl.isTransientLayer()).thenReturn(false);
when(tl.getInfo()).thenReturn(info);
when(tl.getLayerInfo()).thenReturn(layerWithNoGeometry);
when(catalog.getLayer(layerWithNoGeometry.getId())).thenReturn(layerWithNoGeometry);
when(catalog.getLayerByName(eq(tileLayerName(layerWithNoGeometry)))).thenReturn(
layerWithNoGeometry);
config.addLayer(tl);
config.save();
verify(this.tileLayerCatalog, never()).save(info);
}
@Test
public void testConfigurationDeadlock() throws Exception {
// to make it reproducible with some reliability on my machine
// 100000 loops need to be attempted. With the fix it works, but runs for
// a minute and a half, so not suitable for actual builds.
// For in-build tests I've thus settled down for 1000 loops instead
final int LOOPS = 1000;
ExecutorService service = Executors.newFixedThreadPool(8);
Runnable reloader = new Runnable() {
@Override
public void run() {
config.initialize(gridSetBroker);
}
};
Runnable tileLayerFetcher = new Runnable() {
@Override
public void run() {
config.getTileLayer(layer1.getName());
config.getTileLayer(layer2.getName());
config.getTileLayer(group1.getName());
config.getTileLayer(group2.getName());
}
};
try {
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < LOOPS; i++) {
futures.add(service.submit(reloader));
futures.add(service.submit(tileLayerFetcher));
}
for (Future<?> future : futures) {
future.get();
}
} finally {
service.shutdown();
}
}
@Test
public void getLayerByIdWithLocalWorkspace() {
try {
// create test workspaces
WorkspaceInfo testWorkspace = new WorkspaceInfoImpl();
testWorkspace.setName("test");
WorkspaceInfo otherWorkspace = new WorkspaceInfoImpl();
otherWorkspace.setName("other");
// setting the local workspace equal to layers workspace
LocalWorkspace.set(testWorkspace);
assertThat(config.getTileLayerById(layer1.getId()), notNullValue());
assertThat(config.getTileLayerById(layer2.getId()), notNullValue());
// setting the local workspace different from layers workspaces
LocalWorkspace.set(otherWorkspace);
assertThat(config.getTileLayerById(layer1.getId()), nullValue());
assertThat(config.getTileLayerById(layer2.getId()), nullValue());
} finally {
// cleaning
LocalWorkspace.set(null);
}
}
@Test
public void getLayersWithLocalWorkspace() {
try {
// create test workspaces
WorkspaceInfo testWorkspace = new WorkspaceInfoImpl();
testWorkspace.setName("test");
WorkspaceInfo otherWorkspace = new WorkspaceInfoImpl();
otherWorkspace.setName("other");
// setting the local workspace equal to layers workspace
LocalWorkspace.set(testWorkspace);
assertThat(Iterators.size(config.getLayers().iterator()), is(2));
// setting the local workspace different from layers workspaces
LocalWorkspace.set(otherWorkspace);
assertThat(Iterators.size(config.getLayers().iterator()), is(0));
} finally {
// cleaning
LocalWorkspace.set(null);
}
}
}