/*! * 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-2016 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.action.mondrian.catalog; import com.google.common.annotations.VisibleForTesting; import mondrian.i18n.LocalizingDynamicSchemaProcessor; import mondrian.olap.Connection; import mondrian.olap.MondrianDef; import mondrian.olap.MondrianException; import mondrian.olap.Util; import mondrian.olap.Util.PropertyList; import mondrian.rolap.RolapConnectionProperties; import mondrian.spi.DynamicSchemaProcessor; import mondrian.util.ClassResolver; import mondrian.xmla.DataSourcesConfig; import mondrian.xmla.DataSourcesConfig.DataSources; import org.apache.commons.collections.list.SetUniqueList; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.VFS; import org.apache.commons.vfs2.impl.DefaultFileSystemManager; import org.dom4j.Document; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.eigenbase.xom.DOMWrapper; import org.eigenbase.xom.Parser; import org.eigenbase.xom.XMLOutput; import org.eigenbase.xom.XOMException; import org.eigenbase.xom.XOMUtil; import org.olap4j.OlapConnection; import org.owasp.encoder.Encode; import org.pentaho.platform.api.data.DBDatasourceServiceException; import org.pentaho.platform.api.data.IDBDatasourceService; import org.pentaho.platform.api.engine.ICacheManager; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.repository2.unified.IAclNodeHelper; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.RepositoryFileAcl; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.api.repository2.unified.data.node.DataNode; import org.pentaho.platform.api.repository2.unified.data.node.NodeRepositoryFileData; import org.pentaho.platform.api.util.XmlParseException; import org.pentaho.platform.engine.core.system.PentahoRequestContextHolder; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.solution.PentahoEntityResolver; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogServiceException.Reason; import org.pentaho.platform.plugin.action.olap.IOlapService; import org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper; import org.pentaho.platform.repository.solution.filebased.MondrianVfs; import org.pentaho.platform.repository2.ClientRepositoryPaths; import org.pentaho.platform.repository2.unified.jcr.JcrAclNodeHelper; import org.pentaho.platform.util.logging.Logger; import org.pentaho.platform.util.messages.LocaleHelper; import org.pentaho.platform.util.xml.XMLParserFactoryProducer; import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; /** * Reads in file containing Mondrian data sources and catalogs. (Contains code copied from <code>XmlaServlet</code>.) * * @author mlowery */ public class MondrianCatalogHelper implements IAclAwareMondrianCatalogService { // ~ Static fields/initializers ====================================================================================== private static final Log logger = LogFactory.getLog( MondrianCatalogHelper.class ); // ~ Instance fields ================================================================================================= private String dataSourcesConfig; /** * true to use schema name from catalog definition (aka schema file) as catalog name. */ private boolean useSchemaNameAsCatalogName = true; /** * Holds the additional catalog information */ private Map<String, MondrianCatalogComplementInfo> catalogComplementInfoMap; /** * Tells the MCH to generate datasources.xml with a DB name equals to that of the 4.X servers. This is necessary until * the next release of olap4j-xmla. Using the legacy DB name allows requests of MDSCHEMA_MEMBERS to be batched. See * MONDRIAN-2229. */ private final boolean useLegacyDbName; private IAclNodeHelper aclHelper; private MondrianCatalogRepositoryHelper catalogRepositoryHelper; public static final String MONDRIAN_DATASOURCE_FOLDER = "mondrian"; //$NON-NLS-1$ // ~ Constructors ==================================================================================================== @SuppressWarnings( "unchecked" ) protected List<MondrianCatalog> getCatalogs( IPentahoSession pentahoSession ) { Map<String, MondrianCatalog> catalogsMap = (Map<String, MondrianCatalog>) PentahoSystem.getCacheManager( pentahoSession ).getFromRegionCache( MONDRIAN_CATALOG_CACHE_REGION, getLocale().toString() ); List<MondrianCatalog> catalogs = new ArrayList<MondrianCatalog>(); // Analyzer cache is also placed in the MONDRIAN_CATALOG_CACHE_REGION // so we need to filter the list to only keep MondrianCatalog objects for ( Object o : catalogsMap.values() ) { if ( o instanceof MondrianCatalog ) { catalogs.add( (MondrianCatalog) o ); } } // Sort Collections.sort( catalogs, new Comparator<MondrianCatalog>() { @Override public int compare( MondrianCatalog o1, MondrianCatalog o2 ) { return o1.getName().compareTo( o2.getName() ); } } ); // remove duplicates SetUniqueList uniqueCatalogs = SetUniqueList.decorate( catalogs ); return uniqueCatalogs; } /** * Performs a search for an existing catalog based on the datasource info and catalog definition. * * @param catalog The catalog to compare against * @param pentahoSession The session with which this request is associated (Used to locate the cache) * @return True if an existing match has been found for the catalog */ protected boolean catalogExists( MondrianCatalog catalog, IPentahoSession pentahoSession ) { if ( catalog != null ) { MondrianCatalog foundCatalog = getCatalogFromCache( catalog.getName(), pentahoSession ); // Was the catalog found by name? if ( foundCatalog != null ) { // first check dataSourceInfo String foundDataSourceInfo = cleanseDataSourceInfo( foundCatalog.getDataSourceInfo() ); String newDataSourceInfo = cleanseDataSourceInfo( catalog.getDataSourceInfo() ); if ( !foundDataSourceInfo.equals( newDataSourceInfo ) ) { return false; } // now check definition String foundDefinition = foundCatalog.getDefinition(); String newDefinition = catalog.getDefinition(); if ( definitionEquals( foundDefinition, newDefinition ) ) { return true; } } } return false; } protected MondrianCatalog getCatalogFromCache( String context, IPentahoSession pentahoSession ) { // NOTE that the context can be the catalog name or the definition string for the catalog. If you are using the // definition string to // retrieve the catalog form the cache, you cannot be guaranteed what datasource is in play; so under these // circumstances, this catalog's // definition is the only part of the catalog that can be trusted. As this feature was added to enable looking up // Mondrian // roles from the schema, we don't much care which datasource is in play. Map<String, MondrianCatalog> catalogs = (Map<String, MondrianCatalog>) PentahoSystem.getCacheManager( pentahoSession ).getFromRegionCache( MONDRIAN_CATALOG_CACHE_REGION, getLocale().toString() ); return catalogs.get( context ); } /** * TODO Delete this method. Calling this method ties you to this implementation. * * @deprecated Please use PentahoSystem.get to get the Mondrian Catalog Service */ @Deprecated public static MondrianCatalogHelper getInstance() { // IMondrianCatalogService is a singleton; IPentahoSession not required return (MondrianCatalogHelper) PentahoSystem .get( IMondrianCatalogService.class, "IMondrianCatalogService", null ); //$NON-NLS-1$ } @VisibleForTesting @Deprecated public MondrianCatalogHelper( IAclNodeHelper aclHelper ) { this(); this.aclHelper = aclHelper; } public MondrianCatalogHelper() { this( false ); } public MondrianCatalogHelper( boolean useLegacyDbName ) { super(); this.useLegacyDbName = useLegacyDbName; try { DefaultFileSystemManager dfsm = (DefaultFileSystemManager) VFS.getManager(); dfsm.addProvider( "mondrian", new MondrianVfs() ); //$NON-NLS-1$ } catch ( FileSystemException e ) { logger.error( e.getMessage() ); } } public static String MONDRIAN_CATALOG_CACHE_REGION = "mondrian-catalog-cache"; //$NON-NLS-1$ // ~ Methods ========================================================================================================= protected synchronized void init( final IPentahoSession pentahoSession ) { // First check if the catalogs are initialized for the current locale final ICacheManager cacheMgr = PentahoSystem.getCacheManager( pentahoSession ); if ( cacheMgr.cacheEnabled( MONDRIAN_CATALOG_CACHE_REGION ) && cacheMgr.getFromRegionCache( MONDRIAN_CATALOG_CACHE_REGION, getLocale().toString() ) != null ) { return; } if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "init" ); //$NON-NLS-1$ } // By default, we will use the system to load all schemas into the cache. // access to these schemas is controlled later via the hasAccess() method loadCatalogsIntoCache( makeDataSources(), PentahoSessionHolder.getSession() ); } @Override public synchronized void reInit( final IPentahoSession pentahoSession ) { final ICacheManager cacheMgr = PentahoSystem.getCacheManager( pentahoSession ); if ( cacheMgr.cacheEnabled( MONDRIAN_CATALOG_CACHE_REGION ) ) { cacheMgr.clearRegionCache( MONDRIAN_CATALOG_CACHE_REGION ); } init( pentahoSession ); } private static Locale getLocale() { final Locale locale = LocaleHelper.getLocale(); if ( locale != null ) { return locale; } else { return Locale.getDefault(); } } /** * Same as implemented in <code>XmlaServlet</code> except takes advantage of Spring's Resource framework. */ protected DataSourcesConfig.DataSources makeDataSources() { try { URL dataSourcesConfigUrl = null; if ( dataSourcesConfig == null ) { String datasourcesXML = generateInMemoryDatasourcesXml( PentahoSystem.get( IUnifiedRepository.class, PentahoSessionHolder .getSession() ) ); return parseDataSources( datasourcesXML ); } else if ( dataSourcesConfig.startsWith( "file:" ) ) { //$NON-NLS-1$ dataSourcesConfigUrl = new URL( dataSourcesConfig ); // dataSourcesConfigResource.getURL(); return ( dataSourcesConfigUrl == null ) ? null : parseDataSourcesUrl( dataSourcesConfigUrl ); } else if ( dataSourcesConfig.startsWith( "classpath:" ) ) { //$NON-NLS-1$ dataSourcesConfigUrl = getClass().getResource( dataSourcesConfig.substring( 10 ) ); return ( dataSourcesConfigUrl == null ) ? null : parseDataSourcesUrl( dataSourcesConfigUrl ); } else { throw new MondrianCatalogServiceException( "dataSourcesConfig is not a valid URL or does not exist", //$NON-NLS-1$ Reason.GENERAL ); } } catch ( IOException e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0001_INVALID_DATASOURCE_CONFIG", dataSourcesConfig ), //$NON-NLS-1$ e, Reason.GENERAL ); } } public String generateInMemoryDatasourcesXml( IUnifiedRepository unifiedRepository ) { String etcMondrian = ClientRepositoryPaths.getEtcFolderPath() + RepositoryFile.SEPARATOR + MONDRIAN_DATASOURCE_FOLDER; RepositoryFile etcMondrianFolder = unifiedRepository.getFile( etcMondrian ); if ( etcMondrianFolder == null ) { return null; } StringBuffer datasourcesXML = new StringBuffer(); datasourcesXML.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<DataSources>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<DataSource>\n" ); //$NON-NLS-1$ if ( useLegacyDbName ) { datasourcesXML.append( "<DataSourceName>Provider=Mondrian</DataSourceName>\n" ); //$NON-NLS-1$ } else { datasourcesXML.append( "<DataSourceName>Pentaho Mondrian</DataSourceName>\n" ); //$NON-NLS-1$ } datasourcesXML .append( "<DataSourceDescription>Pentaho BI Platform Datasources</DataSourceDescription>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<URL>" + contextPathFromRequestContextHolder() + "Xmla</URL>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<DataSourceInfo>Provider=Mondrian</DataSourceInfo>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<ProviderName>PentahoXMLA</ProviderName>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<ProviderType>MDP</ProviderType>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<AuthenticationMode>Unauthenticated</AuthenticationMode>\n" ); //$NON-NLS-1$ appendCatalogsSection( unifiedRepository, etcMondrian, etcMondrianFolder, datasourcesXML ); datasourcesXML.append( "</DataSource>\n" ); //$NON-NLS-1$ datasourcesXML.append( "</DataSources>\n" ); //$NON-NLS-1$ return datasourcesXML.toString(); } @VisibleForTesting protected String contextPathFromRequestContextHolder() { return PentahoRequestContextHolder.getRequestContext().getContextPath(); } @VisibleForTesting protected void appendCatalogsSection( IUnifiedRepository unifiedRepository, String etcMondrian, RepositoryFile etcMondrianFolder, StringBuffer datasourcesXML ) { datasourcesXML.append( "<Catalogs>\n" ); //$NON-NLS-1$ // Creates <Catalogs> from the "/etc/mondrian/<catalog>/metadata" nodes. /* * IPentahoSession pentahoSession = PentahoSessionHolder.getSession(); String tenantEtcFolder = null; * if(pentahoSession != null) { String tenantId = (String) * pentahoSession.getAttribute(IPentahoSession.TENANT_ID_KEY); tenantEtcFolder = * ServerRepositoryPaths.getTenantEtcFolderPath(tenantId); } else { tenantEtcFolder = * ServerRepositoryPaths.getTenantEtcFolderPath(); } */ List<RepositoryFile> mondrianCatalogs = unifiedRepository.getChildren( etcMondrianFolder.getId() ); for ( RepositoryFile catalog : mondrianCatalogs ) { String catalogName = catalog.getName(); RepositoryFile metadata = unifiedRepository.getFile( etcMondrian + RepositoryFile.SEPARATOR + catalogName + RepositoryFile.SEPARATOR + "metadata" ); //$NON-NLS-1$ if ( metadata != null ) { DataNode metadataNode = unifiedRepository.getDataForRead( metadata.getId(), NodeRepositoryFileData.class ).getNode(); String datasourceInfo = metadataNode.getProperty( "datasourceInfo" ).getString(); //$NON-NLS-1$ String definition = metadataNode.getProperty( "definition" ).getString(); //$NON-NLS-1$ datasourcesXML .append( "<Catalog name=\"" + Encode.forXml( catalogName ) + "\">\n" ); //$NON-NLS-1$ //$NON-NLS-2$ datasourcesXML .append( "<DataSourceInfo>" + Encode.forXml( datasourceInfo ) + "</DataSourceInfo>\n" ); //$NON-NLS-1$ datasourcesXML.append( "<Definition>" + Encode.forXml( definition ) + "</Definition>\n" ); //$NON-NLS-1$ //$NON-NLS-2$ datasourcesXML.append( "</Catalog>\n" ); //$NON-NLS-1$ } else { logger .warn( Messages.getInstance().getString( "MondrianCatalogHelper.WARN_META_DATA_IS_NULL" ) ); //$NON-NLS-1$ } } datasourcesXML.append( "</Catalogs>\n" ); //$NON-NLS-1$ } protected DataSourcesConfig.DataSources parseDataSourcesUrl( final URL dataSourcesConfigUrl ) { try { String dataSourcesConfigString = readDataSourcesContent( dataSourcesConfigUrl ); return parseDataSources( dataSourcesConfigString ); } catch ( Exception e ) { throw Util .newError( e, Messages .getInstance() .getErrorString( "MondrianCatalogHelper.ERROR_0002_FAILED_TO_PARSE_DATASOURCE_CONFIG", dataSourcesConfigUrl.toExternalForm() ) ); } } protected String readDataSourcesContent( final URL dataSourcesConfigUrl ) throws IOException { return Util.readURL( dataSourcesConfigUrl, Util.toMap( System.getProperties() ) ); } protected DataSourcesConfig.DataSources parseDataSources( final String dataSourcesConfigString ) { try { if ( dataSourcesConfigString == null ) { MondrianCatalogHelper.logger.warn( Messages.getInstance().getString( "MondrianCatalogHelper.WARN_PARSE_NULL_INPUT" ) ); //$NON-NLS-1$ return null; } String replacedConfigString = Util.replaceProperties( dataSourcesConfigString, Util.toMap( System.getProperties() ) ); if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { String msg = "parseDataSources: dataSources=" + replacedConfigString; //$NON-NLS-1$ MondrianCatalogHelper.logger.debug( msg ); } final Parser parser = XOMUtil.createDefaultParser(); final DOMWrapper doc = parser.parse( replacedConfigString ); catalogComplementInfoMap = makeCatalogComplementInfoMap( doc ); return new DataSourcesConfig.DataSources( doc ); } catch ( XOMException e ) { throw Util.newError( e, Messages.getInstance() .getErrorString( "MondrianCatalogHelper.ERROR_0002_FAILED_TO_PARSE_DATASOURCE_CONFIG", dataSourcesConfigString ) ); } } protected Map<String, MondrianCatalogComplementInfo> makeCatalogComplementInfoMap( final DOMWrapper doc ) { HashMap<String, MondrianCatalogComplementInfo> map = new HashMap<String, MondrianCatalogComplementInfo>(); if ( doc.getElementChildren().length > 0 ) { DOMWrapper dataSource = doc.getElementChildren()[ 0 ]; DOMWrapper catalogs = null; // Search Catalogs for ( int i = 0; i < dataSource.getElementChildren().length; i++ ) { DOMWrapper element = dataSource.getElementChildren()[ i ]; if ( element.getTagName().equals( "Catalogs" ) ) { //$NON-NLS-1$ catalogs = element; break; } } // Generate the map. We need the name and the variables for ( int i = 0; i < catalogs.getElementChildren().length; i++ ) { final DOMWrapper catalog = catalogs.getElementChildren()[ i ]; if ( !"Catalog".equals( catalog.getTagName() ) ) { //$NON-NLS-1$ continue; } MondrianCatalogComplementInfo complementInfo = new MondrianCatalogComplementInfo(); // Iterate through whereConditions for ( int j = 0; j < catalog.getElementChildren().length; j++ ) { final DOMWrapper whereNode = catalog.getElementChildren()[ j ]; if ( "WhereCondition".equals( whereNode.getTagName() ) ) { //$NON-NLS-1$ complementInfo.addWhereCondition( whereNode.getAttribute( "cube" ), whereNode.getText() ); //$NON-NLS-1$ } } map.put( getDOMWrapperElementText( catalog, "Definition" ), complementInfo ); //$NON-NLS-1$ } } return map; } private String getDOMWrapperElementText( final DOMWrapper element, final String name ) { for ( int i = 0; i < element.getElementChildren().length; i++ ) { DOMWrapper child = element.getElementChildren()[ i ]; if ( child.getTagName().equals( name ) ) { return child.getText(); } } return null; } protected Map<String, MondrianCatalog> makeCatalogMap( final List<MondrianCatalog> cats ) { Map<String, MondrianCatalog> map = new HashMap<String, MondrianCatalog>(); for ( MondrianCatalog catalog : cats ) { map.put( catalog.getName(), catalog ); } return map; } /** * Equals that is tolerant of inconsistencies in solution path (leading slash vs. no leading slash). */ private boolean definitionEquals( final String def1, final String def2 ) { if ( def1.equals( def2 ) ) { return true; } String tmp = def1.startsWith( "solution:/" ) ? "solution:" + def1.substring( 10 ) : "solution:/" + def1.substring( 9 ); //$NON-NLS-1$ return tmp.equals( def2 ); } protected String cleanseDataSourceInfo( String dataSourceInfo ) { if ( dataSourceInfo == null ) { return null; } // remove EnableXmla if necessary before building the key PropertyList propertyList = Util.parseConnectString( dataSourceInfo ); if ( propertyList.get( "EnableXmla" ) != null ) { //$NON-NLS-1$ propertyList.remove( "EnableXmla" ); //$NON-NLS-1$ } if ( propertyList.get( "overwrite" ) != null ) { //$NON-NLS-1$ propertyList.remove( "overwrite" ); //$NON-NLS-1$ } return propertyList.toString(); } public String getDataSourcesConfig() { return dataSourcesConfig; } public void setDataSourcesConfig( final String dataSourcesConfig ) { this.dataSourcesConfig = dataSourcesConfig; } @Override public List<MondrianCatalog> listCatalogs( final IPentahoSession pentahoSession, final boolean jndiOnly ) { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "listCatalogs" ); //$NON-NLS-1$ } init( pentahoSession ); // defensive copy return Collections.unmodifiableList( filter( getCatalogs( pentahoSession ), pentahoSession, jndiOnly ) ); } /** * use the in memory session value of input stream (used by test harness) */ @Override public synchronized void addCatalog( final MondrianCatalog catalog, final boolean overwrite, final IPentahoSession pentahoSession ) throws MondrianCatalogServiceException { String mondrianSchema = (String) pentahoSession.getAttribute( "MONDRIAN_SCHEMA_XML_CONTENT" ); //$NON-NLS-1$ InputStream schemaInputStream = IOUtils.toInputStream( mondrianSchema ); addCatalog( schemaInputStream, catalog, overwrite, pentahoSession ); } /** * {@inheritDoc} */ @Override public void addCatalog( InputStream inputStream, MondrianCatalog catalog, boolean overwriteInRepossitory, IPentahoSession session ) { addCatalog( inputStream, catalog, overwriteInRepossitory, null, session ); } /** * new method to pass the input stream directly from data access put and post schema * * @param schemaInputStream * @param catalog * @param overwrite * @param acl catalog ACL * @param pentahoSession * @throws MondrianCatalogServiceException */ @Override public synchronized void addCatalog( InputStream schemaInputStream, final MondrianCatalog catalog, final boolean overwrite, RepositoryFileAcl acl, final IPentahoSession pentahoSession ) throws MondrianCatalogServiceException { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "addCatalog" ); //$NON-NLS-1$ } init( pentahoSession ); // check for existing dataSourceInfo+catalog final boolean catalogExistsWithSameDatasource = catalogExists( catalog, pentahoSession ); if ( catalogExistsWithSameDatasource && !overwrite ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0004_ALREADY_EXISTS" ), Reason.ALREADY_EXISTS ); //$NON-NLS-1$ } // Checks if a catalog of the same name but with a different file // path exists. MondrianCatalog fileLocationCatalogTest = null; for ( MondrianCatalog currentCatalogCheck : getCatalogs( pentahoSession ) ) { if ( currentCatalogCheck.getName().equals( catalog.getName() ) ) { fileLocationCatalogTest = currentCatalogCheck; break; } } //compare the catalog names and throw exception if same and NOT ovewrite final boolean catalogExistsWithDifferentDatasource; try { catalogExistsWithDifferentDatasource = fileLocationCatalogTest != null && definitionEquals( fileLocationCatalogTest.getDefinition(), "mondrian:/" + URLEncoder.encode( catalog.getName(), "UTF-8" ) ); } catch ( UnsupportedEncodingException e ) { throw new MondrianCatalogServiceException( e ); } if ( catalogExistsWithDifferentDatasource && !overwrite ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0004_ALREADY_EXISTS" ), //$NON-NLS-1$ Reason.XMLA_SCHEMA_NAME_EXISTS ); } MondrianCatalogRepositoryHelper helper = getMondrianCatalogRepositoryHelper(); try { helper.addHostedCatalog( schemaInputStream, catalog.getName(), catalog.getDataSourceInfo() ); } catch ( Exception e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0008_ERROR_OCCURRED" ), //$NON-NLS-1$ Reason.valueOf( e.getMessage() ) ); } if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger .debug( "refreshing from dataSourcesConfig (" + dataSourcesConfig + ")" ); //$NON-NLS-1$ //$NON-NLS-2$ } try { reInit( pentahoSession ); setAclFor( catalog.getName(), acl ); if ( catalogExistsWithSameDatasource || catalogExistsWithDifferentDatasource ) { flushCacheForCatalog( catalog.getName(), pentahoSession ); } } catch ( MondrianException e ) { helper.deleteHostedCatalog( catalog.getName() ); reInit( pentahoSession ); throw e; } } private void flushCacheForCatalog( String catalogName, IPentahoSession pentahoSession ) { IOlapService olapService = PentahoSystem.get( IOlapService.class, "IOlapService", pentahoSession ); Connection unwrap = null; try { OlapConnection connection = olapService.getConnection( catalogName, pentahoSession ); unwrap = connection.unwrap( Connection.class ); unwrap.getCacheControl( null ).flushSchema( unwrap.getSchema() ); } catch ( Throwable e ) { MondrianCatalogHelper.logger.warn( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0019_FAILED_TO_FLUSH", catalogName ), e ); } finally { if ( unwrap != null ) { unwrap.close(); } } } protected IUnifiedRepository getRepository() { return PentahoSystem.get( IUnifiedRepository.class ); } protected synchronized MondrianCatalogRepositoryHelper getMondrianCatalogRepositoryHelper() { if ( catalogRepositoryHelper == null ) { catalogRepositoryHelper = new MondrianCatalogRepositoryHelper( PentahoSystem.get( IUnifiedRepository.class ) ); } return catalogRepositoryHelper; } protected synchronized IAclNodeHelper getAclHelper() { if ( aclHelper == null ) { aclHelper = new JcrAclNodeHelper( PentahoSystem.get( IUnifiedRepository.class ) ); } return aclHelper; } public synchronized void setAclHelper( IAclNodeHelper helper ) { aclHelper = helper; } @Override public void setAclFor( String catalogName, RepositoryFileAcl acl ) { getAclHelper().setAclFor( getMondrianCatalogRepositoryHelper().getMondrianCatalogFile( catalogName ), acl ); } @Override public RepositoryFileAcl getAclFor( String catalogName ) { return getAclHelper().getAclFor( getMondrianCatalogRepositoryHelper().getMondrianCatalogFile( catalogName ) ); } public void importSchema( File mondrianFile, String databaseConnection, String parameters ) { try { String datasourceInfo = "Provider=mondrian;DataSource=" + databaseConnection; //$NON-NLS-1$ if ( !StringUtils.isEmpty( parameters ) ) { datasourceInfo = parameters; } // Note: Mondrian parameters could be validated here and throw subsequent exception if they do not conform to // spec. FileInputStream parsingInputStream = new FileInputStream( mondrianFile ); org.w3c.dom.Document document = getMondrianXmlDocument( parsingInputStream ); NodeList schemas = document.getElementsByTagName( "Schema" ); //$NON-NLS-1$ Node schema = schemas.item( 0 ); if ( schema == null ) { throw new SAXParseException( "", null ); // Generic schema error message will be provided at catch statement. } Node name = schema.getAttributes().getNamedItem( "name" ); //$NON-NLS-1$ String catalogName = name.getTextContent(); parsingInputStream.close(); FileInputStream schemaInputStream = new FileInputStream( mondrianFile ); org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper helper = new org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper( PentahoSystem .get( IUnifiedRepository.class ) ); helper.addSchema( schemaInputStream, catalogName, datasourceInfo ); reInit( PentahoSessionHolder.getSession() ); flushCacheForCatalog( catalogName, PentahoSessionHolder.getSession() ); } catch ( SAXParseException e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getString( "MondrianCatalogHelper.ERROR_0018_IMPORT_SCHEMA_ERROR" ) ); //$NON-NLS-1$ } catch ( Exception e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getString( "MondrianCatalogHelper.ERROR_0008_ERROR_OCCURRED" ), //$NON-NLS-1$ Reason.valueOf( e.getMessage() ) ); } } @Deprecated protected void writeDataSources( DataSources dataSources ) { File dataSourcesFile; try { dataSourcesFile = new File( new URL( dataSourcesConfig ).getFile() ); // dataSourcesConfigResource.getFile(); } catch ( IOException e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0005_RESOURCE_NOT_AVAILABLE" ), e, Reason.GENERAL ); //$NON-NLS-1$ } Writer sxml; try { sxml = new FileWriter( dataSourcesFile ); } catch ( IOException e ) { throw new MondrianCatalogServiceException( e ); } StringWriter sw = new StringWriter(); XMLOutput pxml = new XMLOutput( sw ); pxml.print( "<?xml version=\"1.0\"?>\n" ); //$NON-NLS-1$ dataSources.displayXML( pxml, 0 ); Document doc = null; try { doc = XmlDom4JHelper.getDocFromString( sw.toString(), new PentahoEntityResolver() ); } catch ( XmlParseException e ) { throw new MondrianCatalogServiceException( e ); } // pretty print try { OutputFormat format = OutputFormat.createPrettyPrint(); format.setEncoding( doc.getXMLEncoding() ); XMLWriter writer = new XMLWriter( sxml, format ); writer.write( doc ); writer.close(); // CleanXmlHelper.saveDomToWriter(doc, sxml); } catch ( IOException e ) { throw new MondrianCatalogServiceException( e ); } IOUtils.closeQuietly( sxml ); } @Override public MondrianCatalog getCatalog( final String context, final IPentahoSession pentahoSession ) { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "getCatalog" ); //$NON-NLS-1$ } init( pentahoSession ); MondrianCatalog cat = getCatalogFromCache( context, pentahoSession ); if ( null != cat ) { if ( hasAccess( cat, RepositoryFilePermission.READ ) ) { return cat; } else { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "user requested catalog with name \"" + context //$NON-NLS-1$ + "\", but had insufficient privileges; returning null" ); //$NON-NLS-1$ } return null; } } else { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger .debug( "user requested catalog with name\"" + context + "\", but catalog doesn't exist" ); //$NON-NLS-1$ } return null; } } protected void loadCatalogsIntoCache( final DataSourcesConfig.DataSources dataSources, final IPentahoSession pentahoSession ) { // Create the cache region if necessary. ICacheManager cacheMgr = PentahoSystem.getCacheManager( pentahoSession ); if ( !cacheMgr.cacheEnabled( MONDRIAN_CATALOG_CACHE_REGION ) ) { // Create the region cacheMgr.addCacheRegion( MONDRIAN_CATALOG_CACHE_REGION ); } Map<String, MondrianCatalog> catalogs = (Map<String, MondrianCatalog>) cacheMgr.getFromRegionCache( MONDRIAN_CATALOG_CACHE_REGION, getLocale() .toString() ); if ( catalogs == null ) { catalogs = new HashMap<String, MondrianCatalog>(); } else { return; } for ( DataSourcesConfig.DataSource dataSource : dataSources.dataSources ) { List<String> catalogNames = new ArrayList<String>(); for ( DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs ) { catalogNames.add( catalog.name ); } for ( DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs ) { if ( catalog.definition.startsWith( "mondrian:" ) || catalog.definition .startsWith( "solution:" ) ) { //$NON-NLS-1$ // try catch here so the whole thing doesn't blow up if one datasource is configured incorrectly. MondrianSchema schema = null; try { schema = makeSchema( getCatalogAsString( pentahoSession, catalog ) ); } catch ( Exception e ) { MondrianCatalogHelper.logger.error( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0013_FAILED_TO_LOAD_SCHEMA", catalog.definition ), e ); //$NON-NLS-1$ if ( e instanceof MondrianException ) { throw (MondrianException) e; } } MondrianCatalog mondrianCatalog = null; if ( schema == null ) { mondrianCatalog = new MondrianCatalog( catalog.name, catalog.dataSourceInfo, catalog.definition, schema ); } else { mondrianCatalog = new MondrianCatalog( useSchemaNameAsCatalogName ? schema.getName() : catalog.name, catalog.dataSourceInfo, catalog.definition, schema ); } catalogs.put( mondrianCatalog.getName(), mondrianCatalog ); catalogs.put( mondrianCatalog.getDefinition(), mondrianCatalog ); } else { MondrianCatalogHelper.logger.warn( Messages.getInstance().getString( "MondrianCatalogHelper.WARN_SKIPPING_DATASOURCE_DEF", catalog.definition ) ); //$NON-NLS-1$ } } } cacheMgr.putInRegionCache( MONDRIAN_CATALOG_CACHE_REGION, getLocale().toString(), catalogs ); } protected String applyDSP( IPentahoSession ps, String catalogDsInfo, String catalogDefinition ) throws Exception { PropertyList pl = Util.parseConnectString( catalogDsInfo ); String dsp = pl.get( RolapConnectionProperties.DynamicSchemaProcessor.name() ); if ( dsp != null ) { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "applyDSP: " + dsp ); //$NON-NLS-1$ } DynamicSchemaProcessor dynProc = ClassResolver.INSTANCE.instantiateSafe( dsp ); pl.put( "Locale", getLocale().toString() ); return dynProc.processSchema( catalogDefinition, pl ); } else { return docAtUrlToString( catalogDefinition, ps ); } } protected String getCatalogAsString( IPentahoSession ps, DataSourcesConfig.Catalog catalog ) throws Exception { if ( catalog.dataSourceInfo != null ) { return applyDSP( ps, catalog.dataSourceInfo, catalog.definition ); } else { MondrianCatalogHelper.logger.warn( Messages.getInstance().getString( "MondrianCatalogHelper.WARN_NO_CATALOG_DATASOURCE_INFO", catalog.name ) ); return docAtUrlToString( catalog.definition, ps ); } } @Deprecated protected List<MondrianCatalog> transformIntoCatalogList( final DataSourcesConfig.DataSources dataSources, final IPentahoSession pentahoSession ) { List<MondrianCatalog> localCatalogs = new ArrayList<MondrianCatalog>(); for ( DataSourcesConfig.DataSource dataSource : dataSources.dataSources ) { List<String> catalogNames = new ArrayList<String>(); for ( DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs ) { catalogNames.add( catalog.name ); } for ( DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs ) { if ( catalog.definition.startsWith( "solution:" ) ) { //$NON-NLS-1$ // try catch here so the whole thing doesn't blow up if one datasource is configured incorrectly. try { MondrianSchema schema = makeSchema( docAtUrlToString( catalog.definition, pentahoSession ) ); MondrianCatalogComplementInfo catalogComplementInfo = getCatalogComplementInfoMap( catalog.definition ); MondrianCatalog mondrianCatalog = new MondrianCatalog( useSchemaNameAsCatalogName ? schema.getName() : catalog.name, catalog.dataSourceInfo, catalog.definition, schema, catalogComplementInfo ); localCatalogs.add( mondrianCatalog ); } catch ( Exception e ) { MondrianCatalogHelper.logger.error( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0013_FAILED_TO_LOAD_SCHEMA", catalog.definition ), e ); //$NON-NLS-1$ } } else { MondrianCatalogHelper.logger.warn( Messages.getInstance().getString( "MondrianCatalogHelper.WARN_SKIPPING_DATASOURCE_DEF", catalog.definition ) ); //$NON-NLS-1$ } } } return localCatalogs; } /** * Method to access the MondrianCatalogComplementInfo taken a catalog name. * * @param name Catalog schema location * @return MondrianCatalogComplementInfo object */ public MondrianCatalogComplementInfo getCatalogComplementInfoMap( String name ) { return catalogComplementInfoMap.get( name ); } /** * this method loads a mondrian schema * * @param solutionLocation location of the schema * @param pentahoSession current session object * @return Mondrian Schema object. */ @Override public MondrianSchema loadMondrianSchema( final String solutionLocation, final IPentahoSession pentahoSession ) { return makeSchema( docAtUrlToString( solutionLocation, pentahoSession ) ); } protected String docAtUrlToString( final String urlStr, final IPentahoSession pentahoSession ) { // String relPath = getSolutionRepositoryRelativePath(urlStr, pentahoSession); String res = null; InputStream in = null; try { LocalizingDynamicSchemaProcessor schemaProcessor = new LocalizingDynamicSchemaProcessor(); PropertyList localeInfo = new PropertyList(); localeInfo.put( "Locale", getLocale().toString() ); //$NON-NLS-1$ FileSystemManager fsManager = VFS.getManager(); FileObject mondrianDS = fsManager.resolveFile( urlStr ); in = mondrianDS.getContent().getInputStream(); res = schemaProcessor.filter( null, localeInfo, in ); } catch ( FileNotFoundException fnfe ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0007_FILE_NOT_FOUND" ), fnfe ); //$NON-NLS-1$ } catch ( Exception e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0006_IO_PROBLEM" ), e ); //$NON-NLS-1$ } finally { IOUtils.closeQuietly( in ); } return res; } protected MondrianSchema getSchema( final String catalogName, final IPentahoSession pentahoSession ) { MondrianCatalog catalog = getCatalogFromCache( catalogName, pentahoSession ); if ( null == catalog ) { return null; } else { return catalog.getSchema(); } } protected MondrianSchema makeSchema( final String catalogStr ) { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger .debug( "makeSchema (catalogStr=" + catalogStr.substring( 0, Math.min( 40, catalogStr.length() ) ) + "...)" ); } MondrianSchema schema = null; try { final Parser xmlParser = XOMUtil.createDefaultParser(); final DOMWrapper def = xmlParser.parse( catalogStr ); MondrianDef.Schema schemaFromXml = new MondrianDef.Schema( def ); String schemaName = schemaFromXml.name; List<MondrianCube> mondrianCubes = new ArrayList<MondrianCube>(); for ( MondrianDef.Cube cube : schemaFromXml.cubes ) { if ( cube.enabled == null || cube.enabled.booleanValue() ) { String name = cube.caption; if ( StringUtils.isBlank( name ) ) { name = cube.name; } mondrianCubes.add( new MondrianCube( name, cube.name ) ); } } for ( MondrianDef.VirtualCube cube : schemaFromXml.virtualCubes ) { String name = cube.caption; if ( StringUtils.isBlank( name ) ) { name = cube.name; } if ( cube.enabled == null || cube.enabled.booleanValue() ) { mondrianCubes.add( new MondrianCube( name, cube.name ) ); } } // Interpret the role names MondrianDef.Role[] roles = schemaFromXml.roles; String[] roleNames = null; if ( ( roles != null ) && ( roles.length > 0 ) ) { roleNames = new String[ roles.length ]; for ( int i = 0; i < roles.length; i++ ) { roleNames[ i ] = roles[ i ].name; // Note - getName() doesn't return the role name, it returns the word Role } } schema = new MondrianSchema( schemaName, mondrianCubes, roleNames ); } catch ( XOMException e ) { if ( MondrianCatalogHelper.logger.isErrorEnabled() ) { MondrianCatalogHelper.logger.error( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0008_ERROR_OCCURRED" ), e ); //$NON-NLS-1$ } throw Util.newError( e, Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0009_WHILE_PARSING_CATALOG", catalogStr ) ); //$NON-NLS-1$ } return schema; } protected static enum CatalogPermission { READ, WRITE } protected List<MondrianCatalog> filter( final List<MondrianCatalog> origList, final IPentahoSession pentahoSession, final boolean jndiOnly ) { List<MondrianCatalog> filtered = new ArrayList<MondrianCatalog>(); for ( MondrianCatalog orig : origList ) { if ( hasAccess( orig, RepositoryFilePermission.READ ) && ( !jndiOnly || orig.isJndi() ) ) { filtered.add( orig ); } } return filtered; } /** * This (hacky) implementation bases its decision on whether or not the user has the permission (indicated by * <code>CatalogPermission</code>) based on whether the user has permission on the file in the solution repository * indicated by <code>catalog.getDefinition()</code>. * <p/> * Why is this class even enforcing security anyway!? */ protected boolean hasAccess( MondrianCatalog cat, RepositoryFilePermission permission ) { return getAclHelper().canAccess( getMondrianCatalogRepositoryHelper().getMondrianCatalogFile( cat.getName() ), EnumSet.of( permission ) ); } protected String getSolutionRepositoryRelativePath( final String path, final IPentahoSession pentahoSession ) { try { FileSystemManager fsManager = VFS.getManager(); return fsManager.resolveFile( path ).getName().getPath(); } catch ( FileSystemException e ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0012_FILESYSTEM_PROBLEM" ), e ); //$NON-NLS-1$ } } public boolean isUseSchemaNameAsCatalogName() { return useSchemaNameAsCatalogName; } public void setUseSchemaNameAsCatalogName( final boolean useSchemaNameAsCatalogName ) { this.useSchemaNameAsCatalogName = useSchemaNameAsCatalogName; } public static int addToCatalog( String baseUrl, boolean enableXmla, String schemaSolutionPath, IPentahoSession session, String jndiName, boolean overwrite ) { IMondrianCatalogService mondrianCatalogService = PentahoSystem.get( IMondrianCatalogService.class, "IMondrianCatalogService", session ); //$NON-NLS-1$ String dsUrl = baseUrl; if ( !dsUrl.endsWith( "/" ) ) { //$NON-NLS-1$ dsUrl += "/"; //$NON-NLS-1$ } dsUrl += "Xmla"; //$NON-NLS-1$ String catDef = "solution:" + schemaSolutionPath; //$NON-NLS-1$ MondrianSchema mondrianSchema = mondrianCatalogService.loadMondrianSchema( catDef, session ); String catName = mondrianSchema.getName(); String[] roleNames = mondrianSchema.getRoleNames(); // verify JNDI try { IDBDatasourceService datasourceService = PentahoSystem.getObjectFactory().get( IDBDatasourceService.class, null ); datasourceService.getDSBoundName( jndiName ); } catch ( ObjectFactoryException objface ) { Logger .error( "MondrianCatalogHelper", Messages.getInstance() .getErrorString( "MondrianCatalogPublisher.ERROR_0006_UNABLE_TO_FACTORY_OBJECT", jndiName ), objface ); } catch ( DBDatasourceServiceException dse ) { Logger .error( "MondrianCatalogHelper", Messages.getInstance() .getErrorString( "MondrianCatalogPublisher.ERROR_0001_JNDI_NAMING_ERROR", jndiName ), dse ); return -1; } // used in both the catalog and the catalog datasource // Note: we use the unbound JNDI name here, the PentahoXmlaServlet resolves the JNDI name String catConnectStr = "Provider=mondrian;DataSource=" + jndiName; //$NON-NLS-1$ // MB - 12/2009 - TODO: Figure out the empty list // Curious why we aren't adding the cubes from the read schema into the created schema. MondrianCatalog cat = new MondrianCatalog( catName, catConnectStr, catDef, new MondrianSchema( catName, new ArrayList<MondrianCube>(), roleNames ) ); try { mondrianCatalogService.addCatalog( cat, overwrite, session ); } catch ( MondrianCatalogServiceException e ) { Logger .error( "MondrianCatalogHelper", Messages.getInstance().getErrorString( "MondrianCatalogPublisher.ERROR_0002_EXCEPTION_OCCURRED" ), e ); return -1; } return 0; } /** * This method removes a mondrian catalog from the JCR repository. */ @Override public void removeCatalog( final String catalogName, final IPentahoSession pentahoSession ) throws MondrianCatalogServiceException { // // find the catalog to be removed // if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "removeCatalog" ); //$NON-NLS-1$ } MondrianCatalog catalog = getCatalog( catalogName, pentahoSession ); if ( catalog == null ) { throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0015_CATALOG_NOT_FOUND", catalogName ) ); //$NON-NLS-1$ } // // do an access check first // if ( !hasAccess( catalog, RepositoryFilePermission.WRITE ) ) { if ( MondrianCatalogHelper.logger.isDebugEnabled() ) { MondrianCatalogHelper.logger.debug( "user does not have access; throwing exception" ); //$NON-NLS-1$ } throw new MondrianCatalogServiceException( Messages.getInstance().getErrorString( "MondrianCatalogHelper.ERROR_0003_INSUFFICIENT_PERMISSION" ), Reason.ACCESS_DENIED ); //$NON-NLS-1$ } flushCacheForCatalog( catalog.getName(), pentahoSession ); getAclHelper().removeAclFor( getMondrianCatalogRepositoryHelper().getMondrianCatalogFile( catalog.getName() ) ); IUnifiedRepository solutionRepository = getRepository(); RepositoryFile deletingFile = solutionRepository.getFile( RepositoryFile.SEPARATOR + "etc" //$NON-NLS-1$ + RepositoryFile.SEPARATOR + "mondrian" + RepositoryFile.SEPARATOR + catalog.getName() ); //$NON-NLS-1$ solutionRepository.deleteFile( deletingFile.getId(), true, "" ); //$NON-NLS-1$ reInit( pentahoSession ); } @VisibleForTesting org.w3c.dom.Document getMondrianXmlDocument( InputStream is ) throws ParserConfigurationException, SAXException, IOException { DocumentBuilderFactory factory = XMLParserFactoryProducer.createSecureDocBuilderFactory(); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.parse( is ); } }