/*!
* 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;
}
}