All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.autonomy.aci.client.services.impl.AbstractStAXProcessor Maven / Gradle / Ivy

/*
 * Copyright 2006-2018 Open Text.
 *
 * Licensed under the MIT License (the "License"); you may not use this file
 * except in compliance with the License.
 *
 * The only warranties for products and services of Open Text and its affiliates
 * and licensors ("Open Text") are as may be set forth in the express warranty
 * statements accompanying such products and services. Nothing herein should be
 * construed as constituting an additional warranty. Open Text shall not be
 * liable for technical or editorial errors or omissions contained herein. The
 * information contained herein is subject to change without notice.
 */

package com.autonomy.aci.client.services.impl;

import com.autonomy.aci.client.services.AciErrorException;
import com.autonomy.aci.client.services.ProcessorException;
import com.autonomy.aci.client.services.StAXProcessor;
import com.autonomy.aci.client.transport.AciResponseInputStream;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.events.XMLEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Locale;

/**
 * Abstract Processor that should be used by all processors wanting to process the ACI response via the
 * JSR173 StAX API. Some of the properties that can be set on the
 * {@link XMLInputFactory} are exposed, so that the implementation can be modified to suit the required situation.
 * For example, setting {@link #namespaceAware} and {@link #validating} to false should theoretically speed
 * up parsing, especially as ACI responses shouldn't require either to be parsed, (both are false by default
 * for this very reason). When a subclass is created, this class checks to see if any of the properties have been set
 * as system properties and if they have it sets the corresponding property to this value, these values can then be
 * overridden by using the appropriate setter method.
 */
public abstract class AbstractStAXProcessor implements StAXProcessor {

    private static final long serialVersionUID = -6678111384531149923L;

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractStAXProcessor.class);

    /**
     * Holds the factory for creating XMLStreamReader's...
     */
    private transient XMLInputFactory xmlInputFactory;

    /**
     * Turns on/off implementation specific DTD validation.
     */
    private boolean namespaceAware;

    /**
     * Turns on/off namespace processing for XML 1.0 support.
     */
    private boolean validating;

    /**
     * Requires the processor to coalesce adjacent character data.
     */
    private boolean coalescing;

    /**
     * Replace internal entity references with their replacement text and report them as characters.
     */
    private boolean replacingEntityReferences;

    /**
     * Resolve external parsed entities.
     */
    private boolean supportingExternalEntities;

    /**
     * Use this property to request processors that do not support DTDs.
     */
    private boolean supportDtd;

    /**
     * Holds the processor to use when an error response is detected.
     */
    private StAXProcessor errorProcessor;

    /**
     * This constructor gets a new {@link XMLInputFactory} instance that is reused every time
     * {@link #process(com.autonomy.aci.client.transport.AciResponseInputStream)} is called, this
     * should be faster than creating a new instance every time this method is called.
     * 

* The properties are set to the following defaults if they are not specified as system properties: *

* * * * * * * *
PropertyDefault
XMLInputFactory.IS_NAMESPACE_AWAREfalse
XMLInputFactory.IS_VALIDATINGfalse
XMLInputFactory.IS_COALESCINGfalse
XMLInputFactory.IS_REPLACING_ENTITY_REFERENCEStrue
XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIESfalse
XMLInputFactory.SUPPORT_DTDtrue
*/ protected AbstractStAXProcessor() { // See if the various XMLInputFactory properties are set as system properties... namespaceAware = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.IS_NAMESPACE_AWARE), "false")); validating = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.IS_VALIDATING), "false")); coalescing = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.IS_COALESCING), "false")); replacingEntityReferences = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES), "true")); supportingExternalEntities = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES), "false")); supportDtd = BooleanUtils.toBoolean(StringUtils.defaultString(System.getProperty(XMLInputFactory.SUPPORT_DTD), "true")); // Create the XMLStreamReader factory... xmlInputFactory = XMLInputFactory.newInstance(); } private void readObject(final ObjectInputStream inputStream) throws IOException, ClassNotFoundException { // Read in all the serialized properties... inputStream.defaultReadObject(); // Recreate the XMLStreamReader factory... xmlInputFactory = XMLInputFactory.newInstance(); } /** * This method firstly checks that the content type of the response is text based and can be parsed. If so, it * converts the AciResponseInputStream into a StAX XMLStreamReader and calls the the {@link * #process(javax.xml.stream.XMLStreamReader)} method that should be implemented in a subclass to do all the work. * @param aciResponseInputStream The ACI response to process * @return An object of type T * @throws AciErrorException If the ACI response was an error response * @throws ProcessorException If an error occurred during the processing of the IDOL response */ public T process(final AciResponseInputStream aciResponseInputStream) { LOGGER.trace("process() called..."); if (!aciResponseInputStream.getContentType().toLowerCase(Locale.ROOT).startsWith("text/xml")) { throw new ProcessorException("This processor is unable to process non-text ACI responses. The content type for this response is " + aciResponseInputStream.getContentType()); } // Define this here so we can make sure it's closed when the processor is finished... XMLStreamReader xmlStreamReader = null; try { // Update the factory with the various properties as they might have changed since the last run... xmlInputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, namespaceAware); xmlInputFactory.setProperty(XMLInputFactory.IS_VALIDATING, validating); xmlInputFactory.setProperty(XMLInputFactory.IS_COALESCING, coalescing); xmlInputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, replacingEntityReferences); xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, supportingExternalEntities); xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, supportDtd); // Convert the input stream.. xmlStreamReader = xmlInputFactory.createXMLStreamReader(aciResponseInputStream); return process(xmlStreamReader); } catch (final XMLStreamException xmlse) { throw new ProcessorException("Unable to convert the InputStream to a XMLStreamReader", xmlse); } finally { if (xmlStreamReader != null) { try { // This does NOT close the underlying AciResponseInputStream xmlStreamReader.close(); } catch (final XMLStreamException xmlse) { LOGGER.error("Unable to close the XMLStreamReader.", xmlse); } } } } /** * Process the ACI response input into an object of type T. * @param xmlStreamReader The ACI response to process * @return An object of type T * @throws AciErrorException If the ACI response was an error response * @throws ProcessorException If an error occurred during the processing of the IDOL response */ public abstract T process(final XMLStreamReader xmlStreamReader); /** * Reads from the XML stream and tries to determine if the ACI response contains an error or not. The stream is left * at the end of the body content of the /autnresponse/response element, if one could be found. * @param xmlStreamReader The response to process * @return true if the response contains an error, false otherwise * @throws javax.xml.stream.XMLStreamException If there was a problem reading the IDOL Server response. */ protected boolean isErrorResponse(final XMLStreamReader xmlStreamReader) throws XMLStreamException { LOGGER.trace("isErrorResponse() called..."); // Get the /autnresponse/response element... while (xmlStreamReader.hasNext()) { // Get the event type... final int eventType = xmlStreamReader.next(); // Check to see if it's a start event... if ((XMLEvent.START_ELEMENT == eventType) && ("response".equalsIgnoreCase(xmlStreamReader.getLocalName()))) { return "ERROR".equalsIgnoreCase(xmlStreamReader.getElementText()); } } // Couldn't find a /autnresponse/response element... throw new XMLStreamException("Unable to find /autnresponse/response element."); } /** * Process the remainder of the IDOL response with the configured error processor. * @param xmlStreamReader The IDOL response fragment containing the error information * @throws AciErrorException Once the response has been parsed for all the error information it contains * @throws ProcessorException If something went wrong while trying to process the error response * @throws IllegalArgumentException if no error processor has been set. */ protected void processErrorResponse(final XMLStreamReader xmlStreamReader) { LOGGER.trace("processErrorResponse() "); // Sanity check... Validate.notNull(errorProcessor, "Unable to process the error response, as no errorProcessor has been configured."); // Process the error response and propagate the resulting exception... errorProcessor.process(xmlStreamReader); } /** * Move the cursor forward through the XML stream to the next start element. * @param xmlStreamReader The XML stream to use * @throws XMLStreamException If there was an error using the stream */ protected void forwardToNextStartElement(final XMLStreamReader xmlStreamReader) throws XMLStreamException { while (xmlStreamReader.hasNext()) { final int eventType = xmlStreamReader.next(); if (XMLEvent.START_ELEMENT == eventType) { return; } } throw new XMLStreamException("No more START_ELEMENT events found"); } /** * Move the cursor forward through the XML stream to the next start or end element, which ever comes first. * @param xmlStreamReader The XML stream to use * @return The type of event forwarded to, i.e. XMLEvent.START_ELEMENT or XMLEvent.END_ELEMENT. * @throws XMLStreamException If there was an error using the stream */ protected int forwardToNextStartOrEndElement(final XMLStreamReader xmlStreamReader) throws XMLStreamException { while (xmlStreamReader.hasNext()) { final int eventType = xmlStreamReader.next(); if ((XMLEvent.START_ELEMENT == eventType) || (XMLEvent.END_ELEMENT == eventType)) { return eventType; } } throw new XMLStreamException("No more START_ELEMENT or END_ELEMENT events found"); } /** * Forwards through the stream looking for the an element with elementName * @param elementName The name of the element to find. * @param xmlStreamReader The stream to forward through * @throws XMLStreamException If there was an error using the stream, or if no element with elementName * could be found */ protected void forwardToNamedStartElement(final String elementName, final XMLStreamReader xmlStreamReader) throws XMLStreamException { while (xmlStreamReader.hasNext()) { final int eventType = xmlStreamReader.next(); if ((eventType == XMLEvent.START_ELEMENT) && (elementName.equals(xmlStreamReader.getLocalName()))) { return; } } throw new XMLStreamException("Unable to find a start element for, " + elementName); } public StAXProcessor getErrorProcessor() { return errorProcessor; } public void setErrorProcessor(final StAXProcessor errorProcessor) { this.errorProcessor = errorProcessor; } public boolean isNamespaceAware() { return namespaceAware; } public void setNamespaceAware(final boolean namespaceAware) { this.namespaceAware = namespaceAware; } public boolean isValidating() { return validating; } public void setValidating(final boolean validating) { this.validating = validating; } public boolean isCoalescing() { return coalescing; } public void setCoalescing(final boolean coalescing) { this.coalescing = coalescing; } public boolean isReplacingEntityReferences() { return replacingEntityReferences; } public void setReplacingEntityReferences(final boolean replacingEntityReferences) { this.replacingEntityReferences = replacingEntityReferences; } public boolean isSupportingExternalEntities() { return supportingExternalEntities; } public void setSupportingExternalEntities(final boolean supportingExternalEntities) { this.supportingExternalEntities = supportingExternalEntities; } public boolean isSupportDtd() { return supportDtd; } public void setSupportDtd(final boolean supportDtd) { this.supportDtd = supportDtd; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy