/*! * 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-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.web.http; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.platform.api.engine.IContentOutputHandler; import org.pentaho.platform.api.engine.IMimeTypeListener; import org.pentaho.platform.api.engine.IOutputHandler; import org.pentaho.platform.api.engine.IPentahoSession; import org.pentaho.platform.api.repository.IContentItem; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.web.http.messages.Messages; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Iterator; import java.util.Map; public class HttpOutputHandler implements IOutputHandler, IMimeTypeListener { private static final Log logger = LogFactory.getLog( HttpOutputHandler.class ); private HttpServletResponse response; protected IContentItem outputContent; private IContentItem feedbackContent; boolean allowFeedback; @Deprecated private boolean contentGenerated; private IPentahoSession session; private IMimeTypeListener mimeTypeListener; private int outputType = IOutputHandler.OUTPUT_TYPE_DEFAULT; private OutputStream destinationOutputStream; private boolean responseExpected; public HttpOutputHandler( final HttpServletResponse response, final OutputStream outputStream, final boolean allowFeedback ) { this.response = response; destinationOutputStream = outputStream; outputContent = new HttpContentItem( destinationOutputStream, this ); ( (HttpContentItem) outputContent ).setMimeTypeListener( this ); feedbackContent = new HttpContentItem( destinationOutputStream, this ); this.allowFeedback = allowFeedback; contentGenerated = false; } public void setSession( final IPentahoSession session ) { this.session = session; } public IPentahoSession getSession() { return session; } public void setOutputPreference( final int outputType ) { this.outputType = outputType; } public int getOutputPreference() { return outputType; } public void setMimeType( final String mimeType ) { if ( mimeTypeListener != null ) { mimeTypeListener.setMimeType( mimeType ); } response.setContentType( mimeType ); } public void setName( String name ) { if ( mimeTypeListener != null ) { mimeTypeListener.setName( name ); } } @Deprecated public boolean contentDone() { return contentGenerated; } public boolean allowFeedback() { return allowFeedback; } /* * (non-Javadoc) * * @see org.pentaho.core.solution.IOutputHandler#getFeedbackStream() */ public IContentItem getFeedbackContentItem() { if ( allowFeedback ) { // assume that content is generated becuase of this contentGenerated = true; /* * if someone is requesting a feedbackContentItem, we can assume they tend to write feedback back to the client, * so we set the flag here */ responseExpected = true; return feedbackContent; } return null; } /* * (non-Javadoc) * * @see org.pentaho.core.solution.IOutputHandler#getContentStream() */ public IContentItem getOutputContentItem( final String objectName, final String contentName, final String instanceId, final String mimeType ) { if ( objectName.equals( IOutputHandler.RESPONSE ) && contentName.equals( IOutputHandler.CONTENT ) ) { // assume that content is generated becuase of this // change the content type if necessary if ( mimeType != null ) { outputContent.setMimeType( mimeType ); } contentGenerated = true; /* * if someone is requesting an outputContentItem, they are intending to write to the response output stream. */ responseExpected = true; return outputContent; } else { IContentOutputHandler output = null; // this code allows us to stay backwards compatible if ( ( contentName != null ) && ( contentName.indexOf( ":" ) == -1 ) ) { //$NON-NLS-1$ output = PentahoSystem.getOutputDestinationFromContentRef( objectName + ":" + contentName, session ); //$NON-NLS-1$ } else { output = PentahoSystem.getOutputDestinationFromContentRef( contentName, session ); if ( output == null ) { output = PentahoSystem.getOutputDestinationFromContentRef( objectName + ":" + contentName, session ); //$NON-NLS-1$ } } if ( output != null ) { output.setInstanceId( instanceId ); String filePath = "~/workspace/" + contentName; //$NON-NLS-1$ if ( contentName.startsWith( "/" ) ) { filePath = contentName; } output.setSolutionPath( filePath ); output.setMimeType( mimeType ); output.setSession( session ); return output.getFileOutputContentItem(); } } return null; } public void setOutput( final String name, final Object value ) throws IOException { if ( value == null ) { HttpOutputHandler.logger.warn( Messages.getInstance().getString( "HttpOutputHandler.WARN_0001_VALUE_IS_NULL" ) ); //$NON-NLS-1$ return; } /* * if setOutput is called, it means someone intends to write some data to the response stream, which they may not * have gotten a handle to earlier through getOutputContentItem*. So we need to set the flag here. */ responseExpected = true; if ( "redirect".equalsIgnoreCase( name ) ) { //$NON-NLS-1$ try { response.sendRedirect( value.toString() ); } catch ( IOException ioe ) { HttpOutputHandler.logger.error( Messages.getInstance().getString( "HttpOutputHandler.ERROR_0001_REDIRECT_FAILED", value.toString() ), ioe ); //$NON-NLS-1$ } } else if ( "header".equalsIgnoreCase( name ) ) { //$NON-NLS-1$ try { if ( value instanceof Map ) { for ( Iterator it = ( (Map) value ).entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = (Map.Entry) it.next(); response.addHeader( entry.getKey().toString(), entry.getValue().toString() ); } } else { String strVal = value.toString(); int i = strVal.indexOf( '=' ); String headerName = strVal.substring( 0, i ); String headerValue = strVal.substring( i + 1 ); response.addHeader( headerName, headerValue ); } } catch ( IndexOutOfBoundsException e ) { HttpOutputHandler.logger.error( e.getLocalizedMessage() ); } } else if ( IOutputHandler.CONTENT.equalsIgnoreCase( name ) ) { if ( value instanceof IContentItem ) { IContentItem content = (IContentItem) value; // See if we should process the input stream. If it's from // the content repository, then there's an input stream. // SimpleContentItem and HttpContentItem both return null from // getInputStream(). InputStream inStr = content.getInputStream(); if ( inStr != null ) { if ( ( response.getContentType() == null ) || ( !response.getContentType().equalsIgnoreCase( content.getMimeType() ) ) ) { // response.setContentType( content.getMimeType() ); setMimeType( content.getMimeType() ); } try { // Bad idea to write to the output stream directly. This prevents us from // auditing writes. Use the managed destinationOutputStream instead. // OutputStream outStr = response.getOutputStream(); int inCnt = 0; byte[] buf = new byte[4096]; while ( -1 != ( inCnt = inStr.read( buf ) ) ) { destinationOutputStream.write( buf, 0, inCnt ); } } finally { try { inStr.close(); } catch ( IOException ignored ) { //ignored } } contentGenerated = true; } } else { if ( response.getContentType() == null ) { setMimeType( "text/html" ); //$NON-NLS-1$ } destinationOutputStream.write( value.toString().getBytes() ); contentGenerated = true; } } } public IMimeTypeListener getMimeTypeListener() { return mimeTypeListener; } public void setMimeTypeListener( final IMimeTypeListener mimeTypeListener ) { this.mimeTypeListener = mimeTypeListener; } public HttpServletResponse getResponse() { return response; } public void setResponse( final HttpServletResponse response ) { this.response = response; } public boolean isResponseExpected() { return responseExpected; } public boolean isWritable() { return false; } }