org.apache.wicket.protocol.http.BufferedWebResponse.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.protocol.http.BufferedWebResponse.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;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.Cookie;

import org.apache.wicket.Application;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.response.filter.IResponseFilter;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.time.Time;

/**
 * Subclass of {@link WebResponse} that buffers the actions and performs those on another response.
 * 
 * @see #writeTo(WebResponse)
 * 
 * @author Matej Knopp
 */
public class BufferedWebResponse extends WebResponse implements IMetaDataBufferingWebResponse {
    private final WebResponse originalResponse;

    /**
     * Construct.
     * 
     * @param originalResponse
     */
    public BufferedWebResponse(WebResponse originalResponse) {
        // if original response had some metadata set
        // we should transfer it to the current response
        if (originalResponse instanceof IMetaDataBufferingWebResponse) {
            ((IMetaDataBufferingWebResponse) originalResponse).writeMetaData(this);
        }
        this.originalResponse = originalResponse;
    }

    /**
     * transfer cookie operations (add, clear) to given web response
     * 
     * @param response
     *            web response that should receive the current cookie operation
     */
    @Override
    public void writeMetaData(WebResponse response) {
        for (Action action : actions) {
            if (action instanceof MetaDataAction)
                action.invoke(response);
        }
    }

    @Override
    public String encodeURL(CharSequence url) {
        if (originalResponse != null) {
            return originalResponse.encodeURL(url);
        } else {
            return url != null ? url.toString() : null;
        }
    }

    @Override
    public String encodeRedirectURL(CharSequence url) {
        if (originalResponse != null) {
            return originalResponse.encodeRedirectURL(url);
        } else {
            return url != null ? url.toString() : null;
        }
    }

    private enum ActionType {
        HEADER, NORMAL, DATA;
    }

    private static abstract class Action implements Comparable<Action> {
        protected abstract void invoke(WebResponse response);

        protected ActionType getType() {
            return ActionType.NORMAL;
        }

        @Override
        public final int compareTo(Action o) {
            return getType().ordinal() - o.getType().ordinal();
        }
    }

    /**
     * Actions not related directly to the content of the response, eg setting cookies, headers.
     * 
     * @author igor
     */
    private static abstract class MetaDataAction extends Action {
        @Override
        protected ActionType getType() {
            return ActionType.HEADER;
        }
    }

    private static class WriteCharSequenceAction extends Action {
        private final StringBuilder builder = new StringBuilder(4096);

        public WriteCharSequenceAction() {

        }

        public void append(CharSequence sequence) {
            builder.append(sequence);
        }

        @Override
        protected void invoke(WebResponse response) {
            AppendingStringBuffer responseBuffer = new AppendingStringBuffer(builder);

            List<IResponseFilter> responseFilters = Application.get().getRequestCycleSettings()
                    .getResponseFilters();

            if (responseFilters != null) {
                for (IResponseFilter filter : responseFilters) {
                    responseBuffer = filter.filter(responseBuffer);
                }
            }
            response.write(responseBuffer);
        }

        @Override
        protected ActionType getType() {
            return ActionType.DATA;
        }
    }

    private static class WriteDataAction extends Action {
        private final ByteArrayOutputStream stream = new ByteArrayOutputStream();

        public WriteDataAction() {

        }

        public void append(byte data[]) {
            try {
                stream.write(data);
            } catch (IOException e) {
                throw new WicketRuntimeException(e);
            }
        }

        public void append(byte data[], int offset, int length) {
            try {
                stream.write(data, offset, length);
            } catch (Exception e) {
                throw new WicketRuntimeException(e);
            }
        }

        @Override
        protected void invoke(WebResponse response) {
            writeStream(response, stream);
        }

        @Override
        protected ActionType getType() {
            return ActionType.DATA;
        }
    }

    private static class CloseAction extends Action {
        @Override
        protected void invoke(WebResponse response) {
            response.close();
        }
    }

    private static class AddCookieAction extends MetaDataAction {
        private final Cookie cookie;

        public AddCookieAction(Cookie cookie) {
            this.cookie = cookie;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.addCookie(cookie);
        }
    }

    private static class ClearCookieAction extends MetaDataAction {
        private final Cookie cookie;

        public ClearCookieAction(Cookie cookie) {
            this.cookie = cookie;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.clearCookie(cookie);
        }
    }

    private static class SetHeaderAction extends MetaDataAction {
        private final String name;
        private final String value;

        public SetHeaderAction(String name, String value) {
            this.name = name;
            this.value = value;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.setHeader(name, value);
        }
    }

    private static class AddHeaderAction extends MetaDataAction {
        private final String name;
        private final String value;

        public AddHeaderAction(String name, String value) {
            this.name = name;
            this.value = value;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.addHeader(name, value);
        }
    }

    private static class SetDateHeaderAction extends MetaDataAction {
        private final String name;
        private final Time value;

        public SetDateHeaderAction(String name, Time value) {
            this.name = name;
            this.value = Args.notNull(value, "value");
        }

        @Override
        protected void invoke(WebResponse response) {
            response.setDateHeader(name, value);
        }
    }

    private static class SetContentLengthAction extends MetaDataAction {
        private final long contentLength;

        public SetContentLengthAction(long contentLength) {
            this.contentLength = contentLength;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.setContentLength(contentLength);
        }
    }

    private static class SetContentTypeAction extends MetaDataAction {
        private final String contentType;

        public SetContentTypeAction(String contentType) {
            this.contentType = contentType;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.setContentType(contentType);
        }
    }

    private static class SetStatusAction extends MetaDataAction {
        private final int sc;

        public SetStatusAction(int sc) {
            this.sc = sc;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.setStatus(sc);
        }
    }

    private static class DisableCachingAction extends MetaDataAction {
        @Override
        protected void invoke(WebResponse response) {
            response.disableCaching();
            ;
        }
    }

    private static class SendErrorAction extends Action {
        private final int sc;
        private final String msg;

        public SendErrorAction(int sc, String msg) {
            this.sc = sc;
            this.msg = msg;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.sendError(sc, msg);
        }
    }

    private static class SendRedirectAction extends Action {
        private final String url;

        public SendRedirectAction(String url) {
            this.url = url;
        }

        @Override
        protected void invoke(WebResponse response) {
            response.sendRedirect(url);
        }
    }

    private static class FlushAction extends Action {
        @Override
        protected void invoke(WebResponse response) {
            response.flush();
        }
    }

    private final List<Action> actions = new ArrayList<Action>();
    private WriteCharSequenceAction charSequenceAction;
    private WriteDataAction dataAction;

    @Override
    public void reset() {
        super.reset();
        actions.clear();
        charSequenceAction = null;
        dataAction = null;
    }

    @Override
    public void addCookie(Cookie cookie) {
        actions.add(new AddCookieAction(cookie));
    }

    @Override
    public void clearCookie(Cookie cookie) {
        actions.add(new ClearCookieAction(cookie));
    }

    @Override
    public void setContentLength(long length) {
        actions.add(new SetContentLengthAction(length));
    }

    @Override
    public void setContentType(String mimeType) {
        actions.add(new SetContentTypeAction(mimeType));
    }

    @Override
    public void setDateHeader(String name, Time date) {
        actions.add(new SetDateHeaderAction(name, date));
    }

    @Override
    public void setHeader(String name, String value) {
        actions.add(new SetHeaderAction(name, value));
    }

    @Override
    public void addHeader(String name, String value) {
        actions.add(new AddHeaderAction(name, value));
    }

    @Override
    public void disableCaching() {
        actions.add(new DisableCachingAction());
    }

    @Override
    public void write(CharSequence sequence) {
        if (dataAction != null) {
            throw new IllegalStateException("Can't call write(CharSequence) after write(byte[]) has been called.");
        }

        if (charSequenceAction == null) {
            charSequenceAction = new WriteCharSequenceAction();
            actions.add(charSequenceAction);
        }
        charSequenceAction.append(sequence);
    }

    /**
     * Returns the text already written to this response.
     * 
     * @return text
     */
    public CharSequence getText() {
        if (dataAction != null) {
            throw new IllegalStateException("write(byte[]) has already been called.");
        }
        if (charSequenceAction != null) {
            return charSequenceAction.builder;
        } else {
            return null;
        }
    }

    /**
     * Replaces the text in this response
     * 
     * @param text
     */
    public void setText(CharSequence text) {
        if (dataAction != null) {
            throw new IllegalStateException("write(byte[]) has already been called.");
        }
        if (charSequenceAction != null) {
            charSequenceAction.builder.setLength(0);
        }
        write(text);
    }

    @Override
    public void write(byte[] array) {
        if (charSequenceAction != null) {
            throw new IllegalStateException("Can't call write(byte[]) after write(CharSequence) has been called.");
        }
        if (dataAction == null) {
            dataAction = new WriteDataAction();
            actions.add(dataAction);
        }
        dataAction.append(array);
    }

    @Override
    public void write(byte[] array, int offset, int length) {
        if (charSequenceAction != null) {
            throw new IllegalStateException("Can't call write(byte[]) after write(CharSequence) has been called.");
        }
        if (dataAction == null) {
            dataAction = new WriteDataAction();
            actions.add(dataAction);
        }
        dataAction.append(array, offset, length);
    }

    @Override
    public void sendRedirect(String url) {
        actions.add(new SendRedirectAction(url));
    }

    @Override
    public void setStatus(int sc) {
        actions.add(new SetStatusAction(sc));
    }

    @Override
    public void sendError(int sc, String msg) {
        actions.add(new SendErrorAction(sc, msg));
    }

    /**
     * Writes the content of the buffer to the specified response. Also sets the properties and and
     * headers.
     * 
     * @param response
     */
    public void writeTo(final WebResponse response) {
        Args.notNull(response, "response");

        Collections.sort(actions);

        for (Action action : actions) {
            action.invoke(response);
        }
    }

    @Override
    public boolean isRedirect() {
        for (Action action : actions) {
            if (action instanceof SendRedirectAction) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void flush() {
        actions.add(new FlushAction());
    }

    private static void writeStream(final Response response, ByteArrayOutputStream stream) {
        final boolean copied[] = { false };
        try {
            // try to avoid copying the array
            stream.writeTo(new OutputStream() {
                @Override
                public void write(int b) throws IOException {

                }

                @Override
                public void write(byte[] b, int off, int len) throws IOException {
                    if (off == 0 && len == b.length) {
                        response.write(b);
                        copied[0] = true;
                    }
                }
            });
        } catch (IOException e1) {
            throw new WicketRuntimeException(e1);
        }
        if (copied[0] == false) {
            response.write(stream.toByteArray());
        }
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        final String toString;
        if (charSequenceAction != null) {
            toString = charSequenceAction.builder.toString();
        } else {
            toString = super.toString();
        }
        return toString;
    }

    @Override
    public Object getContainerResponse() {
        return originalResponse.getContainerResponse();
    }
}