de.ingrid.interfaces.csw.domain.encoding.impl.XMLEncoding.java Source code

Java tutorial

Introduction

Here is the source code for de.ingrid.interfaces.csw.domain.encoding.impl.XMLEncoding.java

Source

/*
 * **************************************************-
 * ingrid-interface-csw
 * ==================================================
 * Copyright (C) 2014 - 2016 wemove digital solutions GmbH
 * ==================================================
 * Licensed under the EUPL, Version 1.1 or  as soon they will be
 * approved by the European Commission - subsequent versions of the
 * EUPL (the "Licence");
 * 
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 * 
 * http://ec.europa.eu/idabc/eupl5
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 * **************************************************#
 */
/*
 * Copyright (c) 2012 wemove digital solutions. All rights reserved.
 */
package de.ingrid.interfaces.csw.domain.encoding.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import de.ingrid.interfaces.csw.config.ApplicationProperties;
import de.ingrid.interfaces.csw.domain.constants.ConfigurationKeys;
import de.ingrid.interfaces.csw.domain.constants.ElementSetName;
import de.ingrid.interfaces.csw.domain.constants.Namespace;
import de.ingrid.interfaces.csw.domain.constants.Operation;
import de.ingrid.interfaces.csw.domain.constants.ResultType;
import de.ingrid.interfaces.csw.domain.constants.TypeName;
import de.ingrid.interfaces.csw.domain.encoding.CSWMessageEncoding;
import de.ingrid.interfaces.csw.domain.exceptions.CSWException;
import de.ingrid.interfaces.csw.domain.exceptions.CSWInvalidParameterValueException;
import de.ingrid.interfaces.csw.domain.exceptions.CSWMissingParameterValueException;
import de.ingrid.interfaces.csw.domain.exceptions.CSWOperationNotSupportedException;
import de.ingrid.interfaces.csw.domain.query.CSWQuery;
import de.ingrid.interfaces.csw.domain.query.impl.GenericQuery;
import de.ingrid.interfaces.csw.tools.OGCFilterTools;
import de.ingrid.interfaces.csw.tools.StringUtils;
import de.ingrid.utils.xml.Csw202NamespaceContext;
import de.ingrid.utils.xpath.XPathUtils;

/**
 * XMLEncoding deals with messages defined in the XML format.
 *
 * @author ingo herwig <ingo@wemove.com>
 */
public class XMLEncoding extends DefaultEncoding implements CSWMessageEncoding {

    private Node requestBody = null;
    private Operation operation = null;
    private List<String> acceptVersions = null;
    private CSWQuery query = null;

    /** Tool for evaluating xpath **/
    private XPathUtils xpath = new XPathUtils(new Csw202NamespaceContext());

    /** Parameter xpath (namespace agnostic) **/
    private static String SERVICE_PARAM_XPATH = "/*/@service";
    private static String VERSION_PARAM_XPATH = "/*/@version";
    private static String DESCREC_VERSION_PARAM_XPATH = "/csw:DescribeRecord/@version";
    private static String GETCAP_VERSION_PARAM_XPATH = "/csw:GetCapabilities/csw:AcceptVersions/csw:Version";

    private static Log log = LogFactory.getLog(XMLEncoding.class);

    /** Supported operations **/
    private static List<Operation> SUPPORTED_OPERATIONS = Collections
            .unmodifiableList(Arrays.asList(new Operation[] { Operation.GET_CAPABILITIES, Operation.DESCRIBE_RECORD,
                    Operation.GET_RECORDS, Operation.GET_RECORD_BY_ID }));
    private static List<Operation> SUPPORTED_OPERATIONS_CSWT = Collections
            .unmodifiableList(Arrays.asList(new Operation[] { Operation.TRANSACTION }));

    @Override
    public final void initialize(HttpServletRequest request, HttpServletResponse response) {
        this.setRequest(request);
        this.setResponse(response);

        // reset member variables
        this.requestBody = null;
        this.operation = null;
        this.acceptVersions = null;
        this.query = null;

        this.setRequestBody(this.extractRequestBody(request));

        if (log.isDebugEnabled()) {
            log.debug("Request initialized with: " + StringUtils.nodeToString(this.requestBody));
        }

    }

    protected static Document extractFromDocument(Node node) throws Exception {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.newDocument();
        Node copy = node.cloneNode(true);
        Node adopted = doc.adoptNode(copy);
        doc.appendChild(adopted);
        return doc;
    }

    /**
     * Extract the request body from the request. Subclasses will override this.
     *
     * @param request
     * @return Element
     */
    protected Node extractRequestBody(HttpServletRequest request) {
        DocumentBuilderFactory df = DocumentBuilderFactory.newInstance();
        df.setNamespaceAware(true);
        try {
            Document requestDocument = df.newDocumentBuilder().parse(request.getInputStream());
            return requestDocument.getDocumentElement();
        } catch (Exception e) {
            throw new RuntimeException("Error parsing request: ", e);
        }
    }

    @Override
    public void validateRequest() throws CSWException {
        this.checkInitialized();

        // check the service parameter
        String service = this.xpath.getString(this.getRequestBody(), SERVICE_PARAM_XPATH);
        if (service == null || service.length() == 0) {
            throw new CSWMissingParameterValueException("Attribute 'service' is not specified or has no value",
                    "service");
        } else {
            if (!service.equals("CSW")) {
                StringBuffer errorMsg = new StringBuffer();
                errorMsg.append("Parameter 'service' has an unsupported value.\n");
                errorMsg.append("Supported values: CSW\n");
                throw new CSWInvalidParameterValueException(errorMsg.toString(), "service");
            }
        }

        // check the version parameter
        String version = this.xpath.getString(this.getRequestBody(), VERSION_PARAM_XPATH);
        if (version != null && version.length() > 0) {
            if (!version.equals(ConfigurationKeys.CSW_VERSION_2_0_2)) {
                StringBuffer errorMsg = new StringBuffer();
                errorMsg.append("Parameter 'version' has an unsupported value.\n");
                errorMsg.append("Supported values: 2.0.2\n");
                throw new CSWInvalidParameterValueException(errorMsg.toString(), "version");
            }
        }

    }

    @Override
    public List<Operation> getSupportedOperations(Type type) {
        if (type == Type.CSWT) {
            return SUPPORTED_OPERATIONS_CSWT;
        } else {
            return SUPPORTED_OPERATIONS;
        }
    }

    @Override
    public Operation getOperation() throws CSWOperationNotSupportedException {
        this.checkInitialized();

        if (this.operation == null) {
            String operationName = this.getRequestBody().getLocalName();
            this.operation = Operation.getByName(operationName);
        }
        return this.operation;
    }

    @Override
    public List<String> getAcceptVersions() {
        this.checkInitialized();

        if (this.acceptVersions == null) {
            this.acceptVersions = new ArrayList<String>();

            NodeList versionNodes = this.xpath.getNodeList(this.getRequestBody(), GETCAP_VERSION_PARAM_XPATH);
            int length = versionNodes.getLength();
            for (int i = 0; i < length; i++) {
                Node curVersionNode = versionNodes.item(i);
                if (curVersionNode != null) {
                    this.acceptVersions.add(curVersionNode.getTextContent());
                }
            }
            this.acceptVersions = Collections.unmodifiableList(this.acceptVersions);
        }
        return this.acceptVersions;
    }

    @Override
    public String getVersion() {
        this.checkInitialized();
        return this.xpath.getString(this.getRequestBody(), DESCREC_VERSION_PARAM_XPATH);
    }

    @Override
    public CSWQuery getQuery() throws CSWException {
        this.checkInitialized();

        // NOTE: getting enum values may throw an IllegalArgumentException,
        // which is ok
        return this.getQuery(this.getRequestBody());

    }

    /**
     * Get a CSWQuery from a request node.
     *
     * @param requestNode
     * @return CSWQuery
     * @throws CSWException
     */
    public CSWQuery getQuery(Node requestNode) throws CSWException {

        this.requestBody = requestNode;

        if (this.query == null) {
            this.query = new GenericQuery();
            try {
                // only required for GetRecordById, GetRecords operations
                Operation operation = this.getOperation();
                if (operation == Operation.GET_RECORD_BY_ID) {
                    // extract the id
                    String id = this.xpath.getString(requestNode, "/csw:GetRecordById/csw:Id");
                    if (id != null) {
                        this.query.setId(id);
                    }
                    // extract the element set name
                    String elementSetNameStr = this.xpath.getString(requestNode,
                            "/csw:GetRecordById/csw:ElementSetName");
                    if (elementSetNameStr != null) {
                        this.query.setElementSetName(ElementSetName.valueOf(elementSetNameStr.toUpperCase()));
                    }
                    // extract the output schema
                    String schemaUri = this.xpath.getString(requestNode, "/csw:GetRecordById/csw:OutputSchema");
                    if (schemaUri != null) {
                        this.query.setOutputSchema(Namespace.getByUri(schemaUri));
                    }
                } else if (operation == Operation.GET_RECORDS) {

                    // ADD request parameter to filter Query
                    // extract the filter constraint
                    if (this.getRequest() != null) {
                        Node query = this.xpath.getNode(requestNode, "/csw:GetRecords/csw:Query");
                        String[] queryConstraints = ApplicationProperties
                                .get(ConfigurationKeys.QUERY_PARAMETER_2_CONSTRAINTS, "").split(",");
                        for (String queryConstraint : queryConstraints) {
                            if (queryConstraint.trim().length() > 0) {
                                String param = this.getRequest().getParameter(queryConstraint);
                                if (param != null && param.length() >= 0) {
                                    OGCFilterTools.addPropertyIsEqual(query, queryConstraint, param);
                                }

                            }
                        }
                    }

                    // extract the filter constraint
                    Node filter = this.xpath.getNode(requestNode,
                            "/csw:GetRecords/csw:Query/csw:Constraint/ogc:Filter");

                    if (filter != null) {
                        this.query.setConstraint(extractFromDocument(filter));
                    }
                    // extract the constraint language version
                    String filterVersion = this.xpath.getString(requestNode,
                            "/csw:GetRecords/csw:Query/csw:Constraint/@version");
                    if (filterVersion != null) {
                        this.query.setConstraintLanguageVersion(filterVersion);
                    }
                    // extract the element set name
                    String elementSetNameStr = this.xpath.getString(requestNode,
                            "/csw:GetRecords/csw:Query/csw:ElementSetName");
                    if (elementSetNameStr != null) {
                        this.query.setElementSetName(ElementSetName.valueOf(elementSetNameStr.toUpperCase()));
                    }
                    // extract the typeNames
                    String typeNamesStr = this.xpath.getString(requestNode, "/csw:GetRecords/csw:Query/@typeNames");
                    if (typeNamesStr != null) {
                        String[] typeNameStrings = typeNamesStr.split(",");
                        TypeName[] typeNames = new TypeName[typeNameStrings.length];
                        for (int i = 0; i < typeNameStrings.length; i++) {
                            typeNames[i] = TypeName.getFromQualifiedString(typeNameStrings[i].trim());
                        }
                        this.query.setTypeNames(typeNames);
                    } else {
                        this.query.setTypeNames(null);
                    }
                    // extract the result type
                    String resultTypeStr = this.xpath.getString(requestNode, "/csw:GetRecords/@resultType");
                    if (resultTypeStr != null) {
                        this.query.setResultType(ResultType.valueOf(resultTypeStr.toUpperCase()));
                    }

                    // extract the maxRecords
                    Integer maxRecords = this.xpath.getInt(requestNode, "/csw:GetRecords/@maxRecords");
                    if (maxRecords == null) {
                        maxRecords = Integer.MAX_VALUE;
                    }
                    maxRecords = Math.min(maxRecords, ApplicationProperties
                            .getInteger(ConfigurationKeys.MAX_RETURNED_HITS, Integer.MAX_VALUE));
                    if (maxRecords < 0) {
                        StringBuffer errorMsg = new StringBuffer();
                        errorMsg.append("Parameter 'maxRecords' has an unsupported value.\n");
                        errorMsg.append("Supported values: positive integer\n");
                        throw new CSWInvalidParameterValueException(errorMsg.toString(), "maxRecords");
                    }

                    this.query.setMaxRecords(maxRecords);

                    // extract the startPosition
                    Integer startPosition = this.xpath.getInt(requestNode, "/csw:GetRecords/@startPosition");
                    if (startPosition == null) {
                        startPosition = 1;
                    }
                    if (startPosition <= 0) {
                        StringBuffer errorMsg = new StringBuffer();
                        errorMsg.append("Parameter 'startPosition' has an unsupported value.\n");
                        errorMsg.append("Supported values: positive integer\n");
                        throw new CSWInvalidParameterValueException(errorMsg.toString(), "startPosition");
                    }
                    this.query.setStartPosition(startPosition);

                    // extract the sort part
                    Node sort = this.xpath.getNode(requestNode, "/csw:GetRecords/csw:Query/csw:SortBy");

                    if (sort != null) {
                        this.query.setSortBy(extractFromDocument(sort));
                    }

                }
            } catch (CSWInvalidParameterValueException ex) {
                // re-throw CSW Exception
                throw ex;
            } catch (Exception ex) {
                throw new CSWException("An error occured while extracting the query: ", ex);
            }
        }
        return this.query;
    }

    @Override
    public void validateResponse() throws CSWException {
        // TODO Auto-generated method stub
    }

    /**
     * Check if the instance is initialized
     */
    protected void checkInitialized() {
        if (this.getRequestBody() == null) {
            throw new RuntimeException("No request document found. Maybe initialize() was not called.");
        }
    }

    /**
     * Get the request body
     *
     * @return Node
     */
    public Node getRequestBody() {
        return this.requestBody;
    }

    /**
     * Set the request body
     *
     * @param requestBody
     *            The requestBody node to set
     */
    protected void setRequestBody(Node requestBody) {
        this.requestBody = requestBody;
    }
}