org.apache.wicket.protocol.http.mock.MockHttpServletResponse.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.protocol.http.mock.MockHttpServletResponse.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.protocol.http.mock;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;

import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.protocol.http.IMetaDataBufferingWebResponse;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.util.value.ValueMap;

/**
 * Mock servlet response. Implements all of the methods from the standard HttpServletResponse class
 * plus helper methods to aid viewing the generated response.
 * 
 * @author Chris Turner
 */
public class MockHttpServletResponse implements HttpServletResponse, IMetaDataBufferingWebResponse {
    private static final int MODE_BINARY = 1;

    private static final int MODE_NONE = 0;

    private static final int MODE_TEXT = 2;

    private ByteArrayOutputStream byteStream;

    private String characterEncoding = "UTF-8";

    private final List<Cookie> cookies = new ArrayList<Cookie>();

    private String errorMessage = null;

    private final ValueMap headers = new ValueMap();

    private Locale locale = null;

    private int mode = MODE_NONE;

    private PrintWriter printWriter;

    private String redirectLocation = null;

    private ServletOutputStream servletStream;

    private int status = HttpServletResponse.SC_OK;

    private StringWriter stringWriter;

    private final MockHttpServletRequest servletRequest;

    /**
     * Create the response object.
     * 
     * @param servletRequest
     */
    public MockHttpServletResponse(MockHttpServletRequest servletRequest) {
        this.servletRequest = servletRequest;
        initialize();
    }

    /**
     * Add a cookie to the response.
     * 
     * @param cookie
     *            The cookie to add
     */
    @Override
    public void addCookie(final Cookie cookie) {
        // remove any potential duplicates
        // see http://www.ietf.org/rfc/rfc2109.txt, p.4.3.3
        Iterator<Cookie> iterator = cookies.iterator();
        while (iterator.hasNext()) {
            Cookie old = iterator.next();
            if (Cookies.isEqual(cookie, old)) {
                iterator.remove();
            }
        }
        cookies.add(cookie);
    }

    /**
     * Add a date header.
     * 
     * @param name
     *            The header value
     * @param l
     *            The long value
     */
    @Override
    public void addDateHeader(String name, long l) {
        DateFormat df = DateFormat.getDateInstance(DateFormat.FULL);
        addHeader(name, df.format(new Date(l)));
    }

    /**
     * Add the given header value, including an additional entry if one already exists.
     * 
     * @param name
     *            The name for the header
     * @param value
     *            The value for the header
     */
    @Override
    @SuppressWarnings("unchecked")
    public void addHeader(final String name, final String value) {
        List<String> list = (List<String>) headers.get(name);
        if (list == null) {
            list = new ArrayList<String>(1);
            headers.put(name, list);
        }
        list.add(value);
    }

    /**
     * Add an int header value.
     * 
     * @param name
     *            The header name
     * @param i
     *            The value
     */
    @Override
    public void addIntHeader(final String name, final int i) {
        addHeader(name, "" + i);
    }

    /**
     * Check if the response contains the given header name.
     * 
     * @param name
     *            The name to check
     * @return Whether header in response or not
     */
    @Override
    public boolean containsHeader(final String name) {
        return headers.containsKey(name);
    }

    /**
     * Encode the redirectLocation URL. Does no changes as this test implementation uses cookie
     * based url tracking.
     * 
     * @param url
     *            The url to encode
     * @return The encoded url
     */
    @Override
    public String encodeRedirectUrl(final String url) {
        return url;
    }

    /**
     * Encode the redirectLocation URL. Does no changes as this test implementation uses cookie
     * based url tracking.
     * 
     * @param url
     *            The url to encode
     * @return The encoded url
     */
    @Override
    public String encodeRedirectURL(final String url) {
        return url;
    }

    /**
     * Encode the URL. Does no changes as this test implementation uses cookie based url tracking.
     * 
     * @param url
     *            The url to encode
     * @return The encoded url
     */
    @Override
    public String encodeUrl(final String url) {
        return url;
    }

    /**
     * Encode the URL. Does no changes as this test implementation uses cookie based url tracking.
     * 
     * @param url
     *            The url to encode
     * @return The encoded url
     */
    @Override
    public String encodeURL(final String url) {
        return url;
    }

    /**
     * Flush the buffer.
     * 
     * @throws IOException
     */
    @Override
    public void flushBuffer() throws IOException {
    }

    /**
     * Get the binary content that was written to the servlet stream.
     * 
     * @return The binary content
     */
    public byte[] getBinaryContent() {
        return byteStream.toByteArray();
    }

    /**
     * Return the current buffer size
     * 
     * @return The buffer size
     */
    @Override
    public int getBufferSize() {
        if (mode == MODE_NONE) {
            return 0;
        } else if (mode == MODE_BINARY) {
            return byteStream.size();
        } else {
            return stringWriter.getBuffer().length();
        }
    }

    /**
     * Get the character encoding of the response.
     * 
     * @return The character encoding
     */
    @Override
    public String getCharacterEncoding() {
        return characterEncoding;
    }

    /**
     * Get all of the cookies that have been added to the response.
     * 
     * @return The collection of cookies
     */
    public List<Cookie> getCookies() {
        List<Cookie> copies = new ArrayList<Cookie>();
        for (Cookie cookie : cookies) {
            copies.add(Cookies.copyOf(cookie));
        }
        return copies;
    }

    /**
     * Get the text document that was written as part of this response.
     * 
     * @return The document
     */
    public String getDocument() {
        if (mode == MODE_BINARY) {
            return new String(byteStream.toByteArray());
        } else {
            return stringWriter.getBuffer().toString();
        }
    }

    /**
     * Get the error message.
     * 
     * @return The error message, or null if no message
     */
    public String getErrorMessage() {
        return errorMessage;
    }

    /**
     * Return the value of the given named header.
     * 
     * @param name
     *            The header name
     * @return The value, or null
     */
    @SuppressWarnings("unchecked")
    public String getHeader(final String name) {
        List<String> l = (List<String>) headers.get(name);
        if (l == null || l.size() < 1) {
            return null;
        } else {
            return l.get(0);
        }
    }

    /**
     * Get the names of all of the headers.
     * 
     * @return The header names
     */
    public Set<String> getHeaderNames() {
        return headers.keySet();
    }

    /**
     * Get the encoded locale
     * 
     * @return The locale
     */
    @Override
    public Locale getLocale() {
        return locale;
    }

    /**
     * Get the output stream for writing binary data from the servlet.
     * 
     * @return The binary output stream.
     */
    @Override
    public ServletOutputStream getOutputStream() {
        if (mode == MODE_TEXT) {
            throw new IllegalArgumentException("Can't write binary after already selecting text");
        }
        mode = MODE_BINARY;
        return servletStream;
    }

    /**
     * Get the location that was redirected to.
     * 
     * @return The redirect location, or null if not a redirect
     */
    public String getRedirectLocation() {
        return redirectLocation;
    }

    /**
     * Get the status code.
     * 
     * @return The status code
     */
    public int getStatus() {
        return status;
    }

    /**
     * Get the print writer for writing text output for this response.
     * 
     * @return The writer
     * @throws IOException
     *             Not used
     */
    @Override
    public PrintWriter getWriter() throws IOException {
        if (mode == MODE_BINARY) {
            throw new IllegalArgumentException("Can't write text after already selecting binary");
        }
        mode = MODE_TEXT;
        return printWriter;
    }

    /**
     * Reset the response ready for reuse.
     */
    public void initialize() {
        cookies.clear();
        headers.clear();
        errorMessage = null;
        redirectLocation = null;
        status = HttpServletResponse.SC_OK;
        characterEncoding = "UTF-8";
        locale = null;

        byteStream = new ByteArrayOutputStream();
        servletStream = new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setWriteListener(WriteListener writeListener) {
            }

            @Override
            public void write(int b) {
                byteStream.write(b);
            }
        };
        stringWriter = new StringWriter();
        printWriter = new PrintWriter(stringWriter) {
            @Override
            public void close() {
                // Do nothing
            }

            @Override
            public void flush() {
                // Do nothing
            }
        };
        mode = MODE_NONE;
    }

    /**
     * Always returns false.
     * 
     * @return Always false
     */
    @Override
    public boolean isCommitted() {
        return false;
    }

    /**
     * Return whether the servlet returned an error code or not.
     * 
     * @return Whether an error occurred or not
     */
    public boolean isError() {
        return (status != HttpServletResponse.SC_OK);
    }

    /**
     * Check whether the response was redirected or not.
     * 
     * @return Whether the state was redirected or not
     */
    public boolean isRedirect() {
        return (redirectLocation != null);
    }

    /**
     * Delegate to initialize method.
     */
    @Override
    public void reset() {
        initialize();
    }

    /**
     * Clears the buffer.
     */
    @Override
    public void resetBuffer() {
        if (mode == MODE_BINARY) {
            byteStream.reset();
        } else if (mode == MODE_TEXT) {
            stringWriter.getBuffer().delete(0, stringWriter.getBuffer().length());
        }
    }

    /**
     * Send an error code. This implementation just sets the internal error state information.
     * 
     * @param code
     *            The code
     * @throws IOException
     *             Not used
     */
    @Override
    public void sendError(final int code) throws IOException {
        status = code;
        errorMessage = null;
    }

    /**
     * Send an error code. This implementation just sets the internal error state information.
     * 
     * @param code
     *            The error code
     * @param msg
     *            The error message
     * @throws IOException
     *             Not used
     */
    @Override
    public void sendError(final int code, final String msg) throws IOException {
        status = code;
        errorMessage = msg;
    }

    /**
     * @return url
     * @see org.apache.wicket.request.Request#getUrl()
     */
    private String getURL() {
        /*
         * Servlet 2.3 specification :
         * 
         * Servlet Path: The path section that directly corresponds to the mapping which activated
         * this request. This path starts with a "/" character except in the case where the request
         * is matched with the "/*" pattern, in which case it is the empty string.
         * 
         * PathInfo: The part of the request path that is not part of the Context Path or the
         * Servlet Path. It is either null if there is no extra path, or is a string with a leading
         * "/".
         */
        String url = servletRequest.getServletPath();
        final String pathInfo = servletRequest.getPathInfo();

        if (pathInfo != null) {
            url += pathInfo;
        }

        final String queryString = servletRequest.getQueryString();

        if (queryString != null) {
            url += ("?" + queryString);
        }

        // If url is non-empty it will start with '/', which we should lose
        if (url.length() > 0 && url.charAt(0) == '/') {
            // Remove leading '/'
            url = url.substring(1);
        }
        return url;
    }

    /**
     * Indicate sending of a redirectLocation to a particular named resource. This implementation
     * just keeps hold of the redirectLocation info and makes it available for query.
     * 
     * @param location
     *            The location to redirectLocation to
     * @throws IOException
     *             Not used
     */
    @Override
    public void sendRedirect(String location) throws IOException {
        redirectLocation = location;
        status = HttpServletResponse.SC_FOUND;
    }

    /**
     * Method ignored.
     * 
     * @param size
     *            The size
     */
    @Override
    public void setBufferSize(final int size) {
    }

    /**
     * Set the character encoding.
     * 
     * @param characterEncoding
     *            The character encoding
     */
    @Override
    public void setCharacterEncoding(final String characterEncoding) {
        this.characterEncoding = characterEncoding;
    }

    /**
     * Set the content length.
     * 
     * @param length
     *            The length
     */
    @Override
    public void setContentLength(final int length) {
        setIntHeader("Content-Length", length);
    }

    @Override
    public void setContentLengthLong(long len) {
        setContentLength((int) len);
    }

    /**
     * Set the content type.
     * 
     * @param type
     *            The content type
     */
    @Override
    public void setContentType(final String type) {
        setHeader("Content-Type", type);
    }

    /**
     * @return value of content-type header
     */
    @Override
    public String getContentType() {
        return getHeader("Content-Type");
    }

    /**
     * Set a date header.
     * 
     * @param name
     *            The header name
     * @param l
     *            The long value
     */
    @Override
    public void setDateHeader(final String name, final long l) {
        setHeader(name, formatDate(l));
    }

    /**
     * @param l
     * @return formatted date
     */
    public static String formatDate(long l) {
        StringBuilder _dateBuffer = new StringBuilder(32);
        Calendar _calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
        _calendar.setTimeInMillis(l);
        formatDate(_dateBuffer, _calendar, false);
        return _dateBuffer.toString();
    }

    /* BEGIN: This code comes from Jetty 6.1.1 */
    private static String[] DAYS = { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    private static String[] MONTHS = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
            "Dec", "Jan" };

    /**
     * Format HTTP date "EEE, dd MMM yyyy HH:mm:ss 'GMT'" or "EEE, dd-MMM-yy HH:mm:ss 'GMT'"for
     * cookies
     * 
     * @param buf
     * @param calendar
     * @param cookie
     */
    public static void formatDate(StringBuilder buf, Calendar calendar, boolean cookie) {
        // "EEE, dd MMM yyyy HH:mm:ss 'GMT'"
        // "EEE, dd-MMM-yy HH:mm:ss 'GMT'", cookie

        int day_of_week = calendar.get(Calendar.DAY_OF_WEEK);
        int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
        int month = calendar.get(Calendar.MONTH);
        int year = calendar.get(Calendar.YEAR);
        int century = year / 100;
        year = year % 100;

        int epoch = (int) ((calendar.getTimeInMillis() / 1000) % (60 * 60 * 24));
        int seconds = epoch % 60;
        epoch = epoch / 60;
        int minutes = epoch % 60;
        int hours = epoch / 60;

        buf.append(DAYS[day_of_week]);
        buf.append(',');
        buf.append(' ');
        append2digits(buf, day_of_month);

        if (cookie) {
            buf.append('-');
            buf.append(MONTHS[month]);
            buf.append('-');
            append2digits(buf, year);
        } else {
            buf.append(' ');
            buf.append(MONTHS[month]);
            buf.append(' ');
            append2digits(buf, century);
            append2digits(buf, year);
        }
        buf.append(' ');
        append2digits(buf, hours);
        buf.append(':');
        append2digits(buf, minutes);
        buf.append(':');
        append2digits(buf, seconds);
        buf.append(" GMT");
    }

    /**
     * @param buf
     * @param i
     */
    public static void append2digits(StringBuilder buf, int i) {
        if (i < 100) {
            buf.append((char) (i / 10 + '0'));
            buf.append((char) (i % 10 + '0'));
        }
    }

    /* END: This code comes from Jetty 6.1.1 */

    /**
     * Set the given header value.
     * 
     * @param name
     *            The name for the header
     * @param value
     *            The value for the header
     */
    @Override
    public void setHeader(final String name, final String value) {
        List<String> l = new ArrayList<String>(1);
        l.add(value);
        headers.put(name, l);
    }

    /**
     * Set an int header value.
     * 
     * @param name
     *            The header name
     * @param i
     *            The value
     */
    @Override
    public void setIntHeader(final String name, final int i) {
        setHeader(name, "" + i);
    }

    /**
     * Set the locale in the response header.
     * 
     * @param locale
     *            The locale
     */
    @Override
    public void setLocale(final Locale locale) {
        this.locale = locale;
    }

    /**
     * Set the status for this response.
     * 
     * @param status
     *            The status
     */
    @Override
    public void setStatus(final int status) {
        this.status = status;
    }

    /**
     * Set the status for this response.
     * 
     * @param status
     *            The status
     * @param msg
     *            The message
     * @deprecated
     */
    @Override
    @Deprecated
    public void setStatus(final int status, final String msg) {
        setStatus(status);
    }

    /**
     * @return binary response
     */
    public String getBinaryResponse() {
        String ctheader = getHeader("Content-Length");
        if (ctheader == null) {
            return getDocument();
        } else {
            return getDocument().substring(0, Integer.valueOf(ctheader));
        }
    }

    /**
     * @param name
     * @return headers with given name
     */
    public Collection<String> getHeaders(String name) {
        return Collections.singletonList(headers.get(name).toString());
    }

    @Override
    public void writeMetaData(WebResponse webResponse) {
        for (Cookie cookie : cookies) {
            webResponse.addCookie(cookie);
        }
        for (String name : headers.keySet()) {
            webResponse.setHeader(name, headers.get(name).toString());
        }
        webResponse.setStatus(status);
    }
}