/* (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.config.util;
import java.awt.geom.AffineTransform;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.thoughtworks.xstream.io.json.JettisonStaxWriter;
import com.thoughtworks.xstream.io.xml.StaxWriter;
import org.apache.commons.collections.MultiHashMap;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONObject;
import org.codehaus.jettison.mapped.MappedXMLStreamWriter;
import org.codehaus.jettison.util.FastStack;
import org.geoserver.catalog.AttributeTypeInfo;
import org.geoserver.catalog.AttributionInfo;
import org.geoserver.catalog.AuthorityURLInfo;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.CatalogInfo;
import org.geoserver.catalog.CoverageDimensionInfo;
import org.geoserver.catalog.CoverageInfo;
import org.geoserver.catalog.CoverageStoreInfo;
import org.geoserver.catalog.CoverageView;
import org.geoserver.catalog.CoverageView.CoverageBand;
import org.geoserver.catalog.CoverageView.InputCoverageBand;
import org.geoserver.catalog.DataLinkInfo;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.FeatureTypeInfo;
import org.geoserver.catalog.Info;
import org.geoserver.catalog.Keyword;
import org.geoserver.catalog.KeywordInfo;
import org.geoserver.catalog.LayerGroupInfo;
import org.geoserver.catalog.LayerGroupInfo.Mode;
import org.geoserver.catalog.LayerIdentifierInfo;
import org.geoserver.catalog.LayerInfo;
import org.geoserver.catalog.LegendInfo;
import org.geoserver.catalog.MetadataLinkInfo;
import org.geoserver.catalog.MetadataMap;
import org.geoserver.catalog.NamespaceInfo;
import org.geoserver.catalog.PublishedInfo;
import org.geoserver.catalog.ResourceInfo;
import org.geoserver.catalog.StoreInfo;
import org.geoserver.catalog.StyleInfo;
import org.geoserver.catalog.WMSLayerInfo;
import org.geoserver.catalog.WMSStoreInfo;
import org.geoserver.catalog.WorkspaceInfo;
import org.geoserver.catalog.impl.AttributeTypeInfoImpl;
import org.geoserver.catalog.impl.AttributionInfoImpl;
import org.geoserver.catalog.impl.AuthorityURL;
import org.geoserver.catalog.impl.CatalogImpl;
import org.geoserver.catalog.impl.CoverageDimensionImpl;
import org.geoserver.catalog.impl.CoverageInfoImpl;
import org.geoserver.catalog.impl.CoverageStoreInfoImpl;
import org.geoserver.catalog.impl.DataStoreInfoImpl;
import org.geoserver.catalog.impl.DefaultCatalogFacade;
import org.geoserver.catalog.impl.DimensionInfoImpl;
import org.geoserver.catalog.impl.FeatureTypeInfoImpl;
import org.geoserver.catalog.impl.LayerGroupInfoImpl;
import org.geoserver.catalog.impl.LayerIdentifier;
import org.geoserver.catalog.impl.LayerInfoImpl;
import org.geoserver.catalog.impl.LegendInfoImpl;
import org.geoserver.catalog.impl.MetadataLinkInfoImpl;
import org.geoserver.catalog.impl.NamespaceInfoImpl;
import org.geoserver.catalog.impl.ResolvingProxy;
import org.geoserver.catalog.impl.ResourceInfoImpl;
import org.geoserver.catalog.impl.StoreInfoImpl;
import org.geoserver.catalog.impl.StyleInfoImpl;
import org.geoserver.catalog.impl.WMSLayerInfoImpl;
import org.geoserver.catalog.impl.WMSStoreInfoImpl;
import org.geoserver.catalog.impl.WorkspaceInfoImpl;
import org.geoserver.config.ContactInfo;
import org.geoserver.config.CoverageAccessInfo;
import org.geoserver.config.GeoServer;
import org.geoserver.config.GeoServerInfo;
import org.geoserver.config.JAIEXTInfo;
import org.geoserver.config.JAIInfo;
import org.geoserver.config.LoggingInfo;
import org.geoserver.config.ServiceInfo;
import org.geoserver.config.SettingsInfo;
import org.geoserver.config.impl.ContactInfoImpl;
import org.geoserver.config.impl.CoverageAccessInfoImpl;
import org.geoserver.config.impl.GeoServerImpl;
import org.geoserver.config.impl.GeoServerInfoImpl;
import org.geoserver.config.impl.JAIEXTInfoImpl;
import org.geoserver.config.impl.JAIInfoImpl;
import org.geoserver.config.impl.LoggingInfoImpl;
import org.geoserver.config.impl.ServiceInfoImpl;
import org.geoserver.config.impl.SettingsInfoImpl;
import org.geoserver.ows.util.OwsUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.geoserver.security.GeoServerSecurityManager;
import org.geoserver.security.SecureCatalogImpl;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.Geometries;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.jdbc.RegexpValidator;
import org.geotools.jdbc.VirtualTable;
import org.geotools.jdbc.VirtualTableParameter;
import org.geotools.jdbc.VirtualTableParameter.Validator;
import org.geotools.measure.Measure;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.crs.DefaultProjectedCRS;
import org.geotools.referencing.operation.transform.AffineTransform2D;
import org.geotools.referencing.wkt.Formattable;
import org.geotools.util.Converters;
import org.geotools.util.MeasureConverterFactory;
import org.geotools.util.NumberRange;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.SingleValueConverterWrapper;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
import com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter;
import com.thoughtworks.xstream.converters.collections.CollectionConverter;
import com.thoughtworks.xstream.converters.collections.MapConverter;
import com.thoughtworks.xstream.converters.reflection.FieldDictionary;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.converters.reflection.SortableFieldKeySorter;
import com.thoughtworks.xstream.converters.reflection.SunUnsafeReflectionProvider;
import com.thoughtworks.xstream.io.ExtendedHierarchicalStreamWriterHelper;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.ClassAliasingMapper;
import com.thoughtworks.xstream.mapper.DynamicProxyMapper;
import com.thoughtworks.xstream.mapper.Mapper;
import com.vividsolutions.jts.geom.Geometry;
/**
* Utility class which loads and saves catalog and configuration objects to and
* from an xstream.
*
* @author Justin Deoliveira, The Open Planning Project
*
*/
public class XStreamPersister {
private boolean unwrapNulls = true;
/**
* Callback interface or xstream persister.
*/
public static class Callback {
/** Return the CatalogInfo object being modified by the current request */
protected CatalogInfo getCatalogObject() { return null; }
/** Return the ServiceInfo object being modified by the current request */
protected ServiceInfo getServiceObject() { return null; }
/** Return the class of the object being acted upon by the current request */
protected Class<? extends Info> getObjectClass() { return null; }
protected void postEncodeWorkspace( WorkspaceInfo ws, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeNamespace( NamespaceInfo ns, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeDataStore( DataStoreInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeCoverageStore( CoverageStoreInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeFeatureType( FeatureTypeInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeWMSLayer( WMSLayerInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeCoverage( CoverageInfo ds, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeLayer( LayerInfo ls, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeLayerGroup( LayerGroupInfo ls, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
/**
* @deprecated use {@link #postEncodeReference(Object, String, String, HierarchicalStreamWriter, MarshallingContext)}
*/
protected void postEncodeReference( Object obj, String ref, HierarchicalStreamWriter writer, MarshallingContext context ) {
}
protected void postEncodeReference( Object obj, String ref, String prefix, HierarchicalStreamWriter writer, MarshallingContext context ) {
if (prefix == null) {
//call other method for backward compatability
postEncodeReference(obj, ref, writer, context);
}
}
protected void postEncodeWMSStore(WMSStoreInfo store, HierarchicalStreamWriter writer, MarshallingContext context) {
}
}
/**
* logging instance
*/
static Logger LOGGER = Logging.getLogger( "org.geoserver" );
/**
* internal xstream instance
*/
XStream xs;
/**
* GeoServer reference used to resolve references to gloal from services
*/
GeoServer geoserver;
/**
* Catalog reference, used to resolve references to stores, workspaces
* + namespaces
*/
Catalog catalog;
/**
* Callback instance.
*/
Callback callback;
/**
* Flag controlling how references to objects are encoded.
*/
boolean referenceByName = false;
/**
* The type map used in {@link BreifMapConverter} to handle complex objects
*/
Map<String, Class<?>> forwardBreifMap = new HashMap<String, Class<?>>();
Map<Class<?>, String> backwardBreifMap = new HashMap<Class<?>, String>();
private Level forceLevel = LOGGER.getLevel() == null? Level.INFO : LOGGER.getLevel();
/**
* Flag controlling whether the persister should perform encryption on password fields
*/
boolean encryptPasswordFields = true;
/**
* Constructs the persister and underlying xstream.
*/
protected XStreamPersister() {
this(null);
}
/**
* Constructs the persister and underlying xstream specifying the stream driver explicitly.
*/
protected XStreamPersister(HierarchicalStreamDriver streamDriver) {
//control the order in which fields are sorted
SortableFieldKeySorter sorter = new SortableFieldKeySorter();
//sorter.registerFieldOrder( DefaultCatalogDAO.class, new String[]{ "workspaces", "namespaces", "stores", "styles",
/* these we actually omit, but the sorter needs them specified */
// "layerGroups", "resources", "maps", "defaultStores", "listeners", "layers", "resourcePool", "resourceLoader", "LOGGER" } );
ReflectionProvider reflectionProvider = new CustomReflectionProvider( new FieldDictionary( sorter ) );
//new Sun14ReflectionProvider( new FieldDictionary( sorter ) );
if ( streamDriver != null ) {
xs = new SecureXStream(reflectionProvider, streamDriver);
}
else {
xs = new SecureXStream(reflectionProvider);
}
xs.setMode(XStream.NO_REFERENCES);
init(xs);
}
/**
* Sets null handling in proxy objects.
* Defaults to unwrap. If set to false, proxy object are not transformed to nulls.
*
* @param unwrapNulls
*/
public void setUnwrapNulls(boolean unwrapNulls) {
this.unwrapNulls = unwrapNulls;
}
protected void init(XStream xs) {
// Default implementations
initImplementationDefaults(xs);
// ignore unkonwn fields, this should help using data dirs that has new config elements
// with older versions of GeoServer
xs.ignoreUnknownElements();
// Aliases
xs.alias("global", GeoServerInfo.class);
xs.alias("settings", SettingsInfo.class);
xs.alias("logging", LoggingInfo.class);
xs.alias("jai", JAIInfo.class);
xs.alias("coverageAccess", CoverageAccessInfo.class);
xs.alias("catalog", Catalog.class);
xs.alias("namespace", NamespaceInfo.class);
xs.alias("workspace", WorkspaceInfo.class);
xs.alias("dataStore", DataStoreInfo.class);
xs.alias("wmsStore", WMSStoreInfo.class);
xs.alias("coverageStore", CoverageStoreInfo.class);
xs.alias("style",StyleInfo.class);
xs.alias( "legend", LegendInfo.class);
xs.alias( "featureType", FeatureTypeInfo.class);
xs.alias( "coverage", CoverageInfo.class);
xs.alias( "wmsLayer", WMSLayerInfo.class);
xs.alias( "coverageDimension", CoverageDimensionInfo.class);
xs.alias( "coverageBand", CoverageBand.class);
xs.alias( "inputCoverageBand", InputCoverageBand.class);
xs.alias( "metadataLink", MetadataLinkInfo.class);
xs.alias( "dataLink", DataLinkInfo.class);
xs.alias( "attribute", AttributeTypeInfo.class );
xs.alias( "layer", LayerInfo.class);
xs.alias( "layerGroup", LayerGroupInfo.class);
xs.alias( "published", PublishedInfo.class);
xs.alias( "gridGeometry", GridGeometry2D.class);
xs.alias( "projected", DefaultProjectedCRS.class);
xs.alias( "attribution", AttributionInfo.class );
xs.aliasField("abstract", ResourceInfoImpl.class, "_abstract" );
xs.alias("AuthorityURL", AuthorityURLInfo.class);
xs.alias("Identifier", LayerIdentifierInfo.class);
// GeoServerInfo
xs.omitField(impl(GeoServerInfo.class), "clientProperties");
xs.omitField(impl(GeoServerInfo.class), "geoServer");
xs.registerLocalConverter(impl(GeoServerInfo.class), "metadata", new MetadataMapConverter());
// ServiceInfo
xs.omitField(impl(ServiceInfo.class), "clientProperties");
xs.omitField(impl(ServiceInfo.class), "geoServer");
xs.registerLocalConverter(impl(ServiceInfo.class), "workspace", new ReferenceConverter(WorkspaceInfo.class));
xs.registerLocalConverter(impl(ServiceInfo.class), "metadata", new MetadataMapConverter());
xs.registerLocalConverter(impl(ServiceInfo.class), "keywords", new KeywordListConverter());
// Catalog
xs.omitField(impl(Catalog.class), "resourcePool");
xs.omitField(impl(Catalog.class), "resourceLoader");
xs.omitField(impl(Catalog.class), "listeners");
xs.omitField(impl(Catalog.class), "LOGGER");
xs.omitField(impl(DefaultCatalogFacade.class), "catalog");
xs.omitField(impl(DefaultCatalogFacade.class), "resources");
xs.omitField(impl(DefaultCatalogFacade.class), "layers");
xs.omitField(impl(DefaultCatalogFacade.class), "maps");
xs.omitField(impl(DefaultCatalogFacade.class), "layerGroups");
xs.registerLocalConverter(DefaultCatalogFacade.class, "stores",
new StoreMultiHashMapConverter());
xs.registerLocalConverter(DefaultCatalogFacade.class, "namespaces",
new SpaceMapConverter("namespace"));
xs.registerLocalConverter(DefaultCatalogFacade.class, "workspaces",
new SpaceMapConverter("workspace"));
//WorkspaceInfo
xs.omitField( impl(WorkspaceInfo.class), "_default");
xs.registerLocalConverter( impl(WorkspaceInfo.class), "metadata", new MetadataMapConverter() );
//NamespaceInfo
xs.omitField( impl(NamespaceInfo.class), "catalog");
xs.omitField( impl(NamespaceInfo.class), "_default");
xs.registerLocalConverter( impl(NamespaceInfo.class), "metadata", new MetadataMapConverter() );
// StoreInfo
xs.omitField(impl(StoreInfo.class), "catalog");
xs.omitField(impl(StoreInfo.class), "error");
//xs.omitField(StoreInfo.class), "workspace"); //handled by StoreInfoConverter
xs.registerLocalConverter(impl(StoreInfo.class), "workspace", new ReferenceConverter(WorkspaceInfo.class));
xs.registerLocalConverter(impl(StoreInfo.class), "connectionParameters", new BreifMapConverter() );
xs.registerLocalConverter(impl(StoreInfo.class), "metadata", new MetadataMapConverter());
xs.registerLocalConverter(impl(WMSStoreInfo.class), "password", new EncryptedFieldConverter());
// StyleInfo
xs.omitField(impl(StyleInfo.class), "catalog");
xs.registerLocalConverter(impl(StyleInfo.class), "workspace", new ReferenceConverter(WorkspaceInfo.class));
xs.registerLocalConverter(impl(StyleInfo.class), "metadata", new MetadataMapConverter() );
// ResourceInfo
xs.omitField( impl(ResourceInfo.class), "catalog");
xs.omitField( impl(ResourceInfo.class), "crs" );
xs.registerLocalConverter( impl(ResourceInfo.class), "nativeCRS", new CRSConverter());
xs.registerLocalConverter( impl(ResourceInfo.class), "store", new ReferenceConverter(StoreInfo.class));
xs.registerLocalConverter( impl(ResourceInfo.class), "namespace", new ReferenceConverter(NamespaceInfo.class));
xs.registerLocalConverter( impl(ResourceInfo.class), "metadata", new MetadataMapConverter() );
xs.registerLocalConverter( impl(ResourceInfo.class), "keywords", new KeywordListConverter());
// FeatureTypeInfo
// CoverageInfo
xs.registerLocalConverter( impl(CoverageInfo.class), "supportedFormats", new LaxCollectionConverter(xs.getMapper()));
xs.registerLocalConverter( impl(CoverageInfo.class), "requestSRS", new LaxCollectionConverter(xs.getMapper()));
xs.registerLocalConverter( impl(CoverageInfo.class), "responseSRS", new LaxCollectionConverter(xs.getMapper()));
xs.registerLocalConverter( impl(CoverageInfo.class), "interpolationMethods", new LaxCollectionConverter(xs.getMapper()));
xs.registerLocalConverter( impl(CoverageInfo.class), "dimensions", new LaxCollectionConverter(xs.getMapper()));
// CoverageDimensionInfo
xs.registerLocalConverter( impl(CoverageDimensionInfo.class), "range", new NumberRangeConverter());
// AttributeTypeInfo
xs.omitField( impl(AttributeTypeInfo.class), "featureType");
xs.omitField( impl(AttributeTypeInfo.class), "attribute");
// LayerInfo
//xs.omitField( LayerInfo.class), "resource");
xs.registerLocalConverter( impl(LayerInfo.class), "resource", new ReferenceConverter( ResourceInfo.class ) );
xs.registerLocalConverter( impl(LayerInfo.class), "defaultStyle", new ReferenceConverter( StyleInfo.class) );
xs.registerLocalConverter( impl(LayerInfo.class), "styles", new ReferenceCollectionConverter( StyleInfo.class ) );
xs.registerLocalConverter( impl(LayerInfo.class), "metadata", new MetadataMapConverter() );
// LayerGroupInfo
xs.registerLocalConverter(impl(LayerGroupInfo.class), "workspace", new ReferenceConverter(WorkspaceInfo.class));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "rootLayer", new ReferenceConverter(LayerInfo.class));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "rootLayerStyle", new ReferenceConverter(StyleInfo.class));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "layers", new ReferenceCollectionConverter( LayerInfo.class ));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "publishables", new ReferenceCollectionConverter( PublishedInfo.class, LayerInfo.class, LayerGroupInfo.class ));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "styles", new ReferenceCollectionConverter( StyleInfo.class ));
xs.registerLocalConverter(impl(LayerGroupInfo.class), "metadata", new MetadataMapConverter() );
//ReferencedEnvelope
xs.registerLocalConverter( ReferencedEnvelope.class, "crs", new SRSConverter() );
xs.registerLocalConverter( GeneralEnvelope.class, "crs", new SRSConverter() );
// ServiceInfo
xs.registerConverter(new ServiceInfoConverter());
xs.omitField( impl(ServiceInfo.class), "geoServer" );
// Converters
xs.registerConverter(new SpaceInfoConverter());
xs.registerConverter(new StoreInfoConverter());
xs.registerConverter(new ResourceInfoConverter());
xs.registerConverter(new FeatureTypeInfoConverter());
xs.registerConverter(new CoverageInfoConverter());
xs.registerConverter(new LayerInfoConverter());
xs.registerConverter(new LayerGroupInfoConverter());
xs.registerConverter(new GridGeometry2DConverter());
xs.registerConverter(new ProxyCollectionConverter( xs.getMapper() ) );
xs.registerConverter(new VirtualTableConverter());
xs.registerConverter(new KeywordInfoConverter());
xs.registerConverter(new SettingsInfoConverter());
xs.registerConverter(new MeasureConverter());
xs.registerConverter(new MultimapConverter(xs.getMapper()));
// register Virtual structure handling
registerBreifMapComplexType("virtualTable", VirtualTable.class);
registerBreifMapComplexType("coverageView", CoverageView.class);
registerBreifMapComplexType("dimensionInfo", DimensionInfoImpl.class);
callback = new Callback();
// setup white list of accepted classes
xs.allowTypeHierarchy(Info.class);
xs.allowTypeHierarchy(Multimap.class);
xs.allowTypeHierarchy(JAIInfo.class);
xs.allowTypes(new Class[] {DynamicProxyMapper.DynamicProxy.class});
xs.allowTypes(new String[] { "java.util.Collections$SingletonList" });
xs.allowTypesByWildcard(new String[] { "org.geoserver.catalog.**" });
xs.allowTypesByWildcard(new String[] { "org.geoserver.security.**" });
}
/**
* Use this method to register complex types that cannot be simply represented as a string
* in a {@link BreifMapConverter}. The {@code typeId} will be used as a type discriminator
* in the brief map, as well as the element root for the complex object to be converted.
* @param typeId
* @param clazz
*/
public void registerBreifMapComplexType(String typeId, Class clazz) {
forwardBreifMap.put(typeId, clazz);
backwardBreifMap.put(clazz, typeId);
xs.allowTypes(new Class[] { clazz });
}
public XStream getXStream() {
return xs;
}
public ClassAliasingMapper getClassAliasingMapper() {
return (ClassAliasingMapper) xs.getMapper().lookupMapperOfType( ClassAliasingMapper.class );
}
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
}
public void setGeoServer(GeoServer geoserver) {
this.geoserver = geoserver;
}
public GeoServerSecurityManager getSecurityManager() {
return GeoServerExtensions.bean(GeoServerSecurityManager.class);
}
public void setCallback(Callback callback) {
this.callback = callback;
}
public void setReferenceByName(boolean referenceByName) {
this.referenceByName = referenceByName;
}
public void setExcludeIds() {
xs.omitField(impl(WorkspaceInfo.class), "id");
xs.omitField(impl(NamespaceInfo.class), "id");
xs.omitField(impl(StoreInfo.class), "id");
xs.omitField(impl(StyleInfo.class), "id");
xs.omitField(impl(ResourceInfo.class), "id");
xs.omitField(impl(LayerInfo.class), "id");
xs.omitField(impl(LayerGroupInfo.class), "id" );
xs.omitField(impl(AttributeTypeInfo.class), "id");
xs.omitField(impl(ServiceInfo.class), "id");
}
public void setHideFeatureTypeAttributes() {
xs.omitField(FeatureTypeInfoImpl.class, "attributes");
}
public void setEncryptPasswordFields(boolean encryptPasswordFields) {
this.encryptPasswordFields = encryptPasswordFields;
}
public boolean isEncryptPasswordFields() {
return encryptPasswordFields;
}
/**
* Sets the minimum level at which messages should be logged by the persister.
* <p>
* When this level is set even messages that the underlying logger is configured to emit will
* be skipped.
* </p>
*/
public void setLoggingLevel(Level level) {
this.forceLevel = level;
}
/*
* Helper to log a message forgoing if level is <= forceLevel.
*/
void log(Level level, String msg) {
if((LOGGER.isLoggable(level) && forceLevel.intValue() <= level.intValue())){
LOGGER.log(level, msg);
}
}
/**
* Saves an object to persistence.
*
* @param obj The object to save.
* @param out The stream to save the object to.
*
* @throws IOException
*/
public void save(Object obj, OutputStream out) throws IOException {
//unwrap dynamic proxies
obj = unwrapProxies( obj );
xs.toXML(obj, new OutputStreamWriter( out, "UTF-8" ));
}
/**
* Unwraps any proxies around the object.
* <p>
* If the object is not being proxied it is passed back.
* </p>
*/
public static Object unwrapProxies( Object obj ) {
obj = SecureCatalogImpl.unwrap( obj );
obj = GeoServerImpl.unwrap( obj );
obj = CatalogImpl.unwrap( obj );
return obj;
}
/**
* Loads an object from peristence.
*
* @param in The input stream to read the object from.
* @param clazz The class of the expected object.
*
* @throws IOException
*/
public <T> T load(InputStream in, Class<T> clazz ) throws IOException {
T obj = clazz.cast( xs.fromXML( in ) );
//call resolve() to ensure that any references created during loading
// get resolved to actual objects, for instance for links from datastores
// to workspaces
if ( obj instanceof CatalogImpl ) {
((CatalogImpl)obj).resolve();
}
return obj;
}
/**
* Builds a converter that will marshal/unmarshal the target class by reference, that is, by
* storing the object id as opposed to fully serializing it
* @param clazz
*
*/
public ReferenceConverter buildReferenceConverter(Class clazz) {
return new ReferenceConverter(clazz);
}
/**
* Same as {@link #buildReferenceConverter(Class)}, but works against a collection of objects
* @param clazz
*
*/
public ReferenceCollectionConverter buildReferenceCollectionConverter(Class clazz) {
return new ReferenceCollectionConverter(clazz);
}
/**
* Sets up mappings from interface to implementation classes.
*
*/
protected void initImplementationDefaults(XStream xs) {
//configuration
xs.addDefaultImplementation(GeoServerInfoImpl.class, GeoServerInfo.class);
xs.addDefaultImplementation(SettingsInfoImpl.class, SettingsInfo.class);
xs.addDefaultImplementation(LoggingInfoImpl.class, LoggingInfo.class);
xs.addDefaultImplementation(JAIInfoImpl.class, JAIInfo.class);
xs.addDefaultImplementation(JAIEXTInfoImpl.class, JAIEXTInfo.class);
xs.addDefaultImplementation(CoverageAccessInfoImpl.class, CoverageAccessInfo.class);
xs.addDefaultImplementation(ContactInfoImpl.class, ContactInfo.class);
xs.addDefaultImplementation(AttributionInfoImpl.class, AttributionInfo.class);
//catalog
xs.addDefaultImplementation(CatalogImpl.class, Catalog.class);
xs.addDefaultImplementation(NamespaceInfoImpl.class, NamespaceInfo.class);
xs.addDefaultImplementation(WorkspaceInfoImpl.class, WorkspaceInfo.class);
xs.addDefaultImplementation(DataStoreInfoImpl.class, DataStoreInfo.class);
xs.addDefaultImplementation(WMSStoreInfoImpl.class, WMSStoreInfo.class);
xs.addDefaultImplementation(CoverageStoreInfoImpl.class, CoverageStoreInfo.class);
xs.addDefaultImplementation(StyleInfoImpl.class, StyleInfo.class);
xs.addDefaultImplementation(LegendInfoImpl.class, LegendInfo.class);
xs.addDefaultImplementation(FeatureTypeInfoImpl.class, FeatureTypeInfo.class );
xs.addDefaultImplementation(CoverageInfoImpl.class, CoverageInfo.class);
xs.addDefaultImplementation(WMSLayerInfoImpl.class, WMSLayerInfo.class);
xs.addDefaultImplementation(CoverageDimensionImpl.class, CoverageDimensionInfo.class);
xs.addDefaultImplementation(MetadataLinkInfoImpl.class, MetadataLinkInfo.class);
xs.addDefaultImplementation(AttributeTypeInfoImpl.class, AttributeTypeInfo.class );
xs.addDefaultImplementation(LayerInfoImpl.class, LayerInfo.class);
xs.addDefaultImplementation(LayerGroupInfoImpl.class, LayerGroupInfo.class );
xs.addDefaultImplementation(LayerIdentifier.class, LayerIdentifierInfo.class );
xs.addDefaultImplementation(AuthorityURL.class, AuthorityURLInfo.class );
//supporting objects
xs.addDefaultImplementation(GridGeometry2D.class, GridGeometry.class );
xs.addDefaultImplementation(DefaultGeographicCRS.class, CoordinateReferenceSystem.class);
//collections
xs.addDefaultImplementation(ArrayList.class, List.class);
xs.addDefaultImplementation(ArrayListMultimap.class, Multimap.class);
}
protected Class impl(Class interfce) {
//special case case classes, they don't get registered as default implementations
// only concrete classes do
if (interfce == ServiceInfo.class) {
return ServiceInfoImpl.class;
}
if (interfce == StoreInfo.class) {
return StoreInfoImpl.class;
}
if (interfce == ResourceInfo.class) {
return ResourceInfoImpl.class;
}
Class clazz = getXStream().getMapper().defaultImplementationOf(interfce);
if (clazz == null) {
throw new RuntimeException("No default mapping for " + interfce);
}
return clazz;
}
/**
* Custom reflection provider which unwraps proxies, and skips empty collections
* and maps.
*/
class CustomReflectionProvider extends SunUnsafeReflectionProvider {
public CustomReflectionProvider( FieldDictionary fd ) {
super( fd );
}
@Override
public void visitSerializableFields(Object object, Visitor visitor) {
super.visitSerializableFields(object, new VisitorWrapper(visitor));
}
class VisitorWrapper implements ReflectionProvider.Visitor {
Visitor wrapped;
public VisitorWrapper( Visitor wrapped ) {
this.wrapped = wrapped;
}
public void visit(String name, Class type, Class definedIn,
Object value) {
//skip empty collections + maps
if ( value instanceof Collection && ((Collection)value).isEmpty() ) {
return;
}
if ( value instanceof Map && ((Map)value).isEmpty() ) {
return;
}
//unwrap any proxies
value = unwrapProxies(value);
wrapped.visit( name, type, definedIn, value);
}
}
}
//
// custom converters
//
//simple object converters
/**
* Map converter which encodes a map more breifly than the standard map converter.
*/
protected class BreifMapConverter extends MapConverter {
static final String ENCRYPTED_FIELDS_KEY = "org.geoserver.config.encryptedFields";
public BreifMapConverter() {
super(getXStream().getMapper());
}
@Override
public boolean canConvert(Class type) {
//handle all types of maps
return Map.class.isAssignableFrom(type);
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
Set<String> encryptionFields = (Set<String>)context.get(ENCRYPTED_FIELDS_KEY);
if (encryptionFields==null) {
encryptionFields=Collections.emptySet();
}
GeoServerSecurityManager secMgr = encryptPasswordFields ? getSecurityManager() : null;
Map map = (Map) source;
for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
Map.Entry entry = (Map.Entry) iterator.next();
if ( entry.getValue() == null ) {
continue;
}
writer.startNode("entry");
writer.addAttribute( "key", entry.getKey().toString());
if ( entry.getValue() != null ) {
Object value = entry.getValue();
String complexTypeId = getComplexTypeId(value.getClass());
if(complexTypeId == null) {
String str = Converters.convert(value, String.class);
if(str == null) {
str = value.toString();
}
if (encryptionFields.contains(entry.getKey()) && secMgr != null) {
str = secMgr.getConfigPasswordEncryptionHelper().encode(str);
}
writer.setValue(str);
} else {
writer.startNode(complexTypeId);
context.convertAnother(value);
writer.endNode();
}
}
writer.endNode();
}
}
@Override
protected void populateMap(HierarchicalStreamReader reader,
UnmarshallingContext context, Map map) {
while (reader.hasMoreChildren()) {
reader.moveDown();
//we support four syntaxes here:
// 1) <key>value</key>
// 2) <key><type>value</type></key>
// 3) <entry key="">value</entry>
// 4) <entry>
// <type>key</type>
// <type>value</type>
// </entry>
String key = reader.getNodeName();
Object value = null;
if ( "entry".equals( key ) ) {
if ( reader.getAttribute( "key") != null ) {
//this is case 3
key = reader.getAttribute( "key" );
// in this case we also support complex objects
if(reader.hasMoreChildren()) {
reader.moveDown();
String typeId = reader.getNodeName();
value = context.convertAnother(null, getComplexTypeClass(typeId));
reader.moveUp();
} else {
value = reader.getValue();
}
}
else if ( reader.hasMoreChildren() ){
//this is case 4
reader.moveDown();
key = reader.getValue();
reader.moveUp();
reader.moveDown();
value = reader.getValue();
reader.moveUp();
}
}
else {
boolean old = false;
if (reader.hasMoreChildren()) {
//this handles case 2
old = true;
reader.moveDown();
}
value = readItem(reader, context, map);
if ( old ) {
reader.moveUp();
}
}
map.put(key, value);
reader.moveUp();
}
}
@Override
protected Object readItem(HierarchicalStreamReader reader, UnmarshallingContext context,
Object current) {
return reader.getValue();
}
private Class getComplexTypeClass(String typeId) {
return forwardBreifMap.get(typeId);
}
private String getComplexTypeId(Class clazz) {
String typeId = backwardBreifMap.get(clazz);
if(typeId == null) {
List<Class> matches = new ArrayList<Class>();
collectSuperclasses(clazz, matches);
for (Iterator it = matches.iterator(); it.hasNext();) {
Class sper = (Class) it.next();
if(backwardBreifMap.get(sper) == null) {
it.remove();
}
}
if(matches.size() > 1) {
Comparator comparator = new Comparator<Class>() {
public int compare(Class c1, Class c2) {
if (c2.isAssignableFrom(c1)) {
return -1;
} else {
return 1;
}
}
};
Collections.sort(matches, comparator);
}
if(matches.size() > 0) {
typeId = backwardBreifMap.get(matches.get(0));
}
}
return typeId;
}
void collectSuperclasses(Class clazz, List<Class> matches) {
matches.add(clazz);
if(clazz.getSuperclass() == null && clazz.getInterfaces().length == 0) {
return;
}
if(clazz.getSuperclass() != null) {
collectSuperclasses(clazz.getSuperclass(), matches);
}
for(Class iface : clazz.getInterfaces()) {
collectSuperclasses(iface, matches);
}
}
}
/**
* Custom converter for the special metadata map.
*/
class MetadataMapConverter extends BreifMapConverter {
@Override
public boolean canConvert(Class type) {
return MetadataMap.class.equals(type) || super.canConvert(type);
}
@Override
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
if ( source instanceof MetadataMap) {
MetadataMap mdmap = (MetadataMap) source;
source = mdmap.getMap();
}
super.marshal(source, writer, context);
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
Map map = (Map) super.unmarshal(reader, context);
if ( !(map instanceof MetadataMap ) ) {
map = new MetadataMap(map);
}
return map;
}
}
/**
* Converter for Google {@link Multimap} objects
*/
public static class MultimapConverter extends AbstractCollectionConverter {
public MultimapConverter(Mapper mapper) {
super(mapper);
}
public boolean canConvert(Class clazz) {
return Multimap.class.isAssignableFrom(clazz);
}
public void marshal(Object value, HierarchicalStreamWriter writer,
MarshallingContext context) {
Multimap map = (Multimap) value;
// similar to BreifMapConverter, but handling the multimap nature of this thing
for (Object key : map.keySet()) {
for (Object v : map.get(key)) {
if (v != null) {
writer.startNode("entry");
writer.addAttribute("key", key.toString());
writeItem(v, context, writer);
writer.endNode();
}
}
}
}
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
ArrayListMultimap<Object, Object> map = ArrayListMultimap.create();
while (reader.hasMoreChildren()) {
reader.moveDown();
String key = reader.getAttribute("key");
Object value = null;
// in this case we also support complex objects
while (reader.hasMoreChildren()) {
reader.moveDown();
value = readItem(reader, context, map);
reader.moveUp();
}
reader.moveUp();
if (value != null) {
map.put(key, value);
}
}
return map;
}
}
/**
* Converters which encodes an object by a reference, or its id.
*/
//class ReferenceConverter extends AbstractSingleValueConverter {
class ReferenceConverter implements Converter {
Class clazz;
public ReferenceConverter( Class clazz ) {
this.clazz = clazz;
}
public boolean canConvert(Class type) {
return clazz.isAssignableFrom( type );
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
//could be a proxy, unwrap it
source = CatalogImpl.unwrap( source );
//gets its id
String id = (String) OwsUtils.get( source, "id" );
if ( id != null && !referenceByName) {
writer.startNode("id");
writer.setValue( id );
writer.endNode();
callback.postEncodeReference( source, id, null, writer, context );
}
else {
//use name if no id set
String name = (String) OwsUtils.get( source, "name" );
//use workspace name as a prefix if available
String wsName = null;
if (OwsUtils.has(source, "workspace")) {
WorkspaceInfo workspace = (WorkspaceInfo) OwsUtils.get(source, "workspace");
if (workspace != null) {
wsName = workspace.getName();
}
}
if ( name != null ) {
writer.startNode("name");
if(wsName == null) {
writer.setValue( name );
} else {
writer.setValue( wsName + ":" + name );
}
writer.endNode();
callback.postEncodeReference( source, name, wsName, writer, context );
}
else {
throw new IllegalArgumentException( "Unable to marshal reference with no id or name.");
}
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
String ref = null;
String pre = null;
if ( reader.hasMoreChildren() ) {
while(reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
if ("workspace".equals(nodeName)) {
if (reader.hasMoreChildren()) {
//specified as <workspace><name>[name]</name></workspace>
reader.moveDown();
pre = reader.getValue();
reader.moveUp();
}
else {
//specified as <workspace>[name]</workspace>
pre = reader.getValue();
}
}
else if("name".equals(nodeName) || "id".equals(nodeName) || "prefix".equals(nodeName)) {
ref = reader.getValue();
}
reader.moveUp();
}
}
else {
ref = reader.getValue();
}
Object proxy = ResolvingProxy.create( ref, pre, clazz );
Object resolved = proxy;
if ( catalog != null ) {
resolved = ResolvingProxy.resolve( catalog, proxy );
}
if(unwrapNulls) {
return CatalogImpl.unwrap( resolved );
} else {
return resolved != null ? CatalogImpl.unwrap( resolved ) : proxy;
}
//
}
}
class ReferenceCollectionConverter extends LaxCollectionConverter {
Class clazz;
Class[] subclasses;
public ReferenceCollectionConverter(Class clazz) {
super( getXStream().getMapper() );
this.clazz = clazz;
}
public ReferenceCollectionConverter(Class clazz, Class... subclasses) {
super( getXStream().getMapper() );
this.clazz = clazz;
this.subclasses = subclasses;
}
@Override
protected void writeItem(Object item, MarshallingContext context,
HierarchicalStreamWriter writer) {
ClassAliasingMapper cam =
(ClassAliasingMapper) mapper().lookupMapperOfType( ClassAliasingMapper.class );
String elementName = cam.serializedClass( clazz );
if ( elementName == null ) {
elementName = cam.serializedClass( item.getClass() );
}
writer.startNode(elementName);
if(item != null) {
if(subclasses != null) {
Class theClass = null;
for (Class clazz : subclasses) {
if(clazz.isInstance(item)) {
theClass = clazz;
break;
}
}
if(theClass == null) {
throw new ConversionException("Unexpected item "
+ item + " whose type is not among: " + subclasses);
}
String typeName = cam.serializedClass( theClass );
writer.addAttribute("type", typeName);
}
context.convertAnother( item, new ReferenceConverter( clazz ) );
} else if (writer instanceof JettisonStaxWriter) {
/*
* GEOS-7771 / GEOS-7873
* Workaround for an array serialization bug in jettison 1.0.1
* Can be removed when we upgrade to jettison 1.2
*/
writer.setValue("null");
try {
Field outField = StaxWriter.class.getDeclaredField("out");
Field nodesField = MappedXMLStreamWriter.class.getDeclaredField("nodes");
outField.setAccessible(true);
nodesField.setAccessible(true);
FastStack nodes = (FastStack) nodesField.get(outField.get(writer.underlyingWriter()));
if (nodes.peek() instanceof JSONArray) {
nodes.pop();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
writer.endNode();
}
@Override
protected Object readItem(HierarchicalStreamReader reader,
UnmarshallingContext context, Object current) {
Class theClass = clazz;
if(subclasses != null) {
String attribute = reader.getAttribute("type");
if(attribute != null) {
theClass = mapper().realClass(attribute);
}
}
return context.convertAnother( current, theClass, new ReferenceConverter( theClass ) );
}
}
/**
* Converter which unwraps proxies in a collection.
*/
class ProxyCollectionConverter extends CollectionConverter {
public ProxyCollectionConverter(Mapper mapper) {
super(mapper);
}
@Override
protected void writeItem(Object item, MarshallingContext context,
HierarchicalStreamWriter writer) {
super.writeItem(unwrapProxies(item), context, writer);
}
}
/**
* Converter for coordinate reference system objects that converts by SRS code.
*/
public static class SRSConverter extends AbstractSingleValueConverter {
public boolean canConvert(Class type) {
return CoordinateReferenceSystem.class.isAssignableFrom(type);
}
@Override
public String toString(Object obj) {
CoordinateReferenceSystem crs = (CoordinateReferenceSystem) obj;
try {
Integer epsg = CRS.lookupEpsgCode(crs, false);
if (epsg != null) {
return "EPSG:" + epsg;
}
}
catch (FactoryException e) {
XStreamPersister.LOGGER.warning( "Could not determine epsg code of crs, encoding as WKT");
}
return new CRSConverter().toString(crs);
}
@Override
public Object fromString(String str) {
if ( str.toUpperCase().startsWith( "EPSG:") ) {
try {
return CRS.decode( str );
}
catch (Exception e) {
XStreamPersister.LOGGER.log( Level.WARNING, "Error decode epsg code: "+str, e );
}
}
else {
try {
return CRS.parseWKT( str );
}
catch (FactoryException e) {
XStreamPersister.LOGGER.log( Level.WARNING, "Error decode wkt: "+str, e );
}
}
return null;
}
}
/**
* Converter for coordinate reference system objects that converts by WKT.
*
*/
public static class CRSConverter extends AbstractSingleValueConverter {
@Override
public boolean canConvert(Class type) {
return CoordinateReferenceSystem.class.isAssignableFrom(type);
}
@Override
public String toString(Object obj) {
return ((Formattable)obj).toWKT(2, false);
}
@Override
public Object fromString(String str) {
try {
return CRS.parseWKT( str );
}
catch (Exception e) {
try {
return new SRSConverter().fromString( str );
}
catch( Exception e1 ) {}
throw new RuntimeException( e );
}
}
}
/**
* Converter for coverage grid geometry.
*
*/
class GridGeometry2DConverter extends AbstractReflectionConverter {
public GridGeometry2DConverter() {
super( GridGeometry2D.class );
}
@Override
protected void doMarshal(Object source,
HierarchicalStreamWriter writer, MarshallingContext context) {
GridGeometry2D g = (GridGeometry2D) source;
MathTransform tx = g.getGridToCRS();
writer.addAttribute("dimension", String.valueOf(g.getGridRange().getDimension()));
//grid range
StringBuffer low = new StringBuffer();
StringBuffer high = new StringBuffer();
for (int r = 0; r < g.getGridRange().getDimension(); r++) {
low.append(g.getGridRange().getLow(r)).append(" ");
high.append(g.getGridRange().getHigh(r)+1).append(" ");
}
low.setLength(low.length()-1);
high.setLength(high.length()-1);
writer.startNode("range");
writer.startNode( "low" ); writer.setValue( low.toString() ); writer.endNode();
writer.startNode( "high" ); writer.setValue( high.toString() ); writer.endNode();
writer.endNode();
//transform
if (tx instanceof AffineTransform) {
AffineTransform atx = (AffineTransform) tx;
writer.startNode("transform");
writer.startNode("scaleX"); writer.setValue(Double.toString( atx.getScaleX())); writer.endNode();
writer.startNode("scaleY"); writer.setValue(Double.toString( atx.getScaleY())); writer.endNode();
writer.startNode("shearX"); writer.setValue(Double.toString( atx.getShearX())); writer.endNode();
writer.startNode("shearY"); writer.setValue(Double.toString( atx.getShearY())); writer.endNode();
writer.startNode("translateX"); writer.setValue(Double.toString( atx.getTranslateX())); writer.endNode();
writer.startNode("translateY"); writer.setValue(Double.toString( atx.getTranslateY())); writer.endNode();
writer.endNode();
}
//crs
writer.startNode("crs");
context.convertAnother( g.getCoordinateReferenceSystem(),
new SingleValueConverterWrapper( new SRSConverter() ) );
writer.endNode();
}
@Override
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
int[] high = null, low = null;
CoordinateReferenceSystem crs = null;
AffineTransform2D gridToCRS = null;
GeneralGridEnvelope gridRange = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if("range".equals(reader.getNodeName())) {
while (reader.hasMoreChildren()) {
reader.moveDown();
if("low".equals(reader.getNodeName())) {
low = toIntArray( reader.getValue() );
}
else if ("high".equals(reader.getNodeName())) {
high = toIntArray( reader.getValue() );
}
reader.moveUp();
}
// new grid range
gridRange = new GeneralGridEnvelope(low, high);
}
if ( "crs".equals( reader.getNodeName() ) ) {
crs = (CoordinateReferenceSystem) context.convertAnother( null, CoordinateReferenceSystem.class,
new SingleValueConverterWrapper( new SRSConverter() ));
}
else if ("transform".equals(reader.getNodeName())) {
double sx = 1.0, sy = 1.0, shx = 0.0, shy = 0.0, tx = 0.0, ty = 0.0;
while (reader.hasMoreChildren()) {
reader.moveDown();
if("scaleX".equals(reader.getNodeName())) {
sx = Double.parseDouble( reader.getValue() );
}
else if ("scaleY".equals(reader.getNodeName())) {
sy = Double.parseDouble( reader.getValue() );
}
else if ("shearX".equals(reader.getNodeName())) {
shx = Double.parseDouble( reader.getValue() );
}
else if ("shearY".equals(reader.getNodeName())) {
shy = Double.parseDouble( reader.getValue() );
}
else if ("translateX".equals(reader.getNodeName())) {
tx = Double.parseDouble( reader.getValue() );
}
else if ("translateY".equals(reader.getNodeName())) {
ty = Double.parseDouble( reader.getValue() );
}
reader.moveUp();
}
// set tranform
gridToCRS = new AffineTransform2D(sx, shx, shy, sy, tx, ty);
}
reader.moveUp();
}
GridGeometry2D gg = new GridGeometry2D( gridRange, gridToCRS, crs );
return serializationMethodInvoker.callReadResolve(gg);
}
int[] toIntArray( String s ) {
String[] split = s.split( " " );
int[] ints = new int[split.length];
for ( int i = 0; i < split.length; i++ ) {
ints[i] = Integer.parseInt( split[i] );
}
return ints;
}
}
class NumberRangeConverter extends AbstractReflectionConverter {
@Override
public boolean canConvert(Class clazz) {
return NumberRange.class.isAssignableFrom( clazz );
}
@Override
public void marshal(Object original, HierarchicalStreamWriter writer,
MarshallingContext context) {
NumberRange range = (NumberRange) original;
writer.startNode("min");
if ( Double.isInfinite( ((Number)range.getMinValue()).doubleValue() ) ) {
context.convertAnother( "-inf" );
}
else {
context.convertAnother( range.getMinValue() );
}
writer.endNode();
writer.startNode("max");
if ( Double.isInfinite( ((Number)range.getMaxValue()).doubleValue() )) {
context.convertAnother( "inf");
}
else {
context.convertAnother( range.getMaxValue() );
}
writer.endNode();
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
//JD: we handle infinite manually b/c the json serializer chokes on inifinte values
// b/c JSON does not support it
Double min = null, max = null;
while( reader.hasMoreChildren() ) {
reader.moveDown();
if ( "min".equals( reader.getNodeName() ) ) {
if ( !"-inf".equals( reader.getValue() ) ) {
min = Double.parseDouble( reader.getValue() );
}
}
if ( "max".equals( reader.getNodeName() ) ) {
if ( !"inf".equals( reader.getValue() ) ) {
max = Double.parseDouble( reader.getValue() );
}
}
reader.moveUp();
}
min = min != null ? min : Double.NEGATIVE_INFINITY;
max = max != null ? max : Double.POSITIVE_INFINITY;
return NumberRange.create( min.doubleValue(), true, max.doubleValue(), true );
}
}
//catalog object converters
/**
* Base class for all custom reflection based converters.
*/
protected class AbstractReflectionConverter extends ReflectionConverter {
Class clazz;
public AbstractReflectionConverter() {
this(Object.class);
}
public AbstractReflectionConverter(Class clazz) {
super(getXStream().getMapper(),getXStream().getReflectionProvider());
this.clazz = clazz;
}
@Override
public boolean canConvert(Class type) {
return clazz.isAssignableFrom( type );
}
@Override
protected void doMarshal(Object source,
HierarchicalStreamWriter writer, MarshallingContext context) {
super.doMarshal(source, writer, context);
postDoMarshal(source,writer,context);
}
protected void postDoMarshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
}
}
/**
* Converter for workspaces and namespaces.
*/
class SpaceInfoConverter extends AbstractReflectionConverter {
@Override
public boolean canConvert(Class type) {
return WorkspaceInfo.class.isAssignableFrom(type) ||
NamespaceInfo.class.isAssignableFrom(type);
}
@Override
protected void postDoMarshal(Object source,
HierarchicalStreamWriter writer, MarshallingContext context) {
if ( source instanceof WorkspaceInfo ) {
callback.postEncodeWorkspace( (WorkspaceInfo)source,writer,context );
}
else {
callback.postEncodeNamespace( (NamespaceInfo) source,writer,context );
}
}
}
/**
* Converter for all {@link CatalogInfo} resources. Obtains the appropriate catalog object in
* {@link #instantiateNewInstance(HierarchicalStreamReader, UnmarshallingContext)} prior to
* reading in the XStream request, so that primitive objects are appropriately initialized.
*
* Supported implementations of {@link AbstractCatalogResource} must implement
* {@link XStreamPersister.Callback.getCatalogObject()} and
* {@link XStreamPersister.Callback.getObjectClass()} when providing an instance of
* {@link XStreamPersister.Callback} to {@link XStreamPersister} in
* {@link AbstractCatalogResource.configurePersister(XStreamPersister, DataFormat)}
*/
protected class AbstractCatalogInfoConverter extends AbstractReflectionConverter {
public AbstractCatalogInfoConverter(Class clazz) {
super(clazz);
}
private <T> void unsafeCopy(Object source, Object target, Class<T> clazz) {
OwsUtils.copy((T)source, (T)target, clazz);
}
@Override
protected Object instantiateNewInstance(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Object emptyObject = super.instantiateNewInstance(reader, context);
//Acquire the current CatalogInfo being acted upon object before unmarshaling the Xstream
Object catalogObject = callback.getCatalogObject();
if (catalogObject != null) {
Class i = callback.getObjectClass();
if (i != null) {
unsafeCopy(catalogObject, emptyObject, i);
}
}
return emptyObject;
}
}
/**
* Converter for {@link DataStoreInfo}, {@link CoverageStoreInfo}, and {@link WMSStoreInfo}
*/
class StoreInfoConverter extends AbstractCatalogInfoConverter {
public StoreInfoConverter() {
super(StoreInfo.class);
}
@Override
protected void doMarshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
GeoServerSecurityManager secMgr = encryptPasswordFields ? getSecurityManager() : null;
if (secMgr != null && secMgr.isInitialized()) {
//set the hint for the map converter as to which fields to encode in the connection
// parameter of this store
context.put(BreifMapConverter.ENCRYPTED_FIELDS_KEY,
secMgr.getConfigPasswordEncryptionHelper().getEncryptedFields((StoreInfo)source));
}
super.doMarshal(source, writer, context);
}
@Override
protected void postDoMarshal(Object result,
HierarchicalStreamWriter writer, MarshallingContext context) {
StoreInfo store = (StoreInfo) result;
if ( store instanceof DataStoreInfo ) {
callback.postEncodeDataStore( (DataStoreInfo) store, writer, context );
} else if( store instanceof CoverageStoreInfo ){
callback.postEncodeCoverageStore( (CoverageStoreInfo) store, writer, context );
} else if (store instanceof WMSStoreInfo){
callback.postEncodeWMSStore( (WMSStoreInfo) store, writer, context );
} else {
throw new IllegalArgumentException("Unknown store type: "
+ (store == null ? "null" : store.getClass().getName()));
}
}
@Override
public Object doUnmarshal(Object result,
HierarchicalStreamReader reader, UnmarshallingContext context) {
StoreInfo store = (StoreInfo) super.doUnmarshal(result, reader, context);
// 2.1.3+ backwards compatibility check
if (store instanceof WMSStoreInfo) {
WMSStoreInfo wmsStore = (WMSStoreInfo) store;
MetadataMap metadata = wmsStore.getMetadata();
Integer maxConnections = null;
Integer connectTimeout = null;
Integer readTimeout = null;
if (metadata != null) {
maxConnections = metadata.get("maxConnections", Integer.class);
connectTimeout = metadata.get("connectTimeout", Integer.class);
readTimeout = metadata.get("readTimeout", Integer.class);
metadata.remove("maxConnections");
metadata.remove("connectTimeout");
metadata.remove("readTimeout");
}
if (wmsStore.getMaxConnections() <= 0) {
wmsStore.setMaxConnections(maxConnections != null
&& maxConnections.intValue() > 0 ? maxConnections
: WMSStoreInfoImpl.DEFAULT_MAX_CONNECTIONS);
}
if (wmsStore.getConnectTimeout() <= 0) {
wmsStore.setConnectTimeout(connectTimeout != null
&& connectTimeout.intValue() > 0 ? connectTimeout
: WMSStoreInfoImpl.DEFAULT_CONNECT_TIMEOUT);
}
if (wmsStore.getReadTimeout() <= 0) {
wmsStore.setReadTimeout(readTimeout != null && readTimeout.intValue() > 0 ? readTimeout
: WMSStoreInfoImpl.DEFAULT_READ_TIMEOUT);
}
}
//process any parameters that require decryption
GeoServerSecurityManager secMgr = encryptPasswordFields ? getSecurityManager() : null;
if (secMgr != null) {
secMgr.getConfigPasswordEncryptionHelper().decode(store);
}
log(Level.INFO, "Loaded store '" + store.getName() + "', " + (store.isEnabled() ? "enabled" : "disabled"));
return store;
}
}
/**
* Converter for multi hash maps containing coverage stores and data stores.
*/
static class StoreMultiHashMapConverter implements Converter {
public boolean canConvert(Class type) {
return MultiHashMap.class.equals(type);
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
MultiHashMap map = (MultiHashMap) source;
for (Object v : map.values()) {
if (v instanceof DataStoreInfo) {
writer.startNode("dataStore");
context.convertAnother(v);
writer.endNode();
}
if (v instanceof CoverageStoreInfo ) {
writer.startNode( "coverageStore" );
context.convertAnother(v);
writer.endNode();
}
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
MultiHashMap map = new MultiHashMap();
while( reader.hasMoreChildren() ) {
reader.moveDown();
Object o = 0;
if ( "dataStore".equals( reader.getNodeName() ) ) {
o = context.convertAnother( map, DataStoreInfoImpl.class );
}
else {
o = context.convertAnother( map, CoverageStoreInfoImpl.class );
}
map.put( o.getClass(), o );
reader.moveUp();
}
return map;
}
}
/**
* Converter for handling maps containing workspaces and namespaces.
*
*/
class SpaceMapConverter implements Converter {
String name;
public SpaceMapConverter( String name ) {
this.name = name;
}
public boolean canConvert(Class type) {
return Map.class.isAssignableFrom(type);
}
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
Map map = (Map) source;
for (Object o : map.entrySet()) {
Map.Entry e = (Map.Entry) o;
if ( e.getKey() == null ) {
continue;
}
writer.startNode(name);
if ( map.get( null ) == e.getValue() ) {
writer.addAttribute("default", "true");
}
context.convertAnother(e.getValue());
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Map map = new HashMap();
while( reader.hasMoreChildren() ) {
reader.moveDown();
boolean def = "true".equals( reader.getAttribute( "default") );
if ( "namespace".equals( name ) ) {
NamespaceInfoImpl ns = (NamespaceInfoImpl) context.convertAnother( map, NamespaceInfoImpl.class );
map.put( ns.getPrefix() , ns );
if ( def ) {
map.put( null, ns );
}
log(Level.INFO, "Loading namespace '" + ns.getPrefix() + "'" );
}
else {
WorkspaceInfoImpl ws = (WorkspaceInfoImpl) context.convertAnother( map, WorkspaceInfoImpl.class );
map.put( ws.getName() , ws );
if ( def ) {
map.put( null, ws );
}
log(Level.INFO, "Loading workspace '" + ws.getName() + "'" );
}
reader.moveUp();
}
return map;
}
}
/**
* Base converter for handling resources.
*/
class ResourceInfoConverter extends AbstractCatalogInfoConverter {
public ResourceInfoConverter() {
this(ResourceInfo.class);
}
public ResourceInfoConverter(Class clazz ) {
super(clazz);
}
public Object doUnmarshal(Object result,
HierarchicalStreamReader reader, UnmarshallingContext context) {
ResourceInfo obj = (ResourceInfo) super.doUnmarshal(result, reader, context);
String enabled = obj.isEnabled() ? "enabled" : "disabled";
String type = obj instanceof CoverageInfo ? "coverage" :
obj instanceof FeatureTypeInfo ? "feature type" : "resource";
log(Level.INFO, "Loaded " + type + " '" + obj.getName() + "', " + enabled);
return obj;
}
}
/**
* Converter for feature types.
*/
class FeatureTypeInfoConverter extends ResourceInfoConverter {
public FeatureTypeInfoConverter() {
super(FeatureTypeInfo.class);
}
@Override
protected void postDoMarshal(Object result,
HierarchicalStreamWriter writer, MarshallingContext context) {
FeatureTypeInfoImpl featureType = (FeatureTypeInfoImpl) result;
//ensure null list does not result
if ( featureType.getAttributes() == null ){
featureType.setAttributes(new ArrayList());
}
if ( featureType.getResponseSRS() == null) {
featureType.setResponseSRS(new ArrayList());
}
if( featureType.getMetadata() == null) {
featureType.setMetadata(new MetadataMap());
}
callback.postEncodeFeatureType(featureType, writer, context);
}
}
/**
* Converter for feature types.
*/
class CoverageInfoConverter extends ResourceInfoConverter {
public CoverageInfoConverter() {
super( CoverageInfo.class );
}
@Override
protected void postDoMarshal(Object result,
HierarchicalStreamWriter writer, MarshallingContext context) {
callback.postEncodeCoverage((CoverageInfo)result, writer, context);
}
}
/**
* Converter for layers.
*/
class LayerInfoConverter extends AbstractCatalogInfoConverter {
public LayerInfoConverter() {
super( LayerInfo.class );
}
@Override
protected void doMarshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
// write out the name, which is a derived property now
// TODO: remove this when resource/publishing split is done
LayerInfo l = (LayerInfo) source;
writer.startNode("name");
if (l.getName() != null) {
writer.setValue(l.getName());
}
writer.endNode();
// {
// String authUrlsSerializedForm = AuthorityURLInfoInfoListConverter.toString(l
// .getAuthorityURLs());
// if (null != authUrlsSerializedForm) {
// l.getMetadata().put("authorityURLs", authUrlsSerializedForm);
// }
// }
//
// {
// String identifiersSerializedForm = LayerIdentifierInfoListConverter.toString(l
// .getIdentifiers());
// if (null != identifiersSerializedForm) {
// l.getMetadata().put("identifiers", identifiersSerializedForm);
// }
// }
super.doMarshal(source, writer, context);
}
@Override
protected void postDoMarshal(Object result,
HierarchicalStreamWriter writer, MarshallingContext context) {
/*
LayerInfo l = (LayerInfo) result;
writer.startNode("resource");
context.convertAnother( l.getResource(), new ReferenceConverter( ResourceInfo.class ) );
writer.endNode();
*/
callback.postEncodeLayer( (LayerInfo) result, writer, context );
}
@Override
public Object doUnmarshal(Object result, HierarchicalStreamReader reader,
UnmarshallingContext context) {
LayerInfoImpl li = (LayerInfoImpl) super.doUnmarshal(result, reader, context);
MetadataMap metadata = li.getMetadata();
if (li.getAuthorityURLs() == null && metadata != null) {
String serialized = metadata.get("authorityURLs", String.class);
List<AuthorityURLInfo> authorities;
if (serialized == null) {
authorities = new ArrayList<AuthorityURLInfo>(1);
} else {
authorities = AuthorityURLInfoInfoListConverter.fromString(serialized);
}
li.setAuthorityURLs(authorities);
}
if (li.getIdentifiers() == null && metadata != null) {
String serialized = metadata.get("identifiers", String.class);
List<LayerIdentifierInfo> identifiers;
if (serialized == null) {
identifiers = new ArrayList<LayerIdentifierInfo>(1);
} else {
identifiers = LayerIdentifierInfoListConverter.fromString(serialized);
}
li.setIdentifiers(identifiers);
}
return li;
}
}
/**
* Converter for layer groups.
*/
class LayerGroupInfoConverter extends AbstractCatalogInfoConverter {
public LayerGroupInfoConverter() {
super( LayerGroupInfo.class );
}
@Override
protected void postDoMarshal(Object result,
HierarchicalStreamWriter writer, MarshallingContext context) {
callback.postEncodeLayerGroup((LayerGroupInfo)result, writer, context);
}
@Override
public Object doUnmarshal(Object result, HierarchicalStreamReader reader,
UnmarshallingContext context) {
LayerGroupInfoImpl lgi = (LayerGroupInfoImpl) super
.doUnmarshal(result, reader, context);
if (lgi.getMode() == null) {
lgi.setMode(Mode.SINGLE);
}
lgi.convertLegacyLayers();
MetadataMap metadata = lgi.getMetadata();
/**
* If we're upgrading from a 2.2.x server we have to read
* property 'title' from metadata
*/
if (lgi.getTitle() == null && metadata != null) {
String title = metadata.get("title", String.class);
if (title != null) {
lgi.setTitle(title);
metadata.remove("title");
}
}
/**
* If we're upgrading from a 2.2.x server we have to read
* property 'abstract' from metadata
*/
if (lgi.getAbstract() == null && metadata != null) {
String abstractTxt = metadata.get("abstract", String.class);
if (abstractTxt != null) {
lgi.setAbstract(abstractTxt);
metadata.remove("abstract");
}
}
if (lgi.getAuthorityURLs() == null && metadata != null) {
String serialized = metadata.get("authorityURLs", String.class);
List<AuthorityURLInfo> authorities;
if (serialized == null) {
authorities = new ArrayList<AuthorityURLInfo>(1);
} else {
authorities = AuthorityURLInfoInfoListConverter.fromString(serialized);
}
lgi.setAuthorityURLs(authorities);
}
if (lgi.getIdentifiers() == null && metadata != null) {
String serialized = metadata.get("identifiers", String.class);
List<LayerIdentifierInfo> identifiers;
if (serialized == null) {
identifiers = new ArrayList<LayerIdentifierInfo>(1);
} else {
identifiers = LayerIdentifierInfoListConverter.fromString(serialized);
}
lgi.setIdentifiers(identifiers);
}
return lgi;
}
@Override
protected void doMarshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
// LayerGroupInfo l = (LayerGroupInfo) source;
//
// {
// String authUrlsSerializedForm = AuthorityURLInfoInfoListConverter.toString(l
// .getAuthorityURLs());
// if (null != authUrlsSerializedForm) {
// l.getMetadata().put("authorityURLs", authUrlsSerializedForm);
// }
// }
//
// {
// String identifiersSerializedForm = LayerIdentifierInfoListConverter.toString(l
// .getIdentifiers());
// if (null != identifiersSerializedForm) {
// l.getMetadata().put("identifiers", identifiersSerializedForm);
// }
// }
super.doMarshal(source, writer, context);
}
}
class VirtualTableConverter implements Converter {
public void marshal(Object source, HierarchicalStreamWriter writer,MarshallingContext context) {
VirtualTable vt = (VirtualTable) source;
writer.startNode("name");
writer.setValue(vt.getName());
writer.endNode();
writer.startNode("sql");
writer.setValue(vt.getSql());
writer.endNode();
writer.startNode("escapeSql");
writer.setValue(Boolean.toString(vt.isEscapeSql()));
writer.endNode();
if(vt.getPrimaryKeyColumns() != null) {
for(String pk : vt.getPrimaryKeyColumns()) {
writer.startNode("keyColumn");
writer.setValue(pk);
writer.endNode();
}
}
if(vt.getGeometries() != null) {
for (String geom : vt.getGeometries()) {
writer.startNode("geometry");
writer.startNode("name");
writer.setValue(geom);
writer.endNode();
writer.startNode("type");
writer.setValue(Geometries.getForBinding(vt.getGeometryType(geom)).getName());
writer.endNode();
writer.startNode("srid");
writer.setValue(String.valueOf(vt.getNativeSrid(geom)));
writer.endNode();
writer.endNode();
}
}
if(vt.getParameterNames().size() > 0) {
for(String name : vt.getParameterNames()) {
VirtualTableParameter param = vt.getParameter(name);
writer.startNode("parameter");
writer.startNode("name");
writer.setValue(name);
writer.endNode();
if(param.getDefaultValue() != null) {
writer.startNode("defaultValue");
writer.setValue(param.getDefaultValue());
writer.endNode();
}
if(param.getValidator() != null) {
if(param.getValidator() instanceof RegexpValidator) {
writer.startNode("regexpValidator");
writer.setValue(((RegexpValidator) param.getValidator()).getPattern().pattern());
writer.endNode();
} else {
throw new RuntimeException("Cannot handle this type of validator," +
" please extend the VirtualTableConverter " + param.getValidator().getClass());
}
}
writer.endNode();
}
}
}
@SuppressWarnings("rawtypes")
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
String name = null;
String sql = null;
List<String> primaryKeys = new ArrayList<String>();
List<VirtualTableParameter> params = new ArrayList<VirtualTableParameter>();
List<String> geomNames=new ArrayList<String>();
List<Class> types=new ArrayList<Class>();
List<Integer> srids= new ArrayList<Integer>();
Boolean escapeSql = false;
while (reader.hasMoreChildren()) {
reader.moveDown();
if (reader.getNodeName().equals("keyColumn")) {
primaryKeys.add(reader.getValue());
} else if (reader.getNodeName().equals("geometry")) {
String geomName = null;
Class type = null;
int srid = -1;
while (reader.hasMoreChildren()) {
reader.moveDown();
if (reader.getNodeName().equals("name"))
geomName = reader.getValue();
else if (reader.getNodeName().equals("type")) {
Geometries geomType = Geometries.getForName(reader.getValue());
type = geomType == null ? Geometry.class : geomType.getBinding();
} else if (reader.getNodeName().equals("srid")) {
srid = Converters.convert(reader.getValue(), Integer.class);
}
reader.moveUp();
}
geomNames.add(geomName);
types.add(type);
srids.add(srid);
} else if (reader.getNodeName().equals("parameter")) {
String pname = null;
String defaultValue = null;
Validator validator = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if (reader.getNodeName().equals("name")) {
pname = reader.getValue();
} else if (reader.getNodeName().equals("defaultValue")) {
defaultValue = reader.getValue();
} else if (reader.getNodeName().equals("regexpValidator")) {
validator = new RegexpValidator(reader.getValue());
}
reader.moveUp();
}
if (pname == null) {
throw new IllegalArgumentException(
"Expect name but could not find it in property tag");
}
params.add(new VirtualTableParameter(pname, defaultValue, validator));
} else if (reader.getNodeName().equals("escapeSql")) {
escapeSql = Boolean.valueOf(reader.getValue());
} else if (reader.getNodeName().equals("name")) {
name = reader.getValue();
} else if (reader.getNodeName().equals("sql")) {
sql = reader.getValue();
}
reader.moveUp();
}
if (name == null) {
throw new IllegalArgumentException("Expect name but could not find it");
}
if (sql == null) {
throw new IllegalArgumentException("Expect sql but could not find it");
}
VirtualTable vt = new VirtualTable(name, sql, false);
for(int i=0; i<geomNames.size();i++){
vt.addGeometryMetadatata(geomNames.get(i), types.get(i), srids.get(i));
}
for (VirtualTableParameter p : params) {
vt.addParameter(p);
}
vt.setEscapeSql(escapeSql);
vt.setPrimaryKeyColumns(primaryKeys);
return vt;
}
public boolean canConvert(Class type) {
return VirtualTable.class.isAssignableFrom(type);
}
}
static class KeywordInfoConverter extends AbstractSingleValueConverter {
static Pattern RE = Pattern.compile(
"([^\\\\]+)(?:\\\\@language=([^\\\\]+)\\\\;)?(?:\\\\@vocabulary=([^\\\\]+)\\\\;)?");
@Override
public boolean canConvert(Class type) {
return Keyword.class.isAssignableFrom(type);
}
@Override
public Object fromString(String str) {
Matcher m = RE.matcher(str);
if (!m.matches()) {
throw new IllegalArgumentException(
String.format("%s does not match regular expression: %s", str, RE));
}
KeywordInfo kw = new Keyword(m.group(1));
if (m.group(2) != null) {
kw.setLanguage(m.group(2));
}
if (m.group(3) != null) {
kw.setVocabulary(m.group(3));
}
return kw;
}
@Override
public String toString(Object obj) {
KeywordInfo kw = (KeywordInfo) obj;
StringBuilder sb = new StringBuilder();
sb.append(kw.getValue());
if (kw.getLanguage() != null) {
sb.append("\\@language=").append(kw.getLanguage()).append("\\;");
}
if (kw.getVocabulary() != null) {
sb.append("\\@vocabulary=").append(kw.getVocabulary()).append("\\;");
}
return sb.toString();
}
}
static class MeasureConverter extends AbstractSingleValueConverter {
org.geotools.util.Converter str2Measure = new MeasureConverterFactory().createConverter(String.class, Measure.class, null);
org.geotools.util.Converter measure2Str = new MeasureConverterFactory().createConverter(Measure.class, String.class, null);
@Override
public boolean canConvert(Class type) {
return Measure.class.isAssignableFrom(type);
}
@Override
public Object fromString(String str) {
try {
return str2Measure.convert(str, Measure.class);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
@Override
public String toString(Object obj) {
try {
return measure2Str.convert(obj, String.class);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}
class KeywordListConverter extends LaxCollectionConverter {
public KeywordListConverter() {
super(getXStream().getMapper());
}
@Override
protected Object readItem(HierarchicalStreamReader reader, UnmarshallingContext context,
Object current) {
return context.convertAnother(current, Keyword.class);
}
@Override
protected void writeItem(Object item, MarshallingContext context,
HierarchicalStreamWriter writer) {
ExtendedHierarchicalStreamWriterHelper.startNode(writer, "string", Keyword.class);
context.convertAnother(item);
writer.endNode();
}
}
/**
* Converter for SettingsInfo class
*/
class SettingsInfoConverter extends AbstractReflectionConverter {
public SettingsInfoConverter() {
super(SettingsInfo.class);
}
public Object doUnmarshal(Object result,
HierarchicalStreamReader reader, UnmarshallingContext context) {
SettingsInfoImpl obj = (SettingsInfoImpl) super.doUnmarshal(result, reader, context);
if(obj.getMetadata() == null){
obj.setMetadata(new MetadataMap());
}
if(obj.getContact() == null){
obj.setContact(new ContactInfoImpl());
}
if(obj.getClientProperties() == null){
obj.setClientProperties(new HashMap<Object, Object>());
}
return obj;
}
}
/**
* Converter for all {@link ServiceInfo} resources. Obtains the appropriate service object in
* {@link #instantiateNewInstance(HierarchicalStreamReader, UnmarshallingContext)} prior to
* reading in the XStream request, so that primitive objects are appropriately initialized.
*
* Supported implementations of {@link ServiceSettingsResource} must implement
* {@link XStreamPersister.Callback.getServiceObject()} and
* {@link XStreamPersister.Callback.getObjectClass()} when providing an instance of
* {@link XStreamPersister.Callback} to {@link XStreamPersister} in
* {@link ServiceSettingsResource.configurePersister(XStreamPersister, DataFormat)}
*/
public class ServiceInfoConverter extends AbstractReflectionConverter {
public ServiceInfoConverter() {
super(ServiceInfo.class);
}
public ServiceInfoConverter(Class<? extends ServiceInfo> clazz) {
super(clazz);
}
public Object doUnmarshal(Object result,
HierarchicalStreamReader reader, UnmarshallingContext context) {
ServiceInfoImpl obj = (ServiceInfoImpl) super.doUnmarshal(result, reader, context);
return obj;
}
@Override
protected Object instantiateNewInstance(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Object emptyObject = super.instantiateNewInstance(reader, context);
//Acquire the current CatalogInfo being acted upon object before unmarshaling the Xstream
Object serviceObject = callback.getServiceObject();
if (serviceObject != null) {
Class i = callback.getObjectClass();
if (i != null) {
OwsUtils.copy(serviceObject, emptyObject, i);
}
}
return emptyObject;
}
}
/**
* Converts a specific string though encryption and decryption
*
* @author Andrea Aime - GeoSolutions
*/
class EncryptedFieldConverter extends AbstractSingleValueConverter {
@Override
public boolean canConvert(Class type) {
return String.class.equals(type);
}
@Override
public Object fromString(String str) {
if (str == null) {
return null;
}
if (encryptPasswordFields) {
GeoServerSecurityManager secMgr = getSecurityManager();
if (secMgr != null) {
return secMgr.getConfigPasswordEncryptionHelper().decode(str);
}
}
return str;
}
@Override
public String toString(Object obj) {
if (obj == null) {
return null;
}
if (encryptPasswordFields) {
GeoServerSecurityManager secMgr = getSecurityManager();
if (secMgr != null) {
return secMgr.getConfigPasswordEncryptionHelper().encode((String) obj);
}
}
return obj.toString();
}
}
}