All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.skyway.spring.util.web.binary.ModelAttributeStreamer Maven / Gradle / Ivy

The newest version!
/**
* Copyright 2007 - 2011 Skyway Software, Inc.
*/
package org.skyway.spring.util.web.binary;

import java.io.ByteArrayOutputStream;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.NotReadablePropertyException;
import org.springframework.web.servlet.view.AbstractView;

/**
 * An implementation of AbstractView that loads binary content from any bean property or variable available in the model and streams it back
 * using optional parameters on the url to control the content type encoding and file name.
 * 
 * The additional URL attributes can either be hard names like image/jpeg or they can be paths to other model attributes that may contain the appropriate content type.
 *
 * @author JKennedy
 */
@SuppressWarnings("unchecked")
public class ModelAttributeStreamer extends AbstractView {
		public static final String BINARY = "binary";
		public static final String ACTION = "action";
	
		/**
		 * Default Buffer size for writing binary stream = 4096
		 */
		private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;
		
		/**
		 * Constant: __contenttype
		 */
		public final static String CONTENT_TYPE = "__contenttype";
		
		/**
		 * Constant: __contenttype
		 */
		public final static String FILE_NAME = "__filename";
		
		/**
		 * Constant: __varpath
		 */
		public final static String VAR_PATH = "__varpath";
		
		
		/**
		 * Variable Context used for getting variables from the model using paths
		 */
		private transient VariableContext variableContext;
		
	
		/**
		 * Writes the Binary content to the output stream for the response.  The header variables like content type and file name are "plucked" from the model 
		 * using the paths that are passed on the binary request. 
		 * 
		 */
		@Override
		protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) throws Exception {
			ByteArrayOutputStream baos = new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
			byte[] binaryContent = null;
			ServletOutputStream out = null;
			String contentType;
			String fileName;
			
			createVariableContext(request, model);
			
			try {
				binaryContent = loadBinaryContent(model, request);
				 
				if (binaryContent == null)
					return;
				
				baos.write(binaryContent);
				
				if ((contentType = deriveContentType(request)) != null)
					response.setContentType(contentType);
				
				
				if ((fileName = deriveFileName (request)) != null)
					response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

				
				response.setContentLength(baos.size());

				// Flush byte array to servlet output stream.
				out = response.getOutputStream();
				baos.writeTo(out);
				out.flush();
			}
			finally {
				if (baos != null)
					baos.close();
				if (out != null)
					out.close();
			}
		}
		
		
		/**
		 * Creates a Variable Context that will allow access to variables based on paths that are based on the names of the model objects in the Model map
		 * There is an assumption that the Variable context will not need to be created for more than one top level object
		 * This class does not deal with models where there are potentially multiple top level objects and assumes that the conversation model
		 * is being used which wraps the model data into one object which all other paths are derived from.
		 * @param model
		 * @param request
		 * @return
		 */
		protected void createVariableContext (HttpServletRequest request, Map model) {
			String varPath = (String)request.getParameter(VAR_PATH);
			variableContext = new VariableContext (getRootObject(varPath, model));
		}
		
		/**
		 * 
		 * @return
		 */
		protected VariableContext getVariableContext() {
			return variableContext;
		}
		
		/**
		 * 
		 * @param variablePath
		 * @return
		 */
		protected Object getRootObject (String variablePath, Map map) {
			if (variablePath == null)
				return null;
			
			if (variablePath.indexOf (".") <= 0)
				return map.get(variablePath);
			
			return map.get(variablePath.substring(0, variablePath.indexOf(".")));
		}
		
		/**
		 * 
		 * @param variablePath
		 * @return
		 */
		protected Object getVariable (String path) {
			if (path.indexOf(".") > 0)
				path = path.substring(path.indexOf(".") + 1);
			
			return getVariableContext().get(path);
		}
		
		
		/**
		 *  Returns the binary content represented by the variable on the url parameter of VAR_PATH
		 *  @see VAR_PATH
		 */
		protected byte[] loadBinaryContent(Map model, HttpServletRequest request) {
			String varPath = (String)request.getParameter(VAR_PATH);
			
			return (byte[])getVariable(varPath);
		}
		
		/**
		 * 
		 */
		public String deriveContentType (HttpServletRequest request) {
			String contentType = (String)request.getParameter(CONTENT_TYPE);
			String contentTypeFromVariable = null;
		
			// avoid returning an empty string for content type
			if (contentType == null || contentType.length() <= 0)
				return null;
				
			try {
				contentTypeFromVariable = (String)getVariable(contentType);
			}
			catch (NotReadablePropertyException nrpe){} 
			
			return (contentTypeFromVariable == null) ? contentType : contentTypeFromVariable;
		}
		
		/**
		 * 
		 * @return
		 */
		public String deriveFileName (HttpServletRequest request) {
			String fileName = (String)request.getParameter(FILE_NAME);
			String fileNameFromVariable = null;
		
			// avoid returning an empty string for content type
			if (fileName == null || fileName.length() <= 0)
				return null;
			
			try {
				fileNameFromVariable = (String)getVariable(fileName);
			}
			catch (NotReadablePropertyException nrpe){} 
			
			return (fileNameFromVariable == null) ? fileName : fileNameFromVariable;
		}
		
		/**
		 * Static Utility Method to return a URL which will will resolve to this view with the appropriate parameters
		 * to dynamically load the binary content from the variable path and stream it
		 * 
		 * @param varPath
		 * @param contentType
		 * @param fileName
		 * @return
		 */
		public String getBinaryContentURL(String contextRoot, String varPath, String contentType, String fileName, String controllerName) {
			StringBuilder url = new StringBuilder();
			url.append(contextRoot);
			url.append("/");
			url.append(controllerName);
			url.append("/");
			url.append(BINARY);
			url.append(".");
			url.append(ACTION);
			url.append("?");
			boolean addAmpersand = false;
			
			addAmpersand = appendNonNullURLParameter (url, ModelAttributeStreamer.VAR_PATH, varPath, addAmpersand);
			
			addAmpersand = appendNonNullURLParameter (url, ModelAttributeStreamer.CONTENT_TYPE, contentType, addAmpersand);
				
			appendNonNullURLParameter (url, ModelAttributeStreamer.FILE_NAME, fileName, addAmpersand);
			
			return url.toString();
		}
		
		
		/**
		 * 
		 * @param builder
		 * @param name
		 * @param value
		 */
		private static boolean appendNonNullURLParameter (StringBuilder builder, String name, String value, boolean addAmpersand) {
			if (builder == null || name == null || value == null || value.length() <= 0)
				return false;

			if (addAmpersand)
				builder.append("&");
			
			builder.append(name);
			builder.append("=");
			builder.append(value);
			
			return true;
		}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy