/* * 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 2008 - 2009 Pentaho Corporation. All rights reserved. * */ package org.pentaho.platform.web.servlet; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.List; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; 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.DocumentException; import org.dom4j.Node; import org.dom4j.DocumentHelper; import org.dom4j.tree.DefaultElement; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.pentaho.actionsequence.dom.ActionInputConstant; import org.pentaho.actionsequence.dom.ActionSequenceDocument; import org.pentaho.actionsequence.dom.IActionSequenceDocument; import org.pentaho.actionsequence.dom.actions.PivotViewAction; import org.pentaho.platform.api.data.IDatasourceService; import org.pentaho.platform.api.engine.IParameterProvider; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.engine.PentahoAccessControlException; import org.pentaho.platform.api.engine.PentahoSystemException; import org.pentaho.platform.api.repository.ISolutionRepository; import org.pentaho.platform.api.util.XmlParseException; import org.pentaho.platform.engine.core.solution.ActionInfo; import org.pentaho.platform.engine.core.solution.SimpleParameterProvider; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.SoapHelper; import org.pentaho.platform.engine.services.WebServiceUtil; import org.pentaho.platform.engine.services.solution.PentahoEntityResolver; import org.pentaho.platform.plugin.action.mondrian.catalog.IMondrianCatalogService; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalog; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCatalogHelper; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianCube; import org.pentaho.platform.plugin.action.mondrian.catalog.MondrianDataSource; import org.pentaho.platform.util.logging.Logger; import org.pentaho.platform.util.messages.LocaleHelper; import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper; import org.pentaho.platform.web.http.request.HttpRequestParameterProvider; import org.pentaho.platform.web.servlet.messages.Messages; public class AnalysisViewService extends ServletBase { public static String ANALYSIS_VIEW_TEMPLATE = "analysis_view_template.xaction"; //$NON-NLS-1$ private static final long serialVersionUID = 831738225052159697L; private static final Log logger = LogFactory.getLog(AnalysisViewService.class); private final IMondrianCatalogService mondrianCatalogService = MondrianCatalogHelper.getInstance(); @Override public Log getLogger() { return AnalysisViewService.logger; } @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { String responseEncoding = PentahoSystem.getSystemSetting("web-service-encoding", "utf-8"); String component = request.getParameter("component"); //$NON-NLS-1$ //Check if we need to forward off before getting output stream. Fixes JasperException if(component.equalsIgnoreCase("newView")){ //$NON-NLS-1$ newAnalysisView(request, response); return; } PentahoSystem.systemEntryPoint(); try { boolean wrapWithSoap = "false".equals(request.getParameter("ajax")); //$NON-NLS-1$ //$NON-NLS-2$ String solutionName = request.getParameter("solution"); //$NON-NLS-1$ String actionPath = request.getParameter("path"); //$NON-NLS-1$ String actionName = request.getParameter("action"); //$NON-NLS-1$ String content = null; try { content = getPayloadAsString(request); } catch (IOException ioEx) { String msg = Messages.getInstance().getErrorString("AdhocWebService.ERROR_0006_FAILED_TO_GET_PAYLOAD_FROM_REQUEST"); //$NON-NLS-1$ error(msg, ioEx); XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(msg + " " + ioEx.getLocalizedMessage()), response.getOutputStream(), responseEncoding, true); } IParameterProvider parameterProvider = null; HashMap parameters = new HashMap(); if (!StringUtils.isEmpty(content)) { Document doc = null; try { doc = XmlDom4JHelper.getDocFromString(content, new PentahoEntityResolver() ); } catch (XmlParseException e) { String msg = Messages.getInstance().getErrorString("HttpWebService.ERROR_0001_ERROR_DURING_WEB_SERVICE"); //$NON-NLS-1$ error(msg, e); XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(msg), response.getOutputStream(), responseEncoding, true); } List parameterNodes = doc.selectNodes("//SOAP-ENV:Body/*/*"); //$NON-NLS-1$ for (int i = 0; i < parameterNodes.size(); i++) { Node parameterNode = (Node) parameterNodes.get(i); String parameterName = parameterNode.getName(); String parameterValue = parameterNode.getText(); // String type = parameterNode.selectSingleNode( "@type" ); // if( "xml-data".equalsIgnoreCase( ) ) if ("action".equals(parameterName)) { //$NON-NLS-1$ ActionInfo info = ActionInfo.parseActionString(parameterValue); solutionName = info.getSolutionName(); actionPath = info.getPath(); actionName = info.getActionName(); parameters.put("solution", solutionName); //$NON-NLS-1$ parameters.put("path", actionPath); //$NON-NLS-1$ parameters.put("name", actionName); //$NON-NLS-1$ } else if ("component".equals(parameterName)) { //$NON-NLS-1$ component = parameterValue; } else { parameters.put(parameterName, parameterValue); } } parameterProvider = new SimpleParameterProvider(parameters); } else { parameterProvider = new HttpRequestParameterProvider(request); } if (!"generatePreview".equals(component)) { //$NON-NLS-1$ response.setContentType("text/xml"); //$NON-NLS-1$ response.setCharacterEncoding(responseEncoding); } // PentahoHttpSession userSession = new PentahoHttpSession( // request.getRemoteUser(), request.getSession(), // request.getLocale() ); IPentahoSession userSession = getPentahoSession(request); // send the header of the message to prevent time-outs while we are working //response.setHeader("expires", "0"); //$NON-NLS-1$ //$NON-NLS-2$ dispatch(request, response, component, parameterProvider, userSession, wrapWithSoap); } catch (IOException ioEx) { String msg = Messages.getInstance().getErrorString("HttpWebService.ERROR_0001_ERROR_DURING_WEB_SERVICE"); //$NON-NLS-1$ error(msg, ioEx); XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(msg), response.getOutputStream(), responseEncoding, true); } catch (PentahoSystemException ex) { String msg = ex.getLocalizedMessage(); error(msg, ex); XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(msg), response.getOutputStream(), responseEncoding, true); } catch (PentahoAccessControlException ex) { String msg = ex.getLocalizedMessage(); error(msg, ex); XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(msg), response.getOutputStream(), responseEncoding, true); } finally { PentahoSystem.systemExitPoint(); } } protected void dispatch(final HttpServletRequest request, final HttpServletResponse response, final String component, final IParameterProvider parameterProvider, final IPentahoSession userSession, final boolean wrapWithSoap) throws IOException, PentahoSystemException, PentahoAccessControlException { if ("createNewView".equals(component)) { //$NON-NLS-1$ saveXAction(userSession, parameterProvider, request, response, wrapWithSoap); } else if ("listCatalogs".equals(component)) { //$NON-NLS-1$ listCatalogs(userSession, response.getOutputStream(), wrapWithSoap); } } private void newAnalysisView(final HttpServletRequest request, final HttpServletResponse response) throws IOException{ PentahoSystem.systemEntryPoint(); try { List<MondrianCatalog> catalogs = mondrianCatalogService.listCatalogs(getPentahoSession(request), true); request.setAttribute("catalog", catalogs); //$NON-NLS-1$ try{ RequestDispatcher dispatcher = request.getRequestDispatcher("NewAnalysisView"); //$NON-NLS-1$ if (dispatcher != null){ dispatcher.forward(request, response); } } catch(ServletException e){ XmlDom4JHelper.saveDom(WebServiceUtil.createErrorDocument(e.getMessage()), response.getOutputStream(), LocaleHelper.getSystemEncoding(), true); } } finally { PentahoSystem.systemExitPoint(); } } public void listCatalogs(final IPentahoSession userSession, final OutputStream outputStream, final boolean wrapWithSoap) throws IOException { StringBuilder builder = new StringBuilder(); List<MondrianCatalog> catalogs = mondrianCatalogService.listCatalogs(userSession, true); Element rootElement = new DefaultElement("catalogs"); //$NON-NLS-1$ Document doc = DocumentHelper.createDocument(rootElement); for (MondrianCatalog catalog : catalogs) { Element catalogElement = rootElement.addElement("catalog").addAttribute("name", catalog.getName()); //$NON-NLS-1$ //$NON-NLS-2$ Element schemaElement = catalogElement.addElement("schema").addAttribute("name", catalog.getSchema().getName()); //$NON-NLS-1$ //$NON-NLS-2$ Element cubesElement = schemaElement.addElement("cubes"); //$NON-NLS-1$ for (MondrianCube cube : catalog.getSchema().getCubes()) { cubesElement.addElement("cube").addAttribute("name", cube.getName()); //$NON-NLS-1$ //$NON-NLS-2$ } } if (wrapWithSoap) { XmlDom4JHelper.saveDom(SoapHelper.createSoapResponseDocument(doc), outputStream, PentahoSystem.getSystemSetting("web-service-encoding", "utf-8"), true); } else { XmlDom4JHelper.saveDom(doc, outputStream, PentahoSystem.getSystemSetting("web-service-encoding", "utf-8"), true); } } public void saveXAction(final IPentahoSession session, final IParameterProvider parameterProvider, final HttpServletRequest request, final HttpServletResponse response, final boolean wrapWithSoap) throws IOException, PentahoSystemException, PentahoAccessControlException { try{ String solutionName = parameterProvider.getStringParameter("solution", null); //$NON-NLS-1$ String solutionPath = parameterProvider.getStringParameter("actionPath", null); //$NON-NLS-1$ String model = parameterProvider.getStringParameter("schema", null); //$NON-NLS-1$ String cube = parameterProvider.getStringParameter("cube", null); //$NON-NLS-1$ String title = parameterProvider.getStringParameter("name", null); //$NON-NLS-1$ String description = parameterProvider.getStringParameter("descr", null); //$NON-NLS-1$ String jndi = null; String jdbc = null; String xactionFilename = parameterProvider.getStringParameter("actionName", null); //$NON-NLS-1$ //get reference to selected mondrian catalog MondrianCatalog selectedCatalog = mondrianCatalogService.getCatalog(model, session); // validate parameters if(selectedCatalog == null){ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0004_MODEL_NULL")); //$NON-NLS-1$ } MondrianDataSource datasource = selectedCatalog.getEffectiveDataSource(); if(datasource.isJndi()){ // by default, this datasource should be unbound. we still support fully qualified JNDI names // specified in the datasources.xml try { IDatasourceService datasourceService = PentahoSystem.getObjectFactory().get(IDatasourceService.class ,null); jndi = datasourceService.getDSUnboundName(datasource.getJndi()); } catch (ObjectFactoryException objface) { Logger.error("AnalysisViewService",Messages.getInstance().getErrorString("AnalysisViewService.ERROR_0001_UNABLE_TO_FACTORY_OBJECT", jndi), objface); //$NON-NLS-1$ //$NON-NLS-2$ } } else { jdbc = datasource.getJdbc(); } model = selectedCatalog.getDefinition(); if ((solutionName == null) || solutionName.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0001_SOLUTION_NAME_NULL")); //$NON-NLS-1$ } if ((solutionPath == null) || solutionPath.equals("")) { //$NON-NLS-1$ solutionPath = "/"; //$NON-NLS-1$ } if ((title == null) || title.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0003_TITLE_NULL")); //$NON-NLS-1$ } if ((model == null) || model.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0004_MODEL_NULL")); //$NON-NLS-1$ } if ((description == null) || description.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0005_DESCRIPTION_NULL")); //$NON-NLS-1$ } if ((jndi == null) || jndi.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0006_JNDI_NULL")); //$NON-NLS-1$ } if ((cube == null) || cube.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0007_CUBE_NULL")); //$NON-NLS-1$ } if ((xactionFilename == null) || xactionFilename.equals("")) { //$NON-NLS-1$ throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0008_XACTION_NULL")); //$NON-NLS-1$ } String path = solutionName; if (!solutionName.endsWith("/") && !solutionPath.startsWith("/")) { //$NON-NLS-1$ //$NON-NLS-2$ path += "/"; //$NON-NLS-1$ } if(!xactionFilename.endsWith(".xaction")){ //$NON-NLS-1$ xactionFilename += ".xaction"; //$NON-NLS-1$ } path += solutionPath; boolean overwrite = parameterProvider.getStringParameter("overwrite", "false").equalsIgnoreCase("true"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ String xaction = generateXAction(session, title, description, model, jndi, jdbc, cube); ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, session); String baseUrl = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$ int xactionSaveStatus = repository.publish(baseUrl, path, xactionFilename, xaction.getBytes(), overwrite); // String msg = WebServiceUtil.getStatusXml(Messages.getInstance().getString("AnalysisViewService.USER_VIEW_SAVED")); //$NON-NLS-1$ if (xactionSaveStatus == ISolutionRepository.FILE_ADD_SUCCESSFUL) { //WebServiceUtil.writeString(response.getOutputStream(), msg, wrapWithSoap); response.sendRedirect("ViewAction?solution=" + solutionName + "&path=" + solutionPath + "&action=" + xactionFilename); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else { // // WebServiceUtil.writeString(response.getOutputStream(), WebServiceUtil.getErrorXml("XAction not created"), false); // } } catch(PentahoSystemException e){ sendXActionError(e.getMessage(), request, response); } } public String generateXAction( final IPentahoSession session, final String title, final String description, final String model, final String jndi, final String jdbc, final String cube ) throws PentahoSystemException { ActionSequenceDocument doc = loadAnalysisViewTemplate(session); doc.setTitle(title); if (session.getName() != null) { doc.setAuthor(session.getName()); } else { doc.setAuthor("Analysis View"); //$NON-NLS-1$ } doc.setDescription(description); PivotViewAction action = (PivotViewAction)doc.getElement("/" + IActionSequenceDocument.ACTION_SEQUENCE + "/" + IActionSequenceDocument.ACTIONS_NAME + "/" + IActionSequenceDocument.ACTION_DEFINITION_NAME + "[component-name='PivotViewComponent']"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ action.setModel(new ActionInputConstant(model, null)); if (jndi != null) { action.setJndi(new ActionInputConstant(jndi, null)); } else { // note, pivot view action does not support jdbc based connections at this time throw new PentahoSystemException(Messages.getInstance().getErrorString("AnalysisViewService.ERROR_0006_JNDI_NULL")); //$NON-NLS-1$ } //TODO: add JDBC datasource support action.setComponentDefinition("cube", cube); //$NON-NLS-1$ return doc.toString(); } /** * on pentaho system startup, load the mondrian.properties file * from system/mondrian/mondrian.properties */ public ActionSequenceDocument loadAnalysisViewTemplate(final IPentahoSession session) throws PentahoSystemException { String analysisViewTemplate = "system" + File.separator + "mondrian" + File.separator + AnalysisViewService.ANALYSIS_VIEW_TEMPLATE; //$NON-NLS-1$ //$NON-NLS-2$ InputStream is = null; try { ISolutionRepository repository = PentahoSystem.get(ISolutionRepository.class, session); if (repository.resourceExists(analysisViewTemplate, ISolutionRepository.ACTION_EXECUTE)) { is = repository.getResourceInputStream(analysisViewTemplate, false, ISolutionRepository.ACTION_EXECUTE); SAXReader reader = new SAXReader(); Document doc = reader.read(is); return new ActionSequenceDocument(doc); } else { throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0009_TEMPLATE_DOES_NOT_EXIST", analysisViewTemplate)); //$NON-NLS-1$ } } catch (DocumentException e) { throw new PentahoSystemException(Messages.getInstance().getString("AnalysisViewService.ERROR_0010_TEMPLATE_DOES_NOT_PARSE", analysisViewTemplate), e); //$NON-NLS-1$ } catch (IOException ioe) { throw new PentahoSystemException(ioe); } finally { try { if (is != null) { is.close(); } } catch (IOException e) { // ignore } } } public String getPayloadAsString(final HttpServletRequest request) throws IOException { InputStream is = request.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream(); String content = null; byte buffer[] = new byte[2048]; int b = is.read(buffer); while (b > 0) { os.write(buffer, 0, b); b = is.read(buffer); } content = os.toString(LocaleHelper.getSystemEncoding()); return content; } private void sendXActionError(final String errorString, final HttpServletRequest request, final HttpServletResponse response) throws IOException{ request.setAttribute("errorMessage", errorString); //$NON-NLS-1$ newAnalysisView(request, response); } }