/* * 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 2005 - 2009 Pentaho Corporation. All rights reserved. * * * Created Sep 21, 2005 * @author wseyler */ package org.pentaho.platform.plugin.action.mdx; import java.io.File; import java.util.Map; import java.util.Properties; 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.IDataComponent; import org.pentaho.platform.api.data.IDatasourceService; import org.pentaho.platform.api.data.IPreparedComponent; import org.pentaho.platform.api.engine.IActionSequenceResource; 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.services.connections.mondrian.MDXConnection; import org.pentaho.platform.plugin.services.connections.mondrian.MDXResultSet; 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", getActionName())); //$NON-NLS-1$ actionValidated = false; } } } catch (Exception e) { actionValidated = false; error(Messages.getInstance().getErrorString("MDXBaseComponent.ERROR_0004_VALIDATION_FAILED", getActionName()), e); //$NON-NLS-1$ } 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", getActionName())); //$NON-NLS-1$ 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", getActionName())); //$NON-NLS-1$ 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", getActionName())); //$NON-NLS-1$ } } 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", getActionName()), e); //$NON-NLS-1$ } 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", getActionName()), e); //$NON-NLS-1$ } 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", getActionName())); //$NON-NLS-1$ 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", getActionName()), e); //$NON-NLS-1$ } 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", getActionName())); //$NON-NLS-1$ 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", getActionName()), e); //$NON-NLS-1$ } 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) { } return con; } catch (Exception ex) { } waitFor(element); } con = getConnection(); try { con.clearWarnings(); } catch (Exception ex) { } return con; } protected void waitFor(final int millis) { try { if (ComponentBase.debug) { debug(Messages.getInstance().getString("MDXBaseComponent.DEBUG_WAITING_FOR_CONNECTION", Integer.toString(millis))); //$NON-NLS-1$ } Thread.sleep(millis); } catch (Exception ex) { // ignore the interrupted exception, if it happens } } protected IPentahoConnection getConnection() { 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.SOLUTION_FILE_RESOURCE) { catalog = PentahoSystem.getApplicationContext().getSolutionPath(catalog); } 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)) { File file = new File(catalog); if (file.exists()) { catalog = file.toURI().toString(); } } } if (catalog == null) { warn(Messages.getInstance().getString("MDXBaseComponent.ERROR_0007_CATALOG_NOT_DEFINED", getActionName())); //$NON-NLS-1$ } 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) { IDatasourceService datasourceService = PentahoSystem.getObjectFactory().get(IDatasourceService.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$ } 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", getActionName()), e); //$NON-NLS-1$ } return null; } @Override public boolean init() { return true; } }