/* * This software is subject to the terms of the Eclipse Public License v1.0 * Agreement, available at the following URL: * http://www.eclipse.org/legal/epl-v10.html. * You must accept the terms of that agreement to use this software. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package mondrian.server; import mondrian.olap.*; import mondrian.rolap.RolapSchema; import mondrian.tui.XmlaSupport; import mondrian.util.Pair; import mondrian.xmla.DataSourcesConfig; import org.apache.log4j.Logger; import java.util.HashMap; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; /** * Implementation of * {@link RepositoryContentFinder} that * periodically reloads the content of the repository. * * <p>The updates are performed by a background thread. * It is important to call {@link DynamicContentFinder#shutdown()} * once this object can be disposed of. * * @author Thiyagu Ajit * @author Luc Boudreau * @author Julian Hyde */ public class DynamicContentFinder extends UrlRepositoryContentFinder { protected String lastDataSourcesConfigString; protected DataSourcesConfig.DataSources dataSources; private static final Logger LOGGER = Logger.getLogger(MondrianServer.class); private final Timer timer; /** * Creates a DynamicContentFinder. * @param dataSourcesConfigUrl URL of repository */ public DynamicContentFinder( String dataSourcesConfigUrl) { super(dataSourcesConfigUrl); reloadDataSources(); timer = Util.newTimer( "mondrian.server.DynamicContentFinder$timer", true); final Pair<Long, TimeUnit> interval = Util.parseInterval( String.valueOf( MondrianProperties.instance() .XmlaSchemaRefreshInterval.get()), TimeUnit.MILLISECONDS); final long period = interval.right.toMillis(interval.left); timer.schedule( new TimerTask() { public void run() { reloadDataSources(); } }, period, period); } /** * Cleans up all background updating jobs. */ public void shutdown() { super.shutdown(); timer.cancel(); } /** * Checks for updates to datasources content, flushes obsolete catalogs. */ public synchronized void reloadDataSources() { try { String dataSourcesConfigString = getContent(); if (!hasDataSourcesContentChanged(dataSourcesConfigString)) { return; } DataSourcesConfig.DataSources newDataSources = XmlaSupport.parseDataSources( dataSourcesConfigString, LOGGER); if (newDataSources == null) { return; } flushObsoleteCatalogs(newDataSources); this.dataSources = newDataSources; this.lastDataSourcesConfigString = dataSourcesConfigString; } catch (Exception e) { throw Util.newError( e, "Failed to parse data sources config '" + url + "'"); } } protected boolean hasDataSourcesContentChanged( String dataSourcesConfigString) { return dataSourcesConfigString != null && !dataSourcesConfigString.equals( this.lastDataSourcesConfigString); } private Map< String, Pair<DataSourcesConfig.DataSource, DataSourcesConfig.Catalog>> createCatalogMap( DataSourcesConfig.DataSources newDataSources) { Map< String, Pair<DataSourcesConfig.DataSource, DataSourcesConfig.Catalog>> newDatasourceCatalogNames = new HashMap<String, Pair<DataSourcesConfig.DataSource, DataSourcesConfig.Catalog>>(); for (DataSourcesConfig.DataSource dataSource : newDataSources.dataSources) { for (DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs) { if (catalog.dataSourceInfo == null) { catalog.dataSourceInfo = dataSource.dataSourceInfo; } newDatasourceCatalogNames.put( catalog.name, Pair.of(dataSource, catalog)); } } return newDatasourceCatalogNames; } public synchronized void flushObsoleteCatalogs( DataSourcesConfig.DataSources newDataSources) { if (dataSources == null) { return; } Map<String, Pair<DataSourcesConfig.DataSource, DataSourcesConfig.Catalog>> newDatasourceCatalogs = createCatalogMap(newDataSources); for (DataSourcesConfig.DataSource oldDataSource : dataSources.dataSources) { for (DataSourcesConfig.Catalog oldCatalog : oldDataSource.catalogs.catalogs) { Pair<DataSourcesConfig.DataSource, DataSourcesConfig.Catalog> pair = newDatasourceCatalogs.get(oldCatalog.name); if (pair == null || !areCatalogsEqual( oldDataSource, oldCatalog, pair.left, pair.right)) { flushCatalog(oldCatalog.name); } } } } protected void flushCatalog(String catalogName) { for (RolapSchema schema : RolapSchema.getRolapSchemas()) { if (schema.getName().equals(catalogName)) { schema.getInternalConnection().getCacheControl(null) .flushSchema(schema); } } } public static boolean areCatalogsEqual( DataSourcesConfig.DataSource dataSource1, DataSourcesConfig.Catalog catalog1, DataSourcesConfig.DataSource dataSource2, DataSourcesConfig.Catalog catalog2) { return Util.equals(dsi(dataSource1, catalog1), dsi(dataSource2, catalog2)) && catalog1.name.equals(catalog2.name) && catalog1.definition.equals(catalog2.definition); } public static String dsi( DataSourcesConfig.DataSource dataSource, DataSourcesConfig.Catalog catalog) { return catalog.dataSourceInfo == null && dataSource != null ? dataSource.dataSourceInfo : catalog.dataSourceInfo; } } // End DynamicContentFinder.java