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