/*! * 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.mondrian; import mondrian.olap.AxisOrdinal; import mondrian.olap.Connection; import mondrian.olap.Cube; import mondrian.olap.Dimension; import mondrian.olap.Hierarchy; import mondrian.olap.Member; import mondrian.olap.MondrianException; import mondrian.olap.Query; import mondrian.olap.Schema; import mondrian.olap.Util; import mondrian.rolap.RolapConnection; import mondrian.rolap.RolapConnectionProperties; import mondrian.server.Locus; import mondrian.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.commons.connection.IPentahoConnection; import org.pentaho.platform.api.engine.IPentahoSession; 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.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.action.mondrian.catalog.MondrianCatalogComplementInfo; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogHelper; import org.pentaho.platform.plugin.services.connections.mondrian.MDXConnection; import org.pentaho.platform.plugin.services.connections.sql.SQLConnection; import org.pentaho.platform.util.logging.Logger; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; /** * @author James Dixon * <p/> * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - * Code Templates */ public class MondrianModelComponent extends ComponentBase { private static final long serialVersionUID = -718697500002076945L; @Override public Log getLogger() { return LogFactory.getLog( MondrianModelComponent.class ); } @Override protected boolean validateSystemSettings() { // This component does not have any system settings to validate return true; } @Override public boolean init() { // get the settings from the system configuration file return true; } @Override public boolean validateAction() { return true; } @Override public boolean executeAction() { return true; } @Override public void done() { } public static String getInitialQuery( final Properties properties, final String cubeName, IPentahoSession session ) throws Throwable { // Apply any properties for this catalog specified in datasource.xml IMondrianCatalogService mondrianCatalogService = PentahoSystem.get( IMondrianCatalogService.class, "IMondrianCatalogService", PentahoSessionHolder.getSession() ); List<MondrianCatalog> catalogs = mondrianCatalogService.listCatalogs( PentahoSessionHolder.getSession(), true ); String propCat = properties.getProperty( RolapConnectionProperties.Catalog.name() ); for ( MondrianCatalog cat : catalogs ) { if ( cat.getDefinition().equalsIgnoreCase( propCat ) ) { Util.PropertyList connectProperties = Util.parseConnectString( cat.getDataSourceInfo() ); Iterator<Pair<String, String>> iter = connectProperties.iterator(); while ( iter.hasNext() ) { Pair<String, String> pair = iter.next(); if ( !properties.containsKey( pair.getKey() ) ) // Only set if not set already { properties.put( pair.getKey(), pair.getValue() ); } } break; } } MDXConnection mdxConnection = (MDXConnection) PentahoConnectionFactory.getConnection( IPentahoConnection.MDX_DATASOURCE, properties, session, null ); // mdxConnection.setProperties( properties ); Connection connection = mdxConnection.getConnection(); if ( connection == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0001_INVALID_CONNECTION", properties.toString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } try { return MondrianModelComponent.getInitialQuery( connection, cubeName ); } catch ( Throwable t ) { if ( t instanceof MondrianException ) { // pull the cause out, otherwise it never gets logged Throwable cause = ( (MondrianException) t ).getCause(); if ( cause != null ) { throw cause; } else { throw t; } } else { throw t; } } } /** * @param modelPath * @param connectionString * @param driver * @param user * @param password * @param cubeName * @return mdx string that represents the initial query * @throws Throwable * @deprecated */ @Deprecated public static String getInitialQuery( final String modelPath, final String connectionString, final String driver, final String user, final String password, final String cubeName, IPentahoSession session ) throws Throwable { return MondrianModelComponent.getInitialQuery( modelPath, connectionString, driver, user, password, cubeName, null, session ); } /** * @param modelPath * @param connectionString * @param driver * @param user * @param password * @param cubeName * @param roleName * @return mdx string that represents the initial query * @throws Throwable * @deprecated */ @Deprecated public static String getInitialQuery( String modelPath, final String connectionString, final String driver, final String user, final String password, final String cubeName, final String roleName, IPentahoSession session ) throws Throwable { Properties properties = new Properties(); // TODO support driver manager connections if ( !PentahoSystem.ignored ) { if ( driver != null ) { properties.put( "Driver", driver ); //$NON-NLS-1$ } if ( user != null ) { properties.put( "User", user ); //$NON-NLS-1$ } if ( password != null ) { properties.put( "Password", password ); //$NON-NLS-1$ } } if ( modelPath.indexOf( "http" ) == 0 ) { //$NON-NLS-1$ properties.put( RolapConnectionProperties.Catalog.name(), modelPath ); //$NON-NLS-1$ } else { if ( modelPath.indexOf( "http" ) == 0 ) { //$NON-NLS-1$ properties.put( RolapConnectionProperties.Catalog.name(), modelPath ); //$NON-NLS-1$ } else { if ( !modelPath.startsWith( "solution:" ) && !modelPath.startsWith( "mondrian:" ) ) { //$NON-NLS-1$ modelPath = "solution:" + modelPath; //$NON-NLS-1$ } properties.put( RolapConnectionProperties.Catalog.name(), modelPath ); //$NON-NLS-1$ } } properties.put( RolapConnectionProperties.Provider.name(), "mondrian" ); //$NON-NLS-1$ //$NON-NLS-2$ properties.put( RolapConnectionProperties.PoolNeeded.name(), "false" ); //$NON-NLS-1$//$NON-NLS-2$ properties.put( RolapConnectionProperties.DataSource.name(), connectionString ); //$NON-NLS-1$ if ( roleName != null ) { properties.put( RolapConnectionProperties.Role.name(), roleName ); //$NON-NLS-1$ } return MondrianModelComponent.getInitialQuery( properties, cubeName, session ); } /** * @param modelPath * @param connectionString * @param cubeName * @return mdx string that represents the initial query * @throws Throwable * @deprecated */ @Deprecated public static String getInitialQuery( final String modelPath, final String connectionString, final String cubeName, IPentahoSession session ) throws Throwable { return MondrianModelComponent.getInitialQuery( modelPath, connectionString, cubeName, null, session ); } /** * @param modelPath * @param jndi * @param cubeName * @param roleName * @return mdx string that represents the initial query * @throws Throwable * @deprecated */ @Deprecated public static String getInitialQuery( String modelPath, String jndi, final String cubeName, final String roleName, IPentahoSession session ) throws Throwable { Properties properties = new Properties(); if ( modelPath.indexOf( "http" ) == 0 ) { //$NON-NLS-1$ properties.put( RolapConnectionProperties.Catalog.name(), modelPath ); //$NON-NLS-1$ } else { if ( !modelPath.startsWith( "solution:" ) && !modelPath.startsWith( "mondrian:" ) ) { //$NON-NLS-1$ modelPath = "solution:" + modelPath; //$NON-NLS-1$ } properties.put( RolapConnectionProperties.Catalog.name(), modelPath ); //$NON-NLS-1$ } properties.put( RolapConnectionProperties.Provider.name(), "mondrian" ); //$NON-NLS-1$ //$NON-NLS-2$ properties.put( RolapConnectionProperties.PoolNeeded.name(), "false" ); //$NON-NLS-1$ //$NON-NLS-2$ properties.put( RolapConnectionProperties.DataSource.name(), jndi ); //$NON-NLS-1$ if ( roleName != null ) { properties.put( RolapConnectionProperties.Role.name(), roleName ); //$NON-NLS-1$ } return MondrianModelComponent.getInitialQuery( properties, cubeName, session ); } public static String getInitialQuery( final Connection connection, final String cubeName ) throws Throwable { String measuresMdx = null; String columnsMdx = null; String whereMdx = ""; //$NON-NLS-1$ StringBuffer rowsMdx = new StringBuffer(); // Get catalog info, if exists String catalog = connection.getCatalogName(); MondrianCatalogComplementInfo catalogComplementInfo = MondrianCatalogHelper.getInstance().getCatalogComplementInfoMap( catalog ); try { Schema schema = connection.getSchema(); if ( schema == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0002_INVALID_SCHEMA", connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } Cube[] cubes = schema.getCubes(); if ( ( cubes == null ) || ( cubes.length == 0 ) ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0003_NO_CUBES", connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } if ( ( cubes.length > 1 ) && ( cubeName == null ) ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0004_CUBE_NOT_SPECIFIED", connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } Cube cube = null; if ( cubes.length == 1 ) { cube = cubes[ 0 ]; } else { for ( Cube element : cubes ) { if ( element.getName().equals( cubeName ) ) { cube = element; break; } } } if ( cube == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0005_CUBE_NOT_FOUND", cubeName, connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } // If we have any whereConditions block, we need to find which hierarchies they are in // and not include them in the rows HashSet<Hierarchy> whereHierarchies = new HashSet<Hierarchy>(); if ( catalogComplementInfo != null && catalogComplementInfo.getWhereCondition( cube.getName() ) != null && !catalogComplementInfo.getWhereCondition( cube.getName() ).equals( "" ) ) { //$NON-NLS-1$ final String rawString = catalogComplementInfo.getWhereCondition( cube.getName() ); // Caveat - It's possible that we have in the where condition a hierarchy that we don't have access // permissions; In this case, we'll ditch the where condition at all. Same for any error that // we find here try { // According to Julian, the better way to resolve the names is to build a query final String queryStr = "select " + rawString + " on columns, {} on rows from " + cube.getName(); //$NON-NLS-1$ //$NON-NLS-2$ final Query query = connection.parseQuery( queryStr ); final Hierarchy[] hierarchies = query.getMdxHierarchiesOnAxis( AxisOrdinal.StandardAxisOrdinal.COLUMNS ); boolean isWhereValid = true; for ( int i = 0; i < hierarchies.length && isWhereValid; i++ ) { final Hierarchy hierarchy = hierarchies[ i ]; if ( connection.getRole().canAccess( hierarchy ) ) { whereHierarchies.add( hierarchy ); } else { isWhereValid = false; whereHierarchies.clear(); } } if ( isWhereValid ) { whereMdx = " WHERE " + rawString; //$NON-NLS-1$ } } catch ( Exception e ) { // We found an error in the where slicer, so we'll just act like it wasn't here whereHierarchies.clear(); } } Dimension[] dimensions = cube.getDimensions(); if ( ( dimensions == null ) || ( dimensions.length == 0 ) ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0006_NO_DIMENSIONS", cubeName, connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } for ( Dimension element : dimensions ) { final Hierarchy hierarchy = element.getHierarchy(); if ( hierarchy == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0007_NO_HIERARCHIES", element.getName(), cubeName, connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } if ( !connection.getRole().canAccess( hierarchy ) ) { // We can't access this element continue; } if ( whereHierarchies.contains( hierarchy ) ) { // We have it on the where condition - skip it continue; } Member member = Locus.execute( (RolapConnection) connection, "Retrieving default members in plugin", new Locus.Action<Member>() { public Member execute() { return connection.getSchemaReader().getHierarchyDefaultMember( hierarchy ); } } ); if ( member == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance() .getErrorString( "MondrianModel.ERROR_0008_NO_DEFAULT_MEMBER", element.getName(), cubeName, connection.getConnectString() ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } if ( element.isMeasures() ) { // measuresMdx = "with member "+ member.getUniqueName(); // //$NON-NLS-1$ measuresMdx = ""; //$NON-NLS-1$ columnsMdx = " select NON EMPTY {" + member.getUniqueName() + "} ON columns, "; //$NON-NLS-1$ //$NON-NLS-2$ } else { if ( rowsMdx.length() > 0 ) { rowsMdx.append( ", " ); //$NON-NLS-1$ } rowsMdx.append( member.getUniqueName() ); } } if ( ( measuresMdx != null ) && ( columnsMdx != null ) && ( rowsMdx.length() > 0 ) ) { StringBuffer result = new StringBuffer( measuresMdx.length() + columnsMdx.length() + rowsMdx.length() + 50 ); result.append( measuresMdx ).append( columnsMdx ).append( "NON EMPTY {(" ) //$NON-NLS-1$ .append( rowsMdx ).append( ")} ON rows " ) //$NON-NLS-1$ .append( "from [" + cube.getName() + "]" ) //$NON-NLS-1$ //$NON-NLS-2$ .append( whereMdx ); return result.toString(); } return null; } catch ( Throwable t ) { if ( t instanceof MondrianException ) { // pull the cause out, otherwise it never gets logged Throwable cause = ( (MondrianException) t ).getCause(); if ( cause != null ) { throw cause; } else { throw t; } } else { throw t; } } } protected SQLConnection getConnection( final String jndiName, final String driver, final String userId, final String password, final String connectionInfo ) { SQLConnection connection = null; try { if ( jndiName != null ) { connection = (SQLConnection) PentahoConnectionFactory.getConnection( IPentahoConnection.SQL_DATASOURCE, jndiName, getSession(), this ); } if ( connection == null ) { connection = (SQLConnection) PentahoConnectionFactory.getConnection( IPentahoConnection.SQL_DATASOURCE, driver, connectionInfo, userId, password, getSession(), this ); } if ( connection == null ) { Logger .error( "MondrianModelComponent", Messages.getInstance().getErrorString( "SQLBaseComponent.ERROR_0005_INVALID_CONNECTION" ) ); //$NON-NLS-1$ //$NON-NLS-2$ return null; } return connection; } catch ( Exception e ) { Logger .error( "MondrianModelComponent", Messages.getInstance().getErrorString( "SQLBaseComponent.ERROR_0006_EXECUTE_FAILED", "" ), e ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } return null; } }