/*!
* 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.dataaccess.datasource.wizard.service.impl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.agilebi.modeler.ModelerException;
import org.pentaho.agilebi.modeler.ModelerWorkspace;
import org.pentaho.agilebi.modeler.geo.GeoContext;
import org.pentaho.agilebi.modeler.geo.GeoContextFactory;
import org.pentaho.agilebi.modeler.geo.GeoContextPropertiesProvider;
import org.pentaho.agilebi.modeler.gwt.GwtModelerWorkspaceHelper;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.database.model.DatabaseConnection;
import org.pentaho.metadata.model.Domain;
import org.pentaho.metadata.model.InlineEtlPhysicalModel;
import org.pentaho.metadata.model.LogicalModel;
import org.pentaho.metadata.model.SqlPhysicalModel;
import org.pentaho.metadata.repository.DomainAlreadyExistsException;
import org.pentaho.metadata.repository.DomainIdNullException;
import org.pentaho.metadata.repository.DomainStorageException;
import org.pentaho.metadata.repository.IMetadataDomainRepository;
import org.pentaho.metadata.repository.InMemoryMetadataDomainRepository;
import org.pentaho.metadata.util.SQLModelGenerator;
import org.pentaho.metadata.util.SQLModelGeneratorException;
import org.pentaho.platform.dataaccess.datasource.beans.BogoPojo;
import org.pentaho.platform.dataaccess.datasource.beans.BusinessData;
import org.pentaho.platform.dataaccess.datasource.beans.LogicalModelSummary;
import org.pentaho.platform.dataaccess.datasource.beans.SerializedResultSet;
import org.pentaho.platform.dataaccess.datasource.wizard.models.DatasourceDTO;
import org.pentaho.platform.dataaccess.datasource.wizard.service.DatasourceServiceException;
import org.pentaho.platform.dataaccess.datasource.wizard.service.QueryValidationException;
import org.pentaho.platform.dataaccess.datasource.wizard.service.gwt.IDSWDatasourceService;
import org.pentaho.platform.dataaccess.datasource.wizard.service.impl.utils.DatasourceInMemoryServiceHelper;
import org.pentaho.platform.dataaccess.datasource.wizard.service.messages.Messages;
import org.pentaho.platform.dataaccess.datasource.wizard.sources.query.QueryDatasourceSummary;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.plugin.services.connections.sql.SQLConnection;
import org.pentaho.platform.util.messages.LocaleHelper;
import com.thoughtworks.xstream.XStream;
/*
* TODO mlowery This class professes to be a datasource service yet it takes as inputs both IDatasource instances and
* lower-level BusinessData instances. (BusinessData instances are stored in IDatasources.) They are not currently being
* kept in sync. I propose that the service only deals with IDatasources from a caller perspective.
*/
public class InMemoryDSWDatasourceServiceImpl implements IDSWDatasourceService {
public static final IMetadataDomainRepository METADATA_DOMAIN_REPO = new InMemoryMetadataDomainRepository();
private static final Log logger = LogFactory.getLog( InMemoryDSWDatasourceServiceImpl.class );
public static final String DEFAULT_UPLOAD_FILEPATH_FILE_NAME = "debug_upload_filepath.properties"; //$NON-NLS-1$
public static final String UPLOAD_FILE_PATH = "upload.file.path"; //$NON-NLS-1$
private static final String BEFORE_QUERY = " SELECT * FROM (";
private static final String AFTER_QUERY = ")";
private IMetadataDomainRepository metadataDomainRepository;
public InMemoryDSWDatasourceServiceImpl() {
this( new ConnectionServiceImpl() );
}
public InMemoryDSWDatasourceServiceImpl( ConnectionServiceImpl connectionService ) {
// this needs to share the same one as MQL editor...
metadataDomainRepository = METADATA_DOMAIN_REPO;
}
public boolean deleteLogicalModel( String domainId, String modelName ) throws DatasourceServiceException {
try {
metadataDomainRepository.removeModel( domainId, modelName );
} catch ( DomainStorageException dse ) {
logger
.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0017_UNABLE_TO_STORE_DOMAIN", domainId ),
dse );
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_STORE_DOMAIN", domainId ), dse ); //$NON-NLS-1$
} catch ( DomainIdNullException dne ) {
logger.error( Messages.getErrorString( "DatasourceServiceDelegate.ERROR_0019_DOMAIN_IS_NULL" ), dne );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0019_DOMAIN_IS_NULL" ), dne ); //$NON-NLS-1$
}
return true;
}
protected List<String> getPermittedRoleList() {
DebugDataAccessViewPermissionHandler dataAccessViewPermHandler = new DebugDataAccessViewPermissionHandler();
return dataAccessViewPermHandler.getPermittedRoleList( PentahoSessionHolder.getSession() );
}
protected List<String> getPermittedUserList() {
DebugDataAccessViewPermissionHandler dataAccessViewPermHandler = new DebugDataAccessViewPermissionHandler();
return dataAccessViewPermHandler.getPermittedUserList( PentahoSessionHolder.getSession() );
}
protected int getDefaultAcls() {
DebugDataAccessViewPermissionHandler dataAccessViewPermHandler = new DebugDataAccessViewPermissionHandler();
return dataAccessViewPermHandler.getDefaultAcls( PentahoSessionHolder.getSession() );
}
private IPentahoResultSet executeQuery( String connectionName, String query, String previewLimit )
throws QueryValidationException {
SQLConnection sqlConnection = null;
int limit = ( previewLimit != null && previewLimit.length() > 0 ) ? Integer.parseInt( previewLimit ) : -1;
try {
sqlConnection = DatasourceInMemoryServiceHelper.getConnection( connectionName );
sqlConnection.setMaxRows( limit );
sqlConnection.setReadOnly( true );
return sqlConnection.executeQuery( BEFORE_QUERY + query + AFTER_QUERY );
} catch ( Exception e ) {
logger.error( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ), e ); //$NON-NLS-1$
throw new QueryValidationException( e.getLocalizedMessage(), e ); //$NON-NLS-1$
} finally {
if ( sqlConnection != null ) {
sqlConnection.close();
}
}
}
public SerializedResultSet doPreview( String connectionName, String query, String previewLimit )
throws DatasourceServiceException {
SerializedResultSet returnResultSet;
try {
executeQuery( connectionName, query, previewLimit );
returnResultSet = DatasourceInMemoryServiceHelper.getSerializeableResultSet( connectionName, query,
Integer.parseInt( previewLimit ), null );
} catch ( QueryValidationException e ) {
logger.error( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ), e ); //$NON-NLS-1$
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ),
e ); //$NON-NLS-1$
}
return returnResultSet;
}
public boolean testDataSourceConnection( String connectionName ) throws DatasourceServiceException {
java.sql.Connection conn = null;
try {
conn = DatasourceInMemoryServiceHelper.getDataSourceConnection( connectionName );
} catch ( DatasourceServiceException dme ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0026_UNABLE_TO_TEST_CONNECTION",
connectionName ), dme );
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0026_UNABLE_TO_TEST_CONNECTION", connectionName ), dme ); //$NON-NLS-1$
} finally {
try {
if ( conn != null ) {
conn.close();
}
} catch ( SQLException e ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0026_UNABLE_TO_TEST_CONNECTION",
connectionName ), e );
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0026_UNABLE_TO_TEST_CONNECTION", connectionName ), e ); //$NON-NLS-1$
}
}
return true;
}
/**
* This method gets the business data which are the business columns, columns types and sample preview data
*
* @param modelName, connection, query, previewLimit
* @return BusinessData
* @throws DatasourceServiceException
*/
public BusinessData generateLogicalModel( String modelName, String connectionName, String dbType, String query,
String previewLimit )
throws DatasourceServiceException {
try {
executeQuery( connectionName, query, previewLimit );
Boolean securityEnabled = ( getPermittedRoleList() != null && getPermittedRoleList().size() > 0 )
|| ( getPermittedUserList() != null && getPermittedUserList().size() > 0 );
SerializedResultSet resultSet = DatasourceInMemoryServiceHelper.getSerializeableResultSet( connectionName, query,
Integer.parseInt( previewLimit ), null );
SQLModelGenerator sqlModelGenerator = new SQLModelGenerator( modelName, connectionName, dbType,
resultSet.getColumnTypes(), resultSet.getColumns(), query, securityEnabled,
getPermittedRoleList(), getPermittedUserList(), getDefaultAcls(), "joe" );
Domain domain = sqlModelGenerator.generate();
return new BusinessData( domain, resultSet.getData() );
} catch ( SQLModelGeneratorException smge ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_GENERATE_MODEL",
smge.getLocalizedMessage() ), smge );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0015_UNABLE_TO_GENERATE_MODEL" ), smge ); //$NON-NLS-1$
} catch ( QueryValidationException e ) {
logger.error( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ), e ); //$NON-NLS-1$
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ),
e ); //$NON-NLS-1$
}
}
public IMetadataDomainRepository getMetadataDomainRepository() {
return metadataDomainRepository;
}
public void setMetadataDomainRepository( IMetadataDomainRepository metadataDomainRepository ) {
this.metadataDomainRepository = metadataDomainRepository;
}
public BusinessData loadBusinessData( String domainId, String modelId ) throws DatasourceServiceException {
Domain domain = getMetadataDomainRepository().getDomain( domainId );
List<List<String>> data = null;
if ( domain.getPhysicalModels().get( 0 ) instanceof InlineEtlPhysicalModel ) {
InlineEtlPhysicalModel model = (InlineEtlPhysicalModel) domain.getPhysicalModels().get( 0 );
data = DatasourceInMemoryServiceHelper.getCsvDataSample( model.getFileLocation(), model.getHeaderPresent(), model
.getDelimiter(), model.getEnclosure(), 5 );
} else {
SqlPhysicalModel model = (SqlPhysicalModel) domain.getPhysicalModels().get( 0 );
String query = model.getPhysicalTables().get( 0 ).getTargetTable();
SerializedResultSet resultSet = DatasourceInMemoryServiceHelper
.getSerializeableResultSet( model.getDatasource().getDatabaseName(), query, 5, null );
data = resultSet.getData();
}
return new BusinessData( domain, data );
}
public boolean saveLogicalModel( Domain domain, boolean overwrite ) throws DatasourceServiceException {
String domainName = domain.getId();
try {
getMetadataDomainRepository().storeDomain( domain, overwrite );
return true;
} catch ( DomainStorageException dse ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0017_UNABLE_TO_STORE_DOMAIN",
domainName ), dse );
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_STORE_DOMAIN", domainName ), dse ); //$NON-NLS-1$
} catch ( DomainAlreadyExistsException dae ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0018_DOMAIN_ALREADY_EXIST",
domainName ), dae );
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0018_DOMAIN_ALREADY_EXIST", domainName ), dae ); //$NON-NLS-1$
} catch ( DomainIdNullException dne ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0019_DOMAIN_IS_NULL" ), dne );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0019_DOMAIN_IS_NULL" ), dne ); //$NON-NLS-1$
}
}
public boolean hasPermission() {
return true;
}
public List<LogicalModelSummary> getLogicalModels( String context ) throws DatasourceServiceException {
List<LogicalModelSummary> logicalModelSummaries = new ArrayList<LogicalModelSummary>();
for ( String domainId : getMetadataDomainRepository().getDomainIds() ) {
Domain domain = getMetadataDomainRepository().getDomain( domainId );
String locale = LocaleHelper.getLocale().toString();
String[] locales = new String[ domain.getLocales().size() ];
for ( int i = 0; i < domain.getLocales().size(); i++ ) {
locales[ i ] = domain.getLocales().get( i ).getCode();
}
locale = LocaleHelper.getClosestLocale( locale, locales );
for ( LogicalModel model : domain.getLogicalModels() ) {
String vis = (String) model.getProperty( "visible" );
if ( vis != null ) {
String[] visibleContexts = vis.split( "," );
boolean visibleToContext = false;
for ( String c : visibleContexts ) {
if ( context.equals( c.trim() ) ) {
visibleToContext = true;
break;
}
}
if ( !visibleToContext ) {
continue;
}
}
logicalModelSummaries
.add( new LogicalModelSummary( domainId, model.getId(), model.getName().getString( locale ) ) );
}
}
return logicalModelSummaries;
}
public BogoPojo gwtWorkaround( BogoPojo pojo ) {
return pojo;
}
public String serializeModelState( DatasourceDTO dto ) throws DatasourceServiceException {
XStream xs = new XStream();
return xs.toXML( dto );
}
public DatasourceDTO deSerializeModelState( String dtoStr ) throws DatasourceServiceException {
try {
XStream xs = new XStream();
return (DatasourceDTO) xs.fromXML( dtoStr );
} catch ( Exception e ) {
e.printStackTrace();
throw new DatasourceServiceException( e );
}
}
@Override
public List<String> listDatasourceNames() throws IOException {
return new ArrayList<String>();
}
@Override
public QueryDatasourceSummary generateQueryDomain( String name, String query, DatabaseConnection connection,
DatasourceDTO datasourceDTO ) throws DatasourceServiceException {
ModelerWorkspace modelerWorkspace = new ModelerWorkspace( new GwtModelerWorkspaceHelper(), getGeoContext() );
ModelerService modelerService = new ModelerService();
modelerWorkspace.setModelName( name );
try {
Boolean securityEnabled = ( getPermittedRoleList() != null && getPermittedRoleList().size() > 0 )
|| ( getPermittedUserList() != null && getPermittedUserList().size() > 0 );
SerializedResultSet resultSet =
DatasourceInMemoryServiceHelper.getSerializeableResultSet( connection.getName(), query,
Integer.parseInt( "10" ), null );
SQLModelGenerator sqlModelGenerator =
new SQLModelGenerator( name, connection.getName(), connection.getDatabaseType().getShortName(),
resultSet.getColumnTypes(), resultSet.getColumns(), query, securityEnabled,
getPermittedRoleList(), getPermittedUserList(), getDefaultAcls(), "joe" );
Domain domain = sqlModelGenerator.generate();
modelerWorkspace.setDomain( domain );
modelerWorkspace.getWorkspaceHelper().autoModelFlat( modelerWorkspace );
modelerWorkspace.setModelName( datasourceDTO.getDatasourceName() );
modelerWorkspace.getWorkspaceHelper().populateDomain( modelerWorkspace );
domain.getLogicalModels().get( 0 ).setProperty( "datasourceModel", serializeModelState( datasourceDTO ) );
domain.getLogicalModels().get( 0 ).setProperty( "DatasourceType", "SQL-DS" );
QueryDatasourceSummary summary = new QueryDatasourceSummary();
modelerService.serializeModels( domain, modelerWorkspace.getModelName() );
summary.setDomain( domain );
return summary;
} catch ( SQLModelGeneratorException smge ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_GENERATE_MODEL",
smge.getLocalizedMessage() ), smge );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0015_UNABLE_TO_GENERATE_MODEL" ), smge ); //$NON-NLS-1$
} catch ( QueryValidationException e ) {
logger.error( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ), e ); //$NON-NLS-1$
throw new DatasourceServiceException( Messages.getErrorString(
"InMemoryDatasourceServiceImpl.ERROR_0009_QUERY_VALIDATION_FAILED", e.getLocalizedMessage() ),
e ); //$NON-NLS-1$
} catch ( ModelerException e ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_GENERATE_MODEL",
e.getLocalizedMessage() ), e );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0015_UNABLE_TO_GENERATE_MODEL" ), e ); //$NON-NLS-1$
} catch ( Exception e ) {
logger.error( Messages.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0016_UNABLE_TO_GENERATE_MODEL",
e.getLocalizedMessage() ), e );
throw new DatasourceServiceException( Messages
.getErrorString( "InMemoryDatasourceServiceImpl.ERROR_0015_UNABLE_TO_GENERATE_MODEL" ), e ); //$NON-NLS-1$
}
}
public String getDatasourceIllegalCharacters() throws DatasourceServiceException {
return "$<>?%^*()!~:;[]{}|/";
}
public GeoContext getGeoContext() throws DatasourceServiceException {
try {
Properties props = new Properties();
props.load( new FileInputStream( new File( "target/test-classes/geoContextSample.properties" ) ) );
GeoContext geo = GeoContextFactory.create( new GeoContextPropertiesProvider( props ) );
return geo;
} catch ( ModelerException e ) {
throw new DatasourceServiceException( e );
} catch ( FileNotFoundException e ) {
throw new DatasourceServiceException( e );
} catch ( IOException e ) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return null;
}
}