001    /*
002    // $Id: //open/mondrian/src/main/mondrian/tui/MockHttpServletResponse.java#6 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2005-2008 Julian Hyde and others
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    
011    package mondrian.tui;
012    
013    import java.io.PrintWriter;
014    import java.io.ByteArrayOutputStream;
015    import java.io.OutputStreamWriter;
016    import java.io.IOException;
017    import java.util.Locale;
018    import java.util.Collections;
019    import java.util.ArrayList;
020    import java.util.Date;
021    import java.util.List;
022    import java.util.HashMap;
023    import java.util.Map;
024    import java.text.DateFormat;
025    import java.text.SimpleDateFormat;
026    import javax.servlet.ServletOutputStream;
027    import javax.servlet.http.HttpServletResponse;
028    import javax.servlet.http.Cookie;
029    
030    /**
031     * This is a partial implementation of the HttpServletResponse where just
032     * enough is present to allow for communication between Mondrian's
033     * XMLA code and other code in the same JVM.
034     * Currently it is used in both the CmdRunner and in XMLA JUnit tests.
035     * <p>
036     * If you need to add to this implementation, please do so.
037     *
038     * @author <a>Richard M. Emberson</a>
039     * @version $Id: //open/mondrian/src/main/mondrian/tui/MockHttpServletResponse.java#6 $
040     */
041    public class MockHttpServletResponse implements HttpServletResponse {
042    
043        public final static String DATE_FORMAT_HEADER = "EEE, d MMM yyyy HH:mm:ss Z";
044    
045    
046        static class MockServletOutputStream extends ServletOutputStream {
047            private ByteArrayOutputStream buffer;
048            private String encoding;
049    
050            public MockServletOutputStream(int size) {
051                this(size, "ISO-8859-1");
052            }
053    
054            public MockServletOutputStream(int size, String encoding) {
055                buffer = new ByteArrayOutputStream(size);
056                this.encoding = encoding;
057            }
058    
059            public void setEncoding(String encoding) {
060                this.encoding = encoding;
061            }
062    
063            public void write(int value) throws IOException {
064                buffer.write(value);
065            }
066    
067            public String getContent() throws IOException {
068                try {
069                    buffer.flush();
070                    return buffer.toString(encoding);
071                } catch (IOException exc) {
072                    throw exc;
073                }
074            }
075    
076            public byte[] getBinaryContent() throws IOException {
077                try {
078                    buffer.flush();
079                    return buffer.toByteArray();
080                } catch (IOException exc) {
081                    throw exc;
082                }
083            }
084    
085            public void clearContent() {
086                buffer = new ByteArrayOutputStream();
087            }
088        }
089    
090    
091        private PrintWriter writer;
092        private Locale locale;
093        private String charEncoding;
094        private List<Cookie> cookies;
095        private MockServletOutputStream outputStream;
096        private int statusCode;
097        private boolean isCommited;
098        private String errorMsg;
099        private int errorCode;
100        private boolean wasErrorSent;
101        private boolean wasRedirectSent;
102        private int bufferSize;
103        private final Map<String, List<String>> headers;
104    
105        public MockHttpServletResponse() {
106            this.isCommited = false;
107            this.cookies = Collections.emptyList();
108            this.bufferSize = 8192;
109            this.charEncoding = "ISO-8859-1";
110            this.errorCode = SC_OK;
111            this.statusCode = SC_OK;
112            this.headers = new HashMap<String, List<String>>();
113            this.outputStream = new MockServletOutputStream(bufferSize);
114        }
115    
116        /**
117         * Returns the name of the charset used for the MIME body sent in this
118         * response.
119         *
120         */
121        public String getCharacterEncoding() {
122            return charEncoding;
123        }
124    
125        /**
126         * Returns a ServletOutputStream suitable for writing binary data in the
127         * response.
128         *
129         * @throws IOException
130         */
131        public ServletOutputStream getOutputStream() throws IOException {
132            return outputStream;
133        }
134    
135        /**
136         * Returns a PrintWriter object that can send character text to the client.
137         *
138         * @throws IOException
139         */
140        public PrintWriter getWriter() throws IOException {
141            if (writer == null) {
142                writer = new PrintWriter(new OutputStreamWriter(
143                                    outputStream, charEncoding), true);
144            }
145    
146            return writer;
147        }
148    
149        public void setCharacterEncoding(String charEncoding) {
150            this.charEncoding = charEncoding;
151            this.outputStream.setEncoding(charEncoding);
152        }
153    
154        /**
155         * Sets the length of the content body in the response In HTTP servlets,
156         * this method sets the HTTP Content-Length header.
157         *
158         */
159        public void setContentLength(int len) {
160            setIntHeader("Content-Length", len);
161        }
162    
163        /**
164         * Sets the content type of the response being sent to the client.
165         *
166         */
167        public void setContentType(String contentType) {
168            setHeader("Content-Type", contentType);
169        }
170    
171        /**
172         * Sets the preferred buffer size for the body of the response.
173         *
174         */
175        public void setBufferSize(int size) {
176            this.bufferSize = size;
177        }
178    
179        /**
180         * Returns the actual buffer size used for the response.
181         *
182         */
183        public int getBufferSize() {
184            return this.bufferSize;
185        }
186    
187        /**
188         * Forces any content in the buffer to be written to the client.
189         *
190         * @throws IOException
191         */
192        public void flushBuffer() throws IOException {
193            if (writer != null) {
194                writer.flush();
195            }
196            outputStream.flush();
197        }
198    
199        public void resetBuffer() {
200            outputStream.clearContent();
201        }
202    
203        /**
204         * Returns a boolean indicating if the response has been committed.
205         *
206         */
207        public boolean isCommitted() {
208            return isCommited;
209        }
210    
211        /**
212         * Clears any data that exists in the buffer as well as the status code and
213         * headers.
214         */
215        public void reset() {
216            headers.clear();
217            resetBuffer();
218        }
219    
220        /**
221         *  Sets the locale of the response, setting the headers (including the
222         *  Content-Type's charset) as appropriate.
223         *
224         */
225        public void setLocale(Locale locale) {
226            this.locale = locale;
227        }
228    
229        /**
230         * Returns the locale assigned to the response.
231         *
232         */
233        public Locale getLocale() {
234            return locale;
235        }
236    
237        /**
238         * Adds the specified cookie to the response.
239         *
240         */
241        public void addCookie(Cookie cookie) {
242            if (cookies.isEmpty()) {
243                cookies = new ArrayList<Cookie>();
244            }
245            cookies.add(cookie);
246        }
247    
248        /**
249         * Returns a boolean indicating whether the named response header has
250         * already been set.
251         *
252         */
253        public boolean containsHeader(String name) {
254            return headers.containsKey(name);
255        }
256    
257        /**
258         * Encodes the specified URL by including the session ID in it, or, if
259         * encoding is not needed, returns the URL unchanged.
260         *
261         */
262        public String encodeURL(String url) {
263            return encode(url);
264        }
265    
266        /**
267         * Encodes the specified URL for use in the sendRedirect method or, if
268         * encoding is not needed, returns the URL unchanged.
269         *
270         */
271        public String encodeRedirectURL(String url) {
272            return encode(url);
273        }
274    
275        /**
276         * @deprecated Method encodeUrl is deprecated
277         */
278    
279        public String encodeUrl(String s) {
280            return encodeURL(s);
281        }
282    
283        /**
284         * @deprecated Method encodeRedirectUrl is deprecated
285         */
286    
287        public String encodeRedirectUrl(String s) {
288            return encodeRedirectURL(s);
289        }
290    
291        /**
292         *  Sends an error response to the client using the specified status code
293         *  and descriptive message.
294         *
295         */
296        public void sendError(int code, String msg) throws IOException {
297            this.errorCode = code;
298            this.wasErrorSent = true;
299            this.errorMsg = msg;
300        }
301    
302        /**
303         * Sends an error response to the client using the specified status.
304         *
305         */
306        public void sendError(int code) throws IOException {
307            this.errorCode = code;
308            this.wasErrorSent = true;
309        }
310    
311        /**
312         * Sends a temporary redirect response to the client using the specified
313         * redirect location URL.
314         *
315         */
316        public void sendRedirect(String location) throws IOException {
317            setHeader("Location", location);
318            wasRedirectSent = true;
319        }
320    
321        /**
322         * Sets a response header with the given name and date-value.
323         *
324         */
325        public void setDateHeader(String name, long date) {
326            Date dateValue = new Date(date);
327            String dateString = DateFormat.getDateInstance().format(dateValue);
328            setHeader(name, dateString);
329        }
330    
331        /**
332         * Adds a response header with the given name and date-value.
333         *
334         */
335        public void addDateHeader(String name, long date) {
336            Date dateValue = new Date(date);
337            String dateString = new SimpleDateFormat(DATE_FORMAT_HEADER, Locale.US).format(dateValue);
338            addHeader(name, dateString);
339        }
340    
341        /**
342         * Sets a response header with the given name and value.
343         *
344         */
345        public void setHeader(String name, String value) {
346            List<String> valueList = headers.get(name);
347            if (valueList == null) {
348                valueList = new ArrayList<String>();
349                headers.put(name, valueList);
350            }
351            valueList.add(value);
352    
353        }
354    
355        /**
356         * Adds a response header with the given name and value.
357         *
358         */
359        public void addHeader(String name, String value) {
360            List<String> valueList = headers.get(name);
361            if (null == valueList) {
362                valueList = new ArrayList<String>();
363                headers.put(name, valueList);
364            }
365            valueList.add(value);
366        }
367    
368        /**
369         *  Sets a response header with the given name and integer value.
370         *
371         */
372        public void setIntHeader(String name, int value) {
373            String stringValue = Integer.toString(value);
374            addHeader(name, stringValue);
375        }
376    
377        /**
378         * Adds a response header with the given name and integer value.
379         *
380         */
381        public void addIntHeader(String name, int value) {
382            String stringValue = Integer.toString(value);
383            addHeader(name, stringValue);
384        }
385    
386        /**
387         *  Sets the status code for this response.
388         *
389         */
390        public void setStatus(int status) {
391            this.statusCode = status;
392        }
393    
394        /**
395         * @deprecated Method setStatus is deprecated
396         * Deprecated. As of version 2.1, due to ambiguous meaning of the message
397         * parameter. To set a status code use setStatus(int), to send an error with
398         * a description use sendError(int, String). Sets the status code and
399         * message for this response.
400         */
401        public void setStatus(int status, String s) {
402            setStatus(status);
403        }
404    
405        /////////////////////////////////////////////////////////////////////////
406        //
407        // implementation access
408        //
409        /////////////////////////////////////////////////////////////////////////
410        public byte[] toByteArray() throws IOException {
411            return outputStream.getBinaryContent();
412        }
413    
414        public String getHeader(String name) {
415            List<String> list = getHeaderList(name);
416    
417            return ((list == null) || (list.size() == 0))
418                ? null
419                : list.get(0);
420    
421        }
422    
423        public String getContentType() {
424            return getHeader("Content-Type");
425        }
426    
427    
428        /////////////////////////////////////////////////////////////////////////
429        //
430        // helpers
431        //
432        /////////////////////////////////////////////////////////////////////////
433        public List<String> getHeaderList(String name) {
434            return headers.get(name);
435        }
436    
437        public int getStatusCode() {
438            return statusCode;
439        }
440    
441        public int getErrorCode() {
442            return errorCode;
443        }
444    
445        public List getCookies() {
446            return cookies;
447        }
448    
449        public boolean wasErrorSent() {
450            return wasErrorSent;
451        }
452    
453        public boolean wasRedirectSent() {
454            return wasRedirectSent;
455        }
456    
457    /*
458        protected void clearHeaders() {
459            this.headers.clear();
460        }
461    */
462    
463        protected String encode(String s) {
464            // TODO
465            return s;
466        }
467    
468    
469    }
470    
471    // End MockHttpServletResponse.java