View Javadoc

1   /**
2    * This file Copyright (c) 2008-2010 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.cache.filter;
35  
36  import org.apache.commons.collections.MultiMap;
37  import org.apache.commons.collections.map.MultiValueMap;
38  
39  import javax.servlet.ServletOutputStream;
40  import javax.servlet.http.HttpServletResponse;
41  import javax.servlet.http.HttpServletResponseWrapper;
42  import java.io.IOException;
43  import java.io.OutputStreamWriter;
44  import java.io.PrintWriter;
45  import java.text.SimpleDateFormat;
46  import java.text.ParseException;
47  import java.util.Date;
48  import java.util.Collection;
49  import java.util.Locale;
50  
51  /**
52   *
53   * @author 
54   * @version $Revision: 14052 $ ($Author: gjoseph $)
55   */
56  public class CacheResponseWrapper extends HttpServletResponseWrapper {
57      private final ServletOutputStream wrappedStream;
58      private PrintWriter wrappedWriter = null;
59      private final MultiMap headers = new MultiValueMap();
60      private int status = SC_OK;
61      private boolean isError;
62      private String redirectionLocation;
63  
64      public CacheResponseWrapper(final HttpServletResponse response, final ServletOutputStream wrappedStream) {
65          super(response);
66          this.wrappedStream = wrappedStream;
67      }
68  
69      // MAGNOLIA-1996: this can be called multiple times, e.g. by chunk writers, but always from a single thread.
70      public ServletOutputStream getOutputStream() throws IOException {
71          return wrappedStream;
72      }
73  
74      public PrintWriter getWriter() throws IOException {
75          if (wrappedWriter == null) {
76              String encoding = getCharacterEncoding();
77              wrappedWriter = encoding != null
78                      ? new PrintWriter(new OutputStreamWriter(getOutputStream(), encoding))
79                      : new PrintWriter(new OutputStreamWriter(getOutputStream()));
80          }
81  
82          return wrappedWriter;
83      }
84  
85      public void flushBuffer() throws IOException {
86          super.flushBuffer();
87          flush();
88      }
89  
90      public void flush() throws IOException {
91          wrappedStream.flush();
92  
93          if (wrappedWriter != null) {
94              wrappedWriter.flush();
95          }
96      }
97  
98  
99      public void reset() {
100         super.reset();
101 //        if (wrappedStream instanceof ByteArrayOutputStream) {
102 //            ((ByteArrayOutputStream)wrappedStream).reset();
103 //        }
104         wrappedWriter = null;
105         status = SC_OK;
106 
107 //         cookies.clear();
108         headers.clear();
109 //        contentType = null;
110 //        contentLength = 0;
111     }
112 
113 
114     public void resetBuffer() {
115         super.resetBuffer();
116 //        if (wrappedStream != null) {
117 //            ((ByteArrayOutputStream)wrappedStream).reset();
118 //        }
119         wrappedWriter = null;
120     }
121 
122     public int getStatus() {
123         return status;
124     }
125 
126     public boolean isError() {
127         return isError;
128     }
129 
130     public MultiMap getHeaders() {
131         return headers;
132     }
133 
134     public long getLastModified() {
135         // we're using a MultiMap. And all this is to workaround code that would possibly set the Last-Modified header with a String value
136         // it will also fail if multiple values have been set.
137         final Collection values = (Collection) headers.get("Last-Modified");
138         if (values == null || values.size() != 1) {
139             throw new IllegalStateException("Can't get Last-Modified header : no or multiple values : " + values);
140         }
141         final Object value = values.iterator().next();
142         if (value instanceof String) {
143             return parseStringDate((String) value);
144         } else if (value instanceof Long) {
145             return ((Long)value).longValue();
146         } else {
147             throw new IllegalStateException("Can't get Last-Modified header : " + value);
148         }
149     }
150 
151     private long parseStringDate(String value) {
152         try {
153             final SimpleDateFormat f = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);
154             final Date date = f.parse(value);
155             return date.getTime();
156         } catch (ParseException e) {
157             throw new IllegalStateException("Could not parse Last-Modified header with value " + value + " : " + e.getMessage());
158         }
159     }
160 
161     public String getRedirectionLocation() {
162         return redirectionLocation;
163     }
164 
165     public void setDateHeader(String name, long date) {
166         super.setDateHeader(name, date);
167         replaceHeader(name, new Long(date));
168     }
169 
170     public void addDateHeader(String name, long date) {
171         super.addDateHeader(name, date);
172         appendHeader(name, new Long(date));
173     }
174 
175     public void setHeader(String name, String value) {
176         super.setHeader(name, value);
177         replaceHeader(name, value);
178     }
179 
180     public void addHeader(String name, String value) {
181         super.addHeader(name, value);
182         appendHeader(name, value);
183     }
184 
185     public void setIntHeader(String name, int value) {
186         super.setIntHeader(name, value);
187         replaceHeader(name, new Integer(value));
188     }
189 
190     public void addIntHeader(String name, int value) {
191         super.addIntHeader(name, value);
192         appendHeader(name, new Integer(value));
193     }
194 
195     private void replaceHeader(String name, Object value) {
196         headers.remove(name);
197         appendHeader(name, value);
198     }
199 
200     private void appendHeader(String name, Object value) {
201         headers.put(name, value);
202     }
203 
204     public void setStatus(int status) {
205         super.setStatus(status);
206         this.status = status;
207     }
208 
209     public void setStatus(int status, String string) {
210         super.setStatus(status, string);
211         this.status = status;
212     }
213 
214     public void sendRedirect(String location) throws IOException {
215         this.status = SC_MOVED_TEMPORARILY;
216         this.redirectionLocation = location;
217         super.sendRedirect(location);
218     }
219 
220     public void sendError(int status, String string) throws IOException {
221         super.sendError(status, string);
222         this.status = status;
223         this.isError = true;
224     }
225 
226     public void sendError(int status) throws IOException {
227         super.sendError(status);
228         this.status = status;
229         this.isError = true;
230     }
231 }