/* * 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 2007 - 2009 Pentaho Corporation. All rights reserved. * * * @created Jun 29, 2007 * @author wseyler */ package org.pentaho.platform.plugin.action.mondrian; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.Node; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.ISolutionFile; import org.pentaho.platform.api.repository.ISolutionRepository; import org.pentaho.platform.engine.core.solution.ActionInfo; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.PentahoMessenger; import org.pentaho.platform.plugin.action.messages.Messages; /** * Utility class used to save an analysis action sequence from a JPivot view. */ public class AnalysisSaver extends PentahoMessenger { private static final long serialVersionUID = 6290291421129174060L; private static final String ATTRIBUTE_TYPE = "type"; //$NON-NLS-1$ private static final String ATTRIBUTE_STRING = "string"; //$NON-NLS-1$ private static final String TITLE_NODE_NAME = "title"; //$NON-NLS-1$ public static final String SUFFIX = ".xaction"; //$NON-NLS-1$ public static final String PROPERTIES_SUFFIX = ".properties"; //$NON-NLS-1$ private static Log logger = null; /* * (non-Javadoc) * * @see org.pentaho.core.system.PentahoBase#getLogger() */ @Override public Log getLogger() { return AnalysisSaver.logger; } public static int saveAnalysis(final IPentahoSession session, final HashMap props, final String path, String fileName, final boolean overwrite) { if ("true".equals(PentahoSystem.getSystemSetting("kiosk-mode", "false"))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ throw new RuntimeException(Messages.getInstance().getErrorString("ANALYSISSAVER.ERROR_0006_SAVE_IS_DISABLED")); //$NON-NLS-1$ } int result = 0; try { AnalysisSaver.logger = LogFactory.getLog(AnalysisSaver.class); String baseUrl = PentahoSystem.getApplicationContext().getSolutionPath(""); //$NON-NLS-1$ ISolutionRepository solutionRepository = PentahoSystem.get(ISolutionRepository.class, session); // We will (at this point in time) always have an original action sequence to start from... String originalActionReference = (String) props.get("actionreference"); //$NON-NLS-1$ if (originalActionReference == null) { throw new MissingParameterException(Messages.getInstance().getErrorString("ANALYSISSAVER.ERROR_0001_MISSING_ACTION_REFERENCE")); //$NON-NLS-1$ } org.dom4j.Document document = solutionRepository.getResourceAsDocument(originalActionReference, ISolutionRepository.ACTION_UPDATE); // Update the document with the stuff we passed in on the props document = AnalysisSaver.updateDocument(document, props); fileName = fileName.endsWith(AnalysisSaver.SUFFIX) ? fileName : fileName + AnalysisSaver.SUFFIX; result = solutionRepository.publish(baseUrl, path, fileName, document.asXML().getBytes(document.getXMLEncoding()), overwrite); // Now save the resource files ActionInfo actionInfo = ActionInfo.parseActionString(originalActionReference); String originalPath = actionInfo.getSolutionName() + "/" + actionInfo.getPath(); //$NON-NLS-1$ String originalFileName = actionInfo.getActionName(); originalFileName = originalFileName.substring(0, originalFileName.lastIndexOf(AnalysisSaver.SUFFIX)); ISolutionFile[] parentFiles = solutionRepository.getSolutionFile(originalPath, ISolutionRepository.ACTION_EXECUTE).listFiles(); String baseFileName = fileName.substring(0, fileName.lastIndexOf(AnalysisSaver.SUFFIX)); for (ISolutionFile aSolutionFile : parentFiles) { if (!aSolutionFile.isDirectory() && aSolutionFile.getFileName().startsWith(originalFileName) && aSolutionFile.getFileName().toLowerCase().endsWith(AnalysisSaver.PROPERTIES_SUFFIX)) { String newFileName = aSolutionFile.getFileName().replaceFirst(originalFileName, baseFileName); result = result & solutionRepository.publish(baseUrl, path, newFileName, aSolutionFile.getData(), overwrite); } } solutionRepository.resetRepository(); } catch (Exception e) { AnalysisSaver.logger.error(Messages.getInstance().getErrorString("ANALYSISSAVER.ERROR_0000_UNKNOWN"), e); //$NON-NLS-1$ result = ISolutionRepository.FILE_ADD_FAILED; } return result; } /** * @param document * @param props * @return */ private static Document updateDocument(final Document document, final HashMap props) { try { Element componentDefinition = null; Element actionOutput = null; Element actionSequenceOutput = null; Node actionSequence = document.selectSingleNode("/action-sequence"); //$NON-NLS-1$ if (actionSequence == null) { throw new InvalidDocumentException(Messages.getInstance().getErrorString("ANALYSISSAVER.ERROR_0004_INVALID_ORIGIN_DOCUMENT")); //$NON-NLS-1$ } Element asElement = ((Element)actionSequence); Node title = null; String propertyTitle = (String) props.get(AnalysisSaver.TITLE_NODE_NAME); title = asElement.selectSingleNode(AnalysisSaver.TITLE_NODE_NAME); if ( (title == null) && (propertyTitle != null) ) { title = asElement.addElement(AnalysisSaver.TITLE_NODE_NAME); } if ( (title != null) && (propertyTitle != null) ) { // remove existing text if it's there title.setText(""); //$NON-NLS-1$ ((Element)title).addCDATA( propertyTitle ); // adds CDATA } // Next, we need to retrieve the PivotViewComponent action and // process/update it.. there could possibly be more than one // PivotViewComponent in an action sequence, however, we have no idea // how to figure out which one to process, so we default to picking the last one we found. componentDefinition = (Element) document.selectSingleNode("//action-definition[component-name='PivotViewComponent']/component-definition"); //$NON-NLS-1$ if (componentDefinition == null) { throw new InvalidDocumentException(Messages.getInstance().getErrorString("ANALYSISSAVER.ERROR_0005_INVALID_NO_PIVOT_ACTION")); //$NON-NLS-1$ } AnalysisSaver.updateComponent(componentDefinition, props); // Get the action's root action-output node, in case we need to add the // appropriate outputs for the pivot view... actionOutput = (Element) document.selectSingleNode("//action-definition[component-name='PivotViewComponent']/action-outputs"); //$NON-NLS-1$ AnalysisSaver.updateOutput(actionOutput, props); // Get the action's root action sequence output node, in case we need to add the // appropriate outputs for the pivot view... actionSequenceOutput = (Element) document.selectSingleNode("//action-sequence/outputs"); //$NON-NLS-1$ AnalysisSaver.updateOutput(actionSequenceOutput, props); } catch (Exception e) { e.printStackTrace(); } return document; } /** * @param componentDefinition * @param props */ private static void updateComponent(final Element componentDefinition, final HashMap props) { Iterator iter = props.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); Node node = componentDefinition.selectSingleNode(key.toString()); if (node == null) { node = componentDefinition.addElement(key.toString()); } if (PivotViewComponent.OPTIONS.equals(node.getName())) { List optionsList = (List) props.get(key); Iterator optsIter = optionsList.iterator(); while (optsIter.hasNext()) { String anOption = optsIter.next().toString(); Node anOptionNode = node.selectSingleNode(anOption); if (anOptionNode == null) { ((Element) node).addElement(anOption); } } } else { Object value = props.get(key); if (value != null) { // remove existing text node.setText(""); //$NON-NLS-1$ ((Element)node).addCDATA( value.toString() ); } } } // the property "mdx" is no longer being put in the hashmap. So, // query will be passed properly now. } /** * @param outputNode * @param props */ private static void updateOutput(final Element outputNode, final HashMap props) { Iterator iter = props.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); Node node = outputNode.selectSingleNode(key.toString()); if (node == null) { outputNode.addElement(key.toString()).addAttribute(AnalysisSaver.ATTRIBUTE_TYPE, "options".equals(key.toString()) ? "list" : AnalysisSaver.ATTRIBUTE_STRING);//$NON-NLS-1$//$NON-NLS-2$ } } } }