Java tutorial
/* (c) 2014 Boundless, http://boundlessgeo.com * This code is licensed under the GPL 2.0 license. */ package com.boundlessgeo.geoserver.api.controllers; import static org.geoserver.catalog.Predicates.and; import static org.geoserver.catalog.Predicates.equal; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import com.boundlessgeo.geoserver.util.RecentObjectCache.Ref; import org.apache.commons.lang.WordUtils; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CoverageInfo; import org.geoserver.catalog.CoverageStoreInfo; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.Info; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.NamespaceInfo; import org.geoserver.catalog.PublishedInfo; import org.geoserver.catalog.ResourceInfo; import org.geoserver.catalog.ResourcePool; 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.util.CloseableIterator; import org.geoserver.config.GeoServer; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.ows.URLMangler.URLType; import org.geoserver.ows.util.ResponseUtils; import org.geoserver.platform.GeoServerResourceLoader; import org.geoserver.platform.resource.Files; import org.geoserver.platform.resource.Paths; import org.geotools.coverage.grid.io.AbstractGridFormat; import org.geotools.coverage.grid.io.GridCoverage2DReader; import org.geotools.data.DataAccess; import org.geotools.data.DataAccessFactory; import org.geotools.data.DataStore; import org.geotools.data.FileDataStoreFactorySpi; import org.geotools.data.DataAccessFactory.Param; import org.geotools.data.ows.Layer; import org.geotools.data.Parameter; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.FeatureTypes; import org.geotools.feature.NameImpl; import org.geotools.filter.text.ecql.ECQL; import org.geotools.filter.visitor.DuplicatingFilterVisitor; import org.geotools.geometry.jts.Geometries; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.jdbc.JDBCDataStoreFactory; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.resources.coverage.FeatureUtilities; import org.geotools.util.Converters; import org.geotools.util.NullProgressListener; import org.geotools.util.logging.Logging; import org.ocpsoft.pretty.time.PrettyTime; import org.opengis.coverage.grid.Format; import org.opengis.coverage.grid.GridCoverageReader; import org.opengis.feature.Feature; import org.opengis.feature.Property; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AssociationDescriptor; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.Name; import org.opengis.feature.type.PropertyDescriptor; import org.opengis.feature.type.PropertyType; import org.opengis.filter.Filter; import org.opengis.filter.expression.PropertyName; import org.opengis.referencing.FactoryException; import org.opengis.referencing.ReferenceIdentifier; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.operation.TransformException; import org.opengis.util.GenericName; import com.boundlessgeo.geoserver.Proj; import com.boundlessgeo.geoserver.api.exceptions.NotFoundException; import com.boundlessgeo.geoserver.json.JSONArr; import com.boundlessgeo.geoserver.json.JSONObj; import com.google.common.base.Function; import com.google.common.base.Throwables; import com.google.common.collect.Iterables; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; /** * Helper for encoding/decoding objects to/from JSON. */ public class IO { static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ssXXX"; /** Kind of provider */ static public enum Kind { FILE, DATABASE, WEB, GENERIC; public String toString() { return name().toLowerCase(); } static Kind of(ResourceInfo resource) { return of(resource.getStore()); } static Kind of(DataAccessFactory format) { Set<String> params = new HashSet<String>(); for (Param info : format.getParametersInfo()) { params.add(info.getName()); } if (format instanceof FileDataStoreFactorySpi) { return Kind.FILE; } if (format instanceof JDBCDataStoreFactory) { return Kind.DATABASE; } if (params.contains("wms") || params.contains("WFSDataStoreFactory:GET_CAPABILITIES_URL")) { return Kind.WEB; } return Kind.GENERIC; } static Kind of(StoreInfo store) { if (store instanceof CoverageStoreInfo) { String url = ((CoverageStoreInfo) store).getURL(); if (url.startsWith("file")) { return Kind.FILE; } else if (url.startsWith("http") || url.startsWith("https") || url.startsWith("ftp") || url.startsWith("sftp")) { return Kind.WEB; } } Map<String, Serializable> params = store.getConnectionParameters(); if (params == null) { return Kind.GENERIC; } else if (params.containsKey("dbtype")) { return Kind.DATABASE; } else if (store instanceof WMSStoreInfo) { return Kind.WEB; } else if (params.keySet().contains("directory") || params.keySet().contains("file")) { return Kind.FILE; } for (Object value : params.values()) { if (value == null) continue; if (value instanceof File || (value instanceof String && ((String) value).startsWith("file:")) || (value instanceof URL && ((URL) value).getProtocol().equals("file"))) { return Kind.FILE; } if ((value instanceof String && ((String) value).startsWith("http:")) || (value instanceof URL && ((URL) value).getProtocol().equals("http"))) { return Kind.WEB; } if (value instanceof String && ((String) value).startsWith("jdbc:")) { return Kind.DATABASE; } } return Kind.GENERIC; } } /** Type of content: raster, vector, service(wms layer), generic resource */ static public enum Type { RASTER, VECTOR, SERVICE, RESOURCE; public String toString() { return name().toLowerCase(); } static Type of(String resource) { return valueOf(resource.toUpperCase()); } static Type of(ResourceInfo resource) { if (resource instanceof CoverageInfo) { return Type.RASTER; } else if (resource instanceof FeatureTypeInfo) { return Type.VECTOR; } else if (resource instanceof WMSLayerInfo) { return Type.SERVICE; } return Type.RESOURCE; } static Type of(StoreInfo store) { if (store instanceof CoverageStoreInfo) { return Type.RASTER; } else if (store instanceof DataStoreInfo) { return Type.VECTOR; } else if (store instanceof WMSStoreInfo) { return Type.SERVICE; } return Type.RESOURCE; } } static Logger LOG = Logging.getLogger(IO.class); /** * Encodes a projection within the specified object. * * @return The object passed in. */ public static JSONObj proj(JSONObj obj, CoordinateReferenceSystem crs, String srs) { if (srs == null && crs != null) { try { srs = CRS.toSRS(crs); } catch (Exception e) { LOG.log(Level.WARNING, "Unable to determine srs from crs: " + crs, e); } } if (crs != null && crs.getName() != null) { ReferenceIdentifier name = crs.getName(); if (name instanceof GenericName) { obj.put("title", ((GenericName) name).tip().toString()); } else { obj.put("title", name.toString()); } } if (srs != null) { obj.put("srs", srs); } else { obj.put("srs", "UNKNOWN"); } if (crs == null && srs != null) { try { crs = CRS.decode(srs); } catch (Exception e) { LOG.log(Level.WARNING, "Unable to determine crs from srs: " + srs, e); } } if (crs != null) { // type obj.put("type", crs instanceof ProjectedCRS ? "projected" : crs instanceof GeographicCRS ? "geographic" : "other"); // units String units = null; try { // try to determine from actual crs String unit = crs.getCoordinateSystem().getAxis(0).getUnit().toString(); if ("ft".equals(unit) || "feets".equals(unit)) units = "ft"; } catch (Exception e) { LOG.log(Level.WARNING, "Unable to determine units from crs", e); } if (units == null) { // fallback: meters for projected, otherwise degrees units = crs instanceof ProjectedCRS ? "m" : "degrees"; } obj.put("unit", units); obj.put("wkt", crs.toString()); } return obj; } /** * Encodes a bounding box within the specified object. * * @return The object passed in. */ public static JSONObj bounds(JSONObj obj, org.opengis.geometry.Envelope bbox) { if (bbox == null) { return obj; } obj.put("west", bbox.getMinimum(0)).put("south", bbox.getMinimum(1)).put("east", bbox.getMaximum(0)) .put("north", bbox.getMaximum(1)); obj.putArray("center").add(bbox.getMedian(0)).add(bbox.getMedian(1)); return obj; } /** * Decodes a bounding box within the specified object. * * The parsed envelope. */ public static Envelope bounds(JSONObj obj) { return new Envelope(obj.doub("west"), obj.doub("east"), obj.doub("south"), obj.doub("north")); } /** * Decodes a projection within the specified object. * * @return The parsed projection, or null. * * @throws java.lang.IllegalArgumentException If the object has no 'srs' property or there was an error decoding * the srs. */ public static CoordinateReferenceSystem crs(JSONObj obj) throws Exception { return Proj.get().crs(srs(obj)); } /** * Decodes am srs within the specified projection object. * * @param obj JSON object with same structure as produced by {@link #proj(JSONObj,CoordinateReferenceSystem, String)}. * * @return The srs. * * @throws java.lang.IllegalArgumentException If the object has no 'srs' property. */ public static String srs(JSONObj obj) { String srs = obj.str("srs"); if (srs == null) { throw new IllegalArgumentException("Projection must have an 'srs' property"); } return srs; } /** * Encodes a workspace within the specified object. * * @param obj The object to encode within. * @param workspace The workspace to encode. * @param namespace The namespace corresponding to the workspace. * @param isDefault Flag indicating whether the workspace is the default. * * @return The object passed in. */ public static JSONObj workspace(JSONObj obj, WorkspaceInfo workspace, NamespaceInfo namespace, boolean isDefault) { obj.put("name", workspace.getName()); if (namespace != null) { obj.put("uri", namespace.getURI()); } obj.put("default", isDefault); return metadata(obj, workspace); } /* * Layers API */ /** * Generates a reasonable title for a layer. * @param layer * @return The layer title, or the resource title, or null */ static Object title(LayerInfo layer) { ResourceInfo r = layer.getResource(); return layer.getTitle() != null ? layer.getTitle() : r != null ? r.getTitle() : null; } /** * Generates a reasonable description for a layer. * @param layer * @return The layer abstract, or the resource abstract, or null */ static Object description(LayerInfo layer) { ResourceInfo r = layer.getResource(); return layer.getAbstract() != null ? layer.getAbstract() : r != null ? r.getAbstract() : null; } /** * Encodes a short layer description within the specified object * * @param json The object to encode within. * @param info The layer to encode * @param req The HTTP request * @return The encoded object */ public static JSONObj layer(JSONObj json, LayerInfo info, HttpServletRequest req) { String wsName = info.getResource().getNamespace().getPrefix(); json.put("name", info.getName()).put("workspace", wsName).put("url", url(req, "/layers/%s/%s", wsName, info.getName())); return json; } /** * Encodes a detailed layer or layer group description within the specified object. Delegates to * {@link #layerDetails(JSONObj, LayerInfo, HttpServletRequest)} and * {@link #layerDetails(JSONObj, LayerGroupInfo, HttpServletRequest)}. * * @param json The object to encode within. * @param layer The layer or layer group to encode * @param req The HTTP request * @return The encoded object */ public static JSONObj layerDetails(JSONObj obj, PublishedInfo layer, HttpServletRequest req) { if (layer == null) { return obj; } if (layer instanceof LayerInfo) { return layerDetails(obj, (LayerInfo) layer, req); } else if (layer instanceof LayerGroupInfo) { return layerDetails(obj, (LayerGroupInfo) layer, req); } else { return obj; } } /** * Encodes a layer group within the specified object. * * @return The object passed in. */ public static JSONObj layerDetails(JSONObj obj, LayerGroupInfo group, HttpServletRequest req) { String wsName = group.getWorkspace().getName(); obj.put("name", group.getName()).put("workspace", wsName) .put("url", url(req, "/maps/%s/%s", wsName, group.getName())).put("title", group.getTitle()) .put("description", group.getAbstract()).put("type", "map").put("group", group.getMode().name()); proj(obj.putObject("proj"), group.getBounds().getCoordinateReferenceSystem(), null); bbox(obj.putObject("bbox"), group); bounds(obj.putObject("projectionExtent"), CRS.getEnvelope(group.getBounds().getCoordinateReferenceSystem())); return obj; } /** * Encodes a layer within the specified object. * * @return The object passed in. */ @SuppressWarnings("unchecked") public static JSONObj layerDetails(JSONObj obj, LayerInfo layer, HttpServletRequest req) { String wsName = layer.getResource().getNamespace().getPrefix(); ResourceInfo r = layer.getResource(); Type type = Type.of(r); //type(r); obj.put("name", layer.getName()).put("workspace", wsName).put("title", title(layer)) .put("description", description(layer)).put("type", type.toString()); StoreInfo store = r.getStore(); if (req != null) { obj.putObject("resource").put("name", r.getName()).put("store", store.getName()) .put("workspace", wsName) .put("url", url(req, "/stores/%s/%s/%s", wsName, store.getName(), r.getName())); } StyleInfo style = layer.getDefaultStyle(); if (style == null) { throw new NotFoundException(String.format("Layer %s:%s has no default style", wsName, layer.getName())); } obj.putObject("style").put("name", style.getName()).put("workspace", style.getWorkspace() == null ? null : style.getWorkspace().getName()); JSONArr keywords = new JSONArr(); keywords.raw().addAll(r.keywordValues()); obj.put("keywords", keywords); proj(obj.putObject("proj"), r.getCRS(), r.getSRS()); bbox(obj.putObject("bbox"), r); IO.bounds(obj.putObject("projectionExtent"), CRS.getEnvelope(r.getCRS())); if (r instanceof FeatureTypeInfo) { FeatureTypeInfo ft = (FeatureTypeInfo) r; FeatureType schema; try { schema = ft.getFeatureType(); obj.put("geometry", geometry(layer)); schema(obj.putObject("schema"), schema, true); } catch (IOException e) { LOG.log(Level.WARNING, "Error looking up schema " + ft.getNativeName(), e); } } else if (r instanceof CoverageInfo) { obj.put("geometry", geometry(layer)); schemaGrid(obj.putObject("schema"), ((CoverageInfo) r), true); } else if (r instanceof WMSLayerInfo) { obj.put("geometry", geometry(layer)); } if (layer.getMetadata().containsKey("timeout")) { obj.put("timeout", layer.getMetadata().get("timeout")); } return metadata(obj, layer); } /** * Provides a text description of a resource * @return "raster", "vector", "wms", or "resource" */ static String type(ResourceInfo r) { if (r instanceof CoverageInfo) { return "raster"; } else if (r instanceof FeatureTypeInfo) { return "vector"; } else if (r instanceof WMSLayerInfo) { return "wms"; } else { return "resource"; } } /** * Provides a text description of a geometry * @return The geometry class name of a vector layer; or "raster", "layer", or "none" */ static String geometry(LayerInfo layer) { ResourceInfo r = layer.getResource(); if (r instanceof FeatureTypeInfo) { GeometryDescriptor gd = null; FeatureTypeInfo ft = (FeatureTypeInfo) r; try { FeatureType schema = ft.getFeatureType(); gd = schema.getGeometryDescriptor(); } catch (IOException e) { LOG.log(Level.WARNING, "Error looking up schema " + ft.getNativeName(), e); } if (gd == null) { return "none"; } @SuppressWarnings("unchecked") Geometries geomType = Geometries.getForBinding((Class<? extends Geometry>) gd.getType().getBinding()); return geomType.getName(); } if (r instanceof CoverageInfo) { return "raster"; } if (r instanceof WMSLayerInfo) { return "layer"; } return "none"; } /** * Encodes the bounding box of a layer group into the passed object * @param bbox The object to encode * @param l The layer group * @return The encoded object */ public static JSONObj bbox(JSONObj bbox, LayerGroupInfo l) { ReferencedEnvelope bounds = l.getBounds(); if (bounds != null) { bounds(bbox.putObject("native"), bounds); try { ReferencedEnvelope latLonBounds = bounds.transform(DefaultGeographicCRS.WGS84, true); bounds(bbox.putObject("lonlat"), latLonBounds); } catch (TransformException e) { } catch (FactoryException e) { } } return bbox; } /** * Encodes the bounding box of a resource into the passed object * @param bbox The object to encode * @param r The resource * @return The encoded object */ public static JSONObj bbox(JSONObj bbox, ResourceInfo r) { if (r.getNativeBoundingBox() != null) { bounds(bbox.putObject("native"), r.getNativeBoundingBox()); } else { // check if the crs is geographic, if so use lat lon if (r.getCRS() instanceof GeographicCRS && r.getLatLonBoundingBox() != null) { bounds(bbox.putObject("native"), r.getLatLonBoundingBox()); } } if (r.getLatLonBoundingBox() != null) { bounds(bbox.putObject("lonlat"), r.getLatLonBoundingBox()); } else { if (r.getNativeCRS() instanceof GeographicCRS && r.getLatLonBoundingBox() != null) { bounds(bbox.putObject("lonlat"), r.getLatLonBoundingBox()); } } return bbox; } /** * Encodes a Feature type schema into the passed object * @param schema The object to encode * @param type The feature type * @param details Flag to list details such as the namespace, and description, as well as any * filters or other constraints. * @return The encoded object */ public static JSONObj schema(JSONObj schema, FeatureType type, boolean details) { if (type != null) { schema.put("name", type.getName().getLocalPart()); schema.put("namespace", type.getName().getNamespaceURI()); schema.put("simple", type instanceof SimpleFeatureType); if (null != type.getGeometryDescriptor()) { schema.put("defaultGeometry", type.getGeometryDescriptor().getName().getLocalPart()); } JSONArr attributes = schema.putArray("attributes"); for (PropertyDescriptor d : type.getDescriptors()) { PropertyType t = d.getType(); final String NAME = d.getName().getLocalPart(); String kind; if (d instanceof GeometryDescriptor) { kind = "geometry"; } else if (d instanceof AttributeDescriptor) { kind = "attribute"; } else if (d instanceof AssociationDescriptor) { kind = "association"; } else { kind = "property"; } JSONObj property = attributes.addObject().put("name", NAME).put("property", kind).put("type", t.getBinding().getSimpleName()); if (d instanceof GeometryDescriptor) { GeometryDescriptor g = (GeometryDescriptor) d; proj(property.putObject("proj"), g.getCoordinateReferenceSystem(), null); } if (details) { property.put("namespace", d.getName().getNamespaceURI()).put("description", t.getDescription()) .put("min-occurs", d.getMinOccurs()).put("max-occurs", d.getMaxOccurs()) .put("nillable", d.isNillable()); int length = FeatureTypes.getFieldLength(d); if (length != FeatureTypes.ANY_LENGTH) { property.put("length", length); } if (d instanceof AttributeDescriptor) { AttributeDescriptor a = (AttributeDescriptor) d; property.put("default-value", a.getDefaultValue()); } if (!t.getRestrictions().isEmpty()) { JSONArr validate = property.putArray("validate"); for (Filter f : t.getRestrictions()) { String cql; try { Filter clean = (Filter) f.accept(new DuplicatingFilterVisitor() { public PropertyName visit(PropertyName e, Object extraData) { String n = e.getPropertyName(); return getFactory(extraData).property(".".equals(n) ? NAME : n, e.getNamespaceContext()); } }, null); cql = ECQL.toCQL(clean); } catch (Throwable ignore) { ignore.printStackTrace(); cql = f.toString(); } validate.add(cql); } } } } } return schema; } /** * Encodes a Feature into the passed array * @param arr The object to encode * @param feature The feature * @return The encoded object */ public static JSONArr feature(JSONArr arr, Feature feature) { for (Property p : feature.getProperties()) { arr.add(p.getValue() == null ? null : p.getValue().toString()); } return arr; } /** * Encodes a FeatureCollection into the passed array * @param arr The object to encode * @param features The feature collection * @return The encoded object */ public static JSONArr features(JSONArr arr, FeatureCollection<FeatureType, Feature> features) { FeatureIterator i = features.features(); while (i.hasNext()) { arr.add(feature(new JSONArr(), i.next())); } return arr; } /** * Generate schema for GridCoverageSchema (see {@link FeatureUtilities#wrapGridCoverage}). */ public static JSONObj schemaGrid(JSONObj schema, CoverageInfo info, boolean details) { if (info != null) { CoordinateReferenceSystem crs = info.getCRS() != null ? info.getCRS() : info.getNativeCRS(); schemaGrid(schema, crs, details); } return schema; } public static JSONObj schemaGrid(JSONObj schema, CoordinateReferenceSystem crs, boolean details) { schema.put("name", "GridCoverage"); schema.put("simple", true); JSONArr attributes = schema.putArray("attributes"); JSONObj geom = attributes.addObject().put("name", "geom").put("property", "geometry").put("type", "Polygon"); if (crs != null) { proj(geom.putObject("proj"), crs, null); } if (details) { geom.put("min-occurs", 0).put("max-occurs", 1).put("nillable", true).put("default-value", null); } JSONObj grid = attributes.addObject().put("name", "grid").put("property", "attribute").put("type", "grid"); if (details) { grid.put("binding", "GridCoverage").put("min-occurs", 0).put("max-occurs", 1).put("nillable", true) .put("default-value", null); } return schema; } private static PrettyTime PRETTY_TIME = new PrettyTime(); /* * Stores API */ /** * Encode a basic store description into the passed object * @param obj Object to encode * @param store The store * @param req HTTP request * @param geoServer GeoServer instance * @return The encoded object */ public static JSONObj store(JSONObj obj, StoreInfo store, HttpServletRequest req, GeoServer geoServer) { String name = store.getName(); obj.put("name", name).put("workspace", store.getWorkspace().getName()).put("enabled", store.isEnabled()) .put("description", store.getDescription()).put("format", store.getType()) .put("url", url(req, "/stores/%s/%s", store.getWorkspace().getName(), store.getName())); String source = source(store, geoServer); if (store instanceof DataStoreInfo) { try { obj.put("type", Kind.of(geoServer.getCatalog().getResourcePool().getDataStoreFactory((DataStoreInfo) store)) .name()); } catch (IOException e) { LOG.log(Level.WARNING, "Could not get data store factory for store " + source + ", generating type from connection parameters.", e); obj.put("type", Kind.of(store).name()); } } else { obj.put("type", Kind.of(store).name()); } obj.put("source", source).put("kind", Type.of(store).name()); return metadata(obj, store); } /** * Encode a detailed store description into the passed object * @param obj the bject to encode * @param store the store * @param req HTTP request * @param geoServer GeoServer instance * @return The encoded object */ public static JSONObj storeDetails(JSONObj json, StoreInfo store, HttpServletRequest req, GeoServer geoServer) throws IOException { store(json, store, req, geoServer); JSONObj connection = new JSONObj(); Map<String, Serializable> params = store.getConnectionParameters(); for (Entry<String, Serializable> param : params.entrySet()) { String key = param.getKey(); Object value = param.getValue(); String text = value == null ? null : value.toString(); connection.put(key, text); } if (store instanceof CoverageStoreInfo) { CoverageStoreInfo info = (CoverageStoreInfo) store; connection.put("raster", info.getURL()); } if (store instanceof WMSStoreInfo) { WMSStoreInfo info = (WMSStoreInfo) store; json.put("wms", info.getCapabilitiesURL()); } json.put("connection", connection); json.put("error", error(new JSONObj(), store.getError())); if (store.isEnabled()) { resources(store, json.putArray("resources"), geoServer); } json.put("layer-count", layerCount(store, geoServer)); return json; } static int layerCount(StoreInfo store, GeoServer geoServer) throws IOException { Catalog cat = geoServer.getCatalog(); WorkspaceInfo ws = store.getWorkspace(); Filter filter = and(equal("store", store), equal("namespace.prefix", ws.getName())); int count = 0; try (CloseableIterator<ResourceInfo> layers = cat.list(ResourceInfo.class, filter);) { while (layers.hasNext()) { ResourceInfo r = layers.next(); for (LayerInfo l : cat.getLayers(r)) { if (l != null) { count++; } } } } return count; } /** * Encode a list of layers into the passed object * @param r The resource containing the layers to list * @param list the object to encode * @param geoServer GeoServer instance * @return The encoded object * @throws IOException */ private static JSONArr layers(ResourceInfo r, JSONArr list, GeoServer geoServer) throws IOException { if (r != null) { Catalog cat = geoServer.getCatalog(); for (LayerInfo l : cat.getLayers(r)) { JSONObj obj = layerDetails(list.addObject(), l, null); } } return list; } /** * Encode a list of layers into the passed object * @param r The store containing the layers to list * @param list the object to encode * @param geoServer GeoServer instance * @return The encoded object * @throws IOException */ private static JSONArr layers(StoreInfo store, JSONArr list, GeoServer geoServer) throws IOException { Catalog cat = geoServer.getCatalog(); WorkspaceInfo ws = store.getWorkspace(); Filter filter = and(equal("store", store), equal("namespace.prefix", ws.getName())); try (CloseableIterator<ResourceInfo> layers = cat.list(ResourceInfo.class, filter);) { while (layers.hasNext()) { ResourceInfo r = layers.next(); for (LayerInfo l : cat.getLayers(r)) { layerDetails(list.addObject(), l, null); } } } return list; } /** * Encode a list of resources into the passed object * @param store the store containing the resources * @param list the object to encode * @param geoServer GeoServer instance * @return the encoded object * @throws IOException */ @SuppressWarnings("unchecked") private static JSONArr resources(StoreInfo store, JSONArr list, GeoServer geoServer) throws IOException { for (String resource : listResources(store)) { //resource( list.addObject(), store, resource, geoServer); JSONObj obj = list.addObject().put("name", resource); JSONArr layers = obj.putArray("layers"); Catalog cat = geoServer.getCatalog(); if (store instanceof CoverageStoreInfo) { // coverage store does not respect native name so we search by id for (CoverageInfo info : cat.getCoveragesByCoverageStore((CoverageStoreInfo) store)) { layers(info, layers, geoServer); } } else { Filter filter = and(equal("namespace.prefix", store.getWorkspace().getName()), equal("nativeName", resource)); try (CloseableIterator<ResourceInfo> published = cat.list(ResourceInfo.class, filter);) { while (published.hasNext()) { ResourceInfo info = published.next(); if (!info.getStore().getId().equals(store.getId())) { continue; // native name is not enough, double check store id } layers(info, layers, geoServer); } } } } return list; } /** * Encode the description of a resource into the passed object * @param obj the object to encode * @param store the resource * @param name the name of the resource * @param geoServer GeoServer instance * @return the encoded object * @throws IOException */ public static JSONObj resource(JSONObj obj, StoreInfo store, String name, GeoServer geoServer) throws IOException { obj.put("name", name); if (store instanceof DataStoreInfo) { DataStoreInfo data = (DataStoreInfo) store; @SuppressWarnings("rawtypes") DataAccess dataStore = data.getDataStore(new NullProgressListener()); FeatureType schema; org.geotools.data.ResourceInfo info; if (dataStore instanceof DataStore) { schema = ((DataStore) dataStore).getSchema(name); info = ((DataStore) dataStore).getFeatureSource(name).getInfo(); } else { NameImpl qname = new NameImpl(name); schema = dataStore.getSchema(qname); info = dataStore.getFeatureSource(qname).getInfo(); } String title = info.getTitle() == null ? WordUtils.capitalize(name) : info.getTitle(); String description = info.getDescription() == null ? "" : info.getDescription(); obj.put("title", title); obj.put("description", description); JSONArr keywords = obj.putArray("keywords"); keywords.raw().addAll(info.getKeywords()); bounds(obj.putObject("bounds"), info.getBounds()); schema(obj.putObject("schema"), schema, false); } if (store instanceof CoverageStoreInfo) { CoverageStoreInfo data = (CoverageStoreInfo) store; GridCoverageReader r = data.getGridCoverageReader(null, null); obj.put("title", WordUtils.capitalize(name)); obj.put("description", ""); if (r instanceof GridCoverage2DReader) { GridCoverage2DReader reader = (GridCoverage2DReader) r; CoordinateReferenceSystem crs = reader.getCoordinateReferenceSystem(name); schemaGrid(obj.putObject("schema"), crs, false); } else { schemaGrid(obj.putObject("schema"), AbstractGridFormat.getDefaultCRS(), false); } } JSONArr layers = obj.putArray("layers"); Catalog cat = geoServer.getCatalog(); if (store instanceof CoverageStoreInfo) { // coverage store does not respect native name so we search by id for (CoverageInfo info : cat.getCoveragesByCoverageStore((CoverageStoreInfo) store)) { layers(info, layers, geoServer); } } else { Filter filter = and(equal("namespace.prefix", store.getWorkspace().getName()), equal("nativeName", name)); try (CloseableIterator<ResourceInfo> published = cat.list(ResourceInfo.class, filter);) { while (published.hasNext()) { ResourceInfo info = published.next(); if (!info.getStore().getId().equals(store.getId())) { continue; // native name is not enough, double check store id } layers(info, layers, geoServer); } } } return obj; } private static Iterable<String> listResources(StoreInfo store) throws IOException { if (store instanceof DataStoreInfo) { return Iterables.transform(((DataStoreInfo) store).getDataStore(null).getNames(), new Function<Name, String>() { @Nullable @Override public String apply(@Nullable Name input) { return input.getLocalPart(); } }); } else if (store instanceof CoverageStoreInfo) { return Arrays .asList(((CoverageStoreInfo) store).getGridCoverageReader(null, null).getGridCoverageNames()); } else if (store instanceof WMSStoreInfo) { return Iterables.transform( ((WMSStoreInfo) store).getWebMapServer(null).getCapabilities().getLayerList(), new com.google.common.base.Function<Layer, String>() { @Nullable @Override public String apply(@Nullable Layer input) { return input.getName(); } }); } throw new IllegalStateException("Unrecognized store type"); } private static String source(StoreInfo store, GeoServer geoServer) { GeoServerResourceLoader resourceLoader = geoServer.getCatalog().getResourceLoader(); GeoServerDataDirectory dataDir = new GeoServerDataDirectory(resourceLoader); if (store instanceof CoverageStoreInfo) { CoverageStoreInfo coverage = (CoverageStoreInfo) store; return sourceURL(coverage.getURL(), dataDir); } Map<String, Serializable> params = ResourcePool.getParams(store.getConnectionParameters(), resourceLoader); if (params.containsKey("dbtype")) { // See JDBCDataStoreFactory for details String host = Converters.convert(params.get("host"), String.class); String port = Converters.convert(params.get("port"), String.class); String dbtype = Converters.convert(params.get("dbtype"), String.class); String schema = Converters.convert(params.get("schema"), String.class); String database = Converters.convert(params.get("database"), String.class); StringBuilder source = new StringBuilder(); source.append(host); if (port != null) { source.append(':').append(port); } source.append('/').append(dbtype).append('/').append(database); if (schema != null) { source.append('/').append(schema); } return source.toString(); } else if (store instanceof WMSStoreInfo) { String url = ((WMSStoreInfo) store).getCapabilitiesURL(); return url; } else if (params.keySet().contains("directory")) { String directory = Converters.convert(params.get("directory"), String.class); return sourceFile(directory, dataDir); } else if (params.keySet().contains("file")) { String file = Converters.convert(params.get("file"), String.class); return sourceFile(file, dataDir); } if (params.containsKey("url")) { String url = Converters.convert(params.get("url"), String.class); return sourceURL(url, dataDir); } for (Object value : params.values()) { if (value instanceof URL) { return source((URL) value, dataDir); } if (value instanceof File) { return source((File) value, dataDir); } if (value instanceof String) { String text = (String) value; if (text.startsWith("file:")) { return sourceURL(text, dataDir); } else if (text.startsWith("http:") || text.startsWith("https:") || text.startsWith("ftp:")) { return text; } } } return "undertermined"; } private static String source(File file, GeoServerDataDirectory dataDir) { File baseDirectory = dataDir.getResourceLoader().getBaseDirectory(); return file.isAbsolute() ? file.toString() : Paths.convert(baseDirectory, file); } private static String source(URL url, GeoServerDataDirectory dataDir) { File baseDirectory = dataDir.getResourceLoader().getBaseDirectory(); if (url.getProtocol().equals("file")) { File file = Files.url(baseDirectory, url.toExternalForm()); if (file != null && !file.isAbsolute()) { return Paths.convert(baseDirectory, file); } } return url.toExternalForm(); } private static String sourceURL(String url, GeoServerDataDirectory dataDir) { File baseDirectory = dataDir.getResourceLoader().getBaseDirectory(); File file = Files.url(baseDirectory, url); if (file != null) { return Paths.convert(baseDirectory, file); } return url; } private static String sourceFile(String file, GeoServerDataDirectory dataDir) { File baseDirectory = dataDir.getResourceLoader().getBaseDirectory(); File f = new File(file); return f.isAbsolute() ? file : Paths.convert(baseDirectory, f); } /* * Basic JSON Utilities */ /** Encode a date */ static JSONObj date(JSONObj obj, Date date) { String timestamp = new SimpleDateFormat(DATE_FORMAT).format(date); return obj.put("timestamp", timestamp).put("pretty", PRETTY_TIME.format(date)); } /** Metadata: created and modified */ static JSONObj metadata(JSONObj obj, Info i) { Date created = Metadata.created(i); if (created != null) { date(obj.putObject("created"), created); } Date modified = Metadata.modified(i); if (modified != null) { date(obj.putObject("modified"), modified); } return obj; } /** Encode an exception */ public static JSONObj error(JSONObj json, Throwable error) { if (error != null) { String message = null; JSONArr cause = new JSONArr(); for (Throwable t : Throwables.getCausalChain(error)) { if (message == null && t.getMessage() != null) { message = t.getMessage(); } StringBuilder trace = new StringBuilder(); for (StackTraceElement e : t.getStackTrace()) { trace.append(e.toString()).append('\n'); } cause.addObject().put("exception", t.getClass().getSimpleName()).put("message", t.getMessage()) .put("trace", trace.toString()); } if (message == null) { message = error.getClass().getSimpleName(); } json.put("message", message != null ? message : error.toString()).put("cause", cause).put("trace", Throwables.getStackTraceAsString(error)); } return json; } /** Encode a parameter */ public static JSONObj param(JSONObj json, Parameter<?> p) { if (p != null) { String title = p.getTitle() != null ? p.getTitle().toString() : WordUtils.capitalize(p.getName()); String description = p.getDescription() != null ? p.getDescription().toString() : null; JSONObj def = json.putObject(p.getName()); def.put("title", title).put("description", description).put("type", p.getType().getSimpleName()) .put("default", safeValue(p.getDefaultValue())).put("level", p.getLevel()) .put("required", p.isRequired()); if (!(p.getMinOccurs() == 1 && p.getMaxOccurs() == 1)) { def.putArray("occurs").add(p.getMinOccurs()).add(p.getMaxOccurs()); } if (p.metadata != null) { for (String key : p.metadata.keySet()) { if (Parameter.LEVEL.equals(key)) { continue; } def.put(key, p.metadata.get(key)); } } } return json; } public static void param(JSONObj json, Format g) { json.put("name", "raster").put("title", "URL").put("description", g.getDescription()) .put("type", URL.class.getSimpleName()).put("default", null).put("level", "user") .put("required", true); } static private Object safeValue(Object value) { if (value == null) { return null; } if (value instanceof String || value instanceof Number || value instanceof Boolean) { return value; } if (value instanceof java.util.TimeZone) { TimeZone zone = (TimeZone) value; return zone.getDisplayName(); } return value.toString(); } /** * Construct a local url from an HTTP request and a relative URL * @param req The HTTP request, containing the base URL * @param path Format string describing the relative url * @param args Arguments for formatting the relative url * @return The resolved URL. */ public static Object url(HttpServletRequest req, String path, Object... args) { if (req == null) { return null; } String baseURL = ResponseUtils.baseURL(req); String relative = String.format("/api" + path, args); String resolved = ResponseUtils.buildURL(baseURL, relative, null, URLType.SERVICE); return resolved; } /** Encode a RecentObjectCache reference */ public static JSONObj ref(JSONObj obj, Ref ref) { obj.put("name", ref.name); if (ref.workspace != null) { obj.put("workspace", ref.workspace); } date(obj.putObject("modified"), ref.modified); return obj; } }