001: /* This software is published under the terms of the OpenSymphony Software
002: * License version 1.1, of which a copy has been included with this
003: * distribution in the LICENSE.txt file. */
004: package com.opensymphony.module.sitemesh.filter;
005:
006: import com.opensymphony.module.sitemesh.Page;
007: import com.opensymphony.module.sitemesh.PageParserSelector;
008:
009: import javax.servlet.ServletOutputStream;
010: import javax.servlet.http.HttpServletResponse;
011: import javax.servlet.http.HttpServletResponseWrapper;
012: import java.io.IOException;
013: import java.io.PrintWriter;
014:
015: /**
016: * Implementation of HttpServletResponseWrapper that captures page data instead of
017: * sending to the writer.
018: * <p/>
019: * <p>Should be used in filter-chains or when forwarding/including pages
020: * using a RequestDispatcher.</p>
021: *
022: * @author <a href="mailto:joe@truemesh.com">Joe Walnes</a>
023: * @author <a href="mailto:scott@atlassian.com">Scott Farquhar</a>
024: * @version $Revision: 1.17 $
025: */
026: public class PageResponseWrapper extends HttpServletResponseWrapper {
027:
028: private final RoutablePrintWriter routablePrintWriter;
029: private final RoutableServletOutputStream routableServletOutputStream;
030: private final PageParserSelector parserSelector;
031:
032: private Buffer buffer;
033: private boolean aborted = false;
034: private boolean parseablePage = false;
035:
036: public PageResponseWrapper(final HttpServletResponse response,
037: PageParserSelector parserSelector) {
038: super (response);
039: this .parserSelector = parserSelector;
040:
041: routablePrintWriter = new RoutablePrintWriter(
042: new RoutablePrintWriter.DestinationFactory() {
043: public PrintWriter activateDestination()
044: throws IOException {
045: return response.getWriter();
046: }
047: });
048: routableServletOutputStream = new RoutableServletOutputStream(
049: new RoutableServletOutputStream.DestinationFactory() {
050: public ServletOutputStream create()
051: throws IOException {
052: return response.getOutputStream();
053: }
054: });
055:
056: }
057:
058: /**
059: * Set the content-type of the request and store it so it can
060: * be passed to the {@link com.opensymphony.module.sitemesh.PageParser}.
061: */
062: public void setContentType(String type) {
063: super .setContentType(type);
064:
065: if (type != null) {
066: HttpContentType httpContentType = new HttpContentType(type);
067:
068: if (parserSelector.shouldParsePage(httpContentType
069: .getType())) {
070: activateSiteMesh(httpContentType.getType(),
071: httpContentType.getEncoding());
072: } else {
073: deactivateSiteMesh();
074: }
075: }
076:
077: }
078:
079: public void activateSiteMesh(String contentType, String encoding) {
080: if (parseablePage) {
081: return; // already activated
082: }
083: buffer = new Buffer(parserSelector.getPageParser(contentType),
084: encoding);
085: routablePrintWriter
086: .updateDestination(new RoutablePrintWriter.DestinationFactory() {
087: public PrintWriter activateDestination() {
088: return buffer.getWriter();
089: }
090: });
091: routableServletOutputStream
092: .updateDestination(new RoutableServletOutputStream.DestinationFactory() {
093: public ServletOutputStream create() {
094: return buffer.getOutputStream();
095: }
096: });
097: parseablePage = true;
098: }
099:
100: private void deactivateSiteMesh() {
101: parseablePage = false;
102: buffer = null;
103: routablePrintWriter
104: .updateDestination(new RoutablePrintWriter.DestinationFactory() {
105: public PrintWriter activateDestination()
106: throws IOException {
107: return getResponse().getWriter();
108: }
109: });
110: routableServletOutputStream
111: .updateDestination(new RoutableServletOutputStream.DestinationFactory() {
112: public ServletOutputStream create()
113: throws IOException {
114: return getResponse().getOutputStream();
115: }
116: });
117: }
118:
119: /**
120: * Prevent content-length being set if page is parseable.
121: */
122: public void setContentLength(int contentLength) {
123: if (!parseablePage)
124: super .setContentLength(contentLength);
125: }
126:
127: /**
128: * Prevent buffer from being flushed if this is a page being parsed.
129: */
130: public void flushBuffer() throws IOException {
131: if (!parseablePage)
132: super .flushBuffer();
133: }
134:
135: /**
136: * Prevent content-length being set if page is parseable.
137: */
138: public void setHeader(String name, String value) {
139: if (name.toLowerCase().equals("content-type")) { // ensure ContentType is always set through setContentType()
140: setContentType(value);
141: } else if (!parseablePage
142: || !name.toLowerCase().equals("content-length")) {
143: super .setHeader(name, value);
144: }
145: }
146:
147: /**
148: * Prevent content-length being set if page is parseable.
149: */
150: public void addHeader(String name, String value) {
151: if (name.toLowerCase().equals("content-type")) { // ensure ContentType is always set through setContentType()
152: setContentType(value);
153: } else if (!parseablePage
154: || !name.toLowerCase().equals("content-length")) {
155: super .addHeader(name, value);
156: }
157: }
158:
159: /**
160: * If 'not modified' (304) HTTP status is being sent - then abort parsing, as there shouldn't be any body
161: */
162: public void setStatus(int sc) {
163: if (sc == HttpServletResponse.SC_NOT_MODIFIED) {
164: aborted = true;
165: // route any content back to the original writer. There shouldn't be any content, but just to be safe
166: deactivateSiteMesh();
167: }
168: super .setStatus(sc);
169: }
170:
171: public ServletOutputStream getOutputStream() {
172: return routableServletOutputStream;
173: }
174:
175: public PrintWriter getWriter() {
176: return routablePrintWriter;
177: }
178:
179: public Page getPage() throws IOException {
180: if (aborted || !parseablePage) {
181: return null;
182: } else {
183: return buffer.parse();
184: }
185: }
186:
187: public void sendError(int sc) throws IOException {
188: aborted = true;
189: super .sendError(sc);
190: }
191:
192: public void sendError(int sc, String msg) throws IOException {
193: aborted = true;
194: super .sendError(sc, msg);
195: }
196:
197: public void sendRedirect(String location) throws IOException {
198: aborted = true;
199: super .sendRedirect(location);
200: }
201:
202: public boolean isUsingStream() {
203: return buffer != null && buffer.isUsingStream();
204: }
205:
206: public char[] getContents() throws IOException {
207: if (aborted || !parseablePage) {
208: return null;
209: } else {
210: return buffer.getContents();
211: }
212: }
213: }
|