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.FileUtil;
019import org.opengion.fukurou.util.Closer;
020
021import java.io.PrintWriter;
022import java.io.BufferedOutputStream;
023import java.io.FileOutputStream;
024import java.io.IOException;
025import java.io.File;
026import java.io.ByteArrayOutputStream;
027import javax.servlet.ServletOutputStream;
028import javax.servlet.ServletResponse;
029
030/**
031 * FileFilter で使用する、File圧縮するServletOutputStreamクラスです。
032 *
033 * @og.group フィルター処理
034 *
035 * @version  4.0
036 * @author   Kazuhiko Hasegawa
037 * @since    JDK5.0,
038 */
039public class FileResponseStream extends ServletOutputStream {
040        /** 内部出力ストリーム */
041        protected ByteArrayOutputStream filestream = null;
042        /** クローズ判定 */
043        protected boolean isClosed = false;
044        /** レスポンスオブジェクト */
045//      protected ServletResponse response = null;              // 5.5.2.6 (2012/05/25)
046        /** サーブレット出力ストリーム */
047        protected ServletOutputStream output = null;
048
049        private final String filename ;
050
051        /**
052         * コンストラクター
053         *
054         * @param       response        レスポンス
055         * @param       filename        ファイル名
056         * @throws IOException 入出力エラーが発生したとき
057         */
058        public FileResponseStream( final ServletResponse response,final String filename ) throws IOException {
059                // 4.3.4.4 (2009/01/01)
060//              super();
061                this.filename = filename;
062                isClosed = false;
063//              this.response = response;               // 5.5.2.6 (2012/05/25)
064                this.output = response.getOutputStream();
065                filestream = new ByteArrayOutputStream();
066        }
067
068        /**
069         * このストリームを閉じ、このストリームに関連するすべてのシステムリソースを解放します。
070         *
071         * close の汎用規約では、close は出力ストリームを閉じます。閉じられたストリームは
072         * 出力処理を実行できません。また、それを開き直すことはできません。
073         *
074         * @og.rev 5.6.4.2 (2013/05/17) fileDownload のエンコードは、UTF-8 ではなく、UnicodeLittle でセーブする。
075         *
076         * @throws IOException 入出力エラーが発生したとき
077         */
078        @Override
079        public void close() throws IOException {
080                if(isClosed) {
081//                      System.out.println( "already been closed !" );
082//                      throw new IOException("This output stream has already been closed");
083                        return ;
084                }
085                try {
086                        filestream.flush();
087
088                        // 5.6.4.2 (2013/05/17) ファイル名が fileDownload で始まる場合は、Streamのままセーブする。
089                        if( filename.indexOf( "fileDownload:" ) >= 0 ) {
090                                BufferedOutputStream toStream = new BufferedOutputStream( new FileOutputStream( filename.replace( "fileDownload:","" ) ) );
091                                filestream.writeTo( toStream );
092                                Closer.ioClose( toStream );
093                        }
094                        else {
095                                String msg = filestream.toString( "UTF-8" );
096                                FileResponseTransform trans = new FileResponseTransform();
097                                msg = trans.replace( filename,msg );
098
099                                PrintWriter writer = FileUtil.getPrintWriter( new File( filename ), "UTF-8" );
100                                writer.print( msg );
101                                Closer.ioClose( writer );
102                        }
103                        System.out.println( filename + " Saved" );
104
105                        output.flush();
106                }
107                catch( Throwable th ) {
108                        System.out.println( filename + " Error! " + th );
109                }
110                finally {
111                        isClosed = true;
112                        Closer.ioClose( filestream );
113                        Closer.ioClose( output );
114                }
115        }
116
117        /**
118         * この出力ストリームをフラッシュし、バッファに入っている出力バイトをすべて強制的書き込みますに。
119         *
120         * flush の汎用規約では、それまでに書き込まれたバイトが出力ストリームの
121         * 実装によってバッファに入れられている場合に flush を呼び出すと、それらのバイトは
122         * ただちにその目的の転送先に書き込まれます。
123         *
124         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
125         * @throws IOException 入出力エラーが発生したとき
126         */
127        @Override
128        public void flush() throws IOException {
129                if(isClosed) {
130//                      throw new IOException("Cannot flush a closed output stream");
131                        return ;
132                }
133                filestream.flush();
134                output.flush();
135        }
136
137        /**
138         * この出力ストリームに指定されたバイトを書き込みます。
139         *
140         * write の汎用規約では、1 バイトが
141         * 出力ストリームに書き込まれます。書き込まれるバイトは、引数 b の下位 8 ビットです。
142         * b の上位 24 ビットは無視されます。
143         *
144         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
145         *
146         * @param       bt      byteデータ
147         * @throws IOException 入出力エラーが発生したとき
148         */
149        @Override
150        public void write(final int bt) throws IOException {
151                if(isClosed) {
152//                      throw new IOException("Cannot write to a closed output stream");
153                        return ;
154                }
155                filestream.write((byte)bt);
156                output.write((byte)bt);
157        }
158
159        /**
160         * 指定されたバイト配列からこの出力ストリームに b.length バイトを書き込みます。
161         *
162         * write(b) の汎用規約では、write(b) の効果は write(b, 0, b.length) を呼び出す
163         * 場合とまったく同じです。
164         *
165         * @param       bt      バイト配列
166         * @throws IOException 入出力エラーが発生したとき
167         */
168        @Override
169        public void write(final byte[] bt) throws IOException {
170                write(bt, 0, bt.length);
171        }
172
173        /**
174         * オフセット off から始まる指定のバイト配列からこの出力ストリームに len バイトを書き込みます。
175         *
176         * write(b, off, len) の汎用規約では、配列 b 内の一定のバイトが出力ストリームに順番に
177         * 書き込まれます。この処理で最初に書き込まれるバイトは要素 b[off]、最後に書き込まれる
178         * バイトは要素 b[off+len-1] です。
179         *
180         * @og.rev 5.1.7.0 (2010/06/01) isClosed == true の場合に Exception でなく、return にする。
181         *
182         * @param       bt      バイト配列
183         * @param       off     オフセット数
184         * @param       len     書き込みバイト数
185         * @throws IOException 入出力エラーが発生したとき
186         */
187        @Override
188        public void write(final byte bt[], final int off, final int len) throws IOException {
189                if(isClosed) {
190//                      throw new IOException("Cannot write to a closed output stream");
191                        return ;
192                }
193                output.write(bt, off, len);
194                filestream.write(bt, off, len);
195        }
196
197        /**
198         * すでにストリームが閉じられているかどうかを返します。
199         *
200         * @return      すでにストリームが閉じられているかどうか
201         */
202        public boolean closed() {
203                return isClosed;
204        }
205
206        /**
207         * Checks if a non-blocking write will succeed. If this returns
208         * <code>false</code>, it will cause a callback to
209         * WriteListener#onWritePossible() when the buffer has emptied. If
210         * this method returns <code>false</code> no further data must be written
211         * until the contain calls WriteListener#onWritePossible().
212         *
213         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
214         *
215         * @return true:書き込み可能/false:不可 (true if data can be written, else false)
216         *
217         * @since Servlet 3.1
218         */
219        //@Override
220        public boolean isReady() { return false; }
221
222        /**
223         * Sets the WriteListener for this ServletOutputStream and
224         * thereby switches to non-blocking IO. It is only valid to switch to
225         * non-blocking IO within async processing or HTTP upgrade processing.
226         *
227         * @og.rev 5.6.8.2 (2013/09/20) 新規追加(Tomcat8 / Servlet 3.1 で追加された abstract メソッド)
228         *
229         * @param listener      The non-blocking IO write listener
230         *
231         * @throws IllegalStateException        If this method is called if neither
232         *                                                                      async nor HTTP upgrade is in progress or
233         *                                                                      if the WriteListener has already
234         *                                                                      been set
235         * @throws NullPointerException         If listener is null
236         *
237         * @since Servlet 3.1
238         */
239        //@Override
240        //      public void setWriteListener( final javax.servlet.WriteListener listener ) {
241        //              // 何も実装しない。
242        //      }
243}