/*!
* 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.services.connections.mondrian;
import mondrian.olap.Connection;
import mondrian.olap.DriverManager;
import mondrian.olap.Query;
import mondrian.olap.Result;
import mondrian.olap.Role;
import mondrian.olap.Util;
import mondrian.rolap.RolapConnectionProperties;
import org.pentaho.commons.connection.IPentahoConnection;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.data.IDBDatasourceService;
import org.pentaho.platform.api.engine.IConnectionUserRoleMapper;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.api.engine.PentahoAccessControlException;
import org.pentaho.platform.engine.core.system.IPentahoLoggingConnection;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.plugin.services.messages.Messages;
import org.pentaho.platform.util.logging.Logger;
import org.pentaho.platform.util.messages.LocaleHelper;
import javax.sql.DataSource;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
/**
* @author wseyler
*/
public class MDXConnection implements IPentahoLoggingConnection {
/**
* Defines the XML element in the component-definition that holds the mondrian-specific MDX Connection string.
*/
public static final String CONNECTION_STRING_KEY = "mdx-connection-string"; //$NON-NLS-1$
public static final String MDX_CONNECTION_MAPPER_KEY = "Mondrian-UserRoleMapper"; //$NON-NLS-1$
protected Connection nativeConnection = null;
private String lastQuery = null;
private IPentahoResultSet resultSet = null;
private ILogger logger = null;
private boolean useExtendedColumnNames = false;
/**
* This role is set as a custom role impl for the Mondrian MDX connection Optional: if not set, the connection
* processes roles as standard
*/
private Role role = null;
public MDXConnection() {
super();
}
public void setLogger( final ILogger logger ) {
this.logger = logger;
}
public void setProperties( final Properties props ) {
// There is a PentahoConnectionFactory connection creation
// method that will send these props in as NULL...
if ( props == null ) {
return;
}
// TODO: consolidate this in the init
String connectStr = props.getProperty( IPentahoConnection.JNDI_NAME_KEY );
if ( connectStr != null ) {
init( connectStr );
} else {
final String connection = props.getProperty( IPentahoConnection.CONNECTION );
final String provider = props.getProperty( IPentahoConnection.PROVIDER );
final String userName = props.getProperty( IPentahoConnection.USERNAME_KEY );
final String password = props.getProperty( IPentahoConnection.PASSWORD_KEY );
if ( connection != null && provider != null ) {
init( connection, provider, userName, password, props );
} else {
init( props );
}
}
}
/**
* @param driver - The name of the driver or the connection string
* @param provider - the provider for MDX usally "mondrian"
* @param userName - User to connect to the datasource with
* @param password - Password for the user
* @see MDXConnection(Properties props, ILogger logger)
* @deprecated
*/
@Deprecated
public MDXConnection( final String driver, final String provider, final String userName, final String password ) {
super();
init( driver, provider, userName, password, new Properties() );
}
public MDXConnection( final String connectStr, final ILogger logger ) {
super();
this.logger = logger;
init( connectStr );
}
protected void init( final String connectStr ) {
Util.PropertyList properties = Util.parseConnectString( connectStr );
init( properties );
}
@SuppressWarnings ( "unchecked" )
protected void init( final Properties properties ) {
Util.PropertyList pl = new Util.PropertyList();
Enumeration enum1 = properties.keys();
while ( enum1.hasMoreElements() ) {
Object key = enum1.nextElement();
Object value = properties.get( key );
pl.put( key.toString(), value.toString() );
}
init( pl );
}
protected void init( final String driver, final String provider, final String userName, final String password,
final Properties props ) {
StringBuffer buffer = new StringBuffer();
buffer.append( "provider=" + provider ); //$NON-NLS-1$
//
// MB - This is a hack. Should instead have either a flag or a
// different method for specifying a datasource instead of this.
//
// TODO: Fix for post 1.2 RC 2
//
//
// WES - This hack was fixed up to maintain backward capability.
// In addition methods were added so that connection info can be passed
// in via a properties map.
if ( driver.indexOf( "dataSource=" ) >= 0 ) { //$NON-NLS-1$
buffer.append( "; " ).append( driver ); //$NON-NLS-1$
} else {
buffer.append( "; Jdbc=" + driver ); //$NON-NLS-1$
}
if ( userName != null ) {
buffer.append( "; JdbcUser=" + userName ); //$NON-NLS-1$
}
if ( password != null ) {
buffer.append( "; JdbcPassword=" + password ); //$NON-NLS-1$
}
Enumeration enum1 = props.keys();
while ( enum1.hasMoreElements() ) {
String key = (String) enum1.nextElement();
if ( IPentahoConnection.CONNECTION.equals( key ) || IPentahoConnection.PROVIDER.equals( key )
|| IPentahoConnection.USERNAME_KEY.equals( key ) || IPentahoConnection.PASSWORD_KEY.equals( key ) ) {
continue;
}
buffer.append( "; " + key + "=" + props.get( key ) );
}
init( buffer.toString() );
}
protected void mapPlatformRolesToMondrianRoles( Util.PropertyList properties ) throws PentahoAccessControlException {
mapPlatformRolesToMondrianRolesHelper( properties );
}
public static void mapPlatformRolesToMondrianRolesHelper( Util.PropertyList properties )
throws PentahoAccessControlException {
if ( properties.get( RolapConnectionProperties.Role.name(), null ) == null ) {
// Only if the action sequence/requester hasn't already injected a role in here do this.
if ( PentahoSystem.getObjectFactory().objectDefined( MDXConnection.MDX_CONNECTION_MAPPER_KEY ) ) {
IConnectionUserRoleMapper mondrianUserRoleMapper =
PentahoSystem.get( IConnectionUserRoleMapper.class, MDXConnection.MDX_CONNECTION_MAPPER_KEY, null );
if ( mondrianUserRoleMapper != null ) {
// Do role mapping
String[] validMondrianRolesForUser =
mondrianUserRoleMapper.mapConnectionRoles( PentahoSessionHolder.getSession(), properties
.get( RolapConnectionProperties.Catalog.name() ) );
if ( ( validMondrianRolesForUser != null ) && ( validMondrianRolesForUser.length > 0 ) ) {
StringBuffer buff = new StringBuffer();
String aRole = null;
for ( int i = 0; i < validMondrianRolesForUser.length; i++ ) {
aRole = validMondrianRolesForUser[ i ];
// According to http://mondrian.pentaho.org/documentation/configuration.php
// double-comma escapes a comma
if ( i > 0 ) {
buff.append( "," ); //$NON-NLS-1$
}
buff.append( aRole.replaceAll( ",", ",," ) ); //$NON-NLS-1$//$NON-NLS-2$
}
properties.put( RolapConnectionProperties.Role.name(), buff.toString() );
}
}
}
}
}
protected void init( Util.PropertyList properties ) {
try {
if ( nativeConnection != null ) { // Assume we're open
close();
}
// Set a locale for this connection if specified in the platform's mondrian metadata
// This is required if mondrian.i18n.LocalizingDynamicSchemaProcessor is being used
if ( properties.get( RolapConnectionProperties.Locale.name() ) == null ) {
properties.put( RolapConnectionProperties.Locale.name(), LocaleHelper.getLocale().toString() );
}
String dataSourceName = properties.get( RolapConnectionProperties.DataSource.name() );
mapPlatformRolesToMondrianRoles( properties );
if ( dataSourceName != null ) {
IDBDatasourceService datasourceService =
PentahoSystem.getObjectFactory().get( IDBDatasourceService.class, null );
DataSource dataSourceImpl = datasourceService.getDataSource( dataSourceName );
if ( dataSourceImpl != null ) {
properties.remove( RolapConnectionProperties.DataSource.name() );
nativeConnection = DriverManager.getConnection( properties, null, dataSourceImpl );
} else {
nativeConnection = DriverManager.getConnection( properties, null );
}
} else {
nativeConnection = DriverManager.getConnection( properties, null );
}
if ( nativeConnection != null ) {
if ( role != null ) {
nativeConnection.setRole( role );
}
}
if ( nativeConnection == null ) {
logger.error( Messages.getInstance().getErrorString(
"MDXConnection.ERROR_0002_INVALID_CONNECTION",
properties != null ? properties.toString() : "null" ) ); //$NON-NLS-1$ //$NON-NLS-2$
}
} catch ( Throwable t ) {
if ( logger != null ) {
logger.error( Messages.getInstance().getErrorString(
"MDXConnection.ERROR_0002_INVALID_CONNECTION", properties != null ? properties.toString() : "null" ),
t ); //$NON-NLS-1$ //$NON-NLS-2$
} else {
Logger.error( this.getClass().getName(), Messages.getInstance().getErrorString(
"MDXConnection.ERROR_0002_INVALID_CONNECTION", properties != null ? properties.toString() : "null" ),
t ); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
public boolean initialized() {
return nativeConnection != null;
}
@SuppressWarnings ( "unchecked" )
public IPentahoResultSet prepareAndExecuteQuery( final String query, final List parameters ) throws Exception {
throw new UnsupportedOperationException();
}
public boolean preparedQueriesSupported() {
return false;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#close()
*/
public void close() {
if ( nativeConnection != null ) {
nativeConnection.close();
}
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#getLastQuery()
*/
public String getLastQuery() {
return lastQuery;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#executeQuery(java.lang.String)
*/
public IPentahoResultSet executeQuery( final String query ) {
Query mdxQuery = nativeConnection.parseQuery( query );
Result result = nativeConnection.execute( mdxQuery );
resultSet = new MDXResultSet( result, nativeConnection, useExtendedColumnNames );
return resultSet;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#isClosed()
*/
public boolean isClosed() {
return false;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#isReadOnly()
*/
public boolean isReadOnly() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#clearWarnings()
*/
public void clearWarnings() {
// TODO Auto-generated method stub
}
public IPentahoResultSet getResultSet() {
return resultSet;
}
public boolean connect( final Properties props ) {
if ( nativeConnection != null ) { // Assume we're open
close();
}
init( props );
String query = props.getProperty( IPentahoConnection.QUERY_KEY );
if ( ( query != null ) && ( query.length() > 0 ) && ( nativeConnection != null ) ) {
executeQuery( query );
}
return nativeConnection != null;
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#setMaxRows(int)
*/
public void setMaxRows( final int maxRows ) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
/*
* (non-Javadoc)
*
* @see org.pentaho.connection.IPentahoConnection#setFetchSize(int)
*/
public void setFetchSize( final int fetchSize ) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
public Connection getConnection() {
return nativeConnection;
}
/**
* return datasource type MDX
*
* @return datasource type
*/
public String getDatasourceType() {
return IPentahoConnection.MDX_DATASOURCE;
}
public void setUseExtendedColumnNames( boolean useExtendedColumnNames ) {
this.useExtendedColumnNames = useExtendedColumnNames;
}
public void setRole( Role customRole ) {
this.role = customRole;
}
}