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.system.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    ;
039        /** GZIP出力ストリーム */
040        protected GZIPOutputStream gzipstream   ;
041        /** クローズ判定 */
042        protected boolean isClosed                              ;
043        /** レスポンスオブジェクト */
044        protected HttpServletResponse response  ;
045        /** サーブレット出力ストリーム */
046        protected ServletOutputStream output    ;
047
048        /**
049         * コンストラクター
050         *
051         * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
052         *
053         * @param response HttpServletResponseオブジェクト
054         * @throws IOException 入出力エラーが発生したとき
055         */
056        public GZIPResponseStream( final HttpServletResponse response ) throws IOException {
057                super();
058                // 4.3.4.4 (2009/01/01)
059                isClosed = false;
060                this.response = response;
061                this.output = response.getOutputStream();
062                baos = new ByteArrayOutputStream();
063                gzipstream = new GZIPOutputStream(baos);
064        }
065
066        /**
067         * このストリームを閉じ、このストリームに関連するすべてのシステムリソースを解放します。
068         *
069         * close の汎用規約では、close は出力ストリームを閉じます。閉じられたストリームは
070         * 出力処理を実行できません。また、それを開き直すことはできません。
071         *
072         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
073         *
074         * @throws IOException 入出力エラーが発生したとき
075         */
076        @Override
077        public void close() throws IOException {
078                if( isClosed ) {
079                        return ;
080                }
081                try {
082                        gzipstream.finish();
083
084                        final byte[] bytes = baos.toByteArray();
085
086                        response.setContentLength( bytes.length );
087                        response.addHeader("Content-Encoding", "gzip");
088                        output.write(bytes);
089                        output.flush();
090                }
091                finally {
092                        isClosed = true;
093                        Closer.ioClose( output );
094                }
095        }
096
097        /**
098         * この出力ストリームをフラッシュし、バッファに入っている出力バイトをすべて強制的書き込みますに。
099         *
100         * flush の汎用規約では、それまでに書き込まれたバイトが出力ストリームの
101         * 実装によってバッファに入れられている場合に flush を呼び出すと、それらのバイトは
102         * ただちにその目的の転送先に書き込まれます。
103         *
104         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
105         *
106         * @throws IOException 入出力エラーが発生したとき
107         */
108        @Override
109        public void flush() throws IOException {
110                if( isClosed ) {
111                        return ;
112                }
113                gzipstream.flush();
114        }
115
116        /**
117         * この出力ストリームに指定されたバイトを書き込みます。
118         *
119         * write の汎用規約では、1 バイトが
120         * 出力ストリームに書き込まれます。書き込まれるバイトは、引数 b の下位 8 ビットです。
121         * b の上位 24 ビットは無視されます。
122         *
123         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
124         *
125         * @param       bt      byteデータ
126         * @throws IOException 入出力エラーが発生したとき
127         */
128        @Override
129        public void write( final int bt ) throws IOException {
130                if( isClosed ) {
131                        return ;
132                }
133                gzipstream.write((byte)bt);
134        }
135
136        /**
137         * 指定されたバイト配列からこの出力ストリームに b.length バイトを書き込みます。
138         *
139         * write(b) の汎用規約では、write(b) の効果は write(b, 0, b.length) を呼び出す
140         * 場合とまったく同じです。
141         *
142         * @param       bt      バイト配列
143         * @throws IOException 入出力エラーが発生したとき
144         */
145        @Override
146        public void write( final byte[] bt ) throws IOException {
147                write(bt, 0, bt.length);
148        }
149
150        /**
151         * オフセット off から始まる指定のバイト配列からこの出力ストリームに len バイトを書き込みます。
152         *
153         * write(b, off, len) の汎用規約では、配列 b 内の一定のバイトが出力ストリームに順番に
154         * 書き込まれます。この処理で最初に書き込まれるバイトは要素 b[off]、最後に書き込まれる
155         * バイトは要素 b[off+len-1] です。
156         *
157         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
158         *
159         * @param       bt      バイト配列
160         * @param       off     オフセット数
161         * @param       len     書き込みバイト数
162         * @throws IOException 入出力エラーが発生したとき
163         */
164        @Override
165        public void write( final byte bt[], final int off, final int len ) throws IOException {
166                if( isClosed ) {
167                        return ;
168                }
169                gzipstream.write(bt, off, len);
170        }
171
172        /**
173         * すでにストリームが閉じられているかどうかを返します。
174         *
175         * @return      すでにストリームが閉じられているかどうか
176         */
177        public boolean closed() {
178                return isClosed;
179        }
180
181        /**
182         * Tomcat8 / Servlet 3.1 で追加された abstract メソッド。
183         *
184         * Checks if a non-blocking write will succeed. If this returns
185         * <code>false</code>, it will cause a callback to
186         * WriteListener#onWritePossible() when the buffer has emptied. If
187         * this method returns <code>false</code> no further data must be written
188         * until the contain calls WriteListener#onWritePossible().
189         *
190         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
191         *
192         * @return true:書き込み可能/false:不可 (true if data can be written, else false)
193         *
194         * @since Servlet 3.1
195         */
196        @Override
197        public boolean isReady() { return false; }
198
199        /**
200         * Tomcat8 / Servlet 3.1 で追加された abstract メソッド。
201         *
202         * Sets the WriteListener for this ServletOutputStream and
203         * thereby switches to non-blocking IO. It is only valid to switch to
204         * non-blocking IO within async processing or HTTP upgrade processing.
205         *
206         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
207         *
208         * @param listener      The non-blocking IO write listener
209         *
210         * @throws IllegalStateException        If this method is called if neither
211         *                                                                      async nor HTTP upgrade is in progress or
212         *                                                                      if the WriteListener has already
213         *                                                                      been set
214         * @throws NullPointerException         If listener is null
215         *
216         * @since Servlet 3.1
217         */
218        @Override
219        public void setWriteListener( final javax.servlet.WriteListener listener ) {
220                // 何も実装しない。
221        }
222}