eionet.gdem.validation.ValidationService.java Source code

Java tutorial

Introduction

Here is the source code for eionet.gdem.validation.ValidationService.java

Source

/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is XMLCONV - Conversion and QA Service
 *
 * The Initial Owner of the Original Code is European Environment
 * Agency. Portions created by TripleDev or Zero Technologies are Copyright
 * (C) European Environment Agency.  All Rights Reserved.
 *
 * Contributor(s):
 *        Enriko Ksper (TripleDev)
 */

package eionet.gdem.validation;

import eionet.gdem.Properties;
import eionet.gdem.dcm.BusinessConstants;
import eionet.gdem.dcm.business.SchemaManager;
import eionet.gdem.dto.Schema;
import eionet.gdem.dto.ValidateDto;
import eionet.gdem.exceptions.DCMException;
import eionet.gdem.qa.QAFeedbackType;
import eionet.gdem.qa.QAResultPostProcessor;
import eionet.gdem.utils.InputFile;
import eionet.gdem.utils.Utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xml.resolver.tools.CatalogResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.List;

/**
 * The class offers validation methods for XMLCONV and remote clients.
 *
 * @author Enriko Ksper, TripleDev
 */

public class ValidationService {
    /** */
    private static final Log LOGGER = LogFactory.getLog(ValidationService.class);

    /** */
    private String uriXml;
    /** ErrorHandler to use when doing XML Schema validation. */
    private ValidatorErrorHandler errHandler;
    /** Validation result object.*/
    private ValidationServiceFeedback validationFeedback;
    /** Ticket for reading password protected files. */
    private String ticket = null;
    /** false for web clients. */
    private boolean trustedMode = true;

    /** Original URL of XML Schema. */
    private String originalSchema = null;
    /** System URL if Schema has cached copy in XMLCONV. */
    private String validatedSchema = null;
    /** Public URL displayed for user. */
    private String validatedSchemaURL = null;

    /** Message displayed for user on web UI. */
    private String warningMessage = null;
    /** Manager to read XML Schema data from database.*/
    private SchemaManager schemaManager = new SchemaManager();

    /**
     * Constructor initializes ErrorHandler and validation feedback object.
     */
    public ValidationService() {
        errHandler = new ValidatorErrorHandler();
        validationFeedback = new ValidationServiceFeedback();
    }

    /**
     * Validate XML, read the schema or DTD from the header of XML.
     *
     * @param srcUrl URL of XML file to be validated.
     * @return Validation result as HTML snippet.
     * @throws DCMException in case of unknown system error.
     */
    public String validate(String srcUrl) throws DCMException {
        return validateSchema(srcUrl, null);
    }

    /**
     * Validate XML. If schema is null, then read the schema or DTD from the header of XML. If schema or DTD is defined, then ignore
     * the defined schema or DTD.
     *
     * @param srcUrl XML file URL to be validated.
     * @param schema XML Schema URL.
     * @return Validation result as HTML snippet.
     * @throws DCMException in case of unknown system error.
     */
    public String validateSchema(String srcUrl, String schema) throws DCMException {
        InputFile src = null;
        uriXml = srcUrl;
        try {
            src = new InputFile(srcUrl);
            src.setTrustedMode(trustedMode);
            src.setAuthentication(ticket);
            src.setStoreLocally(true);
            return validateSchema(src.getSrcInputStream(), schema);
        } catch (MalformedURLException mfe) {
            throw new DCMException(BusinessConstants.EXCEPTION_CONVERT_URL_MALFORMED);
        } catch (IOException ioe) {
            throw new DCMException(BusinessConstants.EXCEPTION_CONVERT_URL_ERROR);
        } catch (Exception e) {
            throw new DCMException(BusinessConstants.EXCEPTION_GENERAL);
        } finally {
            if (src != null) {
                src.close();
            }
        }

    }

    /**
    /**
     * Validate XML. If schema is null, then read the schema or DTD from the header of XML. If schema or DTD is defined, then ignore
     * the defined schema or DTD.
     *
     * @param srcStream XML file as InputStream to be validated.
     * @param schema XML Schema URL.
     * @return Validation result as HTML snippet.
     * @throws DCMException in case of unknnown system error.
     */
    public String validateSchema(InputStream srcStream, String schema) throws DCMException {

        String result = "";
        boolean isDTD = false;
        boolean isBlocker = false;

        if (Utils.isNullStr(schema)) {
            schema = null;
        }

        try {

            SAXParserFactory spfact = SAXParserFactory.newInstance();
            SAXParser parser = spfact.newSAXParser();
            XMLReader reader = parser.getXMLReader();

            reader.setErrorHandler(errHandler);
            XmlconvCatalogResolver catalogResolver = new XmlconvCatalogResolver();
            reader.setEntityResolver(catalogResolver);

            // make parser to validate
            reader.setFeature("http://xml.org/sax/features/validation", true);
            reader.setFeature("http://apache.org/xml/features/validation/schema", true);
            reader.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);

            reader.setFeature("http://xml.org/sax/features/namespaces", true);
            reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
            reader.setFeature("http://apache.org/xml/features/continue-after-fatal-error", false);

            InputAnalyser inputAnalyser = new InputAnalyser();
            inputAnalyser.parseXML(uriXml);
            String namespace = inputAnalyser.getSchemaNamespace();

            // if schema is not in the parameter, then sniff it from the header of xml
            if (schema == null) {
                schema = inputAnalyser.getSchemaOrDTD();
                isDTD = inputAnalyser.isDTD();
            } else {
                // if the given schema ends with dtd, then don't do schema validation
                isDTD = schema.endsWith("dtd");
            }

            // schema is already given as a parameter. Read the default namespace from XML file and set external schema.
            if (schema != null) {
                if (!isDTD) {
                    if (Utils.isNullStr(namespace)) {
                        // XML file does not have default namespace
                        setNoNamespaceSchemaProperty(reader, schema);
                    } else {
                        setNamespaceSchemaProperty(reader, namespace, schema);
                    }
                } else {
                    // validate against DTD
                    setLocalSchemaUrl(schema);
                    LocalEntityResolver localResolver = new LocalEntityResolver(schema, getValidatedSchema());
                    reader.setEntityResolver(localResolver);
                }
            } else {
                return validationFeedback.formatFeedbackText(
                        "Could not validate XML file. Unable to locate XML Schema reference.",
                        QAFeedbackType.WARNING, isBlocker);
            }
            // if schema is not available, then do not parse the XML and throw error
            if (!Utils.resourceExists(getValidatedSchema())) {
                return validationFeedback.formatFeedbackText(
                        "Failed to read schema document from the following URL: " + getValidatedSchema(),
                        QAFeedbackType.ERROR, isBlocker);
            }
            Schema schemaObj = schemaManager.getSchema(getOriginalSchema());
            if (schemaObj != null) {
                isBlocker = schemaObj.isBlocker();
            }
            validationFeedback.setSchema(getOriginalSchema());
            InputSource is = new InputSource(srcStream);
            reader.parse(is);

        } catch (SAXParseException se) {
            return validationFeedback.formatFeedbackText("Document is not well-formed. Column: "
                    + se.getColumnNumber() + "; line:" + se.getLineNumber() + "; " + se.getMessage(),
                    QAFeedbackType.ERROR, isBlocker);
        } catch (IOException ioe) {
            return validationFeedback.formatFeedbackText(
                    "Due to an IOException, the parser could not check the document. " + ioe.getMessage(),
                    QAFeedbackType.ERROR, isBlocker);
        } catch (Exception e) {
            Exception se = e;
            if (e instanceof SAXException) {
                se = ((SAXException) e).getException();
            }
            if (se != null) {
                se.printStackTrace(System.err);
            } else {
                e.printStackTrace(System.err);
            }
            return validationFeedback.formatFeedbackText(
                    "The parser could not check the document. " + e.getMessage(), QAFeedbackType.ERROR, isBlocker);
        }

        validationFeedback.setValidationErrors(getErrorList());
        result = validationFeedback.formatFeedbackText(isBlocker);

        // validation post-processor
        QAResultPostProcessor postProcessor = new QAResultPostProcessor();
        result = postProcessor.processQAResult(result, schema);
        warningMessage = postProcessor.getWarningMessage(schema);

        return result;
    }

    /**
     * Set the noNamespaceSchemaLocation property.
     *
     * @param reader XMLReader.
     * @param schema XML Schema URL.
     * @throws SAXNotRecognizedException
     * @throws SAXNotSupportedException
     */
    private void setNoNamespaceSchemaProperty(XMLReader reader, String schema)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        setLocalSchemaUrl(schema);
        reader.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
                getValidatedSchema());
    }

    /**
     * Set the schemaLocation property. The value is "namespace schemaLocation".
     *
     * @param reader XMLReader.
     * @param namespace XML Schema default namespace.
     * @param schema XML Schema URL.
     * @throws SAXNotRecognizedException
     * @throws SAXNotSupportedException
     */
    private void setNamespaceSchemaProperty(XMLReader reader, String namespace, String schema)
            throws SAXNotRecognizedException, SAXNotSupportedException {
        setLocalSchemaUrl(schema);
        reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
                namespace + " " + getValidatedSchema());
    }

    /**
     * Sets the local URL (cached) of given schema, if available.
     *
     * @param schema XML Schema URL.
     */
    protected void setLocalSchemaUrl(String schema) {
        String systemURL = schema;
        String publicURL = schema;

        try {
            String schemaFileName = schemaManager.getUplSchemaURL(schema);
            if (!schema.equals(schemaFileName)) {
                systemURL = "file:///".concat(Properties.schemaFolder).concat("/").concat(schemaFileName);
                publicURL = Properties.gdemURL.concat("/schema/").concat(schemaFileName);
            }
        } catch (DCMException e) {
            // ignore local schema, use the original schema from remote URL
            LOGGER.error(e);
        }
        setOriginalSchema(schema);
        setValidatedSchema(systemURL);
        setValidatedSchemaURL(publicURL);
    }

    public void setTicket(String ticket) {
        this.ticket = ticket;
    }

    public void setTrustedMode(boolean mode) {
        this.trustedMode = mode;
    }

    public List<ValidateDto> getErrorList() {
        return errHandler.getErrors();
    }

    public String getValidatedSchema() {
        return validatedSchema;
    }

    public void setValidatedSchema(String validatedSchema) {
        this.validatedSchema = validatedSchema;
    }

    public String getValidatedSchemaURL() {
        return validatedSchemaURL;
    }

    public void setValidatedSchemaURL(String validatedSchemaURL) {
        this.validatedSchemaURL = validatedSchemaURL;
    }

    public String getOriginalSchema() {
        return originalSchema;
    }

    public void setOriginalSchema(String originalSchema) {
        this.originalSchema = originalSchema;
    }

    public String getWarningMessage() {
        return warningMessage;
    }

    public void setWarningMessage(String warningMessage) {
        this.warningMessage = warningMessage;
    }

    /**
     *
     * Extends CatalogResolver used by validation service to be able to log the usage of resources.
     *
     * @author Enriko Ksper
     */
    public class XmlconvCatalogResolver extends CatalogResolver {
        @Override
        public Source resolve(String href, String base) throws TransformerException {
            LOGGER.info("Validation service resolves uri=" + href + " ; base=" + base);
            return super.resolve(href, base);
        }

        @Override
        public InputSource resolveEntity(String publicId, String systemId) {
            LOGGER.info("Validation Service resolves entity with publicId=" + publicId + " ; systemId=" + systemId);
            return super.resolveEntity(publicId, systemId);
        }
    }
}