/* * 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 2005 - 2009 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.web.servlet; import java.io.IOException; import java.net.URL; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import mondrian.olap.Connection; import mondrian.olap.DriverManager; import mondrian.olap.Role; import mondrian.olap.Util; import mondrian.olap.Util.PropertyList; import mondrian.rolap.RolapConnection; import mondrian.rolap.RolapConnectionProperties; import mondrian.spi.CatalogLocator; import mondrian.util.Pair; import mondrian.xmla.DataSourcesConfig; import mondrian.xmla.XmlaException; import mondrian.xmla.XmlaHandler; import mondrian.xmla.DataSourcesConfig.Catalog; import mondrian.xmla.DataSourcesConfig.DataSources; import mondrian.xmla.impl.DefaultXmlaServlet; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.Node; import org.pentaho.platform.api.data.DatasourceServiceException; import org.pentaho.platform.api.data.IDatasourceService; 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.util.xml.dom4j.XmlDom4JHelper; import org.pentaho.platform.web.http.PentahoHttpSessionHelper; import org.pentaho.platform.web.servlet.messages.Messages; import org.xml.sax.EntityResolver; /** * Filters out <code>DataSource</code> elements that are not XMLA-related. * <p /> * Background: Pentaho re-used datasources.xml for non-XMLA purposes. But since <code>DefaultXmlaServlet</code> requires * actual XMLA datasources, this servlet extends <code>DefaultXmlaServlet</code> and removes the non-XMLA datasources * before continuing normal <code>DefaultXmlaServlet</code> behavior. * <p /> * The convention here is that any <code>DataSource</code> elements with * <code><ProviderType>None</ProviderType></code> are considered non-XMLA and are filtered out. * * @author mlowery */ public class PentahoXmlaServlet extends DefaultXmlaServlet { private static final String PREFIX = "cxmla"; //$NON-NLS-1$ private static final String KEY_DATASOURCE = "DataSource"; //$NON-NLS-1$ // ~ Static fields/initializers ====================================================================================== private static final Log logger = LogFactory.getLog(PentahoXmlaServlet.class); // private static final Logger LOGGER = Logger.getLogger(PentahoXmlaServlet.class); private static final long serialVersionUID = -5873069189408153768L; // - Constructors ================================ public PentahoXmlaServlet() { super(); } // ~ Methods ========================================================================================================= @Override protected String readDataSourcesContent(final URL dataSourcesConfigUrl) throws IOException { String original = Util.readURL(dataSourcesConfigUrl, Util.toMap(System.getProperties())); EntityResolver loader = new PentahoEntityResolver(); Document originalDocument = null; try { originalDocument = XmlDom4JHelper.getDocFromString(original, loader); } catch(XmlParseException e) { PentahoXmlaServlet.logger.error(Messages.getInstance().getString("PentahoXmlaServlet.ERROR_0004_UNABLE_TO_GET_DOCUMENT_FROM_STRING"), e); //$NON-NLS-1$ return null; } if (PentahoXmlaServlet.logger.isDebugEnabled()) { PentahoXmlaServlet.logger .debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_ORIG_DOC", originalDocument.asXML())); //$NON-NLS-1$ } Document modifiedDocument = (Document) originalDocument.clone(); List<Node> nodesToRemove = modifiedDocument.selectNodes("/DataSources/DataSource/Catalogs/Catalog[contains(DataSourceInfo, 'EnableXmla=False')]"); //$NON-NLS-1$ if (PentahoXmlaServlet.logger.isDebugEnabled()) { PentahoXmlaServlet.logger.debug(Messages.getInstance().getString( "PentahoXmlaServlet.DEBUG_NODES_TO_REMOVE", String.valueOf(nodesToRemove.size()))); //$NON-NLS-1$ } for (Node node : nodesToRemove) { node.detach(); } if (PentahoXmlaServlet.logger.isDebugEnabled()) { PentahoXmlaServlet.logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_MOD_DOC", modifiedDocument.asXML())); //$NON-NLS-1$ } return modifiedDocument.asXML(); } /** * override default doPost and configure SolutionRepositoryVFS, before * calling parent's doPost */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // configure solution repository VFS service SolutionReposHelper.setSolutionRepositoryThreadVariable(PentahoSystem .get(ISolutionRepository.class, PentahoHttpSessionHelper.getPentahoSession(request))); super.doPost(request, response); } @Override protected XmlaHandler getXmlaHandler() { if (this.xmlaHandler == null) { this.xmlaHandler = new PentahoXmlaHandler(this.dataSources, this.catalogLocator, PREFIX); } return this.xmlaHandler; } /** * Overrides XmlaHandler to supply a DataSource from Pentaho DataSource Manager. * * @author mlowery */ private class PentahoXmlaHandler extends XmlaHandler { public PentahoXmlaHandler(DataSources dataSources, CatalogLocator catalogLocator, String prefix) { super(dataSources, catalogLocator, prefix); } /** * Override to see if the datasource is in Pentaho DataSource Manager. Use it if so. Otherwise, this method is * exactly the same as the superclass implementation. */ @Override protected Connection getConnection(Catalog catalog, Role role, String roleName) throws XmlaException { DataSourcesConfig.DataSource ds = catalog.getDataSource(); Util.PropertyList connectProperties = Util.parseConnectString(catalog.getDataSourceInfo()); String catalogUrl = catalogLocator.locate(catalog.definition); if (logger.isDebugEnabled()) { if (catalogUrl == null) { logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_CATALOGURL_NULL")); //$NON-NLS-1$ } else { logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_CATALOGURL", catalogUrl)); //$NON-NLS-1$ } } connectProperties.put(RolapConnectionProperties.Catalog.name(), catalogUrl); // Checking access if (!DataSourcesConfig.DataSource.AUTH_MODE_UNAUTHENTICATED.equalsIgnoreCase(ds.getAuthenticationMode()) && (role == null) && (roleName == null)) { throw new XmlaException(CLIENT_FAULT_FC, HSB_ACCESS_DENIED_CODE, HSB_ACCESS_DENIED_FAULT_FS, new SecurityException(Messages.getInstance().getString("PentahoXmlaServlet.ERROR_0001_ACCESS_DENIED_DATASOURCE"))); //$NON-NLS-1$ } // Role in request overrides role in connect string, if present. if (roleName != null) { connectProperties.put(RolapConnectionProperties.Role.name(), roleName); } String dsName = connectProperties.get(KEY_DATASOURCE); logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_CONNECT_STRING_DATASOURCE", dsName)); //$NON-NLS-1$ javax.sql.DataSource datasource = getDataSource(dsName); logger.debug(Messages.getInstance().getString( "PentahoXmlaServlet.DEBUG_DATASOURCE_FROM_PENTAHO", datasource != null ? datasource.toString() : null)); //$NON-NLS-1$ RolapConnection conn = null; if (StringUtils.isNotBlank(dsName) && datasource != null) { conn = (RolapConnection) DriverManager.getConnection(connectProperties, null, datasource); logger.debug(Messages.getInstance().getString( "PentahoXmlaServlet.DEBUG_CREATED_ROLAP_CONN", conn != null ? conn.toString() : null)); //$NON-NLS-1$ } else { // either using jdbc or pentaho ds mgr does not know about dsName; fall back on "normal" behavior conn = (RolapConnection) DriverManager.getConnection(resolveUboundJndi(connectProperties), null); } if (role != null) { conn.setRole(role); } if (logger.isDebugEnabled()) { if (conn == null) { logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_CONN_NULL")); //$NON-NLS-1$ } else { logger.debug(Messages.getInstance().getString("PentahoXmlaServlet.DEBUG_CONN_NOT_NULL")); //$NON-NLS-1$ } } return conn; } /** * Uses Pentaho DataSource Manager to get datasource. * @param dsName datasource name * @return datasource or <code>null</code> if not found */ private javax.sql.DataSource getDataSource(String dsName) { try { IDatasourceService datasourceSvc = PentahoSystem.getObjectFactory().get(IDatasourceService.class, null); javax.sql.DataSource datasource = datasourceSvc.getDataSource(dsName); return datasource; } catch (ObjectFactoryException e) { logger.error(Messages.getInstance().getErrorString("PentahoXmlaServlet.ERROR_0002_UNABLE_TO_INSTANTIATE"), e); //$NON-NLS-1$ return null; } catch (DatasourceServiceException e) { logger.error(Messages.getInstance().getErrorString("PentahoXmlaServlet.ERROR_0002_UNABLE_TO_INSTANTIATE"), e); //$NON-NLS-1$ return null; } } } private PropertyList resolveUboundJndi(PropertyList orig) { // make a copy of the orig prop list PropertyList newPropList = new Util.PropertyList(); Iterator iter = orig.iterator(); while (iter.hasNext()) { Pair<String, String> pair = (Pair<String, String>) iter.next(); newPropList.put(pair.left, pair.right); } String datasource = orig.get(KEY_DATASOURCE); String resolvedDatasource = datasource; IDatasourceService datasourceService; try { datasourceService = PentahoSystem.getObjectFactory().get(IDatasourceService.class, null); resolvedDatasource = datasourceService.getDSBoundName(datasource); } catch (ObjectFactoryException e) { // this should be a runtime exception anyway throw new RuntimeException(e); } catch (DatasourceServiceException e) { logger.error(Messages.getInstance().getString("PentahoXmlaServlet.ERROR_0003_GETDSBOUNDNAME_FAILED"), e); //$NON-NLS-1$ } newPropList.put(KEY_DATASOURCE, resolvedDatasource); return newPropList; } }