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