com.esri.gpt.server.csw.provider3.GetRecordsProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.esri.gpt.server.csw.provider3.GetRecordsProvider.java

Source

/* 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.server.csw.provider3;

import static com.esri.gpt.catalog.discovery.DiscoveryFilter.getMaxRecordsThreshold;
import com.esri.gpt.framework.util.Val;
import com.esri.gpt.framework.xml.DomUtil;
import com.esri.gpt.server.csw.components.CswConstants;
import com.esri.gpt.server.csw.components.CswNamespaces;
import com.esri.gpt.server.csw.components.IBBOXParser;
import com.esri.gpt.server.csw.components.ICqlParser;
import com.esri.gpt.server.csw.components.IFilterParser;
import com.esri.gpt.server.csw.components.IOperationProvider;
import com.esri.gpt.server.csw.components.IProviderFactory;
import com.esri.gpt.server.csw.components.IQueryEvaluator;
import com.esri.gpt.server.csw.components.IQueryParser;
import com.esri.gpt.server.csw.components.IResponseGenerator;
import com.esri.gpt.server.csw.components.ISortByParser;
import com.esri.gpt.server.csw.components.ISupportedValues;
import com.esri.gpt.server.csw.components.OperationContext;
import com.esri.gpt.server.csw.components.OwsException;
import com.esri.gpt.server.csw.components.ParseHelper;
import com.esri.gpt.server.csw.components.QueryOptions;
import com.esri.gpt.server.csw.components.ServiceProperties;
import com.esri.gpt.server.csw.components.SupportedValues;
import com.esri.gpt.server.csw.components.ValidationHelper;
import java.util.ArrayList;
import java.util.List;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.commons.lang3.ArrayUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Provides the CSW GetRecords operation.
 */
public class GetRecordsProvider implements IOperationProvider {

    /** class variables ========================================================= */

    /** The Logger. */
    private static Logger LOGGER = Logger.getLogger(GetRecordsProvider.class.getName());

    /** constructors ============================================================ */

    /** Default constructor */
    public GetRecordsProvider() {
        super();
    }

    /** methods ================================================================= */

    /**
     * Translates namespaces.
     * @param parsed parsed namespaces
     * @param namespaces available namespaces
     */
    protected void translateNamespaces(String[] parsed, List<String[]> namespaces) {
        if (parsed != null && namespaces != null) {
            NamespaceContext namespaceContext = CswNamespaces.CSW_30.makeNamespaceContext();
            for (int i = 0; i < parsed.length; i++) {
                String name = parsed[i];
                String[] el = name.split(":");
                if (el.length == 2) {
                    String pfx = el[0];
                    for (String[] namespace : namespaces) {
                        if (namespace[1].equals(pfx)) {
                            String uri = namespace[0];
                            String defPfx = namespaceContext.getPrefix(uri);
                            if (defPfx != null) {
                                name = defPfx + ":" + el[1];
                                break;
                            }
                        }
                    }
                }
                parsed[i] = name;
            }
        }
    }

    /**
     * Parses namespace
     * @param array of namespaces
     * @return array of KVP's (uri,prefix)
     * @throws OwsException 
     */
    protected List<String[]> parseNamespace(String[] namespace) throws OwsException {
        ArrayList<String[]> namespaces = new ArrayList<String[]>();
        if (namespace != null) {
            for (String ns : namespace) {
                ns = Val.chkStr(ns);
                String nsPfx = null;
                String nsUri = null;

                if (ns.toLowerCase().startsWith("xmlns(")) {
                    ns = ns.substring(6);
                    if (ns.toLowerCase().endsWith(")")) {
                        ns = ns.substring(0, ns.length() - 1);
                    }
                    ns = Val.chkStr(ns);
                    if (ns.length() > 0) {
                        String[] pair = ns.split("=");
                        if (pair.length == 1) {
                            nsUri = Val.chkStr(pair[0]);
                        } else if (pair.length == 2) {
                            nsPfx = Val.chkStr(pair[0]);
                            nsUri = Val.chkStr(pair[1]);
                        }
                    }
                }
                if ((nsUri == null) || (nsUri.length() == 0)) {
                    String msg = "The namespace must follow the following pattern:";
                    msg += " xmlns(pfx1=uri1),xmlns(pfx2=uri2),...";
                    throw new OwsException(OwsException.OWSCODE_InvalidParameterValue, "namespace", msg);
                } else {
                    nsUri = Val.escapeXml(nsUri);
                    if ((nsPfx == null) || (nsPfx.length() == 0)) {
                        namespaces.add(new String[] { Val.chkStr(nsUri), "" });
                    } else {
                        namespaces.add(new String[] { Val.chkStr(nsUri), Val.chkStr(nsPfx) });
                    }
                }
            }
        }
        return namespaces;
    }

    /**
     * Builds an ogc:Filter node from HTTP GET parameters.
     * @param namespace the namespace parameter values
     * @param constraintFilter the constraint parameter value
     * @throws Exception if a processing exception occurs
     */
    protected Node buildFilterNode(String[] namespace, String constraintFilter) throws Exception {

        // TODO GetRecordsDomBuilder had a different pattern??

        // parse namespaces
        // pattern: namespace=xmlns(ogc=http://www.opengis.net/ogc),xmlns(gml=http://www.opengis.net/gml)...
        StringBuilder nsBuffer = new StringBuilder();
        boolean hasCswUri = false;
        boolean hasCswPfx = false;
        String cswPfx = "";

        List<String[]> parseNamespace = parseNamespace(namespace);
        for (String[] ns : parseNamespace) {
            String nsUri = ns[0];
            String nsPfx = ns[1];
            if (nsUri.equals(CswNamespaces.CSW_30.URI_CSW())) {
                hasCswUri = true;
                if (!nsPfx.isEmpty()) {
                    hasCswPfx = true;
                    cswPfx = nsPfx;
                }
            }
            if (nsPfx.isEmpty()) {
                nsBuffer.append(" xmlns=\"").append(nsUri).append("\"");
            } else {
                nsBuffer.append(" xmlns:").append(nsPfx).append("=\"").append(nsUri).append("\"");
            }
        }

        // use ogc as the default namespace if no namespace parameter was supplied
        if (nsBuffer.length() == 0) {
            nsBuffer.append(" xmlns=\"http://www.opengis.net/ogc\"");
        }

        // build the constraint XML
        StringBuilder sbXml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        if (hasCswUri && hasCswPfx) {
            cswPfx = cswPfx + ":";
        } else if (hasCswUri) {
            cswPfx = "";
        } else {
            cswPfx = "csw:";
            nsBuffer.append(" xmlns:csw=\"http://www.opengis.net/cat/csw/3.0\"");
        }
        sbXml.append("\r\n<").append(cswPfx).append("Constraint");
        if (nsBuffer.length() > 0) {
            sbXml.append(" ").append(nsBuffer);
        }
        sbXml.append(">");
        sbXml.append("\r\n").append(constraintFilter);
        sbXml.append("\r\n</").append(cswPfx).append("Constraint>");

        // make the dom, find the ogc:Filter node
        try {
            Document dom = DomUtil.makeDomFromString(sbXml.toString(), true);
            CswNamespaces ns = CswNamespaces.CSW_30;
            XPath xpath = XPathFactory.newInstance().newXPath();
            xpath.setNamespaceContext(ns.makeNamespaceContext());

            Node ndFilter = null;
            Node ndConstraint = (Node) xpath.evaluate("csw:Constraint", dom, XPathConstants.NODE);
            if (ndConstraint != null) {
                ndFilter = (Node) xpath.evaluate("ogc:Filter", ndConstraint, XPathConstants.NODE);
                ;
            }
            if (ndFilter == null) {
                String msg = "The supplied constraint was not a valid ogc:Filter.";
                throw new OwsException(OwsException.OWSCODE_NoApplicableCode, "constraint", msg);
            } else {
                return ndFilter;
            }

        } catch (SAXException e) {
            String msg = "The supplied namespace/constraint pairs were not well-formed xml: ";
            msg += " " + e.toString();
            throw new OwsException(OwsException.OWSCODE_NoApplicableCode, "constraint", msg);
        }

    }

    /**
     * Builds an ogc:SortBy node from HTTP GET parameters.
     * @param sortBy the sortBy parameter values
     * @throws Exception if a processing exception occurs
     */
    protected Node buildSortByNode(String[] sortBy) throws Exception {

        // parse sort by parameters
        // pattern: sortby=property1:A,property2:D...

        if (sortBy != null) {
            StringBuilder sbXml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
            sbXml.append("\r\n<ogc:SortBy xmlns:ogc=\"http://www.opengis.net/ogc\">");
            boolean hadProperty = false;
            for (String param : sortBy) {
                param = Val.chkStr(param);
                String name = null;
                String dir = null;
                if (param.toLowerCase().endsWith(":a")) {
                    name = Val.chkStr(param.substring(0, param.length() - 2));
                    dir = "ASC";
                } else if (param.toLowerCase().endsWith(":d")) {
                    name = Val.chkStr(param.substring(0, param.length() - 2));
                    dir = "DESC";
                } else {
                    name = Val.chkStr(param);
                }
                if ((name == null) || (name.length() == 0)) {
                    // we'll ignore this condition without an exception
                } else {
                    hadProperty = true;
                    sbXml.append("\r\n<ogc:SortProperty>");
                    sbXml.append("\r\n<ogc:PropertyName>").append(Val.escapeXml(name))
                            .append("</ogc:PropertyName>");
                    if (dir != null) {
                        sbXml.append("\r\n<ogc:SortOrder>").append(Val.escapeXml(dir)).append("</ogc:SortOrder>");
                    }
                    sbXml.append("\r\n</ogc:SortProperty>");
                }
            }
            sbXml.append("\r\n</ogc:SortBy>");
            if (hadProperty) {
                Document dom = DomUtil.makeDomFromString(sbXml.toString(), true);
                NodeList nl = dom.getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    if (nl.item(i).getNodeType() == Node.ELEMENT_NODE) {
                        return nl.item(i);
                    }
                }
            }
        }
        return null;
    }

    /**
     * Executes a parsed operation request.
     * @param context the operation context
     * @throws Exception if a processing exception occurs
     */
    public void execute(OperationContext context) throws Exception {

        // initialize
        LOGGER.finer("Executing csw:GetRecords request...");
        IProviderFactory factory = context.getProviderFactory();
        QueryOptions qOptions = context.getRequestOptions().getQueryOptions();

        // evaluate the query
        IQueryEvaluator evaluator = factory.makeQueryEvaluator(context);
        if (evaluator == null) {
            String msg = "IProviderFactory.makeQueryEvaluator: instantiation failed.";
            LOGGER.log(Level.SEVERE, msg);
            throw new OwsException(msg);
        }
        if (!qOptions.getIDs().isEmpty()) {
            evaluator.evaluateIdQuery(context, qOptions.getIDs().toArray(new String[0]));
        } else {
            evaluator.evaluateQuery(context);
        }

        // generate the response
        IResponseGenerator generator = factory.makeResponseGenerator(context);
        if (generator == null) {
            String msg = "IProviderFactory.makeResponseGenerator: instantiation failed.";
            LOGGER.log(Level.SEVERE, msg);
            throw new OwsException(msg);
        } else {
            generator.generateResponse(context);
        }

    }

    /**
     * Handles a URL based request (HTTP GET).
     * @param context the operation context
     * @param request the HTTP request
     * @throws Exception if a processing exception occurs
     */
    public void handleGet(OperationContext context, HttpServletRequest request) throws Exception {

        // initialize
        LOGGER.finer("Handling csw:GetRecords request URL...");
        QueryOptions qOptions = context.getRequestOptions().getQueryOptions();
        qOptions.setMaxRecordsThreshold(getMaxRecordsThreshold());
        ServiceProperties svcProps = context.getServiceProperties();
        ParseHelper pHelper = new ParseHelper();
        ValidationHelper vHelper = new ValidationHelper();
        String locator;
        String[] parsed;
        ISupportedValues supported;
        IProviderFactory factory = context.getProviderFactory();

        CswNamespaces ns = CswNamespaces.CSW_30;
        XPath xpath = XPathFactory.newInstance().newXPath();
        xpath.setNamespaceContext(ns.makeNamespaceContext());

        // service and version are parsed by the parent RequestHandler

        // TODO typeNames requestId distributedSearch hopCount responseHandler
        // TODO resultype validate is not applicable for a GET request?

        // output format
        locator = "outputFormat";
        parsed = pHelper.getParameterValues(request, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_OutputFormat);
        try {
            context.getOperationResponse()
                    .setOutputFormat(vHelper.validateValue(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // output schema
        locator = "outputSchema";
        parsed = pHelper.getParameterValues(request, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_OutputSchema);
        try {
            qOptions.setOutputSchema(vHelper.validateValue(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // record ids
        locator = "recordIds";
        parsed = pHelper.getParameterValues(request, locator, ",");
        qOptions.setIDs(vHelper.validateValues(locator, parsed, false));

        // keywords
        locator = "q";
        parsed = pHelper.getParameterValues(request, locator, " ");
        if ((parsed != null) && (parsed.length) > 0) {
            IQueryParser parser = factory.makeQueryParser(context);
            if (parser == null) {
                String msg = "IProviderFactory.makeFilterParser: instantiation failed.";
                context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
                throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
            }
            try {
                parser.parseQuery(context, parsed);
            } catch (OwsException ex) {
                context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
                throw ex;
            }
        }

        // bbox
        locator = "bbox";
        parsed = pHelper.getParameterValues(request, locator, ",");
        if ((parsed != null) && (parsed.length) > 0) {
            try {
                IBBOXParser parser = factory.makeBBOXParser(context);
                if (parser == null) {
                    String msg = "IProviderFactory.makeBBOXParser: instantiation failed.";
                    throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                }
                if (parsed.length < 4 || parsed.length > 5) {
                    String msg = "GetRecordsProvider:handleGet: invalid bbox.";
                    throw new OwsException(OwsException.OWSCODE_InvalidParameterValue, locator, msg);
                }
                String crs = null;
                if (parsed.length == 5) {
                    crs = parsed[4];
                    parsed = ArrayUtils.subarray(parsed, 0, 4);
                    supported = svcProps.getSupportedValues(CswConstants.Parameter_Crs);
                    vHelper.validateValue(supported, locator + ":crs", parsed, true);
                }
                parser.parseBBOX(context, parsed, crs);
            } catch (OwsException ex) {
                context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
                throw ex;
            }
        }

        // start and max records
        parsed = pHelper.getParameterValues(request, "startPosition");
        if ((parsed != null) && (parsed.length) > 0) {
            qOptions.setStartRecord(Math.max(Val.chkInt(parsed[0], 1), 1));
        }
        parsed = pHelper.getParameterValues(request, "maxRecords");
        if ((parsed != null) && (parsed.length) > 0) {
            if (parsed[0].equalsIgnoreCase("unlimited")) {
                qOptions.setMaxRecords(getMaxRecordsThreshold());
                qOptions.setUnlimited(true);
            } else {
                qOptions.setMaxRecords(Val.chkInt(parsed[0], 10));
            }
        }

        // result type
        locator = "resultType";
        parsed = pHelper.getParameterValues(request, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_ResultType);
        qOptions.setResultType(vHelper.validateValue(supported, locator, parsed, false));
        if (qOptions.getResultType() == null) {
            qOptions.setResultType(CswConstants.ResultType_Results);
        }

        // query type names
        locator = "typeNames";
        parsed = pHelper.getParameterValues(request, locator);
        if (parsed != null) {
            List<String[]> namespaces = parseNamespace(pHelper.getParameterValues(request, "namespace", ","));
            translateNamespaces(parsed, namespaces);
        }
        supported = svcProps.getSupportedValues(CswConstants.Parameter_TypeNames);
        try {
            qOptions.setQueryTypeNames(vHelper.validateValues(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // response element set type
        locator = "ElementSetName";
        parsed = pHelper.getParameterValues(request, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_ElementSetType);
        try {
            qOptions.setElementSetType(vHelper.validateValue(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // TODO supported ElementNames this for GetRecordById as well?
        locator = "ElementName";
        parsed = pHelper.getParameterValues(request, locator, ",");
        if (parsed != null && qOptions.getElementSetType() != null) {
            String msg = "GetRecordsProvider:handleGet: elementName not allowed if elementSetName present.";
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
        }
        if (parsed != null) {
            List<String[]> namespaces = parseNamespace(pHelper.getParameterValues(request, "namespace", ","));
            translateNamespaces(parsed, namespaces);
        }
        supported = svcProps.getSupportedValues(CswConstants.Parameter_ElementName);
        try {
            qOptions.setElementNames(vHelper.validateValues(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // constraint language
        locator = "constraintLanguage";
        parsed = pHelper.getParameterValues(request, locator);
        supported = new SupportedValues("CQL_TEXT,FILTER", ",");
        String constraintLanguage = vHelper.validateValue(supported, locator, parsed, false);

        // constraint version
        locator = "constraint_language_version";
        parsed = pHelper.getParameterValues(request, locator);
        String constraintVersion = vHelper.validateValue(locator, parsed, false);
        qOptions.setQueryConstraintVersion(constraintVersion);

        // constraint text
        locator = "constraint";
        parsed = pHelper.getParameterValues(request, locator);
        String constraint = vHelper.validateValue(locator, parsed, false);

        // csw:CqlText
        if ((constraintLanguage != null) && constraintLanguage.equalsIgnoreCase("CQL_TEXT")) {
            String cql = Val.chkStr(constraint);
            qOptions.setQueryConstraintCql(cql);
            ICqlParser parser = factory.makeCqlParser(context, constraintVersion);
            if (parser == null) {
                String msg = "IProviderFactory.makeCqlParser: instantiation failed.";
                throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
            } else {
                parser.parseCql(context, cql);
            }
        }

        // ogc:Filter
        if ((constraintLanguage == null) || constraintLanguage.equalsIgnoreCase("FILTER")) {
            Node ndFilter = null;
            IFilterParser parser = factory.makeFilterParser(context, constraintVersion);
            if (parser == null) {
                String msg = "IProviderFactory.makeFilterParser: instantiation failed.";
                throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
            }
            String constraintFilter = Val.chkStr(constraint);
            if (constraintFilter.length() > 0) {
                String[] namespace = pHelper.getParameterValues(request, "namespace", ",");
                ndFilter = this.buildFilterNode(namespace, constraintFilter);
                parser.parseFilter(context, ndFilter, xpath);
            }
        }

        // ogc:SortBy
        locator = "sortBy";
        String[] sortBy = pHelper.getParameterValues(request, "sortBy", ",");
        if (sortBy != null) {
            Node ndSortBy = this.buildSortByNode(sortBy);
            if (ndSortBy != null) {
                ISortByParser parser = factory.makeSortByParser(context);
                if (parser == null) {
                    String msg = "IProviderFactory.makeSortByParser: instantiation failed.";
                    throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                } else {
                    parser.parseSortBy(context, ndSortBy, xpath);
                }
            }
        }

        // execute the request
        this.execute(context);
    }

    /**
     * Handles an XML based request (normally HTTP POST).
     * @param context the operation context
     * @param root the root node
     * @param xpath an XPath to enable queries (properly configured with name spaces)
     * @throws Exception if a processing exception occurs
     */
    public void handleXML(OperationContext context, Node root, XPath xpath) throws Exception {

        // initialize
        LOGGER.finer("Handling csw:GetRecords request XML...");
        QueryOptions qOptions = context.getRequestOptions().getQueryOptions();
        qOptions.setMaxRecordsThreshold(getMaxRecordsThreshold());
        ServiceProperties svcProps = context.getServiceProperties();
        ParseHelper pHelper = new ParseHelper();
        ValidationHelper vHelper = new ValidationHelper();
        String locator;
        String[] parsed;
        ISupportedValues supported;
        IProviderFactory factory = context.getProviderFactory();

        // service and version are parsed by the parent RequestHandler

        // TODO requestId
        locator = "@outputFormat";
        parsed = pHelper.getParameterValues(root, xpath, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_OutputFormat);
        try {
            context.getOperationResponse()
                    .setOutputFormat(vHelper.validateValue(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // output schema
        locator = "@outputSchema";
        parsed = pHelper.getParameterValues(root, xpath, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_OutputSchema);
        try {
            qOptions.setOutputSchema(vHelper.validateValue(supported, locator, parsed, false));
        } catch (OwsException ex) {
            context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
            throw ex;
        }

        // start and max records
        qOptions.setStartRecord(Math.max(Val.chkInt(xpath.evaluate("@startPosition", root), 1), 1));
        qOptions.setMaxRecords(Val.chkInt(xpath.evaluate("@maxRecords", root), 10));

        // result type
        locator = "@resultType";
        parsed = pHelper.getParameterValues(root, xpath, locator);
        supported = svcProps.getSupportedValues(CswConstants.Parameter_ResultType);
        qOptions.setResultType(vHelper.validateValue(supported, locator, parsed, false));
        if (qOptions.getResultType() == null) {
            qOptions.setResultType(CswConstants.ResultType_Results);
        }

        // find the query node
        locator = "csw:Query";
        Node ndQuery = (Node) xpath.evaluate(locator, root, XPathConstants.NODE);
        if (ndQuery != null) {

            // query type names
            locator = "csw:Query/@typeNames";
            parsed = pHelper.getParameterValues(root, xpath, "@typeNames");
            qOptions.setQueryTypeNames(vHelper.validateValues(locator, parsed, false));

            // response element set type
            locator = "csw:ElementSetName";
            parsed = pHelper.getParameterValues(ndQuery, xpath, locator);
            supported = svcProps.getSupportedValues(CswConstants.Parameter_ElementSetType);
            qOptions.setElementSetType(vHelper.validateValue(supported, locator, parsed, false));

            // response element set type names
            String elementSetType = qOptions.getElementSetType();
            if (elementSetType != null) {
                locator = "csw:ElementSetName/@typeNames";
                parsed = pHelper.getParameterValues(ndQuery, xpath, locator);
                qOptions.setElementSetTypeNames(vHelper.validateValues(locator, parsed, false));
            }

            locator = "csw:ElementName";
            parsed = pHelper.getParameterValues(ndQuery, xpath, locator);
            if (parsed != null && qOptions.getElementSetType() != null) {
                String msg = "GetRecordsProvider:handleGet: elementName not allowed if elementSetName present.";
                context.getOperationResponse().setResponseCode(HttpServletResponse.SC_BAD_REQUEST);
                throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
            }
            supported = svcProps.getSupportedValues(CswConstants.Parameter_ElementName);
            qOptions.setElementNames(vHelper.validateValues(supported, locator, parsed, false));

            // find the constraint node
            Node ndConstraint = (Node) xpath.evaluate("csw:Constraint", ndQuery, XPathConstants.NODE);
            if (ndConstraint != null) {

                // constraint version
                String constraintVersion = xpath.evaluate("@version", ndConstraint);
                qOptions.setQueryConstraintVersion(constraintVersion);

                // csw:CqlText
                locator = "csw:CqlText";
                Node ndCql = (Node) xpath.evaluate(locator, ndConstraint, XPathConstants.NODE);
                if (ndCql != null) {
                    String cql = Val.chkStr(ndCql.getTextContent());
                    qOptions.setQueryConstraintCql(cql);
                    ICqlParser parser = factory.makeCqlParser(context, constraintVersion);
                    if (parser == null) {
                        String msg = "IProviderFactory.makeCqlParser: instantiation failed.";
                        throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                    } else {
                        parser.parseCql(context, cql);
                    }
                } else {

                    // ogc:Filter
                    locator = "fes:Filter";
                    Node ndFilter = (Node) xpath.evaluate(locator, ndConstraint, XPathConstants.NODE);
                    if (ndFilter != null) {
                        IFilterParser parser = factory.makeFilterParser(context, constraintVersion);
                        if (parser == null) {
                            String msg = "IProviderFactory.makeFilterParser: instantiation failed.";
                            throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                        } else {
                            parser.parseFilter(context, ndFilter, xpath);
                        }
                    } else {
                        String msg = "An OGC filter for the CSW constraint is required.";
                        throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                    }
                }
            }

            // ogc:SortBy
            locator = "ogc:SortBy";
            Node ndSortBy = (Node) xpath.evaluate(locator, ndQuery, XPathConstants.NODE);
            if (ndSortBy != null) {
                ISortByParser parser = factory.makeSortByParser(context);
                if (parser == null) {
                    String msg = "IProviderFactory.makeSortByParser: instantiation failed.";
                    throw new OwsException(OwsException.OWSCODE_NoApplicableCode, locator, msg);
                } else {
                    parser.parseSortBy(context, ndSortBy, xpath);
                }
            }
        }

        // execute the request
        this.execute(context);
    }

}