/*! * 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 (c) 2002-2016 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.web.http.api.resources; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.XMLWriter; import org.pentaho.actionsequence.dom.IActionDefinition; import org.pentaho.platform.api.engine.IActionParameter; import org.pentaho.platform.api.engine.IActionSequence; import org.pentaho.platform.api.engine.IMessageFormatter; import org.pentaho.platform.api.engine.IMimeTypeListener; import org.pentaho.platform.api.engine.IOutputHandler; import org.pentaho.platform.api.engine.IParameterProvider; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.engine.IRuntimeContext; import org.pentaho.platform.api.engine.ISolutionEngine; import org.pentaho.platform.api.engine.ISystemSettings; import org.pentaho.platform.api.engine.ObjectFactoryException; import org.pentaho.platform.api.repository.IContentItem; import org.pentaho.platform.api.repository2.unified.IUnifiedRepository; import org.pentaho.platform.api.repository2.unified.RepositoryFile; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.engine.core.output.FileContentItem; import org.pentaho.platform.engine.core.output.SimpleOutputHandler; import org.pentaho.platform.engine.core.system.PentahoSessionHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.ActionSequenceJCRHelper; import org.pentaho.platform.engine.services.SoapHelper; import org.pentaho.platform.repository2.unified.fileio.RepositoryFileContentItem; import org.pentaho.platform.util.messages.LocaleHelper; import org.pentaho.platform.util.web.SimpleUrlFactory; import org.pentaho.platform.web.http.HttpOutputHandler; import org.pentaho.platform.web.http.messages.Messages; import org.pentaho.platform.web.http.request.HttpRequestParameterProvider; import org.pentaho.platform.web.http.session.HttpSessionParameterProvider; import org.pentaho.platform.web.servlet.HttpMimeTypeListener; import org.pentaho.reporting.libraries.base.util.StringUtils; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.MediaType; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class XactionUtil { private static final Log logger = LogFactory.getLog( XactionUtil.class ); @SuppressWarnings ( "rawtypes" ) public static void createOutputFileName( RepositoryFile file, IOutputHandler outputHandler ) { IPentahoSession userSession = PentahoSessionHolder.getSession(); ActionSequenceJCRHelper actionHelper = new ActionSequenceJCRHelper( userSession ); IActionSequence actionSequence = actionHelper.getActionSequence( file.getPath(), PentahoSystem.loggingLevel, RepositoryFilePermission.READ ); String fileName = "content"; //$NON-NLS-1$ if ( actionSequence != null ) { String title = actionSequence.getTitle(); if ( ( title != null ) && ( title.length() > 0 ) ) { fileName = title; } else { String sequenceName = actionSequence.getSequenceName(); if ( ( sequenceName != null ) && ( sequenceName.length() > 0 ) ) { fileName = sequenceName; } else { List actionDefinitionsList = actionSequence.getActionDefinitionsAndSequences(); int i = 0; boolean done = false; while ( ( actionDefinitionsList.size() > i ) && ( !done ) ) { IActionDefinition actionDefinition = (IActionDefinition) actionDefinitionsList.get( i ); String componentName = actionDefinition.getComponentName(); if ( ( componentName != null ) && ( componentName.length() > 0 ) ) { fileName = componentName; done = true; } else { ++i; } } } } } IMimeTypeListener mimeTypeListener = outputHandler.getMimeTypeListener(); if ( mimeTypeListener != null ) { mimeTypeListener.setName( fileName ); } } public static OutputStream getOutputStream( HttpServletResponse response, boolean doMessages ) throws ServletException, IOException { OutputStream outputStream = null; if ( doMessages ) { outputStream = new ByteArrayOutputStream(); } else { outputStream = response.getOutputStream(); } return outputStream; } public static void setupOutputHandler( HttpOutputHandler outputHandler, IParameterProvider requestParameters ) { int outputPreference = IOutputHandler.OUTPUT_TYPE_DEFAULT; outputHandler.setOutputPreference( outputPreference ); } public static HttpOutputHandler createOutputHandler( HttpServletResponse response, OutputStream outputStream ) { return new HttpOutputHandler( response, outputStream, true ); } public static String postExecute( IRuntimeContext runtime, boolean debugMessages, boolean doWrapper, IOutputHandler outputHandler, Map<String, IParameterProvider> parameterProviders, HttpServletRequest request, HttpServletResponse response, List<?> messages, boolean deleteGeneratedFiles ) throws Exception { StringBuffer buffer = new StringBuffer(); boolean hasResponse = outputHandler.isResponseExpected(); IContentItem responseContentItem = outputHandler.getOutputContentItem( IOutputHandler.RESPONSE, IOutputHandler.CONTENT, null, null ); boolean success = ( runtime != null && runtime.getStatus() == IRuntimeContext.RUNTIME_STATUS_SUCCESS ); boolean printSuccess = ( runtime != null ) && success && ( !hasResponse || debugMessages ); boolean printError = ( runtime != null ) && !success && !response.isCommitted(); if ( printSuccess || printError ) { final String htmlMimeType = "text/html"; //$NON-NLS-1$ responseContentItem.setMimeType( htmlMimeType ); response.setContentType( htmlMimeType ); IMessageFormatter formatter = PentahoSystem.get( IMessageFormatter.class, PentahoSessionHolder.getSession() ); if ( printSuccess ) { formatter.formatSuccessMessage( htmlMimeType, runtime, buffer, debugMessages, doWrapper ); } else { response.resetBuffer(); formatter.formatFailureMessage( htmlMimeType, runtime, buffer, messages ); } } // clear files which was generated during action execution // http://jira.pentaho.com/browse/BISERVER-12639 IUnifiedRepository unifiedRepository = PentahoSystem.get( IUnifiedRepository.class, null ); if ( unifiedRepository != null ) { for ( IContentItem contentItem : runtime.getOutputContentItems() ) { if ( contentItem != null ) { try { contentItem.closeOutputStream(); if ( deleteGeneratedFiles ) { deleteContentItem( contentItem, unifiedRepository ); } } catch ( Exception e ) { logger.warn( Messages.getInstance().getString( "XactionUtil.CANNOT_REMOVE_OUTPUT_FILE", contentItem.getPath() ), e ); } } } } return buffer.toString(); } static void deleteContentItem( IContentItem contentItem, IUnifiedRepository unifiedRepository ) { if ( contentItem instanceof RepositoryFileContentItem ) { String path = contentItem.getPath(); RepositoryFile repositoryFile = unifiedRepository.getFile( path ); //repositoryFile can be null if we have not access or file does not exist if ( repositoryFile != null ) { unifiedRepository.deleteFile( repositoryFile.getId(), true, null ); } } else if ( contentItem instanceof FileContentItem ) { // Files in the file system must not be deleted here String path = ( (FileContentItem) contentItem ).getFile().getName(); logger.warn( Messages.getInstance().getString( "XactionUtil.SKIP_REMOVING_OUTPUT_FILE", path ) ); } } @SuppressWarnings ( { "unchecked", "rawtypes" } ) public static String executeHtml( RepositoryFile file, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, IPentahoSession userSession, IMimeTypeListener mimeTypeListener ) throws Exception { IParameterProvider requestParams = new HttpRequestParameterProvider( httpServletRequest ); IRuntimeContext runtime = null; try { HttpSessionParameterProvider sessionParameters = new HttpSessionParameterProvider( userSession ); HttpRequestParameterProvider requestParameters = new HttpRequestParameterProvider( httpServletRequest ); boolean doMessages = "true".equalsIgnoreCase( requestParams.getStringParameter( "debug", "false" ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean doWrapper = "true".equalsIgnoreCase( requestParams.getStringParameter( "wrapper", "true" ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ IOutputHandler outputHandler = createOutputHandler( httpServletResponse, getOutputStream( httpServletResponse, doMessages ) ); // configure output handler, this is necessary so that the right content // disposition is set on the response header if ( mimeTypeListener == null ) { mimeTypeListener = new HttpMimeTypeListener( httpServletRequest, httpServletResponse, null ); } outputHandler.setMimeTypeListener( mimeTypeListener ); outputHandler.setSession( userSession ); Map parameterProviders = new HashMap(); parameterProviders.put( "request", requestParameters ); //$NON-NLS-1$ parameterProviders.put( "session", sessionParameters ); //$NON-NLS-1$ createOutputFileName( file, outputHandler ); int outputPreference = IOutputHandler.OUTPUT_TYPE_DEFAULT; outputHandler.setOutputPreference( outputPreference ); boolean forcePrompt = "true".equalsIgnoreCase( requestParams.getStringParameter( "prompt", "false" ) ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ List messages = new ArrayList(); runtime = executeInternal( file, requestParams, httpServletRequest, outputHandler, parameterProviders, userSession, forcePrompt, messages ); String str = postExecute( runtime, doMessages, doWrapper, outputHandler, parameterProviders, httpServletRequest, httpServletResponse, messages, true ); return str; } catch ( Exception e ) { logger.error( Messages.getInstance().getString( "XactionUtil.ERROR_EXECUTING_ACTION_SEQUENCE", file.getName() ), e ); //$NON-NLS-1$ throw e; } finally { if ( runtime != null ) { runtime.dispose(); } } } /** * This method executes an xaction with forcePrompt=true and outputPreference=PARAMETERS, allowing for the xaction to * render the secure filter appropriately when being executed in the background or while being scheduled. * * @param file the location of the xaction * @param httpServletRequest the request object * @param httpServletResponse the response object * @param userSession the user session * @return potential response message * @throws Exception */ @SuppressWarnings ( { "unchecked", "rawtypes" } ) public static String executeScheduleUi( RepositoryFile file, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, IPentahoSession userSession, IMimeTypeListener mimeTypeListener ) throws Exception { IParameterProvider requestParams = new HttpRequestParameterProvider( httpServletRequest ); IRuntimeContext runtime = null; try { HttpSessionParameterProvider sessionParameters = new HttpSessionParameterProvider( userSession ); HttpRequestParameterProvider requestParameters = new HttpRequestParameterProvider( httpServletRequest ); boolean doMessages = "true".equalsIgnoreCase( requestParams.getStringParameter( "debug", "false" ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ boolean doWrapper = "true".equalsIgnoreCase( requestParams.getStringParameter( "wrapper", "true" ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ IOutputHandler outputHandler = createOutputHandler( httpServletResponse, getOutputStream( httpServletResponse, doMessages ) ); if ( mimeTypeListener == null ) { mimeTypeListener = new HttpMimeTypeListener( httpServletRequest, httpServletResponse, null ); } outputHandler.setMimeTypeListener( mimeTypeListener ); outputHandler.setSession( userSession ); Map parameterProviders = new HashMap(); parameterProviders.put( "request", requestParameters ); //$NON-NLS-1$ parameterProviders.put( "session", sessionParameters ); //$NON-NLS-1$ createOutputFileName( file, outputHandler ); int outputPreference = IOutputHandler.OUTPUT_TYPE_PARAMETERS; outputHandler.setOutputPreference( outputPreference ); List messages = new ArrayList(); // forcePrompt=true when displaying the scheduling UI runtime = executeInternal( file, requestParams, httpServletRequest, outputHandler, parameterProviders, userSession, true, messages ); String str = postExecute( runtime, doMessages, doWrapper, outputHandler, parameterProviders, httpServletRequest, httpServletResponse, messages, false ); return str; } catch ( Exception e ) { logger.error( Messages.getInstance().getString( "XactionUtil.ERROR_EXECUTING_ACTION_SEQUENCE", file.getName() ), e ); //$NON-NLS-1$ throw e; } finally { if ( runtime != null ) { runtime.dispose(); } } } @SuppressWarnings ( "rawtypes" ) protected static IRuntimeContext executeInternal( RepositoryFile file, IParameterProvider requestParams, HttpServletRequest httpServletRequest, IOutputHandler outputHandler, Map<String, IParameterProvider> parameterProviders, IPentahoSession userSession, boolean forcePrompt, List messages ) throws Exception { String processId = XactionUtil.class.getName(); String instanceId = httpServletRequest.getParameter( "instance-id" ); //$NON-NLS-1$ SimpleUrlFactory urlFactory = new SimpleUrlFactory( "" ); //$NON-NLS-1$ ISolutionEngine solutionEngine = PentahoSystem.get( ISolutionEngine.class, userSession ); ISystemSettings systemSettings = PentahoSystem.getSystemSettings(); if ( solutionEngine == null ) { throw new ObjectFactoryException( "No Solution Engine" ); } boolean instanceEnds = "true".equalsIgnoreCase( requestParams.getStringParameter( "instanceends", "true" ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ String parameterXsl = systemSettings.getSystemSetting( "default-parameter-xsl", "DefaultParameterForm.xsl" ); //$NON-NLS-1$ //$NON-NLS-2$ solutionEngine.setLoggingLevel( 2 ); solutionEngine.init( userSession ); solutionEngine.setForcePrompt( forcePrompt ); if ( parameterXsl != null ) { solutionEngine.setParameterXsl( parameterXsl ); } return solutionEngine.execute( file.getPath(), processId, false, instanceEnds, instanceId, false, parameterProviders, outputHandler, null, urlFactory, messages ); } @SuppressWarnings ( { "unchecked", "rawtypes" } ) public static String executeXml( RepositoryFile file, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, IPentahoSession userSession ) throws Exception { try { HttpSessionParameterProvider sessionParameters = new HttpSessionParameterProvider( userSession ); HttpRequestParameterProvider requestParameters = new HttpRequestParameterProvider( httpServletRequest ); Map parameterProviders = new HashMap(); parameterProviders.put( "request", requestParameters ); //$NON-NLS-1$ parameterProviders.put( "session", sessionParameters ); //$NON-NLS-1$ List messages = new ArrayList(); IParameterProvider requestParams = new HttpRequestParameterProvider( httpServletRequest ); httpServletResponse.setContentType( "text/xml" ); //$NON-NLS-1$ httpServletResponse.setCharacterEncoding( LocaleHelper.getSystemEncoding() ); boolean forcePrompt = "true".equalsIgnoreCase( requestParams.getStringParameter( "prompt", "false" ) ); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ OutputStream contentStream = new ByteArrayOutputStream(); SimpleOutputHandler outputHandler = new SimpleOutputHandler( contentStream, false ); IRuntimeContext runtime = null; try { runtime = executeInternal( file, requestParams, httpServletRequest, outputHandler, parameterProviders, userSession, forcePrompt, messages ); Document responseDoc = SoapHelper.createSoapResponseDocument( runtime, outputHandler, contentStream, messages ); OutputFormat format = OutputFormat.createCompactFormat(); format.setSuppressDeclaration( true ); format.setEncoding( "utf-8" ); //$NON-NLS-1$ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); XMLWriter writer = new XMLWriter( outputStream, format ); writer.write( responseDoc ); writer.flush(); return outputStream.toString( "utf-8" ); //$NON-NLS-1$ } finally { if ( runtime != null ) { runtime.dispose(); } } } catch ( Exception e ) { logger.warn( Messages.getInstance().getString( "XactionUtil.XML_OUTPUT_NOT_SUPPORTED" ) ); //$NON-NLS-1$ throw e; } } public static String execute( String returnContentType, RepositoryFile file, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, IPentahoSession userSession, IMimeTypeListener mimeTypeListener ) throws Exception { if ( ( returnContentType != null ) && ( returnContentType.equals( MediaType.APPLICATION_XML ) ) ) { return executeXml( file, httpServletRequest, httpServletResponse, userSession ); } return executeHtml( file, httpServletRequest, httpServletResponse, userSession, mimeTypeListener ); } @SuppressWarnings ( { "unchecked", "rawtypes" } ) public static String doParameter( final RepositoryFile file, IParameterProvider parameterProvider, final IPentahoSession userSession ) throws IOException { ActionSequenceJCRHelper helper = new ActionSequenceJCRHelper(); final IActionSequence actionSequence = helper.getActionSequence( file.getPath(), PentahoSystem.loggingLevel, RepositoryFilePermission.READ ); final Document document = DocumentHelper.createDocument(); try { final Element parametersElement = document.addElement( "parameters" ); // noinspection unchecked final Map<String, IActionParameter> params = actionSequence.getInputDefinitionsForParameterProvider( IParameterProvider.SCOPE_REQUEST ); for ( final Map.Entry<String, IActionParameter> entry : params.entrySet() ) { final String paramName = entry.getKey(); final IActionParameter paramDef = entry.getValue(); final String value = paramDef.getStringValue(); final Class type; // yes, the actual type-code uses equals-ignore-case and thus allows the user // to specify type information in a random case. sTrInG is equal to STRING is equal to the value // defined as constant (string) if ( IActionParameter.TYPE_LIST.equalsIgnoreCase( paramDef.getType() ) ) { type = String[].class; } else { type = String.class; } final String label = paramDef.getSelectionDisplayName(); final String[] values; if ( StringUtils.isEmpty( value ) ) { values = new String[ 0 ]; } else { values = new String[] { value }; } createParameterElement( parametersElement, paramName, type, label, "user", "parameters", values ); } createParameterElement( parametersElement, "path", String.class, null, "system", "system", new String[] { file.getPath() } ); createParameterElement( parametersElement, "prompt", String.class, null, "system", "system", new String[] { "yes", "no" } ); createParameterElement( parametersElement, "instance-id", String.class, null, "system", "system", new String[] { parameterProvider.getStringParameter( "instance-id", null ) } ); // no close, as far as I know tomcat does not like it that much .. OutputFormat format = OutputFormat.createCompactFormat(); format.setSuppressDeclaration( true ); format.setEncoding( "utf-8" ); //$NON-NLS-1$ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); XMLWriter writer = new XMLWriter( outputStream, format ); writer.write( document ); writer.flush(); return outputStream.toString( "utf-8" ); } catch ( Exception e ) { logger.warn( Messages.getInstance().getString( "HttpWebService.ERROR_0003_UNEXPECTED" ), e ); return null; } } @SuppressWarnings ( "rawtypes" ) private static Element createParameterElement( final Element parametersElement, final String paramName, final Class type, final String label, final String role, final String group, final String[] values ) { final Element parameterElement = parametersElement.addElement( "parameter" ); parameterElement.addAttribute( "name", paramName ); parameterElement.addAttribute( "type", type.getName() ); if ( StringUtils.isEmpty( label ) == false ) { final Element labelAttr = parameterElement.addElement( "attribute" ); labelAttr.addAttribute( "namespace", "http://reporting.pentaho.org/namespaces/engine/parameter-attributes/core" ); labelAttr.addAttribute( "name", "label" ); labelAttr.addAttribute( "value", label ); } final Element roleAttr = parameterElement.addElement( "attribute" ); roleAttr.addAttribute( "namespace", "http://reporting.pentaho.org/namespaces/engine/parameter-attributes/core" ); roleAttr.addAttribute( "name", "role" ); roleAttr.addAttribute( "value", role ); final Element paramGroupAttr = parameterElement.addElement( "attribute" ); paramGroupAttr.addAttribute( "namespace", "http://reporting.pentaho.org/namespaces/engine/parameter-attributes/core" ); paramGroupAttr.addAttribute( "name", "parameter-group" ); paramGroupAttr.addAttribute( "value", group ); final Element paramGroupLabelAttr = parameterElement.addElement( "attribute" ); paramGroupLabelAttr.addAttribute( "namespace", "http://reporting.pentaho.org/namespaces/engine/parameter-attributes/core" ); paramGroupLabelAttr.addAttribute( "name", "parameter-group-label" ); paramGroupLabelAttr.addAttribute( "value", lookupParameterGroupLabel( group ) ); if ( values.length > 0 ) { final Element valuesElement = parameterElement.addElement( "values" ); for ( final String value : values ) { final Element valueAttr = valuesElement.addElement( "value" ); valueAttr.addAttribute( "type", String.class.getName() ); valueAttr.addAttribute( "value", value ); valueAttr.addAttribute( "selected", String.valueOf( values.length == 1 ) ); } } return parameterElement; } private static String lookupParameterGroupLabel( final String group ) { if ( "system".equals( group ) ) { return Messages.getInstance().getString( "HttpWebService.PARAMETER_GROUP_SYSTEM" ); } return Messages.getInstance().getString( "HttpWebService.PARAMETER_GROUP_USER" ); } }