001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005: package com.opensymphony.webwork.dispatcher;
006:
007: import com.opensymphony.xwork.ActionInvocation;
008:
009: import javax.servlet.http.HttpServletResponse;
010: import java.io.InputStream;
011: import java.io.OutputStream;
012:
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015:
016: /**
017: * <!-- START SNIPPET: description -->
018: *
019: * A custom Result type for send raw data (via an InputStream) directly to the
020: * HttpServletResponse. Very useful for allowing users to download content.
021: *
022: * <!-- END SNIPPET: description -->
023: * <p/>
024: * <b>This result type takes the following parameters:</b>
025: *
026: * <!-- START SNIPPET: params -->
027: *
028: * <ul>
029: *
030: * <li><b>contentType</b> - the stream mime-type as sent to the web browser
031: * (default = <code>text/plain</code>).</li>
032: *
033: * <li><b>contentLength</b> - the stream length in bytes (the browser displays a
034: * progress bar).</li>
035: *
036: * <li><b>contentDispostion</b> - the content disposition header value for
037: * specifing the file name (default = <code>inline</code>, values are typically
038: * <i>filename="document.pdf"</i>.</li>
039: *
040: * <li><b>inputName</b> - the name of the InputStream property from the chained
041: * action (default = <code>inputStream</code>).</li>
042: *
043: * <li><b>bufferSize</b> - the size of the buffer to copy from input to output
044: * (default = <code>1024</code>).</li>
045: *
046: * </ul>
047: *
048: * <!-- END SNIPPET: params -->
049: *
050: * <b>Example:</b>
051: *
052: * <pre><!-- START SNIPPET: example -->
053: * <result name="success" type="stream">
054: * <param name="contentType">image/jpeg</param>
055: * <param name="inputName">imageStream</param>
056: * <param name="contentDisposition">filename="document.pdf"</param>
057: * <param name="bufferSize">1024</param>
058: * </result>
059: * <!-- END SNIPPET: example --></pre>
060: *
061: * @author mcrawford
062: * @author Rainer Hermanns
063: * @author tm_jee
064: */
065: public class StreamResult extends WebWorkResultSupport {
066:
067: private static final long serialVersionUID = -6039988588327688623L;
068:
069: protected static final Log log = LogFactory
070: .getLog(StreamResult.class);
071:
072: protected String contentType = "text/plain";
073: protected String contentLength;
074: protected String contentDisposition = "inline";
075: protected String inputName = "inputStream";
076: protected int bufferSize = 1024;
077:
078: /**
079: * @return Returns the bufferSize.
080: */
081: public int getBufferSize() {
082: return (bufferSize);
083: }
084:
085: /**
086: * @param bufferSize The bufferSize to set.
087: */
088: public void setBufferSize(int bufferSize) {
089: this .bufferSize = bufferSize;
090: }
091:
092: /**
093: * @return Returns the contentType.
094: */
095: public String getContentType() {
096: return (contentType);
097: }
098:
099: /**
100: * @param contentType The contentType to set.
101: */
102: public void setContentType(String contentType) {
103: this .contentType = contentType;
104: }
105:
106: /**
107: * @return Returns the contentLength.
108: */
109: public String getContentLength() {
110: return contentLength;
111: }
112:
113: /**
114: * @param contentLength The contentLength to set.
115: */
116: public void setContentLength(String contentLength) {
117: this .contentLength = contentLength;
118: }
119:
120: /**
121: * @return Returns the Content-disposition header value.
122: */
123: public String getContentDisposition() {
124: return contentDisposition;
125: }
126:
127: /**
128: * @param contentDisposition the Content-disposition header value to use.
129: */
130: public void setContentDisposition(String contentDisposition) {
131: this .contentDisposition = contentDisposition;
132: }
133:
134: /**
135: * @return Returns the inputName.
136: */
137: public String getInputName() {
138: return (inputName);
139: }
140:
141: /**
142: * @param inputName The inputName to set.
143: */
144: public void setInputName(String inputName) {
145: this .inputName = inputName;
146: }
147:
148: protected void doExecute(String finalLocation,
149: ActionInvocation invocation) throws Exception {
150:
151: InputStream oInput = null;
152: OutputStream oOutput = null;
153:
154: try {
155: // Find the inputstream from the invocation variable stack
156: oInput = (InputStream) invocation.getStack().findValue(
157: conditionalParse(inputName, invocation));
158:
159: if (oInput == null) {
160: String msg = ("Can not find a java.io.InputStream with the name ["
161: + inputName + "] in the invocation stack. " + "Check the <param name=\"inputName\"> tag specified for this action.");
162: log.error(msg);
163: throw new IllegalArgumentException(msg);
164: }
165:
166: // Find the Response in context
167: HttpServletResponse oResponse = (HttpServletResponse) invocation
168: .getInvocationContext().get(HTTP_RESPONSE);
169:
170: // Set the content type
171: oResponse.setContentType(conditionalParse(contentType,
172: invocation));
173:
174: // Set the content length
175: if (contentLength != null) {
176: String _contentLength = conditionalParse(contentLength,
177: invocation);
178: int _contentLengthAsInt = -1;
179: try {
180: _contentLengthAsInt = Integer
181: .parseInt(_contentLength);
182: if (_contentLengthAsInt >= 0) {
183: oResponse.setContentLength(_contentLengthAsInt);
184: }
185: } catch (NumberFormatException e) {
186: log
187: .warn(
188: "failed to recongnize "
189: + _contentLength
190: + " as a number, contentLength header will not be set",
191: e);
192: }
193: }
194:
195: // Set the content-disposition
196: if (contentDisposition != null) {
197: oResponse
198: .addHeader("Content-disposition",
199: conditionalParse(contentDisposition,
200: invocation));
201: }
202:
203: // Get the outputstream
204: oOutput = oResponse.getOutputStream();
205:
206: if (log.isDebugEnabled()) {
207: log.debug("Streaming result [" + inputName + "] type=["
208: + contentType + "] length=[" + contentLength
209: + "] content-disposition=["
210: + contentDisposition + "]");
211: }
212:
213: // Copy input to output
214: log.debug("Streaming to output buffer +++ START +++");
215: byte[] oBuff = new byte[bufferSize];
216: int iSize;
217: while (-1 != (iSize = oInput.read(oBuff))) {
218: oOutput.write(oBuff, 0, iSize);
219: }
220: log.debug("Streaming to output buffer +++ END +++");
221:
222: // Flush
223: oOutput.flush();
224: } finally {
225: if (oInput != null)
226: oInput.close();
227: if (oOutput != null)
228: oOutput.close();
229: }
230: }
231:
232: }
|