Java tutorial
/* * 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 2013 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.plugin.action.olap.impl; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import mondrian.olap.MondrianServer; import mondrian.olap.Role; import mondrian.olap.Util; import mondrian.rolap.RolapConnectionProperties; import mondrian.server.DynamicContentFinder; import mondrian.server.MondrianServerRegistry; import mondrian.spi.CatalogLocator; import mondrian.util.LockBox.Entry; import mondrian.xmla.XmlaHandler; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.vfs.FileSystemException; import org.apache.commons.vfs.VFS; import org.apache.commons.vfs.impl.DefaultFileSystemManager; import org.olap4j.OlapConnection; import org.olap4j.OlapException; import org.pentaho.platform.api.engine.ICacheManager; import org.pentaho.platform.api.engine.IConnectionUserRoleMapper; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.PentahoAccessControlException; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.security.SecurityHelper; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.platform.plugin.action.olap.IOlapConnectionFilter; import org.pentaho.platform.plugin.action.olap.IOlapService; import org.pentaho.platform.plugin.action.olap.IOlapServiceException; import org.pentaho.platform.plugin.action.olap.PlatformXmlaExtra; import org.pentaho.platform.plugin.services.connections.mondrian.MDXConnection; import org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper; import org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper.HostedCatalogInfo; import org.pentaho.platform.plugin.services.importexport.legacy.MondrianCatalogRepositoryHelper.Olap4jServerInfo; import org.pentaho.platform.repository.solution.filebased.MondrianVfs; import org.pentaho.platform.util.messages.LocaleHelper; import org.springframework.security.userdetails.UserDetailsService; /** * Implementation of the IOlapService which uses the * {@link MondrianCatalogRepositoryHelper} as a backend to * store the connection informations and uses {@link DriverManager} * to create the connections. * <p/> * <p>It will also check for the presence of a {@link IConnectionUserRoleMapper} * and change the roles accordingly before creating a connection. * <p/> * <p>This implementation is thread safe. It will use a {@link ReadWriteLock} * to manage the access to its metadata. */ public class OlapServiceImpl implements IOlapService { public static String CATALOG_CACHE_REGION = "iolapservice-catalog-cache"; //$NON-NLS-1$ static final String MONDRIAN_DATASOURCE_FOLDER = "mondrian"; //$NON-NLS-1$ final ReadWriteLock cacheLock = new ReentrantReadWriteLock(); /** * This is the default name of an XMLA data source on the server. * Mondrian XMLA servers only support a single data source. */ static final String DATASOURCE_NAME = "Pentaho"; private static final Log LOG = getLogger(); /* * Do not access these two fields directly. They need to be accessed through * getRepository and getHelper because we can't init them before spring is * done initializing the sub modules. */ private IUnifiedRepository repository; private MondrianCatalogRepositoryHelper helper; private MondrianServer server = null; private final List<IOlapConnectionFilter> filters; private Role role; private static Log getLogger() { return LogFactory.getLog(IOlapService.class); } /** * Empty constructor. Creating an instance from here will * use the {@link PentahoSystem} to fetch the {@link IUnifiedRepository} * at runtime. */ public OlapServiceImpl() { this(null, null); } /** * Constructor for testing purposes. Takes a repository as a parameter. */ public OlapServiceImpl(IUnifiedRepository repo, final MondrianServer server) { this.repository = repo; this.filters = new CopyOnWriteArrayList<IOlapConnectionFilter>(); this.server = server; try { DefaultFileSystemManager dfsm = (DefaultFileSystemManager) VFS.getManager(); if (dfsm.hasProvider("mondrian") == false) { dfsm.addProvider("mondrian", new MondrianVfs()); } } catch (FileSystemException e) { throw new RuntimeException(e); } } private Boolean isSec = null; private boolean isSecurityEnabled() { if (isSec != null) { return isSec; } try { UserDetailsService uds = PentahoSystem.get(UserDetailsService.class); if (uds != null) { isSec = true; } else { isSec = false; } } catch (Exception e) { // no op. isSec = false; } return isSec; } synchronized IUnifiedRepository getRepository() { if (repository == null) { repository = PentahoSystem.get(IUnifiedRepository.class); } return repository; } synchronized MondrianCatalogRepositoryHelper getHelper() { if (helper == null) { helper = new MondrianCatalogRepositoryHelper(getRepository()); } return helper; } public synchronized void setHelper(MondrianCatalogRepositoryHelper helper) { this.helper = helper; } /** * Returns a list of catalogs for the current session. * * <p>The cache is stored in the platform's caches in the region * {@link #CATALOG_CACHE_REGION}. It is also segmented by * locale, but we only return the correct sub-region according to the * session passed as a parameter. */ @SuppressWarnings("unchecked") protected synchronized List<IOlapService.Catalog> getCache(IPentahoSession session) { // Create the cache region if necessary. final ICacheManager cacheMgr = PentahoSystem.getCacheManager(session); final Object cacheKey = makeCacheSubRegionKey(getLocale()); final Lock writeLock = cacheLock.writeLock(); try { writeLock.lock(); if (!cacheMgr.cacheEnabled(CATALOG_CACHE_REGION)) { // Create the region. This requires write access. cacheMgr.addCacheRegion(CATALOG_CACHE_REGION); } if (cacheMgr.getFromRegionCache(CATALOG_CACHE_REGION, cacheKey) == null) { // Create the sub-region. This requires write access. cacheMgr.putInRegionCache(CATALOG_CACHE_REGION, cacheKey, new ArrayList<IOlapService.Catalog>()); } return (List<IOlapService.Catalog>) cacheMgr.getFromRegionCache(CATALOG_CACHE_REGION, cacheKey); } finally { writeLock.unlock(); } } /** * Clears all caches for all locales. */ protected void resetCache(IPentahoSession session) { final Lock writeLock = cacheLock.writeLock(); try { writeLock.lock(); final ICacheManager cacheMgr = PentahoSystem.getCacheManager(session); cacheMgr.clearRegionCache(CATALOG_CACHE_REGION); } finally { writeLock.unlock(); } } protected Object makeCacheSubRegionKey(Locale locale) { return locale.toString(); } /** * Initializes the cache. Only the cache specific to the sesison's locale * will be populated. */ protected void initCache(IPentahoSession session) { final List<Catalog> cache = getCache(session); final boolean needUpdate; final Lock readLock = cacheLock.readLock(); try { readLock.lock(); // Check if the cache is empty. if (cache.size() == 0) { needUpdate = true; } else { needUpdate = false; } } finally { readLock.unlock(); } if (needUpdate) { final Lock writeLock = cacheLock.writeLock(); try { writeLock.lock(); // First clear the cache cache.clear(); final Callable<Void> call = new Callable<Void>() { public Void call() throws Exception { // Now build the cache. Use the system session in the holder. for (String name : getHelper().getHostedCatalogs()) { try { addCatalogToCache(PentahoSessionHolder.getSession(), name); } catch (Throwable t) { LOG.error("Failed to initialize the cache for OLAP connection " + name, t); } } for (String name : getHelper().getOlap4jServers()) { try { addCatalogToCache(PentahoSessionHolder.getSession(), name); } catch (Throwable t) { LOG.error("Failed to initialize the cache for OLAP connection " + name, t); } } return null; } }; if (isSecurityEnabled()) { SecurityHelper.getInstance().runAsSystem(call); } else { call.call(); } // Sort it all. Collections.sort(cache, new Comparator<IOlapService.Catalog>() { public int compare(Catalog o1, Catalog o2) { return o1.name.compareTo(o2.name); } }); } catch (Throwable t) { LOG.error("Failed to initialize the connection cache", t); throw new IOlapServiceException(t); } finally { writeLock.unlock(); } } } /** * Adds a catalog and its children to the cache. * Do not use directly. This must be called with a write lock * on the cache. * * @param catalogName The name of the catalog to load in cache. */ private void addCatalogToCache(IPentahoSession session, String catalogName) { final IOlapService.Catalog catalog = new Catalog(catalogName, new ArrayList<IOlapService.Schema>()); OlapConnection connection = null; try { connection = getConnection(catalogName, session); for (org.olap4j.metadata.Schema schema4j : connection.getOlapSchemas()) { connection.setSchema(schema4j.getName()); final IOlapService.Schema schema = new Schema(schema4j.getName(), catalog, new ArrayList<IOlapService.Cube>(), new ArrayList<String>(connection.getAvailableRoleNames())); for (org.olap4j.metadata.Cube cube4j : schema4j.getCubes()) { schema.cubes.add(new IOlapService.Cube(cube4j.getName(), cube4j.getCaption(), schema)); } catalog.schemas.add(schema); } // We're done. getCache(session).add(catalog); } catch (OlapException e) { LOG.warn("Failed to initialize the olap connection cache for catalog " + catalogName, e); } finally { try { if (connection != null) { connection.close(); } } catch (SQLException e) { LOG.warn("Failed to gracefully close an olap connection to catalog " + catalogName, e); } } } public void addHostedCatalog(String name, String dataSourceInfo, InputStream inputStream, boolean overwrite, IPentahoSession session) { // Access if (!hasAccess(name, EnumSet.of(RepositoryFilePermission.WRITE), session)) { LOG.debug("user does not have access; throwing exception"); //$NON-NLS-1$ throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0003_INSUFFICIENT_PERMISSION"), //$NON-NLS-1$ IOlapServiceException.Reason.ACCESS_DENIED); } // check for existing vs. the overwrite flag. if (getCatalogNames(session).contains(name) && !overwrite) { throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0004_ALREADY_EXISTS"), //$NON-NLS-1$ IOlapServiceException.Reason.ALREADY_EXISTS); } try { MondrianCatalogRepositoryHelper helper = new MondrianCatalogRepositoryHelper(getRepository()); helper.addHostedCatalog(inputStream, name, dataSourceInfo); } catch (Exception e) { throw new IOlapServiceException(e, IOlapServiceException.Reason.convert(e)); } } protected boolean hasAccess(final String catalogName, final EnumSet<RepositoryFilePermission> perms, IPentahoSession session) { return getHelper().hasAccess(catalogName, perms, session); } public void addOlap4jCatalog(String name, String className, String URL, String user, String password, Properties props, boolean overwrite, IPentahoSession session) { // Access if (!hasAccess(name, EnumSet.of(RepositoryFilePermission.WRITE), session)) { LOG.debug("user does not have access; throwing exception"); //$NON-NLS-1$ throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0003_INSUFFICIENT_PERMISSION"), //$NON-NLS-1$ IOlapServiceException.Reason.ACCESS_DENIED); } // check for existing vs. the overwrite flag. if (getCatalogNames(session).contains(name) && !overwrite) { throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0004_ALREADY_EXISTS"), //$NON-NLS-1$ IOlapServiceException.Reason.ALREADY_EXISTS); } MondrianCatalogRepositoryHelper helper = new MondrianCatalogRepositoryHelper(getRepository()); helper.addOlap4jServer(name, className, URL, user, password, props); } public void removeCatalog(String name, IPentahoSession session) { // Check Access if (!hasAccess(name, EnumSet.of(RepositoryFilePermission.DELETE), session)) { LOG.debug("user does not have access; throwing exception"); //$NON-NLS-1$ throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0003_INSUFFICIENT_PERMISSION"), //$NON-NLS-1$ IOlapServiceException.Reason.ACCESS_DENIED); } if (!getCatalogNames(session).contains(name)) { throw new IOlapServiceException(Messages.getInstance() .getErrorString("MondrianCatalogHelper.ERROR_0015_CATALOG_NOT_FOUND", name)); } // This could be a remote connection getHelper().deleteCatalog(name); } public void flushAll(IPentahoSession session) { final Lock writeLock = cacheLock.writeLock(); try { writeLock.lock(); // Start by flushing the local cache. resetCache(session); flushHostedAndRemote(session); } catch (Exception e) { throw new IOlapServiceException(e); } finally { writeLock.unlock(); } } private void flushHostedAndRemote(final IPentahoSession session) throws SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { flushCatalogs(getHostedCatalogNames(session), session, true); flushCatalogs(getRemoteCatalogNames(session), session, false); } /** * Flushes all catalogs in the catalogNames collection. If hosted=true * the method breaks after the first successful schemaCacheFlush, since we * know that all schemas will be flushed by the operation. * For remote we assume that each needs to be flushed separately, since * there are possibly multiple servers. */ private void flushCatalogs(Collection<String> catalogNames, IPentahoSession session, boolean hosted) throws SQLException { for (String name : catalogNames) { OlapConnection connection = null; try { connection = getConnection(name, session); XmlaHandler.XmlaExtra xmlaExtra = getXmlaExtra(connection); if (xmlaExtra != null) { xmlaExtra.flushSchemaCache(connection); if (hosted) { // flushing schema cache for one connection flushes all, no need to continue break; } } } catch (Exception e) { LOG.warn(Messages.getInstance().getErrorString("MondrianCatalogHelper.ERROR_0019_FAILED_TO_FLUSH", name), e); } finally { if (connection != null) { connection.close(); } } } } protected XmlaHandler.XmlaExtra getXmlaExtra(final OlapConnection connection) throws SQLException { return PlatformXmlaExtra.unwrapXmlaExtra(connection); } public List<String> getCatalogNames(IPentahoSession pentahoSession) throws IOlapServiceException { // This is the quick implementation to obtain a list of catalogs // without having to open connections. IT can be used by UI tools // and tests. final List<String> names = new ArrayList<String>(); names.addAll(getHostedCatalogNames(pentahoSession)); names.addAll(getRemoteCatalogNames(pentahoSession)); // Sort it all. Collections.sort(names); return names; } private Collection<String> getHostedCatalogNames(final IPentahoSession pentahoSession) { return Collections2.filter(getHelper().getHostedCatalogs(), new Predicate<String>() { @Override public boolean apply(String name) { return hasAccess(name, EnumSet.of(RepositoryFilePermission.READ), pentahoSession); } }); } private Collection<String> getRemoteCatalogNames(final IPentahoSession pentahoSession) { return Collections2.filter(getHelper().getOlap4jServers(), new Predicate<String>() { @Override public boolean apply(String name) { return hasAccess(name, EnumSet.of(RepositoryFilePermission.READ), pentahoSession); } }); } public List<IOlapService.Catalog> getCatalogs(IPentahoSession session) throws IOlapServiceException { // Make sure the cache is initialized. initCache(session); final List<Catalog> cache = getCache(session); final Lock readLock = cacheLock.readLock(); try { readLock.lock(); final List<IOlapService.Catalog> catalogs = new ArrayList<IOlapService.Catalog>(); for (Catalog catalog : cache) { if (hasAccess(catalog.name, EnumSet.of(RepositoryFilePermission.READ), session)) { catalogs.add(catalog); } } // Do not leak the cache list. // Do not allow modifications on the list. return Collections.unmodifiableList(new ArrayList<IOlapService.Catalog>(cache)); } finally { readLock.unlock(); } } public List<IOlapService.Schema> getSchemas(String parentCatalog, IPentahoSession session) { final List<IOlapService.Schema> schemas = new ArrayList<IOlapService.Schema>(); for (IOlapService.Catalog catalog : getCatalogs(session)) { if (parentCatalog == null || catalog.name.equals(parentCatalog)) { schemas.addAll(catalog.schemas); } } return schemas; } public List<Cube> getCubes(String parentCatalog, String parentSchema, IPentahoSession pentahoSession) { final List<IOlapService.Cube> cubes = new ArrayList<IOlapService.Cube>(); for (IOlapService.Schema schema : getSchemas(parentCatalog, pentahoSession)) { if (parentSchema == null || schema.name.equals(parentSchema)) { cubes.addAll(schema.cubes); } } return cubes; } public OlapConnection getConnection(String catalogName, IPentahoSession session) throws IOlapServiceException { if (catalogName == null) { // This is normal. It happens on XMLA's DISCOVER_DATASOURCES try { return getServer().getConnection(DATASOURCE_NAME, null, null, new Properties()); } catch (Exception e) { throw new IOlapServiceException(e); } } // Check Access if (!hasAccess(catalogName, EnumSet.of(RepositoryFilePermission.READ), session)) { LOG.debug("user does not have access; throwing exception"); //$NON-NLS-1$ throw new IOlapServiceException( Messages.getInstance().getErrorString("OlapServiceImpl.ERROR_0003_INSUFFICIENT_PERMISSION"), //$NON-NLS-1$ IOlapServiceException.Reason.ACCESS_DENIED); } // Check its existence. if (!getCatalogNames(session).contains(catalogName)) { throw new IOlapServiceException(Messages.getInstance() .getErrorString("MondrianCatalogHelper.ERROR_0015_CATALOG_NOT_FOUND", catalogName)); } // Check if it is a remote server if (getHelper().getOlap4jServers().contains(catalogName)) { return makeOlap4jConnection(catalogName); } final StringBuilder roleName = new StringBuilder(); Entry roleMonikor = null; if (this.role != null) { // We must use a custom role implementation. // Register the instance with the mondrian server. roleMonikor = getServer().getLockBox().register(this.role); roleName.append(roleMonikor.getMoniker()); } else { final IConnectionUserRoleMapper mapper = PentahoSystem.get(IConnectionUserRoleMapper.class, MDXConnection.MDX_CONNECTION_MAPPER_KEY, null); // Don't use the user session here yet. String[] effectiveRoles = new String[0]; /* * If Catalog/Schema are null (this happens with high level metadata requests, * like DISCOVER_DATASOURCES) we can't use the role mapper, even if it * is present and configured. */ if (session != null && mapper != null) { // Use the role mapper. try { effectiveRoles = mapper.mapConnectionRoles(session, catalogName); if (effectiveRoles == null) { effectiveRoles = new String[0]; } } catch (PentahoAccessControlException e) { throw new IOlapServiceException(e); } } // Now we tokenize that list. boolean addComma = false; for (String role : effectiveRoles) { if (addComma) { roleName.append(","); //$NON-NLS-1$ } roleName.append(role); addComma = true; } } // Populate some properties, like locale. final Properties properties = new Properties(); properties.put(RolapConnectionProperties.Locale.name(), getLocale().toString()); // Return a connection try { return getServer().getConnection(DATASOURCE_NAME, catalogName, Util.isEmpty(roleName.toString()) ? null : roleName.toString(), properties); } catch (Exception e) { throw new IOlapServiceException(e); } finally { // Cleanup our lockbox entry. if (roleMonikor != null) { getServer().getLockBox().deregister(roleMonikor); } } } private OlapConnection makeOlap4jConnection(String name) { final Olap4jServerInfo olapServerInfo = getHelper().getOlap4jServerInfo(name); assert olapServerInfo != null; // Make sure the driver is present try { Class.forName(olapServerInfo.className); } catch (ClassNotFoundException e) { throw new IOlapServiceException(e); } // As per the JDBC specs, we can set the user/pass into // connection properties called 'user' and 'password'. final Properties newProps = new Properties(olapServerInfo.properties); // First, apply the filters. for (IOlapConnectionFilter filter : this.filters) { filter.filterProperties(newProps); } // Then override the user and password. We do this after the filters // so as not to expose this. if (olapServerInfo.user != null) { newProps.put("user", olapServerInfo.user); } if (olapServerInfo.password != null) { newProps.put("password", olapServerInfo.password); } try { final Connection conn = DriverManager.getConnection(olapServerInfo.URL, newProps); return conn.unwrap(OlapConnection.class); } catch (SQLException e) { throw new IOlapServiceException(e); } } private synchronized MondrianServer getServer() { if (server == null) { server = MondrianServerRegistry.INSTANCE .createWithRepository(new DynamicContentFinder("http://not-needed.com") { @Override public String getContent() { // We dynamically generate the XML required by the // XMLA servlet. It must conform to Datasources.dtd, // as specified by olap4j-xmlaserver. return getDatasourcesXml(); } }, new CatalogLocator() { public String locate(String URL) { return URL; } }); } return server; } private String getDatasourcesXml() { final Callable<String> call = new Callable<String>() { public String call() throws Exception { return generateInMemoryDatasourcesXml(); } }; try { if (isSecurityEnabled()) { return SecurityHelper.getInstance().runAsSystem(call); } else { return call.call(); } } catch (Exception e) { throw new IOlapServiceException(e); } } private String generateInMemoryDatasourcesXml() { 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$ datasourcesXML.append("<DataSourceName>" + DATASOURCE_NAME + "</DataSourceName>\n"); //$NON-NLS-1$ datasourcesXML.append("<DataSourceDescription>Pentaho BI Platform Datasources</DataSourceDescription>\n"); //$NON-NLS-1$ datasourcesXML.append("<URL>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$ datasourcesXML.append("<Catalogs>\n"); //$NON-NLS-1$ // Start with local catalogs. for (String name : getHelper().getHostedCatalogs()) { final HostedCatalogInfo hostedServerInfo = getHelper().getHostedCatalogInfo(name); addCatalogXml(datasourcesXML, hostedServerInfo.name, hostedServerInfo.dataSourceInfo, hostedServerInfo.definition); } // Don't add the olap4j catalogs. This doesn't work for now. datasourcesXML.append("</Catalogs>\n"); //$NON-NLS-1$ datasourcesXML.append("</DataSource>\n"); //$NON-NLS-1$ datasourcesXML.append("</DataSources>\n"); //$NON-NLS-1$ return datasourcesXML.toString(); } private void addCatalogXml(StringBuffer str, String catalogName, String dsInfo, String definition) { assert definition != null; str.append("<Catalog name=\"" + catalogName + "\">\n"); //$NON-NLS-1$ //$NON-NLS-2$ if (dsInfo != null) { str.append("<DataSourceInfo>" + dsInfo + "</DataSourceInfo>\n"); //$NON-NLS-1$ //$NON-NLS-2$ } str.append("<Definition>" + definition + "</Definition>\n"); //$NON-NLS-1$ //$NON-NLS-2$ str.append("</Catalog>\n"); //$NON-NLS-1$ } public void setConnectionFilters(Collection<IOlapConnectionFilter> filters) { this.filters.addAll(filters); } public void setMondrianRole(Role role) { this.role = role; } private static Locale getLocale() { final Locale locale = LocaleHelper.getLocale(); if (locale != null) { return locale; } else { return Locale.getDefault(); } } }