Package org.vfny.geoserver.wms.requests

Source Code of org.vfny.geoserver.wms.requests.GetMapXmlReader

/* Copyright (c) 2001, 2003 TOPP - www.openplans.org.  All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.vfny.geoserver.wms.requests;

import com.vividsolutions.jts.geom.Coordinate;
import org.geotools.filter.ExpressionDOMParser;
import org.geotools.filter.FilterFilter;
import org.geotools.gml.GMLFilterDocument;
import org.geotools.gml.GMLFilterGeometry;
import org.geotools.referencing.CRS;
import org.geotools.styling.SLDParser;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.styling.StyledLayer;
import org.geotools.styling.StyledLayerDescriptor;
import org.geotools.styling.UserLayer;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.vfny.geoserver.Request;
import org.vfny.geoserver.global.FeatureTypeInfo;
import org.vfny.geoserver.global.TemporaryFeatureTypeInfo;
import org.vfny.geoserver.util.GETMAPValidator;
import org.vfny.geoserver.util.SLDValidator;
import org.vfny.geoserver.util.requests.readers.XmlRequestReader;
import org.vfny.geoserver.wfs.WfsException;
import org.vfny.geoserver.wfs.requests.FeatureHandler;
import org.vfny.geoserver.wms.WmsException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.ParserAdapter;
import java.awt.Color;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;


/**
* reads in a GetFeature XML WFS request from a XML stream
*
* @author Rob Hranac, TOPP
* @author Chris Holmes, TOPP
* @version $Id: GetMapXmlReader.java 3990 2005-11-29 17:26:57Z groldan $
*/
public class GetMapXmlReader extends XmlRequestReader {
    private static final StyleFactory styleFactory = StyleFactory
        .createStyleFactory();

    /**
     * Creates a new GetMapXmlReader object.
     */
    public GetMapXmlReader() {
    }

    /**
     * Reads the GetMap XML request into a GetMap Request object.
     *
     * @param reader The plain POST text from the client.
     * @param req DOCUMENT ME!
     *
     * @return The GetMap Request from the xml reader.
     *
     * @throws WmsException For any problems reading the request.
     */
    public Request read(Reader reader, HttpServletRequest req)
        throws WmsException {
        GetMapRequest getMapRequest = new GetMapRequest();
        getMapRequest.setHttpServletRequest(req);

        boolean validateSchema = wantToValidate(req);

        try {
            parseGetMapXML(reader, getMapRequest, validateSchema);
        } catch (java.net.UnknownHostException unh) {
         //J--
         //http://www.oreilly.com/catalog/jenut2/chapter/ch19.html ---
         // There is one complication to this example. Most web.xml files contain a <!DOCTYPE> tag that specifies
         //the document type (or DTD). Despite the fact that Example 19.1 specifies that the parser should not
         //validate the document, a conforming XML parser must still read the DTD for any document that has a
         //<!DOCTYPE> declaration. Most web.xml have a declaration like this:
         //..
         //In order to read the DTD, the parser must be able to read the specified URL. If your system is not
         //connected to the Internet when you run the example, it will hang.
         //. Another workaround to this DTD problem is to simply remove (or comment out) the <!DOCTYPE> declaration from the web.xml file you process with ListServlets1./
        //
         //also see:
         //http://doctypechanger.sourceforge.net/
         //J+
            throw new WmsException("unknown host - "
                + unh.getLocalizedMessage()
                + " - if its in a !DOCTYPE, remove the !DOCTYPE tag.");
        } catch (SAXParseException se) {
            throw new WmsException("line " + se.getLineNumber() + " column "
                + se.getColumnNumber() + " -- " + se.getLocalizedMessage());
        } catch (Exception e) {
            throw new WmsException(e);
        }

        return getMapRequest;
    }

    /**
     * Actually read in the XML request and stick it in the request object. We
     * do this using the DOM parser (because the SLD parser is DOM based and
     * we can integrate).  1. parse into DOM 2. parse the SLD  3. grab the
     * rest of the attribute 4. stuff #3 attributes in the request object 5.
     * stuff the SLD info into the request object 6. return  GetMap schema is
     * at http://www.opengeospatial.org/docs/02-017r1.pdf (page 18)  NOTE: see
     * handlePostGet() for people who put the SLD in the POST and the
     * parameters in the GET.
     *
     * @param xml
     * @param getMapRequest
     * @param validateSchema DOCUMENT ME!
     *
     * @throws Exception DOCUMENT ME!
     */
    private void parseGetMapXML(Reader xml, GetMapRequest getMapRequest,
        boolean validateSchema) throws Exception {
        File temp = null;

        if (validateSchema) //copy POST to a file
         {
            //make tempfile
            temp = File.createTempFile("getMapPost", "xml");
            temp.deleteOnExit();

            FileOutputStream fos = new FileOutputStream(temp);
            BufferedOutputStream out = new BufferedOutputStream(fos);

            int c;

            while (-1 != (c = xml.read())) {
                out.write(c);
            }

            xml.close();
            out.flush();
            out.close();
            xml = new BufferedReader(new FileReader(temp)); // pretend like nothing has happened
        }

        try {
            javax.xml.parsers.DocumentBuilderFactory dbf = javax.xml.parsers.DocumentBuilderFactory
                .newInstance();

            dbf.setExpandEntityReferences(false);
            dbf.setValidating(false);
            dbf.setNamespaceAware(true);

            javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder();

            InputSource input = new InputSource(xml);
            org.w3c.dom.Document dom = db.parse(input);

            SLDParser sldParser = new SLDParser(styleFactory);

            Node rootNode = dom.getDocumentElement();

            // we have the SLD component, now we get all the GetMAp components
            // step a  -- attribute "version"
            Node nodeGetMap = rootNode;

            if (!(nodeNameEqual(nodeGetMap, "getmap"))) {
                if (nodeNameEqual(nodeGetMap, "StyledLayerDescriptor")) //oopsy!!  its a SLD POST with get parameters!
                 {
                    if (validateSchema) {
                        validateSchemaSLD(temp, getMapRequest);
                    }

                    handlePostGet(rootNode, sldParser, getMapRequest);

                    return;
                }

                throw new Exception(
                    "GetMap XML parser - start node isnt 'GetMap' or 'StyledLayerDescriptor' tag");
            }

            if (validateSchema) {
                validateSchemaGETMAP(temp, getMapRequest);
            }

            NamedNodeMap atts = nodeGetMap.getAttributes();
            Node wmsVersion = atts.getNamedItem("version");

            if (wmsVersion == null) {
                throw new Exception(
                    "GetMap XML parser - couldnt find attribute 'version' in GetMap tag");
            }

            getMapRequest.setVersion(wmsVersion.getNodeValue());

            //ignore the OWSType since we know its supposed to be WMS
            //step b -bounding box
            parseBBox(getMapRequest, nodeGetMap);

            // for SLD we already have it (from above) (which we'll handle as layers later)
            StyledLayerDescriptor sld = sldParser.parseDescriptor(getNode(
                        rootNode, "StyledLayerDescriptor"));
            processStyles(getMapRequest, sld);

            //step c - "Output"
            parseXMLOutput(nodeGetMap, getMapRequest); //make this function easier to read

            //step d - "exceptions"
            getMapRequest.setExceptions(getNodeValue(nodeGetMap, "Exceptions"));

            //step e - "VendorType
            // we dont actually do anything with this, so...
            //step f - rebuild SLD info.  Ie. fill in the Layer and Style information, just like SLD post-get
        } finally {
            if (temp != null) {
                temp.delete();
            }
        }
    }

    /**
     * This is the hybrid SLD POST way. Normal is the GetMap - with a built in
     * SLD.  The alternate, for stupid people, is the WMS parameters in the
     * GET, and the SLD in the post. This handles that case.
     *
     * @param rootNode
     * @param sldParser
     * @param getMapRequest
     *
     * @throws Exception DOCUMENT ME!
     */
    private void handlePostGet(Node rootNode, SLDParser sldParser,
        GetMapRequest getMapRequest) throws Exception {
        //get the GET parmeters
        HttpServletRequest request = getMapRequest.getHttpServletRequest();

        String qString = request.getQueryString();
        LOGGER.fine("reading request: " + qString);

        //Map requestParams = KvpRequestReader.parseKvpSet(qString);
        Map requestParams = new HashMap();
        String paramName;
        String paramValue;

        for (Enumeration pnames = request.getParameterNames();
                pnames.hasMoreElements();) {
            paramName = (String) pnames.nextElement();
            paramValue = request.getParameter(paramName);
            requestParams.put(paramName.toUpperCase(), paramValue);
        }

        GetMapKvpReader kvpReader = new GetMapKvpReader(requestParams);

        String version = kvpReader.getRequestVersion();
        getMapRequest.setVersion(version);

        kvpReader.parseMandatoryParameters(getMapRequest, false); //false means dont do styles/layers (see below)
        kvpReader.parseOptionalParameters(getMapRequest);

        //get styles/layers from the sld.
        StyledLayerDescriptor sld = sldParser.parseDescriptor(rootNode); //root = <StyledLayerDescriptor>
        processStyles(getMapRequest, sld);
    }

    /**
     * taken from the kvp reader, with modifications
     *
     * @param getMapRequest
     * @param sld
     *
     * @throws Exception DOCUMENT ME!
     * @throws WmsException DOCUMENT ME!
     */
    private void processStyles(GetMapRequest getMapRequest,
        StyledLayerDescriptor sld) throws Exception {
        final StyledLayer[] styledLayers = sld.getStyledLayers();
        final int slCount = styledLayers.length;

        if (slCount == 0) {
            throw new WmsException("SLD document contains no layers");
        }

        final List layers = new ArrayList();
        final List styles = new ArrayList();
        FeatureTypeInfo currLayer;
        Style currStyle;

        StyledLayer sl = null;

        for (int i = 0; i < slCount; i++) {
            sl = styledLayers[i];

            String layerName = sl.getName();

            if (null == layerName) {
                throw new WmsException(
                    "A UserLayer without layer name was passed");
            }

            // TODO: add support for remote WFS here
            //handle the InLineFeature stuff
            if ((sl instanceof UserLayer)
                    && ((((UserLayer) sl)).getInlineFeatureDatastore() != null)) {
                //SPECIAL CASE - we make the temporary version
                UserLayer ul = ((UserLayer) sl);
                currLayer = new TemporaryFeatureTypeInfo(ul
                        .getInlineFeatureDatastore(), ul.getInlineFeatureType());
            } else {
                currLayer = GetMapKvpReader.findLayer(getMapRequest, layerName);
            }

            GetMapKvpReader.addStyles(getMapRequest, currLayer,
                styledLayers[i], layers, styles);
        }

        getMapRequest.setLayers((FeatureTypeInfo[]) layers.toArray(
                new FeatureTypeInfo[layers.size()]));
        getMapRequest.setStyles(styles);
    }

    /**
     * xs:element name="BoundingBox" type="gml:BoxType"/> dont forget the SRS!
     *
     * @param getMapRequest
     * @param nodeGetMap
     *
     * @throws Exception DOCUMENT ME!
     */
    private void parseBBox(GetMapRequest getMapRequest, Node nodeGetMap)
        throws Exception {
        Node bboxNode = getNode(nodeGetMap, "BoundingBox");

        if (bboxNode == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'BoundingBox' in GetMap tag");
        }

        List coordList = ExpressionDOMParser.parseCoords(bboxNode);

        if (coordList.size() != 2) {
            throw new Exception(
                "GetMap XML parser - node 'BoundingBox' in GetMap tag should have 2 coordinates in it");
        }

        com.vividsolutions.jts.geom.Envelope env = new com.vividsolutions.jts.geom.Envelope();

        for (int i = 0; i < coordList.size(); i++) {
            env.expandToInclude((Coordinate) coordList.get(i));
        }

        getMapRequest.setBbox(env);

        // SRS
        NamedNodeMap atts = bboxNode.getAttributes();
        Node srsNode = atts.getNamedItem("srsName");

        if (srsNode != null) {
            String srs = srsNode.getNodeValue();
            String epsgCode = srs.substring(srs.indexOf('#') + 1);
            epsgCode = "EPSG:" + epsgCode;
           
          try {
            CoordinateReferenceSystem  mapcrs = CRS.decode(epsgCode);
            getMapRequest.setCrs(mapcrs);
          }catch (Exception e){
            //couldnt make it - we send off a service exception with the correct info
            throw new WmsException(e.getLocalizedMessage(),"InvalidSRS");
      }
        }
    }

        //J--
  /**
   *
   * <xs:element name="Format" type="ogc:FormatType"/>
   * <xs:element name="Transparent" type="xs:boolean" minOccurs="0"/>
   * <xs:element name="BGcolor" type="xs:string" minOccurs="0"/>
   * <xs:element name="Size">
   *   <xs:complexType>
   *   <xs:sequence>
   *     <xs:element name="Width" type="xs:positiveInteger"/>
   *     <xs:element name="Height" type="xs:positiveInteger"/>
   *   </xs:sequence>
   *   </xs:complexType>
   * </xs:element><!--Size-->
   *
   * @param nodeGetMap
   * @param getMapRequest
   */
        //J+
    private void parseXMLOutput(Node nodeGetMap, GetMapRequest getMapRequest)
        throws Exception {
        Node outputNode = getNode(nodeGetMap, "Output");

        if (outputNode == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'Output' in GetMap tag");
        }

        //Format
        String format = getNodeValue(outputNode, "Format");

        if (format == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'Format' in GetMap/Output tag");
        }

        getMapRequest.setFormat(format);

        //Transparent
        String trans = getNodeValue(outputNode, "Transparent");

        if (trans != null) {
            if (trans.equalsIgnoreCase("false") || trans.equalsIgnoreCase("0")) {
                getMapRequest.setTransparent(false);
            } else {
                getMapRequest.setTransparent(true);
            }
        }

        //BGColor
        String bgColor = getNodeValue(outputNode, "BGcolor");

        if (bgColor != null) {
            getMapRequest.setBgColor(Color.decode(bgColor));
        }

        //SIZE
        Node sizeNode = getNode(outputNode, "Size");

        if (sizeNode == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'Size' in GetMap/Output tag");
        }

        //Size/Width
        String width = getNodeValue(sizeNode, "Width");

        if (width == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'Width' in GetMap/Output/Size tag");
        }

        getMapRequest.setWidth(Integer.parseInt(width));

        //Size/Height
        String height = getNodeValue(sizeNode, "Height");

        if (width == null) {
            throw new Exception(
                "GetMap XML parser - couldnt find node 'Height' in GetMap/Output/Size tag");
        }

        getMapRequest.setHeight(Integer.parseInt(height));
    }

    /**
     * Give a node and the name of a child of that node, return it. This doesnt
     * do anything complex.
     *
     * @param parentNode
     * @param wantedChildName
     *
     * @return
     */
    public Node getNode(Node parentNode, String wantedChildName) {
        NodeList children = parentNode.getChildNodes();

        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);

            if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) {
                continue;
            }

            String childName = child.getLocalName();

            if (childName == null) {
                childName = child.getNodeName();
            }

            if (childName.equalsIgnoreCase(wantedChildName)) {
                return child;
            }
        }

        return null;
    }

    /**
     * Give a node and the name of a child of that node, find its (string)
     * value. This doesnt do anything complex.
     *
     * @param parentNode
     * @param wantedChildName
     *
     * @return
     */
    public String getNodeValue(Node parentNode, String wantedChildName) {
        NodeList children = parentNode.getChildNodes();

        for (int i = 0; i < children.getLength(); i++) {
            Node child = children.item(i);

            if ((child == null) || (child.getNodeType() != Node.ELEMENT_NODE)) {
                continue;
            }

            String childName = child.getLocalName();

            if (childName == null) {
                childName = child.getNodeName();
            }

            if (childName.equalsIgnoreCase(wantedChildName)) {
                return child.getChildNodes().item(0).getNodeValue();
            }
        }

        return null;
    }

    /**
     * returns true if this node is named "name".  Ignores case and namespaces.
     *
     * @param n
     * @param name
     *
     * @return
     */
    public boolean nodeNameEqual(Node n, String name) {
        if (n.getNodeName().equalsIgnoreCase(name)) {
            return true;
        }

        String nname = n.getNodeName();
        int idx = nname.indexOf(':');

        if (idx == -1) {
            return false;
        }

        if (nname.substring(idx + 1).equalsIgnoreCase(name)) {
            return true;
        }

        return false;
    }

    /**
     * This should only be called if the xml starts with StyledLayerDescriptor
     * Don't use on a GetMap.
     *
     * @param f
     * @param getMapRequest
     *
     * @throws Exception
     * @throws WmsException DOCUMENT ME!
     */
    public void validateSchemaSLD(File f, GetMapRequest getMapRequest)
        throws Exception {
        SLDValidator validator = new SLDValidator();
        List errors = null;

        try {
            FileInputStream in = null;

            try {
                in = new FileInputStream(f);
                errors = validator.validateSLD(in,
                        getMapRequest.getHttpServletRequest().getSession()
                                     .getServletContext());
            } finally {
                if (in != null) {
                    in.close();
                }
            }

            if (errors.size() != 0) {
                in = new FileInputStream(f);
                throw new WmsException(SLDValidator.getErrorMessage(in, errors));
            }
        } catch (IOException e) {
            String msg = "Creating remote SLD url: " + e.getMessage();
            LOGGER.log(Level.WARNING, msg, e);
            throw new WmsException(e, msg, "parseSldParam");
        }
    }

    /**
     * This should only be called if the xml starts with GetMap Don't use on a
     * SLD.
     *
     * @param f
     * @param getMapRequest
     *
     * @throws Exception
     * @throws WmsException DOCUMENT ME!
     */
    public void validateSchemaGETMAP(File f, GetMapRequest getMapRequest)
        throws Exception {
        GETMAPValidator validator = new GETMAPValidator();
        List errors = null;

        try {
            FileInputStream in = null;

            try {
                in = new FileInputStream(f);
                errors = validator.validateGETMAP(in,
                        getMapRequest.getHttpServletRequest().getSession()
                                     .getServletContext());
            } finally {
                if (in != null) {
                    in.close();
                }
            }

            if (errors.size() != 0) {
                in = new FileInputStream(f);
                throw new WmsException(GETMAPValidator.getErrorMessage(in,
                        errors));
            }
        } catch (IOException e) {
            String msg = "Creating remote GETMAP url: " + e.getMessage();
            LOGGER.log(Level.WARNING, msg, e);
            throw new WmsException(e, msg, "GETMAP validator");
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @param request
     *
     * @return
     */
    private boolean wantToValidate(HttpServletRequest request) {
        String queryString = request.getQueryString(); // ie.   FORMAT=image/png&TRANSPARENT=TRUE&HEIGHT=480&REQUEST=GetMap&BBOX=-73.94896388053894,40.77323718492597,-73.94105110168456,40.77796711500081&WIDTH=803&SRS=EPSG:4326&VERSION=1.1.1 

        if (queryString == null) {
            return false; // pure POST without any query
        }

        queryString = queryString.toLowerCase();

        if (queryString.startsWith("validateschema")
                || (queryString.indexOf("&validateschema") != -1)) {
            return true;
        }

        return false;
    }
}
TOP

Related Classes of org.vfny.geoserver.wms.requests.GetMapXmlReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.