net.sf.ehcache.constructs.web.PageInfo.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.ehcache.constructs.web.PageInfo.java

Source

/**
 *  Copyright 2003-2007 Luck Consulting Pty Ltd
 *
 *  Licensed 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 net.sf.ehcache.constructs.web;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * A Serializable representation of a {@link HttpServletResponse}.
 *
 * @author <a href="mailto:amurdoch@thoughtworks.com">Adam Murdoch</a>
 * @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
 * @version $Id: PageInfo.java 606 2008-05-02 23:22:40Z gregluck $
 */
public class PageInfo implements Serializable {
    private static final Log LOG = LogFactory.getLog(PageInfo.class.getName());
    private static final int FOUR_KB = 4196;
    private static final int GZIP_MAGIC_NUMBER_BYTE_1 = 31;
    private static final int GZIP_MAGIC_NUMBER_BYTE_2 = -117;
    private final ArrayList headers = new ArrayList();
    private final ArrayList serializableCookies = new ArrayList();
    private String contentType;
    private byte[] gzippedBody;
    private byte[] ungzippedBody;
    private int statusCode;
    private boolean storeGzipped;

    /**
     * Creates a PageInfo.
     * <p/>
     *
     * @param statusCode
     * @param contentType
     * @param headers
     * @param cookies
     * @param body
     * @param storeGzipped      set this to false for images and page fragments which should never
     *                     be gzipped.
     */
    public PageInfo(final int statusCode, final String contentType, final Collection headers,
            final Collection cookies, final byte[] body, boolean storeGzipped) throws AlreadyGzippedException {
        if (headers != null) {
            this.headers.addAll(headers);
        }
        this.headers.remove("Content-Encoding");
        this.contentType = contentType;
        this.storeGzipped = storeGzipped;
        this.statusCode = statusCode;
        extractCookies(cookies);

        try {
            if (storeGzipped) {
                //gunzip on demand
                ungzippedBody = null;
                if (isBodyParameterGzipped()) {
                    gzippedBody = body;
                } else {
                    gzippedBody = gzip(body);
                }
            } else {
                if (isBodyParameterGzipped()) {
                    throw new IllegalArgumentException("Non gzip content has been gzipped.");
                } else {
                    ungzippedBody = body;
                }
            }
        } catch (IOException e) {
            LOG.error("Error ungzipping gzipped body", e);
        }

    }

    private void extractCookies(Collection cookies) {
        if (cookies != null) {
            for (Iterator iterator = cookies.iterator(); iterator.hasNext();) {
                final Cookie cookie = (Cookie) iterator.next();
                serializableCookies.add(new SerializableCookie(cookie));
            }
        }
    }

    /**
     * @param ungzipped the bytes to be gzipped
     * @return gzipped bytes
     */
    private byte[] gzip(byte[] ungzipped) throws IOException, AlreadyGzippedException {
        if (isGzipped(ungzipped)) {
            throw new AlreadyGzippedException("The byte[] is already gzipped. It should not be gzipped again.");
        }
        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        final GZIPOutputStream gzipOutputStream = new GZIPOutputStream(bytes);
        gzipOutputStream.write(ungzipped);
        gzipOutputStream.close();
        return bytes.toByteArray();
    }

    /**
     * The response body will be assumed to be gzipped if the GZIP header has been set.
     *
     * @return true if the body is gzipped
     */
    private boolean isBodyParameterGzipped() {
        for (int i = 0; i < headers.size(); i++) {
            String[] keyValuePair = (String[]) headers.get(i);
            if (keyValuePair[1].equals("gzip")) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks the first two bytes of the candidate byte array for the magic number 0x677a.
     * This magic number was obtained from /usr/share/file/magic. The line for gzip is:
     * <p/>
     * <code>
     * >>14    beshort 0x677a          (gzipped)
     * </code>
     *
     * @param candidate the byte array to check
     * @return true if gzipped, false if null, less than two bytes or not gzipped
     */
    public static boolean isGzipped(byte[] candidate) {
        if (candidate == null || candidate.length < 2) {
            return false;
        } else {
            return (candidate[0] == GZIP_MAGIC_NUMBER_BYTE_1 && candidate[1] == GZIP_MAGIC_NUMBER_BYTE_2);
        }
    }

    /**
     * @return the content type of the response.
     */
    public String getContentType() {
        return contentType;
    }

    /**
     * @return the gzipped version of the body if the content is storeGzipped, otherwise null
     */
    public byte[] getGzippedBody() {
        if (storeGzipped) {
            return gzippedBody;
        } else {
            return null;
        }
    }

    /**
     * Returns the headers of the response.
     */
    public List getHeaders() {
        return headers;
    }

    /**
     * Returns the cookies of the response.
     */
    public List getSerializableCookies() {
        return serializableCookies;
    }

    /**
     * Returns the status code of the response.
     */
    public int getStatusCode() {
        return statusCode;
    }

    /**
     * @return the ungzipped version of the body. This gunzipped on demand when storedGzipped, otherwise
     *         the ungzipped body is returned.
     */
    public byte[] getUngzippedBody() throws IOException {
        if (storeGzipped) {
            return ungzip(gzippedBody);
        } else {
            return ungzippedBody;
        }
    }

    /**
     * A highly performant ungzip implementation. Do not refactor this without taking new timings.
     * See ElementTest in ehcache for timings
     *
     * @param gzipped the gzipped content
     * @return an ungzipped byte[]
     * @throws java.io.IOException
     */
    private byte[] ungzip(final byte[] gzipped) throws IOException {
        final GZIPInputStream inputStream = new GZIPInputStream(new ByteArrayInputStream(gzipped));
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(gzipped.length);
        final byte[] buffer = new byte[FOUR_KB];
        int bytesRead = 0;
        while (bytesRead != -1) {
            bytesRead = inputStream.read(buffer, 0, FOUR_KB);
            if (bytesRead != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
        }
        byte[] ungzipped = byteArrayOutputStream.toByteArray();
        inputStream.close();
        byteArrayOutputStream.close();
        return ungzipped;
    }

    /**
     * @return true if there is a non null gzipped body
     */
    public boolean hasGzippedBody() {
        return (gzippedBody != null);
    }

    /**
     * @return true if there is a non null ungzipped body
     */
    public boolean hasUngzippedBody() {
        return (ungzippedBody != null);
    }

    /**
     * Returns true if the response is Ok.
     * @return true if the response code is 200.
     */
    public boolean isOk() {
        return (statusCode == HttpServletResponse.SC_OK);
    }
}