001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.wicket.protocol.http;
018:
019: import java.io.IOException;
020: import java.io.OutputStream;
021: import java.util.Locale;
022:
023: import javax.servlet.http.Cookie;
024: import javax.servlet.http.HttpServletResponse;
025:
026: import org.apache.wicket.Response;
027: import org.apache.wicket.WicketRuntimeException;
028: import org.apache.wicket.util.string.AppendingStringBuffer;
029: import org.apache.wicket.util.string.Strings;
030: import org.apache.wicket.util.time.Time;
031: import org.slf4j.Logger;
032: import org.slf4j.LoggerFactory;
033:
034: /**
035: * Implements responses over the HTTP protocol by holding an underlying
036: * HttpServletResponse object and providing convenience methods for using that
037: * object. Convenience methods include methods which: add a cookie, close the
038: * stream, encode a URL, redirect a request to another resource, determine if a
039: * redirect has been issued, set the content type, set the locale and, most
040: * importantly, write a String to the response output.
041: *
042: * @author Jonathan Locke
043: */
044: public class WebResponse extends Response {
045: /** Log. */
046: private static final Logger log = LoggerFactory
047: .getLogger(WebResponse.class);
048:
049: /** True if response is a redirect. */
050: protected boolean redirect;
051:
052: /** The underlying response object. */
053: private final HttpServletResponse httpServletResponse;
054:
055: /** Is the request an ajax request? */
056: private boolean ajax;
057:
058: /**
059: * Constructor for testing harness.
060: */
061: public WebResponse() {
062: this .httpServletResponse = null;
063: }
064:
065: /**
066: * Package private constructor.
067: *
068: * @param httpServletResponse
069: * The servlet response object
070: */
071: public WebResponse(final HttpServletResponse httpServletResponse) {
072: this .httpServletResponse = httpServletResponse;
073: }
074:
075: /**
076: * Add a cookie to the web response
077: *
078: * @param cookie
079: */
080: public void addCookie(final Cookie cookie) {
081: if (httpServletResponse != null) {
082: httpServletResponse.addCookie(cookie);
083: }
084: }
085:
086: /**
087: * Convenience method for clearing a cookie.
088: *
089: * @param cookie
090: * The cookie to set
091: * @see WebResponse#addCookie(Cookie)
092: */
093: public void clearCookie(final Cookie cookie) {
094: if (httpServletResponse != null) {
095: cookie.setMaxAge(0);
096: cookie.setValue(null);
097: addCookie(cookie);
098: }
099: }
100:
101: /**
102: * Closes response output.
103: */
104: public void close() {
105: // NOTE: Servlet container will close the response output stream
106: // automatically, so we do nothing here.
107: }
108:
109: /**
110: * Returns the given url encoded.
111: *
112: * @param url
113: * The URL to encode
114: * @return The encoded url
115: */
116: public CharSequence encodeURL(CharSequence url) {
117: if (httpServletResponse != null && url != null) {
118: return httpServletResponse.encodeURL(url.toString());
119: }
120: return url;
121: }
122:
123: /**
124: * Gets the wrapped http servlet response object.
125: *
126: * @return The wrapped http servlet response object
127: */
128: public final HttpServletResponse getHttpServletResponse() {
129: return httpServletResponse;
130: }
131:
132: /**
133: * @see org.apache.wicket.Response#getOutputStream()
134: */
135: public OutputStream getOutputStream() {
136: try {
137: return httpServletResponse.getOutputStream();
138: } catch (IOException e) {
139: throw new WicketRuntimeException(
140: "Error while getting output stream.", e);
141: }
142: }
143:
144: /**
145: * Whether this response is going to redirect the user agent.
146: *
147: * @return True if this response is going to redirect the user agent
148: */
149: public final boolean isRedirect() {
150: return redirect;
151: }
152:
153: /**
154: * CLIENTS SHOULD NEVER CALL THIS METHOD FOR DAY TO DAY USE!
155: * <p>
156: * Redirects to the given url. Implementations should encode the URL to make
157: * sure cookie-less operation is supported in case clients forgot.
158: * </p>
159: *
160: * @param url
161: * The URL to redirect to
162: */
163: public void redirect(String url) {
164: if (!redirect) {
165: if (httpServletResponse != null) {
166: // encode to make sure no caller forgot this
167: url = httpServletResponse.encodeRedirectURL(url)
168: .toString();
169: try {
170: if (httpServletResponse.isCommitted()) {
171: log
172: .error("Unable to redirect to: "
173: + url
174: + ", HTTP Response has already been committed.");
175: }
176:
177: if (log.isDebugEnabled()) {
178: log.debug("Redirecting to " + url);
179: }
180:
181: if (isAjax()) {
182: /*
183: * By reaching this point, make sure the HTTP response
184: * status code is set to 200, otherwise wicket-ajax.js
185: * will not process the Ajax-Location header
186: */
187: httpServletResponse.addHeader("Ajax-Location",
188: url);
189:
190: // safari chokes on empty response. but perhaps this is
191: // not the best place?
192: httpServletResponse.getWriter().write(" ");
193: } else {
194: httpServletResponse.sendRedirect(url);
195: }
196: redirect = true;
197: } catch (IOException e) {
198: log.warn("redirect to " + url + " failed: "
199: + e.getMessage());
200: }
201: }
202: } else {
203: log
204: .info("Already redirecting to an url current one ignored: "
205: + url);
206: }
207: }
208:
209: /**
210: * Set the content type on the response.
211: *
212: * @param mimeType
213: * The mime type
214: */
215: public final void setContentType(final String mimeType) {
216: if (httpServletResponse != null) {
217: httpServletResponse.setContentType(mimeType);
218: }
219: }
220:
221: /**
222: * @see org.apache.wicket.Response#setContentLength(long)
223: */
224: public void setContentLength(long length) {
225: if (httpServletResponse != null) {
226: httpServletResponse.setContentLength((int) length);
227: }
228: }
229:
230: /**
231: * @see org.apache.wicket.Response#setLastModifiedTime(org.apache.wicket.util.time.Time)
232: */
233: public void setLastModifiedTime(Time time) {
234: if (httpServletResponse != null) {
235: if (time != null && time.getMilliseconds() != -1) {
236: httpServletResponse.setDateHeader("Last-Modified", time
237: .getMilliseconds());
238: }
239: }
240: }
241:
242: /**
243: * Output stream encoding. If the deployment descriptor contains a
244: * locale-encoding-mapping-list element, and that element provides a mapping
245: * for the given locale, that mapping is used. Otherwise, the mapping from
246: * locale to character encoding is container dependent. Default is
247: * ISO-8859-1.
248: *
249: * @see javax.servlet.ServletResponse#setLocale(java.util.Locale)
250: *
251: * @param locale
252: * The locale use for mapping the character encoding
253: */
254: public final void setLocale(final Locale locale) {
255: if (httpServletResponse != null) {
256: httpServletResponse.setLocale(locale);
257: }
258: }
259:
260: /**
261: * Writes string to response output.
262: *
263: * @param string
264: * The string to write
265: */
266: public void write(final CharSequence string) {
267: if (string instanceof AppendingStringBuffer) {
268: write((AppendingStringBuffer) string);
269: } else if (string instanceof StringBuffer) {
270: try {
271: StringBuffer sb = (StringBuffer) string;
272: char[] array = new char[sb.length()];
273: sb.getChars(0, sb.length(), array, 0);
274: httpServletResponse.getWriter().write(array, 0,
275: array.length);
276: } catch (IOException e) {
277: throw new WicketRuntimeException(
278: "Error while writing to servlet output writer.",
279: e);
280: }
281: } else {
282: try {
283: httpServletResponse.getWriter()
284: .write(string.toString());
285: } catch (IOException e) {
286: throw new WicketRuntimeException(
287: "Error while writing to servlet output writer.",
288: e);
289: }
290: }
291: }
292:
293: /**
294: * Writes AppendingStringBuffer to response output.
295: *
296: * @param asb
297: * The AppendingStringBuffer to write to the stream
298: */
299: public void write(AppendingStringBuffer asb) {
300: try {
301: httpServletResponse.getWriter().write(asb.getValue(), 0,
302: asb.length());
303: } catch (IOException e) {
304: throw new WicketRuntimeException(
305: "Error while writing to servlet output writer.", e);
306: }
307: }
308:
309: /**
310: * Set a header to the date value in the servlet response stream.
311: *
312: * @param header
313: * @param date
314: */
315: public void setDateHeader(String header, long date) {
316: if (httpServletResponse != null) {
317: httpServletResponse.setDateHeader(header, date);
318: }
319: }
320:
321: /**
322: * Set a header to the string value in the servlet response stream.
323: *
324: * @param header
325: * @param value
326: */
327: public void setHeader(String header, String value) {
328: if (httpServletResponse != null) {
329: httpServletResponse.setHeader(header, value);
330: }
331: }
332:
333: /**
334: * Convenience method for setting the content-disposition:attachment header.
335: * This header is used if the response should prompt the user to download it
336: * as a file instead of opening in a browser.
337: *
338: * @param filename
339: * file name of the attachment
340: */
341: public void setAttachmentHeader(String filename) {
342: setHeader("Content-Disposition", "attachment"
343: + ((!Strings.isEmpty(filename)) ? ("; filename=\""
344: + filename + "\"") : ""));
345: }
346:
347: /**
348: * Is the request, which matches this response an ajax request.
349: *
350: * @return True if the request is an ajax request.
351: */
352: public boolean isAjax() {
353: return ajax;
354: }
355:
356: /**
357: * Set that the request which matches this response is an ajax request.
358: *
359: * @param ajax
360: * True if the request is an ajax request.
361: */
362: public void setAjax(boolean ajax) {
363: this.ajax = ajax;
364: }
365: }
|