/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.esri.gpt.catalog.arcgis.metadata;
import com.esri.gpt.framework.collection.StringSet;
import com.esri.gpt.framework.context.ApplicationConfiguration;
import com.esri.gpt.framework.http.HttpClientRequest;
import com.esri.gpt.framework.util.Val;
import com.esri.arcgisws.Envelope;
import com.esri.arcgisws.EnvelopeN;
import com.esri.arcgisws.GeographicCoordinateSystem;
import com.esri.arcgisws.SpatialReference;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Stores basic properties about a service.
*/
public class ServiceInfo {
/** class variables ========================================================= */
/** Logger */
private static final Logger LOGGER = Logger.getLogger(ServiceInfo.class.getName());
/** instance variables ====================================================== */
private String capabilities;
private String creator;
private String description;
private Envelope envelope;
private StringSet keywords = new StringSet();
private String name;
private String parentType;
private RDFPairs rdfPairs = new RDFPairs();
private String resourceUrl;
private String restUrl;
private String soapUrl;
private String thumbnailUrl;
private String type;
private ServiceInfo parentInfo;
private List<LayerInfo> layersInfo = new ArrayList<LayerInfo>();
private String copyright = "";
private String text = "";
/** constructors ============================================================ */
/** Default constructor. */
public ServiceInfo() {}
/** properties ============================================================== */
/**
* Gets text info.
* @return text info
*/
public String getText() {
return text;
}
/**
* Sets text info.
* @param text text info
*/
public void setText(String text) {
this.text = Val.chkStr(text);
}
/**
* Gets layers info.
* @return list of layers info
*/
public List<LayerInfo> getLayersInfo() {
return layersInfo;
}
/**
* Sets layers info.
* @param layersInfo list of layers info
*/
public void setLayersInfo(List<LayerInfo> layersInfo) {
this.layersInfo = layersInfo!=null? layersInfo: new ArrayList<LayerInfo>();
}
/**
* Gets copyright info.
* @return copyright info
*/
public String getCopyright() {
return copyright;
}
/**
* Sets copyright info.
* @param copyright copyright info
*/
public void setCopyright(String copyright) {
this.copyright = Val.chkStr(copyright);
}
/**
* Gets parent info.
* @return parent info
*/
public ServiceInfo getParentInfo() {
return parentInfo;
}
/**
* Sets parent info.
* @param parentInfo parent info
*/
public void setParentInfo(ServiceInfo parentInfo) {
this.parentInfo = parentInfo;
}
/**
* Gets the service creator.
* @return the service creator
*/
public String getCreator() {
return this.creator;
}
/**
* Sets the service creator.
* @param creator the service creator
*/
public void setCreator(String creator) {
this.creator = creator;
}
/**
* Gets the capabilities string associated with the service.
* @return the capabilities
*/
public String getCapabilities() {
return this.capabilities;
}
/**
* Sets the capabilities string associated with the service.
* @param capabilities the capabilities
*/
public void setCapabilities(String capabilities) {
this.capabilities = capabilities;
}
/**
* Gets the service description.
* @return the description
*/
public String getDescription() {
return this.description;
}
/**
* Sets the service description.
* @param description the description
*/
public void setDescription(String description) {
this.description = description;
}
/**
* Gets the service envelope.
* @return the envelope
*/
public Envelope getEnvelope() {
return this.envelope;
}
/**
* Sets the service envelope.
* @param envelope the envelope
*/
public void setEnvelope(Envelope envelope) {
this.envelope = envelope;
}
/**
* Gets the keywords associated with the service.
* @return the keywords
*/
public StringSet getKeywords() {
return this.keywords;
}
/**
* Gets the service name.
* @return the name
*/
public String getName() {
return this.name;
}
/**
* Sets the service name.
* @param name the name
*/
public void setName(String name) {
this.name = name;
}
/**
* Gets the parent service type.
* @return the parent type
*/
public String getParentType() {
return this.parentType;
}
/**
* Sets the parent service type.
* @param type the parent type
*/
public void setParentType(String type) {
this.parentType = type;
}
/**
* Gets the catch-all map of RDF pairs associated with the service.
* @return the catch-all map of RDF pairs
*/
private RDFPairs getRDFPairs() {
return this.rdfPairs;
}
/**
* Gets the resource URL for the service.
* @return the resource URL
*/
public String getResourceUrl() {
return this.resourceUrl;
}
/**
* Sets the resource URL for the service.
* @param url the resource URL
*/
public void setResourceUrl(String url) {
this.resourceUrl = url;
}
/**
* Gets the REST URL for the service.
* @return the REST URL
*/
public String getRestUrl() {
return this.restUrl;
}
/**
* Sets the REST URL for the service.
* @param url the REST URL
*/
public void setRestUrl(String url) {
this.restUrl = url;
}
/**
* Gets the SOAP URL for the service.
* @return the SOAP URL
*/
public String getSoapUrl() {
return this.soapUrl;
}
/**
* Sets the SOAP URL for the service.
* @param url the SOAP URL
*/
public void setSoapUrl(String url) {
this.soapUrl = url;
}
/**
* Gets the thumbnail URL for the service.
* @return the thumbnail URL
*/
public String getThumbnailUrl() {
return this.thumbnailUrl;
}
/**
* Sets the thumbnail URL for the service.
* @param url the thumbnail URL
*/
public void setThumbnailUrl(String url) {
this.thumbnailUrl = url;
}
/**
* Gets the service type.
* @return the type
*/
public String getType() {
return this.type;
}
/**
* Sets the service type.
* @param type the type
*/
public void setType(String type) {
this.type = type;
}
/** methods ================================================================= */
/**
* Adds one or more keywords to the keyword set.
* @param words the delimited list of words to add
* @param delimiter thedelimited (can be null)
*/
public void addKeywords(String words, String delimiter) {
words = Val.chkStr(words);
if (words.length() > 0) {
if ((delimiter != null) && (delimiter.length() > 0)) {
String[] tokens = words.split(delimiter);
for (String token: tokens) {
String word = Val.chkStr(token);
if (word.length() > 0) {
getKeywords().add(word);
}
}
} else {
getKeywords().add(words);
}
}
}
/**
* Adds a predicate/value pair to the catch-all map of RDF pairs associated with the service.
* @param predicate the predicate URI
* @param value the literal value
*/
public void addRDFPair(String predicate, String value) {
this.getRDFPairs().addValue(predicate,value);
}
/**
* Returns the Dublin Core metadata for the service.
* @param processor the ArcGIS Server service processor
* @return the Dublin Core metadata
* @throws Exception if an exception occurs
*/
public String asDublinCore(AGSProcessor processor) throws Exception {
return asDublinCore(
processor.getContext().getRequestContext().getApplicationConfiguration(),
processor.getContext().getHttpClient());
}
/**
* Returns the Dublin Core metadata for the service.
* @param cfg application configuration
* @param http HTTP client request
* @return the Dublin Core metadata
* @throws Exception if an exception occurs
*/
public String asDublinCore(ApplicationConfiguration cfg, HttpClientRequest http) throws Exception {
String url = this.getResourceUrl();
String tmp;
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
sb.append("\r<rdf:RDF");
sb.append(" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"");
sb.append(" xmlns:dc=\"http://purl.org/dc/elements/1.1/\"");
sb.append(" xmlns:dct=\"http://purl.org/dc/terms/\"");
sb.append(" xmlns:dcmiBox=\"http://dublincore.org/documents/2000/07/11/dcmi-box/\"");
sb.append(" xmlns:ows=\"http://www.opengis.net/ows\"");
sb.append(">");
sb.append("\r<rdf:Description");
if (url.length() > 0) {
sb.append(" rdf:about=\"").append(Val.escapeXml(url)).append("\"");
}
sb.append(">");
// identifier
if (url.length() > 0) {
sb.append("\r<dc:identifier>").append(Val.escapeXml(url)).append("</dc:identifier>");
}
// title, description, creator
tmp = Val.chkStr(this.getName());
if (tmp.length() > 0) {
sb.append("\r<dc:title>").append(Val.escapeXml(tmp)).append("</dc:title>");
}
tmp = Val.chkStr(this.getDescription());
if (tmp.length() > 0) {
sb.append("\r<dc:description>").append(Val.escapeXml(tmp)).append("</dc:description>");
}
tmp = Val.chkStr(this.getCreator());
if (tmp.length() > 0) {
sb.append("\r<dc:creator>").append(Val.escapeXml(tmp)).append("</dc:creator>");
}
// dc:format (mime-type)
// dc:type ??
// dc:date ??
// dct:alternative alternative name for the resource
// resource url
if (url.length() > 0) {
//scheme = "urn:x-esri:specification:ServiceType:ArcIMS:Metadata:Server";
String scheme = "urn:x-esri:specification:ServiceType:ArcGIS";
tmp = Val.chkStr(this.getParentType());
if (tmp.length() > 0) scheme += ":"+tmp;
tmp = Val.chkStr(this.getType());
if (tmp.length() > 0) scheme += ":"+tmp;
sb.append("\r<dct:references");
sb.append(" scheme=\"").append(Val.escapeXml(scheme)).append("\">");
sb.append(Val.escapeXml(url)).append("</dct:references>");
}
// thumbnail url
tmp = Val.chkStr(this.getThumbnailUrl());
if (tmp.length() > 0) {
String scheme = "urn:x-esri:specification:ServiceType:ArcIMS:Metadata:Thumbnail";
sb.append("\r<dct:references");
sb.append(" scheme=\"").append(Val.escapeXml(scheme)).append("\">");
sb.append(Val.escapeXml(tmp)).append("</dct:references>");
}
// keywords
for (String keyword: this.getKeywords()) {
sb.append("\r<dc:subject>").append(Val.escapeXml(keyword)).append("</dc:subject>");
}
// envelope
double[] env = this.validateEnvelope(cfg, http,this.getEnvelope());
if (env != null) {
String lower = env[0]+" "+env[1];
String upper = env[2]+" "+env[3];
sb.append("\r<ows:WGS84BoundingBox>");
sb.append("\r<ows:LowerCorner>").append(Val.escapeXml(lower)).append("</ows:LowerCorner>");
sb.append("\r<ows:UpperCorner>").append(Val.escapeXml(upper)).append("</ows:UpperCorner>");
sb.append("\r</ows:WGS84BoundingBox>");
}
// RDF pairs
if (this.getRDFPairs().size() > 0) {
sb.append("\r<dct:abstract>");
for (Map.Entry<String,RDFPair> entry: this.getRDFPairs().entrySet()) {
RDFPair rdfPair = entry.getValue();
for (String rdfValue: rdfPair.getValues()) {
sb.append("\r<rdf:value");
sb.append(" rdf:resource=\"").append(rdfPair.getPredicate()).append("\">");
sb.append(Val.escapeXml(rdfValue));
sb.append("\r</rdf:value>");
}
}
sb.append("\r</dct:abstract>");
}
sb.append("\r</rdf:Description>");
sb.append("\r</rdf:RDF>");
// TODO : logging here?
//System.err.println(sb.toString());
return sb.toString();
}
/**
* Returns a string representation of the object.
* @return the string
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getName());
sb.append("\n name=").append(this.getName());
sb.append("\n type=").append(this.getType());
sb.append("\n parentType=").append(this.getParentType());
sb.append("\n resourceUrl=").append(this.getResourceUrl());
sb.append("\n restUrl=").append(this.getRestUrl());
sb.append("\n soapUrl=").append(this.getSoapUrl());
sb.append("\n thumbnailUrl=").append(this.getThumbnailUrl());
sb.append("\n capabilities=").append(this.getCapabilities());
sb.append("\n creator=").append(this.getCreator());
sb.append("\n description=").append(this.getDescription());
sb.append("\n keywords=").append(this.getKeywords());
if (this.getEnvelope() != null) {
if (this.getEnvelope() instanceof EnvelopeN) {
EnvelopeN envn = (EnvelopeN)this.getEnvelope();
sb.append("\n envelope=");
sb.append(envn.getXMin()).append(", ").append(envn.getYMin()).append(", ");
sb.append(envn.getXMax()).append(", ").append(envn.getYMax());
if (envn.getSpatialReference() != null) {
sb.append(" wkid=").append(envn.getSpatialReference().getWKID());
}
}
}
return sb.toString();
}
private double[] validateEnvelope(AGSProcessor processor, Envelope env) throws Exception {
return validateEnvelope(
processor.getContext().getRequestContext().getApplicationConfiguration(),
processor.getContext().getHttpClient(), env);
}
private double[] validateEnvelope(ApplicationConfiguration cfg, HttpClientRequest http, Envelope env) throws Exception {
// initialize envelope properties
if ((env == null) || !(env instanceof EnvelopeN)) {
return null;
}
EnvelopeN envn = (EnvelopeN)env;
SpatialReference spref = envn.getSpatialReference();
if ((spref == null) || (spref.getWKID() == null)) {
return null;
}
int wkid = spref.getWKID().intValue();
double xmin = envn.getXMin();
double ymin = envn.getYMin();
double xmax = envn.getXMax();
double ymax = envn.getYMax();
// project if required
if (wkid == 4326) {
return new double[]{xmin,ymin,xmax,ymax};
//} else if (spref instanceof GeographicCoordinateSystem) {
// return new double[]{xmin,ymin,xmax,ymax};
} else {
// determine the rest url to the geometry service
String geomRestUrl = Val.chkStr(cfg.getInteractiveMap().getGeometryServiceUrl());
if (geomRestUrl.length() == 0) {
LOGGER.warning("A geometryServiceUrl has not been configured, envelope projection is unavailable.");
return null;
}
// build the projection service url
StringBuilder sb = new StringBuilder();
StringBuilder sbg = new StringBuilder();
sb.append(geomRestUrl).append("/project");
sb.append("?f=json").append("&inSR=").append(wkid).append("&outSR=4326");
sbg.append("{\"geometryType\":\"esriGeometryEnvelope\",\"geometries\":[{");
sbg.append("\"xmin\":").append(xmin);
sbg.append(",\"ymin\":").append(ymin);
sbg.append(",\"xmax\":").append(xmax);
sbg.append(",\"ymax\":").append(ymax);
sbg.append("}]}");
sb.append("&geometries=").append(URLEncoder.encode(sbg.toString(),"UTF-8"));
String projectionUrl = sb.toString();
// execute the projection, parse the JSON response
http.setUrl(projectionUrl);
try {
String response = http.readResponseAsCharacters();
JSONObject jso = new JSONObject(response);
if (jso != null) {
JSONArray jsoGeometries = jso.getJSONArray("geometries");
if ((jsoGeometries != null) && (jsoGeometries.length() == 1)) {
JSONObject jsoEnv = jsoGeometries.getJSONObject(0);
if (jsoEnv != null) {
double jsoXmin = jsoEnv.getDouble("xmin");
double jsoYmin = jsoEnv.getDouble("ymin");
double jsoXmax = jsoEnv.getDouble("xmax");
double jsoYmax = jsoEnv.getDouble("ymax");
return new double[]{jsoXmin,jsoYmin,jsoXmax,jsoYmax};
}
}
}
} catch (IOException e) {
String msg = "Error projecting envelope, url="+projectionUrl;
LOGGER.warning(msg+"\n "+e.toString());
} catch (JSONException e) {
String msg = "Error projecting envelope, problem parsing JSON response, url="+projectionUrl;
LOGGER.warning(msg+"\n "+e.toString());
}
}
// if we get this far and the spatial reference is geographic, then return the envelope
if (spref instanceof GeographicCoordinateSystem) {
return new double[]{xmin,ymin,xmax,ymax};
}
return null;
}
public static final class LayerInfo {
private String name;
private String title;
public LayerInfo(String name, String title) {
this.name = name;
this.title = title;
}
public String getName() {
return name;
}
public String getTitle() {
return title;
}
}
}