001 /* 002 // This software is subject to the terms of the Common Public License 003 // Agreement, available at the following URL: 004 // http://www.opensource.org/licenses/cpl.html. 005 // Copyright (C) 2006-2008 Julian Hyde 006 // All Rights Reserved. 007 // You must accept the terms of that agreement to use this software. 008 */ 009 010 package mondrian.xmla.impl; 011 012 import mondrian.olap.Util; 013 import mondrian.rolap.CacheControlImpl; 014 import mondrian.rolap.RolapSchema; 015 import mondrian.xmla.DataSourcesConfig; 016 017 import javax.servlet.ServletException; 018 import javax.servlet.http.HttpServletRequest; 019 import javax.servlet.http.HttpServletResponse; 020 import java.io.IOException; 021 import java.net.URL; 022 import java.util.HashMap; 023 import java.util.Iterator; 024 import java.util.Map; 025 026 /** 027 * Extends DefaultXmlaServlet to add dynamic datasource loading capability. 028 * Limitations : Catalog name should be unique across the datasources 029 * 030 * @author Thiyagu, Ajit 031 */ 032 public class DynamicDatasourceXmlaServlet extends DefaultXmlaServlet { 033 protected URL dataSourcesConfigUrl; 034 protected String lastDataSourcesConfigString; 035 036 protected void doPost( 037 HttpServletRequest request, 038 HttpServletResponse response) 039 throws ServletException, IOException { 040 reloadDataSources(); 041 super.doPost(request, response); 042 } 043 044 /** 045 * Checks for updates to datasources content, flushes obsolete catalogs 046 */ 047 void reloadDataSources() { 048 try { 049 String dataSourcesConfigString = readDataSourcesContent(dataSourcesConfigUrl); 050 if (hasDataSourcesContentChanged(dataSourcesConfigString)) { 051 DataSourcesConfig.DataSources newDataSources = 052 parseDataSources(dataSourcesConfigString); 053 if (newDataSources != null) { 054 flushObsoleteCatalogs(newDataSources); 055 this.dataSources = newDataSources; 056 xmlaHandler = null; 057 lastDataSourcesConfigString = dataSourcesConfigString; 058 } 059 } 060 } catch (Exception e) { 061 throw Util.newError(e, "Failed to parse data sources config '" + 062 dataSourcesConfigUrl.toExternalForm() + "'"); 063 } 064 } 065 066 protected boolean hasDataSourcesContentChanged 067 (String dataSourcesConfigString) { 068 return dataSourcesConfigString != null 069 && !dataSourcesConfigString.equals(this.lastDataSourcesConfigString); 070 } 071 072 /** 073 * Overides XmlaServlet.parseDataSourcesUrl to store dataSorucesConfigUrl, 074 * Datasoruces Configuration content 075 * @param dataSourcesConfigUrl 076 */ 077 protected DataSourcesConfig.DataSources parseDataSourcesUrl 078 (URL dataSourcesConfigUrl) { 079 this.dataSourcesConfigUrl = dataSourcesConfigUrl; 080 try { 081 String dataSourcesConfigString = readDataSourcesContent(dataSourcesConfigUrl); 082 if (lastDataSourcesConfigString == null) { 083 // This is the first time we are reading any datasource config 084 this.lastDataSourcesConfigString = dataSourcesConfigString; 085 } 086 return parseDataSources(dataSourcesConfigString); 087 } catch (Exception e) { 088 throw Util.newError(e, "Failed to parse data sources config '" + 089 dataSourcesConfigUrl.toExternalForm() + "'"); 090 } 091 } 092 093 void flushObsoleteCatalogs(DataSourcesConfig.DataSources newDataSources) { 094 Map<String, DataSourcesConfig.Catalog> newDatasourceCatalogs = 095 createCatalogMap(newDataSources); 096 097 for (DataSourcesConfig.DataSource oldDataSource : dataSources.dataSources) { 098 for (DataSourcesConfig.Catalog oldCatalog : oldDataSource.catalogs.catalogs) { 099 DataSourcesConfig.Catalog newCatalog = 100 newDatasourceCatalogs.get(oldCatalog.name); 101 if (!(newCatalog != null && 102 areCatalogsEqual(oldCatalog, newCatalog))) { 103 flushCatalog(oldCatalog.name); 104 } 105 } 106 } 107 } 108 109 private Map<String, DataSourcesConfig.Catalog> createCatalogMap 110 (DataSourcesConfig.DataSources newDataSources) { 111 Map<String, DataSourcesConfig.Catalog> newDatasourceCatalogNames = 112 new HashMap<String, DataSourcesConfig.Catalog>(); 113 for (DataSourcesConfig.DataSource dataSource : newDataSources.dataSources) { 114 for (DataSourcesConfig.Catalog catalog : dataSource.catalogs.catalogs) { 115 newDatasourceCatalogNames.put(catalog.name, catalog); 116 } 117 } 118 return newDatasourceCatalogNames; 119 } 120 121 void flushCatalog(String catalogName) { 122 Iterator schemas = RolapSchema.getRolapSchemas(); 123 while (schemas.hasNext()) { 124 RolapSchema curSchema = (RolapSchema) schemas.next(); 125 if (curSchema.getName().equals(catalogName)) { 126 new CacheControlImpl().flushSchema(curSchema); 127 } 128 } 129 } 130 131 boolean areCatalogsEqual 132 (DataSourcesConfig.Catalog catalog1, DataSourcesConfig.Catalog catalog2) { 133 134 if ((catalog1.getDataSourceInfo() != null && 135 catalog2.getDataSourceInfo() == null) || 136 (catalog2.getDataSourceInfo() != null && 137 catalog1.getDataSourceInfo() == null)) { 138 return false; 139 } 140 141 if ((catalog1.getDataSourceInfo() == null && 142 catalog2.getDataSourceInfo() == null) || 143 (catalog1.getDataSourceInfo().equals(catalog2.getDataSourceInfo()))) { 144 145 return (catalog1.name.equals(catalog2.name) && 146 catalog1.definition.equals(catalog2.definition)); 147 } 148 return false; 149 } 150 } 151 152 // End DynamicDatasourceXmlaServlet.java