/* (c) 2014 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 javax.servlet.http.HttpServletResponse;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.gwc.GWC;
import org.geowebcache.config.ContextualConfigurationProvider;
import org.geowebcache.config.XMLConfigurationProvider;
import org.geowebcache.grid.GridSetBroker;
import org.geowebcache.layer.TileLayer;
import org.geowebcache.service.HttpErrorCodeException;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
/**
* GWC xml configuration {@link XMLConfigurationProvider contributor} so that GWC knows how to
* marshal and unmarshal {@link GeoServerTileLayer} instances for its REST API.
* <p>
* Note this provider is different than {@link GWCGeoServerConfigurationProvider}, which is used to
* save the configuration objects. In contrast, this one is used only for the GWC REST API, as it
* doesn't distinguish betwee {@link TileLayer} objects and tile layer configuration objects (as the
* GWC/GeoServer integration does with {@link GeoServerTileLayer} and {@link GeoServerTileLayerInfo}
* ).
*
*/
public class GWCGeoServerRESTConfigurationProvider implements ContextualConfigurationProvider {
private final Catalog catalog;
public GWCGeoServerRESTConfigurationProvider(Catalog catalog) {
this.catalog = catalog;
}
@Override
public XStream getConfiguredXStream(XStream xs) {
xs.alias("GeoServerLayer", GeoServerTileLayer.class);
xs.processAnnotations(GeoServerTileLayerInfoImpl.class);
xs.processAnnotations(StyleParameterFilter.class);
xs.registerConverter(new RESTConverterHelper());
xs.addDefaultImplementation(GeoServerTileLayerInfoImpl.class, GeoServerTileLayerInfo.class);
// Omit the values cached from the backing layer. They are only needed for the
// persisted config file.
xs.omitField(StyleParameterFilter.class, "availableStyles");
xs.omitField(StyleParameterFilter.class, "defaultStyle");
// Omit autoCacheStyles as it is no longer needed.
// It'd be better to read it but not write it, but blocking it from REST is good enough and
// a lot easier to get XStream to do.
// TODO Remove this
// xs.omitField(GeoServerTileLayerInfoImpl.class, "autoCacheStyles");
return xs;
}
/**
* @author groldan
*
*/
private final class RESTConverterHelper implements Converter {
@Override
public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
return GeoServerTileLayer.class.equals(type);
}
@Override
public GeoServerTileLayer unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Object current = new GeoServerTileLayerInfoImpl();
Class<?> type = GeoServerTileLayerInfo.class;
GeoServerTileLayerInfo info = (GeoServerTileLayerInfo) context.convertAnother(current,
type);
String id = info.getId();
String name = info.getName();
if (id != null && id.length() == 0) {
id = null;
}
if (name != null && name.length() == 0) {
name = null;
}
if (name == null) {// name is mandatory
throw new HttpErrorCodeException(HttpServletResponse.SC_BAD_REQUEST,
"Layer name not provided");
}
LayerInfo layer = null;
LayerGroupInfo layerGroup = null;
if (id != null) {
layer = catalog.getLayer(id);
if (layer == null) {
layerGroup = catalog.getLayerGroup(id);
if (layerGroup == null) {
throw new HttpErrorCodeException(HttpServletResponse.SC_BAD_REQUEST,
"No GeoServer Layer or LayerGroup exists with id '" + id + "'");
}
}
} else {
layer = catalog.getLayerByName(name);
if (layer == null) {
layerGroup = catalog.getLayerGroupByName(name);
if (layerGroup == null) {
throw new HttpErrorCodeException(HttpServletResponse.SC_NOT_FOUND,
"GeoServer Layer or LayerGroup '" + name + "' not found");
}
}
}
final String actualId = layer != null ? layer.getId() : layerGroup.getId();
final String actualName = layer != null ? GWC.tileLayerName(layer) : GWC
.tileLayerName(layerGroup);
if (id != null && !name.equals(actualName)) {
throw new HttpErrorCodeException(HttpServletResponse.SC_BAD_REQUEST,
"Layer with id '" + id + "' found but name does not match: '" + name
+ "'/'" + actualName + "'");
}
info.setId(actualId);
info.setName(actualName);
GeoServerTileLayer tileLayer;
final GridSetBroker gridsets = GWC.get().getGridSetBroker();
if (layer != null) {
tileLayer = new GeoServerTileLayer(layer, gridsets, info);
} else {
tileLayer = new GeoServerTileLayer(layerGroup, gridsets, info);
}
return tileLayer;
}
@Override
public void marshal(/* GeoServerTileLayer */Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
GeoServerTileLayer tileLayer = (GeoServerTileLayer) source;
GeoServerTileLayerInfo info = tileLayer.getInfo();
context.convertAnother(info);
}
}
@Override
public boolean appliesTo(Context ctxt) {
return Context.REST==ctxt;
}
}