org.pentaho.platform.plugin.action.mdx.MDXBaseComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.platform.plugin.action.mdx.MDXBaseComponent.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.mdx;

import mondrian.olap.Util;
import mondrian.rolap.RolapConnectionProperties;
import mondrian.util.Pair;
import org.apache.commons.logging.Log;
import org.pentaho.actionsequence.dom.ActionInputConstant;
import org.pentaho.actionsequence.dom.IActionOutput;
import org.pentaho.actionsequence.dom.actions.MdxConnectionAction;
import org.pentaho.actionsequence.dom.actions.MdxQueryAction;
import org.pentaho.commons.connection.IPentahoConnection;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.data.IDBDatasourceService;
import org.pentaho.platform.api.data.IDataComponent;
import org.pentaho.platform.api.data.IPreparedComponent;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
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.plugin.action.messages.Messages;
import org.pentaho.platform.plugin.action.mondrian.catalog.IMondrianCatalogService;
import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalog;
import org.pentaho.platform.plugin.services.connections.mondrian.MDXConnection;
import org.pentaho.platform.plugin.services.connections.mondrian.MDXResultSet;
import org.pentaho.platform.util.messages.LocaleHelper;

import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

public abstract class MDXBaseComponent extends ComponentBase implements IDataComponent, IPreparedComponent {

    private static final long serialVersionUID = 495868243986115468L;

    public static final String FORMATTED_CELL_VALUES = "formattedCellValues"; //$NON-NLS-1$

    private IPentahoResultSet rSet;

    /** is set to false if using another IPreparedComponents connection vs own */
    private boolean connectionOwner = true;

    /** keep a reference to the connection for prepared component functionality */
    private IPentahoConnection connection;

    /** stores the prepared query for later use */
    String preparedQuery = null;

    @Override
    public abstract boolean validateSystemSettings();

    @Override
    public abstract Log getLogger();

    public IPentahoResultSet getResultSet() {
        return rSet;
    }

    @Override
    protected boolean validateAction() {
        boolean actionValidated = true;
        MdxQueryAction queryAction = null;
        MdxConnectionAction connAction = null;

        try {
            if (getActionDefinition() instanceof MdxQueryAction) {
                queryAction = (MdxQueryAction) getActionDefinition();
                actionValidated = isConnectionInfoSpecified(queryAction);

                if (actionValidated) {
                    if (queryAction.getQuery() == ActionInputConstant.NULL_INPUT) {
                        error(Messages.getInstance().getErrorString(
                                "MDXBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
                        actionValidated = false;
                    }
                }

                if (actionValidated) {
                    if ((queryAction.getOutputResultSet() == null)
                            && (queryAction.getOutputPreparedStatement() == null)) {
                        error(Messages.getInstance().getErrorString(
                                "MDXBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
                        actionValidated = false;
                    }
                }
            } else if (getActionDefinition() instanceof MdxConnectionAction) {
                connAction = (MdxConnectionAction) getActionDefinition();
                actionValidated = isConnectionInfoSpecified(connAction);
                if (connAction.getOutputConnection() == null) {
                    error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", //$NON-NLS-1$
                            getActionName()));
                    actionValidated = false;
                }

            }
        } catch (Exception e) {
            actionValidated = false;
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0004_VALIDATION_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }

        return actionValidated;
    }

    /*
     * 
     */
    private boolean isConnectionInfoSpecified(final MdxConnectionAction connAction) {
        boolean value = true;

        if (connAction instanceof MdxQueryAction) {
            if ((connAction.getConnection() == ActionInputConstant.NULL_INPUT)
                    && (connAction.getMdxConnectionString() == null)
                    && (connAction.getJndi() == ActionInputConstant.NULL_INPUT)
                    && (connAction.getConnectionProps() == ActionInputConstant.NULL_INPUT)
                    && (((MdxQueryAction) connAction).getMdxConnection() == ActionInputConstant.NULL_INPUT)) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0002_CONNECTION_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
                value = false;
            }
        } else if (connAction instanceof MdxConnectionAction) {
            if ((connAction.getConnection() == ActionInputConstant.NULL_INPUT)
                    && (connAction.getMdxConnectionString() == null)
                    && (connAction.getJndi() == ActionInputConstant.NULL_INPUT)
                    && (connAction.getConnectionProps() == ActionInputConstant.NULL_INPUT)) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0002_CONNECTION_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
                value = false;
            }
        }

        return value;
    }

    @Override
    public void done() {
    }

    @Override
    protected boolean executeAction() {
        boolean value = false;
        /*
         * This is the query part. You would need a connection to execute the query. The connection will either come in as
         * an INPUT (prepared_component) or will be specified right there.
         * 
         * So check if a prepared component exists, if not create a new connection. If connection is not null, proceed to
         * work on the query part.
         * 
         * In the query section you can either execute the query right away or prepare it to be used later by a sub report.
         */
        try {
            if (getActionDefinition() instanceof MdxQueryAction) {
                MdxQueryAction queryAction = (MdxQueryAction) getActionDefinition();
                // if there is a prepared component specified as an input, use its connection
                // instead of creating our own.
                if (queryAction.getMdxConnection() != ActionInputConstant.NULL_INPUT) {
                    if (queryAction.getMdxConnection().getValue() != null) {
                        connectionOwner = false;
                        IPreparedComponent component = (IPreparedComponent) queryAction.getMdxConnection()
                                .getValue();
                        IPentahoConnection conn = component.shareConnection();
                        if (conn.getDatasourceType() == IPentahoConnection.MDX_DATASOURCE) {
                            connection = conn;
                        } else {
                            error(Messages.getInstance().getErrorString(
                                    "IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", getActionName())); //$NON-NLS-1$            
                        }
                    } else {
                        error(Messages.getInstance().getErrorString(
                                "IPreparedComponent.ERROR_0002_CONNECTION_NOT_AVAILABLE", getActionName())); //$NON-NLS-1$
                    }
                } else {
                    dispose();
                    connection = getDatasourceConnection();
                }

                if (connection != null) {
                    String query = queryAction.getQuery().getStringValue();
                    if (queryAction.getOutputPreparedStatement() != null) {
                        // prepare the query for execution, but don't execute quite yet.
                        prepareQuery(query);
                        // set the output as self, which will be used later by another component.
                        setOutputValue(IPreparedComponent.PREPARED_COMPONENT_NAME, this);
                        value = true;
                    } else {
                        value = runQuery(connection, query);
                    }
                } else {
                    error(Messages.getInstance().getErrorString("IPreparedComponent.ERROR_0004_NO_CONNECTION_INFO", //$NON-NLS-1$
                            getActionName()));
                }
            } else if (getActionDefinition() instanceof MdxConnectionAction) {
                dispose();
                connection = getDatasourceConnection();

                if (connection != null) {
                    setOutputValue(IPreparedComponent.PREPARED_COMPONENT_NAME, this);
                    value = true;
                }
            } else {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0004_VALIDATION_FAILED",
                        getActionName())); //$NON-NLS-1$
            }
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }

        return value;
    }

    /**
     * called when in prepared-component mode, this method populates the preparedQuery string and preparedParameters
     * object.
     * 
     * @param rawQuery
     * @return
     */
    protected boolean prepareQuery(final String rawQuery) {

        try {
            if (connection == null) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return false;
            }
            if (!connection.initialized()) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return false;
            }

            if (rawQuery != null) {
                preparedQuery = applyInputsToFormat(rawQuery);
            }

            return true;
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }

        return false;
    }

    /**
     * if the owner, dispose of the connection
     */
    public void dispose() {
        if (connectionOwner) {
            if (connection != null) {
                connection.close();
            }
        }
        connection = null;
    }

    /**
     * return this class's connection. This implements the IPreparedComponent interface, which may share its connection
     * with others.
     * 
     * @return connection object
     */
    public IPentahoConnection shareConnection() {
        return connection;
    }

    /**
     * executes a prepared method that returns a result set executePrepared looks up any "PREPARELATER" params in the
     * preparedParams map.
     * 
     * @param preparedParams
     *          a map of possible parameters.
     * @return result set
     */
    public IPentahoResultSet executePrepared(final Map preparedParams) {
        try {
            if (connection == null) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return null;
            }
            if (!connection.initialized()) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return null;
            }
            if (preparedQuery == null) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
                return null;
            }

            // parse preparedQuery, replacing any {PREPARELATER:NAME} with appropriate values
            String query = TemplateUtil.applyTemplate(preparedQuery, getRuntimeContext(), new MapParameterResolver(
                    preparedParams, IPreparedComponent.PREPARE_LATER_PREFIX, getRuntimeContext()));

            if (ComponentBase.debug) {
                debug(Messages.getInstance().getString("MDXBaseComponent.DEBUG_RUNNING_QUERY", query)); //$NON-NLS-1$
            }

            // evaluate
            IPentahoResultSet resultSet = connection.executeQuery(query);
            rSet = resultSet;
            return resultSet;
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }
        return null;
    }

    protected boolean runQuery(final IPentahoConnection localConnection, final String rawQuery) {

        try {
            if (localConnection == null) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return false;
            }
            if (!localConnection.initialized()) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
                return false;
            }
            if (rawQuery == null) {
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$
                        getActionName()));
                return false;
            }

            if (ComponentBase.debug) {
                debug(Messages.getInstance().getString("MDXBaseComponent.DEBUG_RUNNING_QUERY", rawQuery)); //$NON-NLS-1$
            }

            // execute the query, read the results and cache them
            IPentahoResultSet resultSet = localConnection.executeQuery(rawQuery);
            if (resultSet != null && resultSet instanceof MDXResultSet) {
                // BISERVER-3543 - set the result set to return formatted cell values
                boolean formattedCellValues = false;
                if (isDefinedInput(FORMATTED_CELL_VALUES)) {
                    formattedCellValues = getInputBooleanValue(FORMATTED_CELL_VALUES, false);
                }
                ((MDXResultSet) resultSet).setFormattedCellValues(formattedCellValues);
            }
            rSet = resultSet;
            if (resultSet != null) {
                MdxQueryAction mdxQueryAction = (MdxQueryAction) getActionDefinition();
                IActionOutput actionOutput = mdxQueryAction.getOutputResultSet();
                if (actionOutput != null) {
                    actionOutput.setValue(resultSet);
                }
                return true;
            } else {
                // close the connection
                error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED",
                        getActionName())); //$NON-NLS-1$
                localConnection.close();
                return false;
            }

        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }

        return false;
    }

    /**
     * attempt to aquire a connection. if connection isn't available, wait a certain period of time before trying again.
     * 
     * @return connection
     */
    public IPentahoConnection getDatasourceConnection() {
        IPentahoConnection con;
        int[] timeouts = { 200, 500, 2000 };
        for (int element : timeouts) {
            try {
                con = getConnection();
                try {
                    con.clearWarnings();
                } catch (Exception ex) {
                    //ignored
                }
                return con;
            } catch (Exception ex) {
                //ignored
            }
            waitFor(element);
        }
        con = getConnection();
        try {
            con.clearWarnings();
        } catch (Exception ex) {
            //ignore
        }
        return con;
    }

    protected void waitFor(final int millis) {
        try {
            if (ComponentBase.debug) {
                debug(Messages.getInstance().getString("MDXBaseComponent.DEBUG_WAITING_FOR_CONNECTION", //$NON-NLS-1$
                        Integer.toString(millis)));
            }
            Thread.sleep(millis);
        } catch (Exception ex) {
            // ignore the interrupted exception, if it happens
        }
    }

    protected IPentahoConnection getConnection() {

        // first attempt to get the connection metadata from the catalog service. if that is not successful,
        // get the connection using the original approach.

        MdxConnectionAction connAction = (MdxConnectionAction) getActionDefinition();
        String catalogName = connAction.getCatalog().getStringValue();
        IMondrianCatalogService mondrianCatalogService = PentahoSystem.get(IMondrianCatalogService.class,
                "IMondrianCatalogService", PentahoSessionHolder.getSession()); //$NON-NLS-1$
        MondrianCatalog catalog = mondrianCatalogService.getCatalog(catalogName, PentahoSessionHolder.getSession());

        if (catalog == null) {
            return getConnectionOrig();
        }

        Util.PropertyList connectProperties = Util.parseConnectString(catalog.getDataSourceInfo());

        Properties properties = new Properties();

        Iterator<Pair<String, String>> iter = connectProperties.iterator();
        while (iter.hasNext()) {
            Pair<String, String> pair = iter.next();
            properties.put(pair.getKey(), pair.getValue());
        }

        properties.put("Catalog", catalog.getDefinition());
        properties.put("Provider", "mondrian");
        properties.put("PoolNeeded", "false");
        properties.put(RolapConnectionProperties.Locale.name(), LocaleHelper.getLocale().toString());

        debug("Mondrian Connection Properties: " + properties.toString());

        MDXConnection mdxConnection = (MDXConnection) PentahoConnectionFactory.getConnection(
                IPentahoConnection.MDX_DATASOURCE, properties, PentahoSessionHolder.getSession(), this);

        if (connAction != null) {
            if ((connAction.getExtendedColumnNames() != ActionInputConstant.NULL_INPUT)) {
                mdxConnection.setUseExtendedColumnNames(connAction.getExtendedColumnNames().getBooleanValue());
            }
        }

        return mdxConnection;
    }

    protected IPentahoConnection getConnectionOrig() {
        IPentahoConnection localConnection = null;
        MdxConnectionAction connAction = (MdxConnectionAction) getActionDefinition();
        try {
            String mdxConnectionStr = connAction.getMdxConnectionString().getStringValue();
            Properties mdxConnectionProps = (Properties) connAction.getConnectionProps().getValue();
            String jdbcStr = connAction.getConnection().getStringValue();
            String jndiStr = connAction.getJndi().getStringValue();
            String location = connAction.getLocation().getStringValue();
            String role = connAction.getRole().getStringValue();
            String catalog = connAction.getCatalog().getStringValue();

            if ((catalog == null) && (connAction.getCatalogResource() != null)) {
                IActionSequenceResource resource = getResource(connAction.getCatalogResource().getName());
                catalog = resource.getAddress();
                if ((resource.getSourceType() == IActionSequenceResource.URL_RESOURCE)
                        && (catalog.indexOf("solution:") != 0)) { //$NON-NLS-1$
                    // Extra step to make sure that remote mondrian models
                    // fully qualified aren't munged
                    // MB
                    if (!catalog.startsWith("http:")) { //$NON-NLS-1$ 
                        catalog = "solution:" + catalog; //$NON-NLS-1$
                    }
                } else if ((resource.getSourceType() == IActionSequenceResource.SOLUTION_FILE_RESOURCE)
                        || (resource.getSourceType() == IActionSequenceResource.FILE_RESOURCE)) {
                    if (!catalog.startsWith("solution:")) {
                        catalog = "solution:" + catalog; //$NON-NLS-1$
                    }
                }
            }
            if (catalog == null) {
                warn(Messages.getInstance().getString("MDXBaseComponent.ERROR_0007_CATALOG_NOT_DEFINED", //$NON-NLS-1$
                        getActionName()));
            } else {
                if (mdxConnectionProps != null) {
                    mdxConnectionProps.put(MdxConnectionAction.CATALOG_ELEMENT, catalog);
                }
            }

            String userId = connAction.getUserId().getStringValue();
            String password = connAction.getPassword().getStringValue();
            if (mdxConnectionProps != null) {
                localConnection = PentahoConnectionFactory.getConnection(IPentahoConnection.MDX_DATASOURCE,
                        mdxConnectionProps, getSession(), this);
            } else {
                if (mdxConnectionStr != null) {
                    localConnection = PentahoConnectionFactory.getConnection(IPentahoConnection.MDX_DATASOURCE,
                            mdxConnectionStr, getSession(), this);
                } else {
                    String connectStr = null;
                    if (jdbcStr != null) {
                        connectStr = jdbcStr + "; Catalog=" + catalog; //$NON-NLS-1$
                    } else if (jndiStr != null) {

                        IDBDatasourceService datasourceService = PentahoSystem.getObjectFactory()
                                .get(IDBDatasourceService.class, null);
                        if (datasourceService.getDataSource(jndiStr) == null) {
                            error(Messages.getInstance()
                                    .getErrorString("MDXBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$
                            return null;
                        }

                        connectStr = "dataSource=" + jndiStr + "; Catalog=" + catalog; //$NON-NLS-1$ //$NON-NLS-2$
                        // Add extra definitions from platform mondrian metadata
                        MondrianCatalog mc = org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogHelper
                                .getInstance().getCatalog(catalog, getSession());
                        try {
                            connectStr += ";" + mc.getDataSourceInfo();
                        } catch (Exception e) {
                            // Just swallow the exception
                        }
                    }
                    if (role != null) {
                        connectStr += "; Role=" + role; //$NON-NLS-1$
                    }
                    Properties props = new Properties();
                    props.setProperty(IPentahoConnection.CONNECTION, connectStr);
                    props.setProperty(IPentahoConnection.PROVIDER, location);
                    if (userId != null) {
                        props.setProperty(IPentahoConnection.USERNAME_KEY, userId);
                    }
                    if (password != null) {
                        props.setProperty(IPentahoConnection.PASSWORD_KEY, password);
                    }

                    localConnection = PentahoConnectionFactory.getConnection(IPentahoConnection.MDX_DATASOURCE,
                            props, getSession(), this);
                }
                if (localConnection == null) {
                    error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$
                    return null;
                }
            }

            if (localConnection instanceof MDXConnection) {
                MDXConnection mdxConn = (MDXConnection) localConnection;
                if (connAction != null) {
                    if ((connAction.getExtendedColumnNames() != ActionInputConstant.NULL_INPUT)) {
                        mdxConn.setUseExtendedColumnNames(connAction.getExtendedColumnNames().getBooleanValue());
                    }
                }
            }
            return localConnection;
        } catch (Exception e) {
            error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", //$NON-NLS-1$
                    getActionName()), e);
        }
        return null;
    }

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