001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.hayabusa.filter;
017
018import org.opengion.fukurou.util.Closer;
019
020import java.io.ByteArrayOutputStream;
021import java.io.IOException;
022
023import java.util.zip.GZIPOutputStream;
024import javax.servlet.ServletOutputStream;
025import javax.servlet.http.HttpServletResponse;
026
027/**
028 * GZIPFilter で使用する、GZIP圧縮するServletOutputStreamクラスです。
029 *
030 * @og.group フィルター処理
031 *
032 * @version  4.0
033 * @author   Kazuhiko Hasegawa
034 * @since    JDK5.0,
035 */
036public class GZIPResponseStream extends ServletOutputStream {
037        /** 内部出力ストリーム */
038        protected ByteArrayOutputStream baos = null;
039        /** GZIP出力ストリーム */
040        protected GZIPOutputStream gzipstream = null;
041        /** クローズ判定 */
042        protected boolean isClosed = false;
043        /** レスポンスオブジェクト */
044        protected HttpServletResponse response = null;
045        /** サーブレット出力ストリーム */
046        protected ServletOutputStream output = null;
047
048        /**
049         * コンストラクター
050         *
051         * @param response HttpServletResponseオブジェクト
052         * @throws IOException 入出力エラーが発生したとき
053         */
054        public GZIPResponseStream(final HttpServletResponse response) throws IOException {
055                // 4.3.4.4 (2009/01/01)
056//              super();
057                isClosed = false;
058                this.response = response;
059                this.output = response.getOutputStream();
060                baos = new ByteArrayOutputStream();
061                gzipstream = new GZIPOutputStream(baos);
062        }
063
064        /**
065         * このストリームを閉じ、このストリームに関連するすべてのシステムリソースを解放します。
066         *
067         * close の汎用規約では、close は出力ストリームを閉じます。閉じられたストリームは
068         * 出力処理を実行できません。また、それを開き直すことはできません。
069         *
070         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
071         *
072         * @throws IOException 入出力エラーが発生したとき
073         */
074        @Override
075        public void close() throws IOException {
076                if(isClosed) {
077//                      throw new IOException("This output stream has already been closed");
078                        return ;
079                }
080                try {
081                        gzipstream.finish();
082
083                        byte[] bytes = baos.toByteArray();
084
085//                      response.addHeader("Content-Length", Integer.toString(bytes.length));
086                        response.setContentLength( bytes.length );
087// System.out.println( bytes.length );
088                        response.addHeader("Content-Encoding", "gzip");
089                        output.write(bytes);
090                        output.flush();
091                }
092                finally {
093                        isClosed = true;
094                        Closer.ioClose( output );
095                }
096        }
097
098        /**
099         * この出力ストリームをフラッシュし、バッファに入っている出力バイトをすべて強制的書き込みますに。
100         *
101         * flush の汎用規約では、それまでに書き込まれたバイトが出力ストリームの
102         * 実装によってバッファに入れられている場合に flush を呼び出すと、それらのバイトは
103         * ただちにその目的の転送先に書き込まれます。
104         *
105         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
106         *
107         * @throws IOException 入出力エラーが発生したとき
108         */
109        @Override
110        public void flush() throws IOException {
111                if(isClosed) {
112//                      throw new IOException("Cannot flush a closed output stream");
113                        return ;
114                }
115                gzipstream.flush();
116        }
117
118        /**
119         * この出力ストリームに指定されたバイトを書き込みます。
120         *
121         * write の汎用規約では、1 バイトが
122         * 出力ストリームに書き込まれます。書き込まれるバイトは、引数 b の下位 8 ビットです。
123         * b の上位 24 ビットは無視されます。
124         *
125         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
126         *
127         * @param       bt      byteデータ
128         * @throws IOException 入出力エラーが発生したとき
129         */
130        @Override
131        public void write(final int bt) throws IOException {
132                if(isClosed) {
133//                      throw new IOException("Cannot write to a closed output stream");
134                        return ;
135                }
136                gzipstream.write((byte)bt);
137        }
138
139        /**
140         * 指定されたバイト配列からこの出力ストリームに b.length バイトを書き込みます。
141         *
142         * write(b) の汎用規約では、write(b) の効果は write(b, 0, b.length) を呼び出す
143         * 場合とまったく同じです。
144         *
145         * @param       bt      バイト配列
146         * @throws IOException 入出力エラーが発生したとき
147         */
148        @Override
149        public void write(final byte[] bt) throws IOException {
150                write(bt, 0, bt.length);
151        }
152
153        /**
154         * オフセット off から始まる指定のバイト配列からこの出力ストリームに len バイトを書き込みます。
155         *
156         * write(b, off, len) の汎用規約では、配列 b 内の一定のバイトが出力ストリームに順番に
157         * 書き込まれます。この処理で最初に書き込まれるバイトは要素 b[off]、最後に書き込まれる
158         * バイトは要素 b[off+len-1] です。
159         *
160         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
161         *
162         * @param       bt      バイト配列
163         * @param       off     オフセット数
164         * @param       len     書き込みバイト数
165         * @throws IOException 入出力エラーが発生したとき
166         */
167        @Override
168        public void write(final byte bt[], final int off, final int len) throws IOException {
169//              System.out.println("writing...");
170                if(isClosed) {
171//                      throw new IOException("Cannot write to a closed output stream");
172                        return ;
173                }
174                gzipstream.write(bt, off, len);
175        }
176
177        /**
178         * すでにストリームが閉じられているかどうかを返します。
179         *
180         * @return      すでにストリームが閉じられているかどうか
181         */
182        public boolean closed() {
183                return isClosed;
184        }
185
186        /**
187         * Checks if a non-blocking write will succeed. If this returns
188         * <code>false</code>, it will cause a callback to
189         * WriteListener#onWritePossible() when the buffer has emptied. If
190         * this method returns <code>false</code> no further data must be written
191         * until the contain calls WriteListener#onWritePossible().
192         *
193         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
194         *
195         * @return true:書き込み可能/false:不可 (true if data can be written, else false)
196         *
197         * @since Servlet 3.1
198         */
199        //@Override
200        public boolean isReady() { return false; }
201
202        /**
203         * Sets the WriteListener for this ServletOutputStream and
204         * thereby switches to non-blocking IO. It is only valid to switch to
205         * non-blocking IO within async processing or HTTP upgrade processing.
206         *
207         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
208         *
209         * @param listener      The non-blocking IO write listener
210         *
211         * @throws IllegalStateException        If this method is called if neither
212         *                                                                      async nor HTTP upgrade is in progress or
213         *                                                                      if the WriteListener has already
214         *                                                                      been set
215         * @throws NullPointerException         If listener is null
216         *
217         * @since Servlet 3.1
218         */
219        //@Override
220        //      public void setWriteListener( final javax.servlet.WriteListener listener ) {
221        //              // 何も実装しない。
222        //      }
223}