Package org.mule.module.xml.transformer

Source Code of org.mule.module.xml.transformer.XQueryTransformer

/*
* $Id: XQueryTransformer.java 20321 2010-11-24 15:21:24Z dfeist $
* --------------------------------------------------------------------------------------
* Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
*
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.module.xml.transformer;

import org.mule.api.MuleMessage;
import org.mule.api.MuleRuntimeException;
import org.mule.api.lifecycle.Disposable;
import org.mule.api.lifecycle.Initialisable;
import org.mule.api.lifecycle.InitialisationException;
import org.mule.api.transformer.TransformerException;
import org.mule.config.i18n.CoreMessages;
import org.mule.module.xml.i18n.XmlMessages;
import org.mule.transformer.types.DataTypeFactory;
import org.mule.util.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;

import net.sf.saxon.Configuration;
import net.sf.saxon.javax.xml.xquery.XQCommonHandler;
import net.sf.saxon.javax.xml.xquery.XQConnection;
import net.sf.saxon.javax.xml.xquery.XQDataSource;
import net.sf.saxon.javax.xml.xquery.XQException;
import net.sf.saxon.javax.xml.xquery.XQItem;
import net.sf.saxon.javax.xml.xquery.XQItemType;
import net.sf.saxon.javax.xml.xquery.XQPreparedExpression;
import net.sf.saxon.javax.xml.xquery.XQResultSequence;
import net.sf.saxon.xqj.SaxonXQDataSource;

import org.apache.commons.pool.BasePoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.dom4j.io.DOMWriter;
import org.dom4j.io.DocumentSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

/**
* The XQuery Module gives users the ability to perform XQuery transformations on XML messages in Mule
*/
public class XQueryTransformer extends AbstractXmlTransformer implements Disposable
{
    public static final String SOURCE_DOCUMENT_NAMESPACE = "document";

    // keep at least 1 XSLT Transformer ready by default
    private static final int MIN_IDLE_TRANSFORMERS = 1;
    // keep max. 32 XSLT Transformers around by default
    private static final int MAX_IDLE_TRANSFORMERS = 32;
    // MAX_IDLE is also the total limit
    private static final int MAX_ACTIVE_TRANSFORMERS = MAX_IDLE_TRANSFORMERS;

    protected final GenericObjectPool transformerPool;

    private volatile String xqueryFile;
    private volatile String xquery;
    private volatile Map contextProperties;
    private volatile XQCommonHandler commonHandler;
    private volatile XQConnection connection;
    protected Configuration configuration;

    public XQueryTransformer()
    {
        super();
        transformerPool = new GenericObjectPool(new PooledXQueryTransformerFactory());
        transformerPool.setMinIdle(MIN_IDLE_TRANSFORMERS);
        transformerPool.setMaxIdle(MAX_IDLE_TRANSFORMERS);
        transformerPool.setMaxActive(MAX_ACTIVE_TRANSFORMERS);

        registerSourceType(DataTypeFactory.STRING);
        registerSourceType(DataTypeFactory.BYTE_ARRAY);
        registerSourceType(DataTypeFactory.create(DocumentSource.class));
        registerSourceType(DataTypeFactory.create(org.dom4j.Document.class));
        registerSourceType(DataTypeFactory.create(Document.class));
        registerSourceType(DataTypeFactory.create(Element.class));
        registerSourceType(DataTypeFactory.INPUT_STREAM);
        setReturnDataType(DataTypeFactory.create(Element.class));
    }

    public XQueryTransformer(String xqueryFile)
    {
        this();
        this.xqueryFile = xqueryFile;
    }

    /**
     *
     */
    @Override
    public void initialise() throws InitialisationException
    {

        if (xquery != null && xqueryFile != null)
        {
            throw new InitialisationException(XmlMessages.canOnlySetFileOrXQuery(), this);
        }

        try
        {
            if (xqueryFile != null)
            {
                xquery = IOUtils.getResourceAsString(xqueryFile, getClass());
            }
            if (configuration == null)
            {
                configuration = new Configuration();
            }

            XQDataSource ds = new SaxonXQDataSource(configuration);
            if (commonHandler != null)
            {
                ds.setCommonHandler(commonHandler);
            }
            connection = ds.getConnection();

            transformerPool.addObject();

        }
        catch (Throwable te)
        {
            throw new InitialisationException(te, this);
        }
    }

    @Override
    public void dispose()
    {
        try
        {
            connection.close();
        }
        catch (XQException e)
        {
            logger.warn(e.getMessage());
        }
    }

    @Override
    public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException
    {
        try
        {
            XQPreparedExpression transformer = null;
            try
            {
                transformer = (XQPreparedExpression) transformerPool.borrowObject();

                bindParameters(transformer, message);

                bindDocument(message.getPayload(), transformer);

                XQResultSequence result = transformer.executeQuery();
                //No support for return Arrays yet
                List results = new ArrayList();
                while (result.next())
                {
                    XQItem item = result.getItem();

                    Class type = returnType.getType();
                    if (Node.class.isAssignableFrom(type) || Node[].class.isAssignableFrom(type))
                    {
                        results.add(item.getNode());
                    }
                    else if (String.class.isAssignableFrom(type) || String[].class.isAssignableFrom(type))
                    {
                        results.add(item.getItemAsString());
                    }
                    else if (XMLStreamReader.class.isAssignableFrom(type) || XMLStreamReader[].class.isAssignableFrom(type))
                    {
                        try
                        {
                            results.add(item.getItemAsStream());
                        }
                        catch (XQException e)
                        {
                            throw new TransformerException(XmlMessages.streamNotAvailble(getName()));
                        }
                    }
                    else
                    {
                        //This can be a JAXB bound  object instance depending on whether the CommonHandler has been set
                        try
                        {
                            results.add(item.getObject());
                        }
                        catch (XQException e)
                        {
                            throw new TransformerException(XmlMessages.objectNotAvailble(getName()));

                        }
                    }
                    if (!type.isArray())
                    {
                        break;
                    }
                }
                if (returnType.getType().isArray())
                {
                    return results.toArray();
                }
                if (results.size() == 1)
                {
                    return results.get(0);
                }
                else if (results.size() == 0)
                {
                    return null;
                }
                else
                {
                    return results.toArray();
                }

            }
            finally
            {
                if (transformer != null)
                {
                    if (transformer.getWarnings() != null)
                    {
                        logger.warn(transformer.getWarnings().getMessage(), transformer.getWarnings().fillInStackTrace());
                    }
                    // clear transformation parameters before returning transformer to the
                    // pool
                    //TODO find out what the scope is for bound variables, there doesn't seem to be a way to unbind them

                    transformerPool.returnObject(transformer);
                }
            }

        }
        catch (Exception e)
        {
            throw new TransformerException(this, e);
        }
    }

    protected void bindParameters(XQPreparedExpression transformer, MuleMessage message) throws XQException, TransformerException
    {
        // set transformation parameters
        if (contextProperties != null)
        {
            for (Iterator i = contextProperties.entrySet().iterator(); i.hasNext();)
            {
                Map.Entry parameter = (Map.Entry) i.next();
                String key = (String) parameter.getKey();
                Object o = evaluateTransformParameter(key, parameter.getValue(), message);

                if (o instanceof String)
                {
                    transformer.bindAtomicValue(new QName(key), o.toString(), connection.createAtomicItemType(XQItemType.XQBASETYPE_STRING));
                }
                else if (o instanceof Boolean)
                {
                    transformer.bindBoolean(new QName(key), ((Boolean) o).booleanValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_BOOLEAN));
                }
                else if (o instanceof Byte)
                {
                    transformer.bindByte(new QName(key), ((Byte) o).byteValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_BYTE));
                }
                else if (o instanceof Short)
                {
                    transformer.bindShort(new QName(key), ((Short) o).shortValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_SHORT));
                }
                else if (o instanceof Integer)
                {
                    transformer.bindInt(new QName(key), ((Integer) o).intValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_INT));
                }
                else if (o instanceof Long)
                {
                    transformer.bindLong(new QName(key), ((Long) o).longValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_LONG));
                }
                else if (o instanceof Float)
                {
                    transformer.bindFloat(new QName(key), ((Float) o).floatValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_FLOAT));
                }
                else if (o instanceof Double)
                {
                    transformer.bindDouble(new QName(key), ((Double) o).doubleValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_DOUBLE));
                }
                else
                {
                    logger.error("Cannot bind value: " + o + " cannot be bound to the Xquery context. Not of supported type");
                }
            }
        }
    }

    /**
     * Returns the InputSource corresponding to xqueryFile or xquery
     *
     * @param src
     * @param transformer
     * @throws net.sf.saxon.javax.xml.xquery.XQException
     *
     * @throws org.mule.umo.transformer.TransformerException
     *
     */
    protected void bindDocument(Object src, XQPreparedExpression transformer) throws Exception
    {
        if (src instanceof byte[])
        {
            transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource(new ByteArrayInputStream((byte[]) src)));

        }
        else if (src instanceof InputStream)
        {
            transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource((InputStream) src));

        }
        else if (src instanceof String)
        {
            transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource(new StringReader((String) src)));

        }
        else if (src instanceof Document)
        {
            transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), (Document) src, null);

        }
        else if (src instanceof Element)
        {
            transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), (Element) src, null);

        }
        else if (src instanceof org.dom4j.Document)
        {
            DOMWriter domWriter = new DOMWriter();
            Document dom = domWriter.write((org.dom4j.Document) src);
            transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), dom, null);

        }
        else if (src instanceof DocumentSource)
        {
            transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), ((DocumentSource) src).getInputSource());

        }
        else
        {
            throw new IllegalArgumentException(CoreMessages.transformUnexpectedType(src.getClass(), null).getMessage());
        }
    }

    public Configuration getConfiguration()
    {
        return configuration;
    }

    public void setConfiguration(Configuration configuration)
    {
        this.configuration = configuration;
    }

    /**
     * @return Returns the xqueryFile.
     */
    public String getXqueryFile()
    {
        return xqueryFile;
    }

    /**
     * @param xqueryFile The xqueryFile to set.
     */
    public void setXqueryFile(String xqueryFile)
    {
        this.xqueryFile = xqueryFile;
    }

    public String getXquery()
    {
        return xquery;
    }

    public void setXquery(String xquery)
    {
        this.xquery = xquery;
    }

    public XQCommonHandler getCommonHandler()
    {
        return commonHandler;
    }

    public void setCommonHandler(XQCommonHandler commonHandler)
    {
        this.commonHandler = commonHandler;
    }


    protected class PooledXQueryTransformerFactory extends BasePoolableObjectFactory
    {
        @Override
        public Object makeObject() throws Exception
        {
            return connection.prepareExpression(xquery);
        }

        @Override
        public void destroyObject(Object o) throws Exception
        {
            ((XQPreparedExpression) o).close();
            super.destroyObject(o);
        }
    }


    /**
     * @return The current maximum number of allowable active transformer objects in
     *         the pool
     */
    public int getMaxActiveTransformers()
    {
        return transformerPool.getMaxActive();
    }

    /**
     * Sets the the current maximum number of active transformer objects allowed in the
     * pool
     *
     * @param maxActiveTransformers New maximum size to set
     */
    public void setMaxActiveTransformers(int maxActiveTransformers)
    {
        transformerPool.setMaxActive(maxActiveTransformers);
    }

    /**
     * @return The current maximum number of allowable idle transformer objects in the
     *         pool
     */
    public int getMaxIdleTransformers()
    {
        return transformerPool.getMaxIdle();
    }

    /**
     * Sets the the current maximum number of idle transformer objects allowed in the pool
     *
     * @param maxIdleTransformers New maximum size to set
     */
    public void setMaxIdleTransformers(int maxIdleTransformers)
    {
        transformerPool.setMaxIdle(maxIdleTransformers);
    }

    /**
     * Gets the parameters to be used when applying the transformation
     *
     * @return a map of the parameter names and associated values
     * @see javax.xml.transform.Transformer#setParameter(java.lang.String,
     *      java.lang.Object)
     */
    public Map getContextProperties()
    {
        return contextProperties;
    }

    /**
     * Sets the parameters to be used when applying the transformation
     *
     * @param contextProperties a map of the parameter names and associated values
     * @see javax.xml.transform.Transformer#setParameter(java.lang.String,
     *      java.lang.Object)
     */
    public void setContextProperties(Map contextProperties)
    {
        this.contextProperties = contextProperties;
    }

    /**
     * <p>
     * Returns the value to be set for the parameter. This method is called for each
     * parameter before it is set on the transformer. The purpose of this method is to
     * allow dynamic parameters related to the event (usually message properties) to be
     * used. Any expression using the Mule expression syntax can be used.
     * </p>
     * <p>
     * For example: If the current event's message has a property named "myproperty", to
     * pass this in you would set the transform parameter's value to be
     * "#[mule.message:header(myproperty)]".
     * <p/>
     * <p>
     * This method may be overloaded by a sub class to provide a different dynamic
     * parameter implementation.
     * </p>
     *
     * @param name  the name of the parameter
     * @param value the value of the paramter
     * @return the object to be set as the parameter value
     * @throws TransformerException
     */
    protected Object evaluateTransformParameter(String name, Object value, MuleMessage message) throws TransformerException
    {
        if (value instanceof String)
        {
            return muleContext.getExpressionManager().parse(value.toString(), message);
        }
        return value;
    }

    @Override
    public Object clone() throws CloneNotSupportedException
    {
        Object clone = super.clone();
        try
        {
            ((Initialisable) clone).initialise();
            return clone;
        }
        catch (InitialisationException e)
        {
            throw new MuleRuntimeException(CoreMessages.failedToClone(getClass().getName()), e);
        }
    }
}
TOP

Related Classes of org.mule.module.xml.transformer.XQueryTransformer

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.