/**
* Copyright (c) 2001 - 2009 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*
* @author Arne Kepp / OpenGeo
*/
package org.geoserver.gwc;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogException;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.event.CatalogAddEvent;
import org.geoserver.catalog.event.CatalogListener;
import org.geoserver.catalog.event.CatalogModifyEvent;
import org.geoserver.catalog.event.CatalogPostModifyEvent;
import org.geoserver.catalog.event.CatalogRemoveEvent;
import org.geoserver.catalog.impl.StyleInfoImpl;
import org.geoserver.ows.Dispatcher;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.referencing.CRS;
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.BoundingBox;
import org.geowebcache.grid.GridSet;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.grid.GridSetFactory;
import org.geowebcache.grid.GridSubset;
import org.geowebcache.grid.GridSubsetFactory;
import org.geowebcache.grid.SRS;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.layer.TileLayerDispatcher;
import org.geowebcache.layer.wms.WMSGeoServerHelper;
import org.geowebcache.layer.wms.WMSLayer;
import org.geowebcache.util.ApplicationContextProvider;
/**
* This class implements a GeoWebCache configuration object, i.e.
* a source of WMS layer definitions, and a GeoServer catalog listener
* which is loaded on startup and listens for configuration changes.
*/
public class GWCCatalogListener implements CatalogListener, Configuration {
private static Logger log = Logging.getLogger("org.geoserver.gwc.GWCCatalogListener");
final protected Catalog cat;
final protected Dispatcher gsDispatcher;
final protected GridSetBroker gridSetBroker;
final protected GWCCleanser cleanser;
protected TileLayerDispatcher layerDispatcher;
private List<String> mimeFormats = null;
private int[] metaFactors = {4,4};
private String wmsUrl = null;
ArrayList<TileLayer> list;
public GWCCatalogListener(
Catalog cat,
Dispatcher gsDispatcher,
GridSetBroker gridSetBroker,
ApplicationContextProvider ctxProv,
GWCCleanser cleanser) {
this.cat = cat;
this.gridSetBroker = gridSetBroker;
this.gsDispatcher = gsDispatcher;
this.cleanser = cleanser;
mimeFormats = new ArrayList<String>(5);
mimeFormats.add("image/png");
mimeFormats.add("image/gif");
mimeFormats.add("image/png8");
mimeFormats.add("image/jpeg");
mimeFormats.add("application/vnd.google-earth.kml+xml");
cat.addListener(this);
log.fine("GWCCatalogListener registered with catalog");
}
public void setTileLayerDispatcher(TileLayerDispatcher layerDispatcher) {
log.fine("TileLayerDispatcher was set");
this.layerDispatcher = layerDispatcher;
}
public void handleAddEvent(CatalogAddEvent event) throws CatalogException {
Object obj = event.getSource();
WMSLayer wmsLayer = null;
// We only handle layers here. Layer groups are initially empty
if(obj instanceof LayerInfo) {
LayerInfo layerInfo = (LayerInfo) obj;
wmsLayer = getLayer(layerInfo);
}
if (wmsLayer != null && this.list != null) {
addToList(wmsLayer);
layerDispatcher.getLayers();
layerDispatcher.add(wmsLayer);
log.finer(wmsLayer.getName() + " added to TileLayerDispatcher");
}
}
public void handleModifyEvent(CatalogModifyEvent event) throws CatalogException {
// Not dealing with this one just yet
}
public void handlePostModifyEvent(CatalogPostModifyEvent event) throws CatalogException {
Object obj = event.getSource();
if(obj instanceof StyleInfoImpl) {
// TODO First pass only considers default styles,
// which is all GWC will accept anyway
StyleInfoImpl si = (StyleInfoImpl) obj;
String styleName = si.getName();
LinkedList<String> layerNameList = new LinkedList<String>();
// First we collect all the layers that use this style
Iterator<LayerInfo> liter = cat.getLayers().iterator();
while(liter.hasNext()) {
LayerInfo li = liter.next();
if(li.getDefaultStyle().getName().equals(styleName)) {
String prefixedName = li.getResource().getPrefixedName();
layerNameList.add(prefixedName);
cleanser.deleteLayer(prefixedName);
}
}
// Now we check for layer groups that are affected
Iterator<LayerGroupInfo> lgiter = cat.getLayerGroups().iterator();
while(lgiter.hasNext()) {
LayerGroupInfo lgi = lgiter.next();
boolean truncate = false;
// First we check for referenced to affected layers
liter = lgi.getLayers().iterator();
while(! truncate && liter.hasNext()) {
LayerInfo li = liter.next();
if(layerNameList.contains(li.getResource().getPrefixedName())){
truncate = true;
}
}
// Finally we need to check whether the style is used explicitly
if(! truncate) {
Iterator<StyleInfo> siiter = lgi.getStyles().iterator();
while(! truncate && siiter.hasNext()) {
StyleInfo si2 = siiter.next();
if(si2 != null && si2.getName().equals(si.getName())) {
truncate = true;
}
}
}
if(truncate) {
cleanser.deleteLayer(lgi.getName());
}
// Next layer group
}
} else {
WMSLayer wmsLayer = null; //getLayer(obj);
if(obj instanceof LayerInfo) {
LayerInfo li = (LayerInfo) obj;
wmsLayer = getLayer(li);
} else
if(obj instanceof LayerGroupInfo) {
LayerGroupInfo lgInfo = (LayerGroupInfo) obj;
wmsLayer = getLayer(lgInfo);
}
if (wmsLayer != null && this.list != null) {
updateList(wmsLayer);
layerDispatcher.getLayers();
layerDispatcher.update(wmsLayer);
log.finer(wmsLayer.getName() + " updated on TileLayerDispatcher");
cleanser.deleteLayer(wmsLayer.getName());
}
}
}
public void handleRemoveEvent(CatalogRemoveEvent event) throws CatalogException {
Object obj = event.getSource();
String prefixedName = null;
if(obj instanceof LayerGroupInfo) {
LayerGroupInfo lgInfo = (LayerGroupInfo) obj;
prefixedName = lgInfo.getName();
} else
if(obj instanceof LayerInfo) {
LayerInfo layerInfo = (LayerInfo) obj;
prefixedName = layerInfo.getResource().getPrefixedName();
}
if(null != prefixedName) {
removeFromList(prefixedName);
layerDispatcher.remove(prefixedName);
cleanser.deleteLayer(prefixedName);
}
}
public void reloaded() {
try {
layerDispatcher.reInit();
} catch (GeoWebCacheException gwce) {
log.fine("Unable to reinit TileLayerDispatcher gwce.getMessage()");
}
}
public String getIdentifier() throws GeoWebCacheException {
return "GeoServer Catalog Listener";
}
public ServiceInformation getServiceInformation() throws GeoWebCacheException {
// TODO Auto-generated method stub
return null;
}
public List<TileLayer> getTileLayers(boolean reload) throws GeoWebCacheException {
if(! reload && list != null) {
return list;
}
list = new ArrayList<TileLayer>(cat.getLayers().size());
// Adding vector layers
Iterator<LayerInfo> lIter = cat.getLayers().iterator();
while(lIter.hasNext()) {
LayerInfo li = lIter.next();
TileLayer tl = getLayer(li.getResource());
//System.out.println(tl.getName() + " layerinfo");
list.add(tl);
}
// Adding layer groups
Iterator<LayerGroupInfo> lgIter = cat.getLayerGroups().iterator();
while(lgIter.hasNext()) {
LayerGroupInfo lgi = lgIter.next();
TileLayer tl = getLayer(lgi);
//System.out.println(tl.getName() + " layergroupinfo");
list.add(tl);
}
log.fine("Responding with " + list.size() + " to getTileLayers() request from TileLayerDispatcher");
return list;
}
synchronized private void updateList(WMSLayer wmsLayer) {
if(this.list != null) {
removeFromList(wmsLayer);
addToList(wmsLayer);
}
}
private void removeFromList(WMSLayer wmsLayer) {
removeFromList(wmsLayer.getName());
}
synchronized private void removeFromList(String layerName) {
if(this.list != null) {
Iterator<TileLayer> iter = list.iterator();
int i = 0;
while(iter.hasNext()) {
TileLayer tl = iter.next();
if(tl != null && tl.getName().equals(layerName)) {
list.remove(i);
return;
}
i++;
}
log.finer("Did not find " + layerName + " in list.");
}
}
synchronized private void addToList(WMSLayer wmsLayer) {
if(this.list != null) {
list.add(wmsLayer);
}
}
private WMSLayer getLayer(LayerInfo li) {
return getLayer(li.getResource());
}
private WMSLayer getLayer(LayerGroupInfo lgi) {
ReferencedEnvelope bounds = null;
ReferencedEnvelope latLonBounds = null;
String nativeSRSName = null;
try {
bounds = lgi.getBounds();
nativeSRSName = "EPSG:" + CRS.lookupEpsgCode(bounds.getCoordinateReferenceSystem(), true);
latLonBounds = lgi.getBounds().transform(CRS.decode("EPSG:4326"), true);
} catch (Exception e) {
log.warning(e.getMessage());
}
if(latLonBounds == null) {
log.severe("GWCCatalogListener had problems getting or reprojecting "
+ lgi.getBounds() + " to EPSG:4326");
return null;
}
WMSLayer retLayer = new WMSLayer(
lgi.getName(),
getWMSUrl(),
null, // Styles
lgi.getName(),
mimeFormats,
getGrids(lgi.getName(), latLonBounds, bounds, nativeSRSName),
metaFactors,
null,
true);
retLayer.setBackendTimeout(120);
retLayer.setSourceHelper(new WMSGeoServerHelper(this.gsDispatcher));
retLayer.initialize(gridSetBroker);
return retLayer;
}
private WMSLayer getLayer(ResourceInfo fti) {
ReferencedEnvelope nativeEnv = null;
try {
nativeEnv = fti.boundingBox();
} catch (Exception e) {
e.printStackTrace();
}
WMSLayer retLayer = new WMSLayer(
fti.getPrefixedName(),
getWMSUrl(),
null, // Styles
fti.getPrefixedName(),
mimeFormats,
getGrids(fti.getPrefixedName(), fti.getLatLonBoundingBox(), nativeEnv, fti.getSRS()),
metaFactors,
null,
true);
retLayer.setBackendTimeout(120);
retLayer.setSourceHelper(new WMSGeoServerHelper(this.gsDispatcher));
retLayer.initialize(gridSetBroker);
return retLayer;
}
// private WMSLayer getLayer(CoverageInfo ci) {
// WMSLayer retLayer = new WMSLayer(
// ci.getPrefixedName(),
// getWMSUrl(),
// null, // Styles
// ci.getPrefixedName(),
// mimeFormats,
// getGrids(ci.getLatLonBoundingBox()),
// metaFactors,
// null,
// false);
//
// retLayer.setBackendTimeout(120);
// retLayer.setSourceHelper(new WMSGeoServerHelper(this.gsDispatcher));
//
// retLayer.initialize(gridSetBroker);
// return retLayer;
// }
private String[] getWMSUrl() {
String[] strs = { wmsUrl };
return strs;
}
private Hashtable<String,GridSubset> getGrids(String layerName, ReferencedEnvelope latLonEnv,
ReferencedEnvelope nativeEnv, String nativeSRSName) {
// LatLon stuff
double minX = latLonEnv.getMinX();
double minY = latLonEnv.getMinY();
double maxX = latLonEnv.getMaxX();
double maxY = latLonEnv.getMaxY();
BoundingBox bounds4326 = new BoundingBox(minX,minY,maxX,maxY);
BoundingBox bounds900913 = new BoundingBox(
longToSphericalMercatorX(minX),
latToSphericalMercatorY(minY),
longToSphericalMercatorX(maxX),
latToSphericalMercatorY(maxY));
Hashtable<String,GridSubset> grids = new Hashtable<String,GridSubset>(3);
GridSubset gridSubset4326 = GridSubsetFactory.createGridSubSet(
gridSetBroker.WORLD_EPSG4326,
bounds4326, 0, 25 );
grids.put(gridSetBroker.WORLD_EPSG4326.getName(), gridSubset4326);
GridSubset gridSubset900913 = GridSubsetFactory.createGridSubSet(
gridSetBroker.WORLD_EPSG3857,
bounds900913, 0, 25 );
grids.put(gridSetBroker.WORLD_EPSG3857.getName(), gridSubset900913);
// Native stuff
if(nativeSRSName != null && ! nativeSRSName.equalsIgnoreCase("EPSG:4326")) {
minX = nativeEnv.getMinX();
minY = nativeEnv.getMinY();
maxX = nativeEnv.getMaxX();
maxY = nativeEnv.getMaxY();
BoundingBox nativeBounds = new BoundingBox(minX,minY,maxX,maxY);
SRS srs = null;
try {
srs = SRS.getSRS(nativeSRSName);
} catch (GeoWebCacheException e) {
e.printStackTrace();
}
String gridSetKey = nativeSRSName + "_" + layerName;
GridSet gs = GridSetFactory.createGridSet(
gridSetKey, srs,
nativeBounds, false,
25, null,
0.00028,
256, 256);
gridSetBroker.put(gs);
GridSubset gsub = GridSubsetFactory.createGridSubSet(
gs, nativeBounds, 0, 24 );
grids.put(gridSetKey, gsub);
}
return grids;
}
private double longToSphericalMercatorX(double x) {
return (x/180.0)*20037508.34;
}
private double latToSphericalMercatorY(double y) {
if(y > 85.05112) {
y = 85.05112;
}
if(y < -85.05112) {
y = -85.05112;
}
y = (Math.PI/180.0)*y;
double tmp = Math.PI/4.0 + y/2.0;
return 20037508.34 * Math.log(Math.tan(tmp)) / Math.PI;
}
}