/* * 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 2008 - 2009 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.plugin.action.mondrian.catalog; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.Writer; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import mondrian.i18n.LocalizingDynamicSchemaProcessor; import mondrian.olap.MondrianDef; import mondrian.olap.Util; import mondrian.olap.Util.PropertyList; import mondrian.rolap.agg.AggregationManager; import mondrian.xmla.DataSourcesConfig; import mondrian.xmla.DataSourcesConfig.Catalog; import mondrian.xmla.DataSourcesConfig.Catalogs; import mondrian.xmla.DataSourcesConfig.DataSource; import mondrian.xmla.DataSourcesConfig.DataSources; import org.apache.commons.collections.list.SetUniqueList; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.FileSystemManager; import org.apache.commons.vfs.VFS; 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.pentaho.metadata.messages.LocaleHelper; import org.pentaho.platform.api.data.DatasourceServiceException; import org.pentaho.platform.api.data.IDatasourceService; import org.pentaho.platform.api.engine.ICacheManager; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IPermissionMask; import org.pentaho.platform.api.engine.ISolutionFile; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.repository.ISolutionRepository; import org.pentaho.platform.api.util.XmlParseException; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.solution.PentahoEntityResolver; import org.pentaho.platform.engine.services.solution.SolutionReposHelper; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogServiceException.Reason; import org.pentaho.platform.repository.solution.filebased.FileSolutionFile; import org.pentaho.platform.util.logging.Logger; import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper; /** * Reads in file containing Mondrian data sources and catalogs. (Contains code copied from <code>XmlaServlet</code>.) * * @author mlowery */ public class MondrianCatalogHelper implements IMondrianCatalogService { // ~ 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; // ~ Constructors ==================================================================================================== private static final MondrianCatalogHelper instance = new MondrianCatalogHelper(); @SuppressWarnings("unchecked") private List<MondrianCatalog> getCatalogs(IPentahoSession pentahoSession) { List<MondrianCatalog> catalogs = (List<MondrianCatalog>)PentahoSystem.getCacheManager(pentahoSession).getAllValuesFromRegionCache(MONDRIAN_CATALOG_CACHE_REGION); // 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 */ private boolean catalogExists(MondrianCatalog catalog, IPentahoSession pentahoSession) { if(getCatalogByCatalog(catalog, pentahoSession) != null) { return true; } return false; } /** * 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 A matching catalog if one is found, otherwise null */ private MondrianCatalog getCatalogByCatalog(MondrianCatalog catalog, IPentahoSession pentahoSession) { if(catalog != null) { MondrianCatalog foundCatalog = getCatalogFromCache(catalog.getName(), pentahoSession); // Was the catalog found by name? if(foundCatalog != null) { // Use the existing comparator if(makeKey(catalog).equals(makeKey(foundCatalog))) { return foundCatalog; } } } return null; } private static final 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. return (MondrianCatalog)PentahoSystem.getCacheManager(pentahoSession).getFromRegionCache(MONDRIAN_CATALOG_CACHE_REGION, context); } public static MondrianCatalogHelper getInstance() { return MondrianCatalogHelper.instance; } public MondrianCatalogHelper() { super(); // LEGACY: This configures the dataSourcesConfig dataSourcesConfig = "file:" + //$NON-NLS-1$ PentahoSystem.getApplicationContext().getSolutionPath("system/olap/datasources.xml"); //$NON-NLS-1$ } private volatile boolean initialized = false; public static String MONDRIAN_CATALOG_CACHE_REGION = "mondrian-catalog-cache"; //$NON-NLS-1$ // ~ Methods ========================================================================================================= protected synchronized void init(final IPentahoSession pentahoSession) { if (!initialized) { if (MondrianCatalogHelper.logger.isDebugEnabled()) { MondrianCatalogHelper.logger.debug("init"); //$NON-NLS-1$ } loadCatalogsIntoCache(makeDataSources(), pentahoSession); initialized = true; AggregationManager.instance().getCacheControl(null).flushSchemaCache(); } } public synchronized void reInit(final IPentahoSession pentahoSession) { initialized = false; init(pentahoSession); } /** * Same as implemented in <code>XmlaServlet</code> except takes advantage of Spring's Resource framework. */ protected DataSourcesConfig.DataSources makeDataSources() { // Resource dataSourcesConfigResource = resourceLoader.getResource(dataSourcesConfig); URL dataSourcesConfigUrl = null; try { if (dataSourcesConfig.startsWith("file:")) { //$NON-NLS-1$ dataSourcesConfigUrl = new URL(dataSourcesConfig);//dataSourcesConfigResource.getURL(); } else if (dataSourcesConfig.startsWith("classpath:")) { //$NON-NLS-1$ dataSourcesConfigUrl = getClass().getResource(dataSourcesConfig.substring(10)); } 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); } // don't try to parse a null return (dataSourcesConfigUrl == null) ? null : parseDataSourcesUrl(dataSourcesConfigUrl); } 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())); //$NON-NLS-1$ } } 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)); //$NON-NLS-1$ } } protected Map<String, MondrianCatalogComplementInfo> makeCatalogComplementInfoMap(final DOMWrapper doc) { HashMap<String, MondrianCatalogComplementInfo> map = new HashMap<String, MondrianCatalogComplementInfo>(); 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; } protected Map<String, MondrianCatalog> makeDataSourceInfoAndCatalogDefinitionMap(final List<MondrianCatalog> cats) { Map<String, MondrianCatalog> map = new HashMap<String, MondrianCatalog>(); for (MondrianCatalog catalog : cats) { map.put(makeKey(catalog), catalog); } return map; } 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$ } return propertyList.toString(); } protected String makeKey(final MondrianCatalog catalog) { String dataSourceInfo = cleanseDataSourceInfo(catalog.getEffectiveDataSource().getDataSourceInfo()); return dataSourceInfo + "+" + catalog.getDefinition(); //$NON-NLS-1$ } public String getDataSourcesConfig() { return dataSourcesConfig; } public void setDataSourcesConfig(final String dataSourcesConfig) { this.dataSourcesConfig = dataSourcesConfig; } 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)); } public synchronized void addCatalog(final MondrianCatalog catalog, final boolean overwrite, final IPentahoSession pentahoSession) throws MondrianCatalogServiceException { if (MondrianCatalogHelper.logger.isDebugEnabled()) { MondrianCatalogHelper.logger.debug("addCatalog"); //$NON-NLS-1$ } init(pentahoSession); // do an access check first if (!hasAccess(catalog, CatalogPermission.WRITE, pentahoSession)) { 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$ } // check for existing dataSourceInfo+catalog if (catalogExists(catalog, pentahoSession) && !overwrite) { throw new MondrianCatalogServiceException(Messages.getInstance().getErrorString("MondrianCatalogHelper.ERROR_0004_ALREADY_EXISTS"), Reason.ALREADY_EXISTS); //$NON-NLS-1$ } if( catalogExists(catalog, pentahoSession) ) { MondrianCatalog existing = getCatalogByCatalog(catalog, pentahoSession); if( !existing.getDefinition().equals( catalog.getDefinition() ) ) { // this scenario occurs if the file is in a different location but has the same schema name throw new MondrianCatalogServiceException(Messages.getInstance().getErrorString("MondrianCatalogHelper.ERROR_0004_ALREADY_EXISTS"), Reason.ALREADY_EXISTS); //$NON-NLS-1$ } } DataSources dataSources = makeDataSources(); MondrianDataSource mDataSource = catalog.getDataSource(); DataSource ds = null; // see if the ds already exists for(int i = 0; i < dataSources.dataSources.length && ds == null; i++) { DataSource currentDs = dataSources.dataSources[i]; if (mDataSource.getName().equals(currentDs.name)) { ds = currentDs; } } if (ds == null) { ds = new DataSource(); ds.authenticationMode = mDataSource.getAuthenticationMode(); ds.dataSourceInfo = mDataSource.getDataSourceInfo(); ds.description = mDataSource.getDescription(); ds.name = mDataSource.getName(); ds.providerName = mDataSource.getProviderName(); ds.providerType = mDataSource.getProviderType(); ds.url = mDataSource.getUrl(); dataSources.dataSources = (DataSource[])ArrayUtils.add(dataSources.dataSources, ds); } Catalog cat = null; if (catalogExists(catalog, pentahoSession)) { // find the catalog and overwrite for (Catalog currentCat : ds.catalogs.catalogs) { if (cleanseDataSourceInfo(catalog.getEffectiveDataSource().getDataSourceInfo()).equals( cleanseDataSourceInfo(currentCat.dataSourceInfo)) && catalog.getDefinition().equals(currentCat.definition)) { cat = currentCat; } } } else { cat = new Catalog(); if (ds.catalogs == null) { ds.catalogs = new Catalogs(); } if (ds.catalogs.catalogs == null) { ds.catalogs.catalogs = new Catalog[0]; } ds.catalogs.catalogs = (Catalog[])ArrayUtils.add(ds.catalogs.catalogs, cat); cat.setDataSource(ds); } cat.dataSourceInfo = catalog.getDataSourceInfo(); cat.definition = catalog.getDefinition(); cat.name = catalog.getName(); writeDataSources( dataSources ); // if we got here then assume file write was successful; refresh from file if (MondrianCatalogHelper.logger.isDebugEnabled()) { MondrianCatalogHelper.logger.debug("refreshing from dataSourcesConfig (" + dataSourcesConfig + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } reInit(pentahoSession); } 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); } 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, CatalogPermission.READ, pentahoSession)) { 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$ //$NON-NLS-2$ } return null; } } protected void loadCatalogsIntoCache(final DataSourcesConfig.DataSources dataSources, final IPentahoSession pentahoSession) { ICacheManager cacheMgr = PentahoSystem.getCacheManager(pentahoSession); if(!cacheMgr.cacheEnabled(MONDRIAN_CATALOG_CACHE_REGION)) { cacheMgr.addCacheRegion(MONDRIAN_CATALOG_CACHE_REGION); } else { cacheMgr.clearRegionCache(MONDRIAN_CATALOG_CACHE_REGION); } for (DataSourcesConfig.DataSource dataSource : dataSources.dataSources) { List<String> catalogNames = new ArrayList<String>(); for (DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs) { catalogNames.add(catalog.name); } // create dataSource MondrianDataSource mondrianDataSource = new MondrianDataSource(dataSource.name, dataSource.description, dataSource.url, dataSource.dataSourceInfo, dataSource.providerName, dataSource.providerType, dataSource.authenticationMode, catalogNames); 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)); MondrianCatalog mondrianCatalog = new MondrianCatalog(useSchemaNameAsCatalogName ? schema.getName() : catalog.name, catalog.dataSourceInfo, catalog.definition, mondrianDataSource, schema); cacheMgr.putInRegionCache(MONDRIAN_CATALOG_CACHE_REGION, mondrianCatalog.getName(), mondrianCatalog); cacheMgr.putInRegionCache(MONDRIAN_CATALOG_CACHE_REGION, mondrianCatalog.getDefinition(), 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$ } } } } @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); } // create dataSource MondrianDataSource mondrianDataSource = new MondrianDataSource(dataSource.name, dataSource.description, dataSource.url, dataSource.dataSourceInfo, dataSource.providerName, dataSource.providerType, dataSource.authenticationMode, catalogNames); 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, mondrianDataSource, 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. */ 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 { in = PentahoSystem.get(ISolutionRepository.class, pentahoSession).getResourceInputStream(relPath, true, ISolutionRepository.ACTION_EXECUTE); LocalizingDynamicSchemaProcessor schemaProcessor = new LocalizingDynamicSchemaProcessor(); PropertyList localeInfo = new PropertyList(); localeInfo.put("Locale", LocaleHelper.getLocale().toString()); //$NON-NLS-1$ 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())) + "...)"); //$NON-NLS-1$ //$NON-NLS-2$ } 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, CatalogPermission.READ, pentahoSession) && (!jndiOnly || orig.getEffectiveDataSource().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(final MondrianCatalog cat, final CatalogPermission perm, final IPentahoSession pentahoSession) { if (!PentahoSystem.get(ISolutionRepository.class, pentahoSession).supportsAccessControls()) { return true; } IPermissionMask mappedPerm = new IPermissionMask() { public int getMask() { return perm == CatalogPermission.READ ? ISolutionRepository.ACTION_EXECUTE : ISolutionRepository.ACTION_CREATE | ISolutionRepository.ACTION_UPDATE; } }; String relPath = getSolutionRepositoryRelativePath(cat.getDefinition(), pentahoSession); ISolutionFile solutionFile = PentahoSystem.get(ISolutionRepository.class, pentahoSession).getSolutionFile(relPath, ISolutionRepository.ACTION_EXECUTE); if (null == solutionFile) { // try to get parent folder relPath = relPath.substring(0, relPath.lastIndexOf("/")); //$NON-NLS-1$ solutionFile = PentahoSystem.get(ISolutionRepository.class, pentahoSession).getSolutionFile(relPath, ISolutionRepository.ACTION_EXECUTE); if (null == solutionFile) { // file not found throw new MondrianCatalogServiceException(Messages.getInstance().getErrorString("MondrianCatalogHelper.ERROR_0010_PATH_NOT_FOUND", cat.getDefinition())); //$NON-NLS-1$ } } // mlowery I believe there's a bug in db-based solution repository where if the file is not yet in the PRO_FILES // table yet in the filesystem, getFileByPath happily delegates to its parent which just checks the file on disk if (solutionFile instanceof FileSolutionFile) { throw new MondrianCatalogServiceException(Messages.getInstance().getErrorString("MondrianCatalogHelper.ERROR_0011_REPOSITORY_ERROR", cat.getDefinition())); //$NON-NLS-1$ } return PentahoSystem.get(ISolutionRepository.class, pentahoSession).hasAccess(solutionFile, mappedPerm.getMask()); } protected String getSolutionRepositoryRelativePath(final String path, final IPentahoSession pentahoSession) { SolutionReposHelper.setSolutionRepositoryThreadVariable(PentahoSystem.get(ISolutionRepository.class, 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 = MondrianCatalogHelper.getInstance(); String dsUrl = baseUrl; if (!dsUrl.endsWith("/")) { //$NON-NLS-1$ dsUrl += "/"; //$NON-NLS-1$ } dsUrl += "Xmla"; //$NON-NLS-1$ String dsAuthMode = DataSource.AUTH_MODE_UNAUTHENTICATED; String dsProviderName = "Pentaho"; //$NON-NLS-1$ // DataSources where ProviderType=None are filtered by PentahoXmlaServlet String dsProviderType = enableXmla ? DataSource.PROVIDER_TYPE_MDP : "None"; //$NON-NLS-1$ String catDef = "solution:" + schemaSolutionPath; //$NON-NLS-1$ MondrianSchema mondrianSchema = mondrianCatalogService.loadMondrianSchema(catDef, session); String catName = mondrianSchema.getName(); String dsName = "Provider=Mondrian;DataSource=" + mondrianSchema.getName(); //$NON-NLS-1$ String dsDesc = "Published Mondrian Schema " + mondrianSchema.getName() + " using jndi datasource " + jndiName; //$NON-NLS-1$ //$NON-NLS-2$ String[] roleNames = mondrianSchema.getRoleNames(); // verify JNDI try { IDatasourceService datasourceService = PentahoSystem.getObjectFactory().get(IDatasourceService.class ,null); datasourceService.getDSBoundName(jndiName); } catch (ObjectFactoryException objface) { Logger.error("MondrianCatalogHelper",Messages.getInstance().getErrorString("MondrianCatalogPublisher.ERROR_0006_UNABLE_TO_FACTORY_OBJECT", jndiName), objface); //$NON-NLS-1$ //$NON-NLS-2$ } catch (DatasourceServiceException dse) { Logger.error( "MondrianCatalogHelper", Messages.getInstance().getErrorString("MondrianCatalogPublisher.ERROR_0001_JNDI_NAMING_ERROR", jndiName), dse); //$NON-NLS-1$ //$NON-NLS-2$ return -1; } // used in both the catalog and the catalog datasource // Note: we use the unbound JNDI name here, the PentahoXmlaServlet and PivotViewComponent resolve the JNDI name String catConnectStr = "Provider=mondrian;DataSource=" + jndiName; //$NON-NLS-1$ MondrianDataSource ds = new MondrianDataSource(dsName, dsDesc, dsUrl, catConnectStr, dsProviderName, dsProviderType, dsAuthMode, null); // 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, ds, 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); //$NON-NLS-1$ //$NON-NLS-2$ return -1; } return 0; } }