org.pentaho.platform.plugin.action.xml.xquery.XQueryBaseComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.plugin.action.xml.xquery.XQueryBaseComponent.java

Source

/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * Copyright (c) 2002-2013 Pentaho Corporation..  All rights reserved.
 */

package org.pentaho.platform.plugin.action.xml.xquery;

import net.sf.saxon.trans.XPathException;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.dom4j.Document;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.pentaho.actionsequence.dom.ActionInputConstant;
import org.pentaho.actionsequence.dom.IActionDefinition;
import org.pentaho.actionsequence.dom.IActionInput;
import org.pentaho.actionsequence.dom.IActionOutput;
import org.pentaho.actionsequence.dom.actions.XQueryAction;
import org.pentaho.actionsequence.dom.actions.XQueryConnectionAction;
import org.pentaho.commons.connection.IPentahoConnection;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.data.IPreparedComponent;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.services.connection.PentahoConnectionFactory;
import org.pentaho.platform.engine.services.runtime.MapParameterResolver;
import org.pentaho.platform.engine.services.runtime.TemplateUtil;
import org.pentaho.platform.engine.services.solution.ComponentBase;
import org.pentaho.platform.engine.services.solution.StandardSettings;
import org.pentaho.platform.plugin.action.messages.Messages;
import org.pentaho.platform.plugin.services.connections.xquery.XQConnection;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * XQueryBaseComponent provides a mechanism to run xqueries within the Pentaho BI Platform.
 * <p/>
 * <p/>
 * TODO: In regards to IPreparedComponent, implement a method for choosing the datasource on the fly
 */
public abstract class XQueryBaseComponent extends ComponentBase implements IPreparedComponent {

    private static final long serialVersionUID = -4390530975622541328L;

    private IPentahoResultSet rSet;

    /**
     * reference to connection object
     */
    protected IPentahoConnection connection;

    /**
     * keeps track of ownership of connection
     */
    protected boolean connectionOwner = true;

    private static final String FILENAME_PREFIX = "tmp"; //$NON-NLS-1$

    private static final String EXTENSION = ".xml"; //$NON-NLS-1$

    //  private static final String TEMP_DIRECTORY = "system/tmp/"; //$NON-NLS-1$

    private static final String XML_DOCUMENT_TAG = "XML_DOCUMENT"; //$NON-NLS-1$

    @Override
    public abstract boolean validateSystemSettings();

    @Override
    public abstract Log getLogger();

    private int maxRows = -1;

    /**
     * string to hold prepared query until execution
     */
    String preparedQuery = null;

    /**
     * string array to hold prepared column types until execution
     */
    String[] preparedColumnTypes = null;

    public IPentahoResultSet getResultSet() {
        return rSet;
    }

    @Override
    protected boolean validateAction() {
        boolean result = false;
        IActionDefinition actionDefinition = getActionDefinition();

        if (actionDefinition instanceof XQueryAction) {
            XQueryAction xQueryAction = (XQueryAction) actionDefinition;
            if ((xQueryAction.getSourceXml() == ActionInputConstant.NULL_INPUT)
                    && (xQueryAction.getXmlDocument() == null)) {
                error(Messages.getInstance().getString("XQueryBaseComponent.ERROR_0008_SOURCE_NOT_DEFINED", //$NON-NLS-1$
                        getActionName()));
            } else if (xQueryAction.getQuery() == ActionInputConstant.NULL_INPUT) {
                error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
            } else if ((xQueryAction.getOutputPreparedStatement() == null)
                    && (xQueryAction.getOutputResultSet() == null)) {
                error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
            } else {
                result = true;
            }
        } else if (actionDefinition instanceof XQueryConnectionAction) {
            XQueryConnectionAction xQueryConnectionAction = (XQueryConnectionAction) actionDefinition;
            if (xQueryConnectionAction.getOutputConnection() == null) {
                error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
            } else {
                result = true;
            }
        } else {
            error(Messages.getInstance().getErrorString("ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", //$NON-NLS-1$
                    actionDefinition.getElement().asXML()));
        }
        return result;
    }

    @Override
    public void done() {
        // TODO Auto-generated method stub
    }

    @Override
    protected boolean executeAction() {
        boolean result = false;
        IActionDefinition actionDefinition = getActionDefinition();
        // int maxRows = -1;
        // int queryTimeout = -1;
        if (actionDefinition instanceof XQueryAction) {
            XQueryAction xQueryAction = (XQueryAction) actionDefinition;

            // Not implemented yet
            // IActionInput queryTimeoutInput = xQueryAction.getQueryTimeout();

            IActionInput maxRowsInput = xQueryAction.getMaxRows();
            if (maxRowsInput != ActionInputConstant.NULL_INPUT) {
                this.setMaxRows(maxRowsInput.getIntValue());
            }

            IPreparedComponent sharedConnection = (IPreparedComponent) xQueryAction.getSharedConnection()
                    .getValue();
            if (sharedConnection != null) {
                connectionOwner = false;
                connection = sharedConnection.shareConnection();
            } else {
                connection = getConnection();
            }
            if (connection == null) {
                error(Messages.getInstance()
                        .getErrorString("IPreparedComponent.ERROR_0002_CONNECTION_NOT_AVAILABLE", getActionName())); //$NON-NLS-1$
            } else if (connection.getDatasourceType() != IPentahoConnection.XML_DATASOURCE) {
                error(Messages.getInstance().getErrorString("IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", //$NON-NLS-1$
                        getActionName()));
            } else {
                result = runQuery(connection, xQueryAction.getQuery().getStringValue());
            }
        } else if (actionDefinition instanceof XQueryConnectionAction) {
            XQueryConnectionAction xQueryConnectionAction = (XQueryConnectionAction) getActionDefinition();
            connection = getConnection();
            if (connection == null) {
                error(Messages.getInstance()
                        .getErrorString("IPreparedComponent.ERROR_0002_CONNECTION_NOT_AVAILABLE", getActionName())); //$NON-NLS-1$
            } else if (connection.getDatasourceType() != IPentahoConnection.XML_DATASOURCE) {
                error(Messages.getInstance().getErrorString("IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", //$NON-NLS-1$
                        getActionName()));
            } else {
                xQueryConnectionAction.getOutputConnection().setValue(this);
                result = true;
            }
        }
        return result;
    }

    protected boolean runQuery(final IPentahoConnection localConnection, String rawQuery) {
        XQueryAction xQueryAction = (XQueryAction) getActionDefinition();
        try {
            if (localConnection == null) {
                return false;
            }
            if (ComponentBase.debug) {
                debug(Messages.getInstance().getString("XQueryBaseComponent.DEBUG_RUNNING_QUERY", rawQuery)); //$NON-NLS-1$
            }
            String documentPath = null;
            int resourceType = -1;
            String srcXml = xQueryAction.getSourceXml().getStringValue();
            org.pentaho.actionsequence.dom.IActionResource xmlResource = xQueryAction.getXmlDocument();
            InputStream inputStream = null;
            URL url = null;
            if (srcXml != null) {
                inputStream = new FileInputStream(new File(createTempXMLFile(srcXml)));
            } else if (xmlResource != null) {
                // we have a local document to use as the data source
                IActionSequenceResource resource = getResource(xmlResource.getName());
                resourceType = resource.getSourceType();
                if ((resourceType == IActionSequenceResource.SOLUTION_FILE_RESOURCE)
                        || (resourceType == IActionSequenceResource.FILE_RESOURCE)) {
                    inputStream = resource.getInputStream(RepositoryFilePermission.READ);
                } else if (resourceType == IActionSequenceResource.XML) {
                    inputStream = new FileInputStream(new File(createTempXMLFile(resource.getAddress())));
                } else {
                    url = new URL(documentPath);
                }
            }

            // Retrieve the column types
            String[] columnTypes = null;
            if (retrieveColumnTypes()) {
                try {
                    SAXReader reader = new SAXReader();
                    Document document;
                    if (url != null) {
                        document = reader.read(url);
                    } else {
                        document = reader.read(inputStream);
                    }
                    Node commentNode = document.selectSingleNode("/result-set/comment()"); //$NON-NLS-1$
                    if (commentNode != null) {
                        String commentString = commentNode.getText();
                        StringTokenizer st = new StringTokenizer(commentString, ","); //$NON-NLS-1$
                        List columnTypesList = new LinkedList();
                        while (st.hasMoreTokens()) {
                            String token = st.nextToken().trim();
                            columnTypesList.add(token);
                        }
                        columnTypes = (String[]) columnTypesList.toArray(new String[0]);
                    }
                } catch (Exception e) {
                    getLogger().warn(Messages.getInstance()
                            .getString("XQueryBaseComponent.ERROR_0009_ERROR_BUILDING_COLUMN_TYPES"), e); //$NON-NLS-1$
                }
            }

            if (rawQuery != null) {
                if (rawQuery.indexOf("{" + XQueryBaseComponent.XML_DOCUMENT_TAG + "}") >= 0) { //$NON-NLS-1$//$NON-NLS-2$
                    rawQuery = TemplateUtil.applyTemplate(rawQuery, XQueryBaseComponent.XML_DOCUMENT_TAG,
                            documentPath);
                } else {
                    Calendar now = Calendar.getInstance();
                    File temp = File.createTempFile("tempXQuery" + now.getTimeInMillis(), ".xml");
                    temp.deleteOnExit();

                    OutputStream out = new FileOutputStream(temp);
                    IActionSequenceResource resource = getResource(xmlResource.getName());
                    inputStream = resource.getInputStream(RepositoryFilePermission.READ);
                    byte[] buf = new byte[1024];
                    int len;
                    while ((len = inputStream.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    out.close();
                    inputStream.close();
                    documentPath = temp.getAbsolutePath();
                    documentPath = FilenameUtils.separatorsToUnix(documentPath);

                    rawQuery = "doc(\"" + documentPath + "\")" + rawQuery; //$NON-NLS-1$ //$NON-NLS-2$
                }
            }

            if (xQueryAction.getOutputPreparedStatement() != null) {
                return prepareFinalQuery(rawQuery, columnTypes);
            } else {
                return runFinalQuery(localConnection, rawQuery, columnTypes);
            }
        } catch (Exception e) {
            getLogger().error(
                    Messages.getInstance().getString("XQueryBaseComponent.ERROR_0010_ERROR_RUNNING_QUERY"), e); //$NON-NLS-1$
            return false;
        }
    }

    protected boolean prepareFinalQuery(final String rawQuery, final String[] columnTypes) {
        if (rawQuery != null) {
            preparedQuery = applyInputsToFormat(rawQuery);
        }
        preparedColumnTypes = columnTypes;
        ((XQueryAction) getActionDefinition()).getOutputPreparedStatement().setValue(this);
        return true;
    }

    protected boolean runFinalQuery(final IPentahoConnection localConnection, final String rawQuery,
            final String[] columnTypes) {
        XQueryAction xQueryAction = (XQueryAction) getActionDefinition();
        boolean success = false;
        String finalQuery = applyInputsToFormat(rawQuery);
        // execute the query, read the results and cache them
        try {
            IPentahoResultSet resultSet = ((XQConnection) localConnection).executeQuery(finalQuery, columnTypes);
            if (resultSet != null) {
                if (!xQueryAction.getLive().getBooleanValue(true)) {
                    resultSet = resultSet.memoryCopy();
                }
                try {
                    IActionOutput resultSetOutput = xQueryAction.getOutputResultSet();
                    if (resultSetOutput != null) {
                        resultSetOutput.setValue(resultSet);
                    }
                    success = true;
                } finally {
                    resultSet.close();
                }
            }
        } catch (XPathException e) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED",
                    getActionName()), e); //$NON-NLS-1$
        }
        return success;
    }

    protected String createTempXMLFile(final String xmlString) {
        // Save it to a temporary file
        File file;
        String documentPath = null;
        try {
            file = PentahoSystem.getApplicationContext().createTempFile(getSession(),
                    XQueryBaseComponent.FILENAME_PREFIX, XQueryBaseComponent.EXTENSION, true);

            documentPath = file.getCanonicalPath();

            BufferedWriter out = new BufferedWriter(new FileWriter(file));
            out.write(xmlString);
            out.close();
        } catch (IOException e) {
            getLogger().error(
                    Messages.getInstance().getString("XQueryBaseComponent.ERROR_0011_ERROR_CREATING_TEMP_FILE"), e); //$NON-NLS-1$
        }

        documentPath = documentPath.replaceAll("\\\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$
        return documentPath;
    }

    protected IPentahoConnection getConnection() {
        IPentahoConnection conn = null;
        try {
            conn = PentahoConnectionFactory.getConnection(IPentahoConnection.XML_DATASOURCE, getSession(), this);
            if (conn == null) {
                error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$
                return null;
            }
            if (this.getMaxRows() >= 0) {
                conn.setMaxRows(this.getMaxRows());
            }
            return conn;
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED",
                    getActionName()), e); //$NON-NLS-1$
        }
        return null;
    }

    @Override
    public boolean init() {
        return true;
    }

    /**
     * implements IPreparedComponents shareConnection, allowing other xquery components to access the connection
     *
     * @return shared connection
     */
    public IPentahoConnection shareConnection() {
        return connection;
    }

    /**
     * implements the IPreparedComponent executePrepared, which allows other components to execute the prepared
     * statement.
     *
     * @param preparedParams lookup for prepared parameters
     * @return pentaho result set
     */
    public IPentahoResultSet executePrepared(final Map preparedParams) {

        if (connection == null) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0012_NO_CONNECTION",
                    getActionName())); //$NON-NLS-1$
            return null;
        }
        if (!connection.initialized()) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0012_NO_CONNECTION", //$NON-NLS-1$
                    getActionName()));
            return null;
        }

        if (preparedQuery == null) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$
                    getActionName()));
            return null;
        }

        String finalQuery = TemplateUtil.applyTemplate(preparedQuery, getRuntimeContext(), new MapParameterResolver(
                preparedParams, IPreparedComponent.PREPARE_LATER_PREFIX, getRuntimeContext()));

        // execute the query, read the results and cache them
        try {
            IPentahoResultSet resultSet = ((XQConnection) connection).executeQuery(finalQuery, preparedColumnTypes);
            if (resultSet != null) {
                boolean live = getInputBooleanValue(StandardSettings.LIVE, true);
                if (!live) {
                    resultSet = resultSet.memoryCopy();
                }
                try {
                    return resultSet;
                } finally {
                    resultSet.close();
                }
            }
        } catch (XPathException e) {
            error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED",
                    getActionName()), e); //$NON-NLS-1$
        }
        return null;
    }

    /**
     * Determines if the action should attempt to retrieve the columns types
     */
    protected boolean retrieveColumnTypes() {
        return true;
    }

    public int getMaxRows() {
        return this.maxRows;
    }

    public void setMaxRows(final int value) {
        if (rSet == null) {
            this.maxRows = value;
        } else {
            throw new UnsupportedOperationException(Messages.getInstance()
                    .getErrorString("XQueryBaseComponent.ERROR_0013_INVALID_ORDER_OF_OPERATION")); //$NON-NLS-1$
        }
    }

    /**
     * disposes of the connection this is called by the runtime context if the object is used as an iprepared component
     */
    public void dispose() {
        if (connectionOwner) {
            if (connection != null) {
                connection.close();
            }
        }
    }
}