/* Copyright (c) 2010 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.gwc.layer;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Logger;
import javax.servlet.http.Cookie;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.gwc.GWC;
import org.geoserver.gwc.config.GWCConfig;
import org.geoserver.gwc.config.GWCConfigPersister;
import org.geoserver.ows.Response;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.platform.Operation;
import org.geoserver.platform.Service;
import org.geoserver.wms.GetMapRequest;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WebMap;
import org.geotools.util.logging.Logging;
import org.geowebcache.GeoWebCacheException;
import org.geowebcache.config.Configuration;
import org.geowebcache.config.meta.ServiceInformation;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.io.Resource;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.mime.MimeType;
import org.springframework.util.Assert;
/**
* A GWC's {@link Configuration} implementation that provides {@link TileLayer}s directly from the
* GeoServer {@link Catalog}'s {@link LayerInfo}s and {@link LayerGroupInfo}s.
* <p>
* The sole responsibility of the class is to
*
* @see #createLayer(LayerInfo)
* @see #createLayer(LayerGroupInfo)
* @see #getTileLayers(boolean)
* @see CatalogStyleChangeListener
*/
public class CatalogConfiguration implements Configuration {
private static Logger log = Logging.getLogger(CatalogConfiguration.class);
private static Map<String, Response> cachedTileEncoders = new HashMap<String, Response>();
private Catalog catalog;
private CatalogLayerEventListener catalogLayerEventListener;
private CatalogStyleChangeListener catalogStyleChangeListener;
private final GWCConfigPersister configProvider;
private final GridSetBroker gridSetBroker;
private final WMS wmsFacade;
/**
*
* @param mediator
*/
public CatalogConfiguration(final Catalog catalog, final GWCConfigPersister configProvider,
final GridSetBroker gridSetBroker, final WMS wmsFacade) {
Assert.notNull(catalog);
Assert.notNull(configProvider);
Assert.notNull(gridSetBroker);
this.catalog = catalog;
this.configProvider = configProvider;
this.gridSetBroker = gridSetBroker;
this.wmsFacade = wmsFacade;
this.catalogLayerEventListener = new CatalogLayerEventListener(this);
this.catalogStyleChangeListener = new CatalogStyleChangeListener(this);
catalog.addListener(catalogLayerEventListener);
catalog.addListener(catalogStyleChangeListener);
}
/**
*
* @see org.geowebcache.config.Configuration#getIdentifier()
*/
public String getIdentifier() {
return "GeoServer Catalog Configuration";
}
private GWC getGWC() {
return GWC.get();
}
/**
* @see org.geowebcache.config.Configuration#getServiceInformation()
* @return {@code null}
*/
public ServiceInformation getServiceInformation() {
return null;
}
/**
* @see org.geowebcache.config.Configuration#isRuntimeStatsEnabled()
*/
public boolean isRuntimeStatsEnabled() {
return true;
}
/**
* Returns the list of {@link GeoServerTileLayer} objects matching the GeoServer ones.
* <p>
* The list is built dynamically on each call.
* </p>
*
* @see org.geowebcache.config.Configuration#getTileLayers(boolean)
* @see #createLayer(LayerInfo)
* @see #createLayer(LayerGroupInfo)
* @see org.geowebcache.config.Configuration#getTileLayers()
*/
public List<GeoServerTileLayer> getTileLayers() {
List<LayerGroupInfo> layerGroups = catalog.getLayerGroups();
List<LayerInfo> layerInfos = catalog.getLayers();
List[] sublists = { layerInfos, layerGroups };
CompositeList composite = new CompositeList(sublists);
LazyGeoServerTileLayerList tileLayers = new LazyGeoServerTileLayerList(composite, this);
return tileLayers;
}
/**
* Returns a dynamic list of cached layer names out of the GeoServer {@link Catalog}
*
* @see org.geowebcache.config.Configuration#getTileLayerNames()
*/
public Set<String> getTileLayerNames() {
Set<String> names = new HashSet<String>();
for (LayerGroupInfo lgi : catalog.getLayerGroups()) {
names.add(lgi.getName());
}
for (LayerInfo li : catalog.getLayers()) {
names.add(li.getResource().getPrefixedName());
}
return names;
}
/**
* @see org.geowebcache.config.Configuration#getTileLayer(java.lang.String)
*/
public GeoServerTileLayer getTileLayer(final String layerName) {
// System.err.println("Returning new GeoServerTileLayer " + layerName);
// return layers.get(layerName);
LayerInfo layerInfo = catalog.getLayerByName(layerName);
if (layerInfo != null) {
return new GeoServerTileLayer(this, layerInfo);
}
LayerGroupInfo lgi = catalog.getLayerGroupByName(layerName);
if (lgi != null) {
return new GeoServerTileLayer(this, lgi);
}
return null;
}
/**
* @see org.geowebcache.config.Configuration#getTileLayerCount()
*/
public int getTileLayerCount() {
List<LayerGroupInfo> layerGroups = catalog.getLayerGroups();
List<LayerInfo> layerInfos = catalog.getLayers();
int count = layerGroups.size() + layerInfos.size();
return count;
}
/**
* @see org.geowebcache.config.Configuration#remove(java.lang.String)
*/
public boolean remove(String layerName) {
// nothing to remove, we're lazy, but let TileLayerDispatcher continue trying with the other
// configurations is may hold
return false;
}
private static class CompositeList extends AbstractList<Object> {
private final List<Object>[] decorated;
@SuppressWarnings("unchecked")
public CompositeList(List[] sublists) {
this.decorated = sublists;
}
@Override
public Object get(final int index) {
int subIndex = index;
List<Object> sublist;
for (int i = 0; i < decorated.length; i++) {
sublist = decorated[i];
if (subIndex < sublist.size()) {
return sublist.get(subIndex);
}
subIndex -= sublist.size();
}
throw new IndexOutOfBoundsException();
}
@Override
public int size() {
int size = 0;
List<Object> sublist;
for (int i = 0; i < decorated.length; i++) {
sublist = decorated[i];
size += sublist.size();
}
return size;
}
}
private static class LazyGeoServerTileLayerList extends AbstractList<GeoServerTileLayer> {
private final List<Object> infos;
private final CatalogConfiguration mediator;
public LazyGeoServerTileLayerList(final List<Object> infos,
final CatalogConfiguration catalogConfiguration) {
this.infos = infos;
this.mediator = catalogConfiguration;
}
@Override
public GeoServerTileLayer get(int index) {
Object object = infos.get(index);
if (object instanceof LayerInfo) {
return new GeoServerTileLayer(mediator, (LayerInfo) object);
} else if (object instanceof LayerGroupInfo) {
return new GeoServerTileLayer(mediator, (LayerGroupInfo) object);
}
throw new IllegalStateException();
}
@Override
public int size() {
return infos.size();
}
}
public GWCConfig getConfig() {
return configProvider.getConfig();
}
public GridSetBroker getGridSetBroker() {
return gridSetBroker;
}
public void save(GeoServerTileLayer layer) {
log.info("Saving " + layer.getName());
MetadataMap metadata;
LayerInfo layerInfo = layer.getLayerInfo();
LayerGroupInfo layerGroupInfo = layer.getLayerGroupInfo();
if (layerInfo == null) {
metadata = layerGroupInfo.getMetadata();
} else {
metadata = layerInfo.getMetadata();
}
GeoServerTileLayerInfo tileLayerInfo = layer.getInfo();
tileLayerInfo.saveTo(metadata);
if (layerInfo != null) {
catalog.save(layerInfo);
} else {
catalog.save(layerGroupInfo);
}
}
public boolean isQueryable(final GeoServerTileLayer geoServerTileLayer) {
LayerInfo layerInfo = geoServerTileLayer.getLayerInfo();
if (layerInfo != null) {
return wmsFacade.isQueryable(layerInfo);
}
LayerGroupInfo lgi = geoServerTileLayer.getLayerGroupInfo();
return wmsFacade.isQueryable(lgi);
}
/**
* @param cookies
* @see GWC#dispatchOwsRequest(Map, Cookie[])
*/
public Resource dispatchOwsRequest(Map<String, String> params, Cookie[] cookies)
throws Exception {
return getGWC().dispatchOwsRequest(params, cookies);
}
/**
* @return the {@link LayerInfo} based on the given {@link LayerInfo#getId() layerId}
*/
public LayerInfo getLayerInfoById(final String layerId) {
return catalog.getLayer(layerId);
}
public LayerGroupInfo getLayerGroupById(final String layerGroupId) {
return catalog.getLayerGroup(layerGroupId);
}
public void renameTileLayer(final String oldLayerName, final String newLayerName) {
getGWC().layerRenamed(oldLayerName, newLayerName);
}
/**
* @see GWC#truncate(String, String)
*/
public void truncate(String layerName, String styleName) {
getGWC().truncate(layerName, styleName);
}
/**
* @see GWC#truncate(String)
*/
public void truncate(String layerName) {
getGWC().truncate(layerName);
}
/**
* @return
* @see GWC#layerRemoved(String)
*/
public boolean removeLayer(final String tileLayerName) {
getGWC().layerRemoved(tileLayerName);
return true;
}
/**
* LayerInfo has been created, add a matching {@link GeoServerTileLayer}
*
* @see CatalogLayerEventListener#handleAddEvent
* @see CatalogConfiguration#createLayer(LayerInfo)
* @see GWC#layerAdded(GeoServerTileLayer)
*/
public void createLayer(LayerInfo layerInfo) {
// /GeoServerTileLayer tileLayer = embeddedConfig.createLayer(layerInfo);
GeoServerTileLayer tileLayer = new GeoServerTileLayer(this, layerInfo);
save(tileLayer);
getGWC().layerAdded(tileLayer);
}
/**
* LayerGroupInfo has been created, add a matching {@link GeoServerTileLayer}
*
* @see CatalogLayerEventListener#handleAddEvent
* @see CatalogConfiguration#createLayer(LayerGroupInfo)
* @see GWC#layerAdded(GeoServerTileLayer)
*/
public void createLayer(LayerGroupInfo lgi) {
// /GeoServerTileLayer tileLayer = embeddedConfig.createLayer(lgi);
GeoServerTileLayer tileLayer = new GeoServerTileLayer(this, lgi);
save(tileLayer);
getGWC().layerAdded(tileLayer);
}
/**
* Returns the tile layers that refer to the given style, either as the tile layer's
* {@link GeoServerTileLayer#getStyles() default style} or one of the
* {@link GeoServerTileLayerInfo#getCachedStyles() cached styles}.
* <p>
* The result may be different from {@link #getLayerInfosFor(StyleInfo)} and
* {@link #getLayerGroupsFor(StyleInfo)} as the {@link GeoServerTileLayerInfo}'s backing each
* {@link GeoServerTileLayer} may have assigned a subset of the layerinfo styles for caching.
* </p>
*/
public List<GeoServerTileLayer> getTileLayersForStyle(final String styleName) {
List<GeoServerTileLayer> tileLayers = getTileLayers();
List<GeoServerTileLayer> affected = new ArrayList<GeoServerTileLayer>();
for (GeoServerTileLayer tl : tileLayers) {
GeoServerTileLayerInfo info = tl.getInfo();
String defaultStyle = tl.getStyles();// may be null if backed by a LayerGroupInfo
Set<String> cachedStyles = info.getCachedStyles();
if (styleName.equals(defaultStyle) || cachedStyles.contains(styleName)) {
affected.add(tl);
}
}
return affected;
}
/**
* @return all the {@link LayerInfo}s in the {@link Catalog} that somehow refer to the given
* style
*/
public Iterable<LayerInfo> getLayerInfosFor(final StyleInfo style) {
final String styleName = style.getName();
List<LayerInfo> result = new ArrayList<LayerInfo>();
{
List<LayerInfo> layers = catalog.getLayers();
for (LayerInfo layer : layers) {
String name = layer.getDefaultStyle().getName();
if (styleName.equals(name)) {
result.add(layer);
continue;
}
for (StyleInfo alternateStyle : layer.getStyles()) {
name = alternateStyle.getName();
if (styleName.equals(name)) {
result.add(layer);
break;
}
}
}
}
return result;
}
/**
* @return all the layergroups that somehow refer to the given style
*/
public Iterable<LayerGroupInfo> getLayerGroupsFor(final StyleInfo style) {
List<LayerGroupInfo> layerGroups = new ArrayList<LayerGroupInfo>();
for (LayerGroupInfo layerGroup : catalog.getLayerGroups()) {
final List<StyleInfo> explicitLayerGroupStyles = layerGroup.getStyles();
final List<LayerInfo> groupLayers = layerGroup.getLayers();
for (int layerN = 0; layerN < groupLayers.size(); layerN++) {
LayerInfo childLayer = groupLayers.get(layerN);
StyleInfo assignedLayerStyle = explicitLayerGroupStyles.get(layerN);
if (assignedLayerStyle == null) {
assignedLayerStyle = childLayer.getDefaultStyle();
}
if (style.equals(assignedLayerStyle)) {
layerGroups.add(layerGroup);
break;
}
}
}
return layerGroups;
}
/**
* @see org.geowebcache.config.Configuration#initialize(org.geowebcache.grid.GridSetBroker)
*/
public int initialize(GridSetBroker gridSetBroker) throws GeoWebCacheException {
return getTileLayerCount();
}
@SuppressWarnings("unchecked")
public Response getResponseEncoder(final MimeType responseFormat, final WebMap webMap) {
final String format = responseFormat.getFormat();
final String mimeType = responseFormat.getMimeType();
Response response = cachedTileEncoders.get(format);
if (response == null) {
final Operation operation;
{
GetMapRequest getMap = new GetMapRequest();
getMap.setFormat(mimeType);
Object[] parameters = { getMap };
Service service = (Service) GeoServerExtensions.bean("wms-1_1_1-ServiceDescriptor");
if (service == null) {
throw new IllegalStateException(
"Didn't find service descriptor 'wms-1_1_1-ServiceDescriptor'");
}
operation = new Operation("GetMap", service, (Method) null, parameters);
}
final List<Response> extensions = GeoServerExtensions.extensions(Response.class);
final Class<?> webMapClass = webMap.getClass();
for (Response r : extensions) {
if (r.getBinding().isAssignableFrom(webMapClass) && r.canHandle(operation)) {
synchronized (cachedTileEncoders) {
cachedTileEncoders.put(mimeType, r);
response = r;
break;
}
}
}
if (response == null) {
throw new IllegalStateException("Didn't find a " + Response.class.getName()
+ " to handle " + mimeType);
}
}
return response;
}
// @Override
public void modifyLayer(TileLayer tl) throws NoSuchElementException {
// TODO Auto-generated method stub
}
// @Override
public void save() throws IOException {
// TODO Auto-generated method stub
}
}