/*! * 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-2017 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.dataaccess.datasource.wizard.service.impl; import com.thoughtworks.xstream.XStream; import org.apache.commons.io.FilenameUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.agilebi.modeler.ModelerWorkspace; import org.pentaho.agilebi.modeler.gwt.GwtModelerWorkspaceHelper; import org.pentaho.metadata.model.Domain; import org.pentaho.metadata.model.LogicalModel; import org.pentaho.platform.api.engine.IAuthorizationPolicy; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.dataaccess.datasource.beans.BogoPojo; import org.pentaho.platform.dataaccess.datasource.wizard.csv.CsvUtils; import org.pentaho.platform.dataaccess.datasource.wizard.csv.FileUtils; import org.pentaho.platform.dataaccess.datasource.wizard.models.CsvFileInfo; import org.pentaho.platform.dataaccess.datasource.wizard.models.CsvTransformGeneratorException; import org.pentaho.platform.dataaccess.datasource.wizard.models.DatasourceDTO; import org.pentaho.platform.dataaccess.datasource.wizard.models.FileInfo; import org.pentaho.platform.dataaccess.datasource.wizard.models.ModelInfo; import org.pentaho.platform.dataaccess.datasource.wizard.service.DatasourceServiceException; import org.pentaho.platform.dataaccess.datasource.wizard.service.agile.AgileHelper; import org.pentaho.platform.dataaccess.datasource.wizard.service.agile.CsvTransformGenerator; import org.pentaho.platform.dataaccess.datasource.wizard.service.gwt.ICsvDatasourceService; import org.pentaho.platform.dataaccess.datasource.wizard.sources.csv.FileTransformStats; import org.pentaho.platform.engine.core.system.PentahoBase; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.plugin.action.kettle.KettleSystemListener; import org.pentaho.reporting.libraries.base.util.StringUtils; import org.pentaho.platform.dataaccess.metadata.messages.Messages; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; @SuppressWarnings( "unchecked" ) public class CsvDatasourceServiceImpl extends PentahoBase implements ICsvDatasourceService { public static final byte[] lock = new byte[ 0 ]; private static final long serialVersionUID = 2498165533158485182L; private Log logger = LogFactory.getLog( CsvDatasourceServiceImpl.class ); private ModelerService modelerService = new ModelerService(); private DSWDatasourceServiceImpl datasourceService = new DSWDatasourceServiceImpl(); private ModelerWorkspace modelerWorkspace; public CsvDatasourceServiceImpl() { super(); modelerWorkspace = new ModelerWorkspace( new GwtModelerWorkspaceHelper() ); try { modelerWorkspace.setGeoContext( datasourceService.getGeoContext() ); } catch ( DatasourceServiceException e ) { logger.warn( "Could not get a GeoContext, auto-modeling will not use be able to auto detect geographies", e ); } modelerService = new ModelerService(); } public Log getLogger() { return logger; } public String getEncoding( String fileName ) { checkPermissions(); String encoding = null; try { CsvUtils csvModelService = new CsvUtils(); encoding = csvModelService.getEncoding( fileName ); } catch ( Exception e ) { logger.error( e ); } return encoding; } public ModelInfo stageFile( String fileName, String delimiter, String enclosure, boolean isFirstRowHeader, String encoding ) throws Exception { checkPermissions(); ModelInfo modelInfo; fileName = FilenameUtils.getName( fileName ); try { int headerRows = isFirstRowHeader ? 1 : 0; modelInfo = new CsvUtils().generateFields( "", fileName, AgileHelper.getCsvSampleRowSize(), delimiter, enclosure, headerRows, true, true, encoding ); //$NON-NLS-1$ } catch ( FileNotFoundException e ) { logger.error( e ); throw new Exception( "File was not found: " + fileName ); } catch ( Exception e ) { logger.error( e ); throw e; } return modelInfo; } public FileInfo[] getStagedFiles() throws Exception { checkPermissions(); FileInfo[] files; try { FileUtils fileService = new FileUtils(); files = fileService.listFiles(); } catch ( Exception e ) { logger.error( e ); throw e; } return files; } public FileTransformStats generateDomain( DatasourceDTO datasourceDto ) throws Exception { checkPermissions(); synchronized ( lock ) { ModelInfo modelInfo = datasourceDto.getCsvModelInfo(); IPentahoSession pentahoSession = null; try { pentahoSession = PentahoSessionHolder.getSession(); KettleSystemListener.environmentInit( pentahoSession ); String statsKey = FileTransformStats.class.getSimpleName() + "_" + modelInfo.getFileInfo().getTmpFilename(); //$NON-NLS-1$ FileTransformStats stats = new FileTransformStats(); pentahoSession.setAttribute( statsKey, stats ); CsvTransformGenerator csvTransformGenerator = new CsvTransformGenerator( modelInfo, AgileHelper.getDatabaseMeta() ); csvTransformGenerator.setTransformStats( stats ); try { csvTransformGenerator.dropTable( modelInfo.getStageTableName() ); } catch ( CsvTransformGeneratorException e ) { // this is ok, the table may not have existed. logger.info( "Could not drop table before staging" ); //$NON-NLS-1$ } csvTransformGenerator.createOrModifyTable( pentahoSession ); // no longer need to truncate the table since we dropped it a few lines up, so just pass false csvTransformGenerator.loadTable( false, pentahoSession, true ); ArrayList<String> combinedErrors = new ArrayList<String>( modelInfo.getCsvInputErrors() ); combinedErrors.addAll( modelInfo.getTableOutputErrors() ); if ( stats.getErrors() != null && stats.getErrors().size() > 0 ) { stats.getErrors().addAll( combinedErrors ); } else { stats.setErrors( combinedErrors ); } // wait until it it done while ( !stats.isRowsFinished() ) { Thread.sleep( 200 ); } modelerWorkspace.setDomain( modelerService.generateCSVDomain( modelInfo ) ); modelerWorkspace.getWorkspaceHelper().autoModelFlat( modelerWorkspace ); modelerWorkspace.getWorkspaceHelper().autoModelRelationalFlat( modelerWorkspace ); modelerWorkspace.setModelName( modelInfo.getDatasourceName() ); modelerWorkspace.getWorkspaceHelper().populateDomain( modelerWorkspace ); Domain workspaceDomain = modelerWorkspace.getDomain(); XStream xstream = new XStream(); String serializedDto = xstream.toXML( datasourceDto ); workspaceDomain.getLogicalModels().get( 0 ).setProperty( "datasourceModel", serializedDto ); workspaceDomain.getLogicalModels().get( 0 ).setProperty( "DatasourceType", "CSV" ); prepareForSerialization( workspaceDomain ); modelerService.serializeModels( workspaceDomain, modelerWorkspace.getModelName() ); stats.setDomain( modelerWorkspace.getDomain() ); return stats; } catch ( Exception e ) { logger.error( e.getMessage() ); throw e; } finally { if ( pentahoSession != null ) { pentahoSession.destroy(); } } } } protected void prepareForSerialization( Domain domain ) throws IOException { /* * This method is responsible for cleaning up legacy information when * changing datasource types and also manages CSV files for CSV based * datasources. */ String relativePath = PentahoSystem.getSystemSetting( "file-upload-defaults/relative-path", String.valueOf( FileUtils.DEFAULT_RELATIVE_UPLOAD_FILE_PATH ) ); //$NON-NLS-1$ String path = PentahoSystem.getApplicationContext().getSolutionPath( relativePath ); String TMP_FILE_PATH = File.separatorChar + "system" + File.separatorChar + File.separatorChar + "tmp" + File.separatorChar; String sysTmpDir = PentahoSystem.getApplicationContext().getSolutionPath( TMP_FILE_PATH ); LogicalModel logicalModel = domain.getLogicalModels().get( 0 ); String modelState = (String) logicalModel.getProperty( "datasourceModel" ); //$NON-NLS-1$ if ( modelState != null ) { XStream xs = new XStream(); DatasourceDTO datasource = (DatasourceDTO) xs.fromXML( modelState ); CsvFileInfo csvFileInfo = datasource.getCsvModelInfo().getFileInfo(); String tmpFileName = csvFileInfo.getTmpFilename(); String csvFileName = csvFileInfo.getFilename(); File tmpFile = new File( sysTmpDir + File.separatorChar + tmpFileName ); // Move CSV temporary file to final destination. if ( tmpFile.exists() ) { File csvFile = new File( path + File.separatorChar + csvFileName ); org.apache.commons.io.FileUtils.copyFile( tmpFile, csvFile ); } // Cleanup logic when updating from SQL datasource to CSV // datasource. datasource.setQuery( null ); // Update datasourceModel with the new modelState modelState = xs.toXML( datasource ); logicalModel.setProperty( "datasourceModel", modelState ); } } public List<String> getPreviewRows( String filename, boolean isFirstRowHeader, int rows, String encoding ) throws Exception { checkPermissions(); List<String> previewRows = null; if ( !StringUtils.isEmpty( filename ) ) { CsvUtils service = new CsvUtils(); ModelInfo mi = service.getFileContents( "", filename, ",", "\"", rows, isFirstRowHeader, encoding ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ previewRows = mi.getFileInfo().getContents(); } return previewRows; } /** * Returns true if the current user has Manage Data Source Security. Otherwise returns false. * @return */ protected boolean hasManageDataAccessPermission() { // If this breaks an OEM's plugin, provide a get-out-of-jail card with an entry in the pentaho.xml. final String override = PentahoSystem.getSystemSetting( "data-access-override", "false" ); final Boolean rtnOverride = Boolean.valueOf( override ); if ( !rtnOverride ) { final IAuthorizationPolicy policy = PentahoSystem.get( IAuthorizationPolicy.class ); if ( policy != null ) { return policy.isAllowed( "org.pentaho.platform.dataaccess.datasource.security.manage" ); } else { return false; } } else { return true; // Override the security policy with the entry in the pentaho.xml. } } private void checkPermissions() throws SecurityException { if ( !hasManageDataAccessPermission() ) { throw new SecurityException( Messages.getErrorString( "CsvDatasourceServiceImpl.ERROR_0009_UNAUTHORIZED" ) ); } } @Override public BogoPojo gwtWorkaround( BogoPojo pojo ) { return pojo; } }