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.hayabusa.common.HybsSystem;
019
020import java.util.Locale;
021
022import java.io.File;
023import java.io.IOException;
024import java.io.UnsupportedEncodingException;
025import javax.servlet.ServletRequest;
026import javax.servlet.ServletResponse;
027import javax.servlet.ServletContext;
028import javax.servlet.Filter;
029import javax.servlet.FilterChain;
030import javax.servlet.FilterConfig;
031import javax.servlet.ServletException;
032import javax.servlet.http.HttpServletRequest;
033import javax.servlet.http.HttpServletResponse;
034
035/**
036 * Filter インターフェースを継承した HTMLデモ画面を作成するフィルタクラスです。
037 * web.xml で filter 設定することにより、使用できます。
038 * このフィルターでは、通常の画面アクセスを行うと、指定のフォルダに対して
039 * JSPをHTMLに変換した形で、ファイルをセーブしていきます。このHTMLは、
040 * デモサンプル画面として、使用できます。
041 * 出来る限り、デモ画面として使えるように、画面間リンクや、ボタン制御を
042 * JavaScript を挿入する事で実現しています。
043 *
044 * フィルターに対してweb.xml でパラメータを設定します。
045 *   ・saveDir  :ファイルをセーブするディレクトリ
046 *
047 * パラメータがない場合は、G:/webapps/作番/filetemp/DIR/ 以下に自動設定されます。
048 * また、ディレクトリが、相対パスの場合は、G:/webapps/作番/ 以下に、絶対パスの
049 * 場合は、そのパスの下に作成されます。 *
050 *
051 * 【WEB-INF/web.xml】
052 *     <filter>
053 *         <filter-name>FileFilter</filter-name>
054 *         <filter-class>org.opengion.hayabusa.filter.FileFilter</filter-class>
055 *         <init-param>
056 *             <param-name>saveDir</param-name>
057 *             <param-value>filetemp/DIR/</param-value>
058 *         </init-param>
059 *     </filter>
060 *
061 *     <filter-mapping>
062 *         <filter-name>FileFilter</filter-name>
063 *         <url-pattern>/jsp/*</url-pattern>
064 *     </filter-mapping>
065 *
066 * @og.group フィルター処理
067 *
068 * @version  4.0
069 * @author   Kazuhiko Hasegawa
070 * @since    JDK5.0,
071 */
072public class FileFilter implements Filter {
073        private String saveDir = null;  // "G:/webapps/gf/filetemp/DIR/" など
074
075        /**
076         * Filter インターフェースの doFilter メソッド
077         *
078         * Filter クラスの doFilter メソッドはコンテナにより呼び出され、 最後のチェーンにおける
079         * リソースへのクライアントリクエストのために、 毎回リクエスト・レスポンスのペアが、
080         * チェーンを通して渡されます。 このメソッドに渡される FilterChain を利用して、Filter が
081         * リクエストやレスポンスをチェーン内の次のエンティティ(Filter)にリクエストとレスポンスを
082         * 渡す事ができます。
083         * このメソッドの典型的な実装は以下のようなパターンとなるでしょう。
084         * 1. リクエストの検査
085         * 2. オプションとして、入力フィルタリング用にコンテンツもしくはヘッダをフィルタリング
086         *    するためにカスタム実装によるリクエストオブジェクトのラップ
087         * 3. オプションとして、出力フィルタリング用にコンテンツもしくはヘッダをフィルタリング
088         *    するためにカスタム実装によるレスポンスオブジェクトラップ
089         * 4. 以下の a)、b) のどちらか
090         *    a) FileterChain オブジェクト(chain.doFilter()) を利用してチェーンの次のエンティティを呼び出す
091         *    b) リクエスト処理を止めるために、リクエスト・レスポンスのペアをフィルタチェーンの次の
092         *       エンティティに渡さない
093         * 5. フィルタチェーンの次のエンティティの呼び出した後、直接レスポンスのヘッダをセット
094         *
095         * @param       req             ServletRequestオブジェクト
096         * @param       res             ServletResponseオブジェクト
097         * @param       chain   FilterChainオブジェクト
098         * @throws IOException 入出力エラーが発生したとき
099         * @throws ServletException サーブレット関係のエラーが発生した場合、throw されます。
100         */
101        public void doFilter( final ServletRequest req,
102                                                        final ServletResponse res,
103                                                        final FilterChain chain )
104                                                                throws IOException, ServletException {
105                if(req instanceof HttpServletRequest && res instanceof HttpServletResponse) {
106                        HttpServletRequest  request  = (HttpServletRequest) req;
107                        HttpServletResponse response = (HttpServletResponse) res;
108
109                        try {
110                                request.setCharacterEncoding( "UTF-8" );
111                        }
112                        catch ( UnsupportedEncodingException ex ) {
113                                throw new RuntimeException( ex );
114                        }
115
116                        String filename = makeFileName( request );
117                        if( filename != null ) {
118                                FileResponseWrapper wrappedResponse = new FileResponseWrapper(response,filename);
119                                chain.doFilter(req, wrappedResponse);
120                                wrappedResponse.finishResponse();
121                        }
122                        else {
123                                chain.doFilter(req, res);
124                        }
125                }
126        }
127
128        /**
129         * フィルターの初期処理メソッドです。
130         *
131         * フィルターに対してweb.xml で初期パラメータを設定します。
132         *   ・startTime:停止開始時刻
133         *   ・stopTime :停止終了時刻
134         *   ・filename :停止時メッセージ表示ファイル名
135         *
136         * @og.rev 5.7.3.2 (2014/02/28) Tomcat8 対応。getRealPath( "/" ) の互換性のための修正。
137         *
138         * @param filterConfig FilterConfigオブジェクト
139         */
140        public void init(final FilterConfig filterConfig) {
141                ServletContext context = filterConfig.getServletContext();
142//              String realPath = context.getRealPath( "/" );
143                String realPath = context.getRealPath( "" ) + File.separator;   // 5.7.3.2 (2014/02/28) Tomcat8 対応
144
145                String dir = filterConfig.getInitParameter("saveDir");
146                if( dir != null && dir.length() > 1 ) {
147                        dir = dir.replace( '\\','/' );
148                        if( dir.charAt(0) == '/' || dir.charAt(1) == ':' ) {
149                                saveDir = dir;
150                        }
151                        else {
152                                saveDir = realPath + dir ;
153                        }
154
155                        if( dir.charAt(dir.length()-1) != '/' ) {
156                                saveDir = saveDir + "/" ;
157                        }
158                }
159                else {
160                        saveDir = realPath + "filetemp/DIR/" ;
161                }
162        }
163
164        /**
165         * Filter インターフェースの destroy メソッド (何もしません)。
166         *
167         * サービス状態を終えた事を Filter に伝えるために Web コンテナが呼び出します。
168         * Filter の doFilter メソッドが終了したか、タイムアウトに達した全てのスレッドにおいて、
169         * このメソッドを一度だけ呼び出されます。 Web コンテナがこのメソッドを呼び出した後は、
170         * Filter のこのインスタンスにおいて二度と doFilter メソッドを呼び出す事はありません。
171         *
172         * このメソッドは、フィルタに保持されている(例えば、メモリ、ファイルハンドル、スレッド)
173         * 様々なリソースを開放する機会を与え、 あらゆる永続性の状態が、メモリ上における Filter
174         * の現在の状態と同期しているように注意してください。
175         */
176        public void destroy() {
177                // noop
178        }
179
180        /**
181         * セーブするファイル名を、リクエスト情報より取得します。
182         *
183         * リクエストされたファイル(.jsp)を、HTMLファイル(.htm)にするだけでなく、
184         * 呼び出されたときの command を元に、ファイル名を作成します。
185         *   command="NEW"    + forward.jsp ⇒  "forward.htm"
186         *   command="RENEW"  + forward.jsp ⇒  "renew.htm"
187         *   command="日本語名+ forward.jsp ⇒  "コマンド名.htm"
188         *   command="日本語名+ update.jsp  ⇒  "コマンド名.htm"
189         *   command="NEW"    + index.jsp   ⇒  "indexNW.htm"
190         *   command="RENEW"  + index.jsp   ⇒  "indexRNW.htm"
191         *   command="NEW"    + query.jsp   ⇒  "queryNW.htm"
192         *   command="NEW"    + resultXX.jsp ⇒  "forwardXX.htm"                 5.6.3.4 (2013/04/26) result.jsp にフレームを使うパターン(3ペイン)
193         *   matrixMenu対応
194         *                 URI分離          URI分離           request取出
195         *              @ gamenId="jsp"  + index.jsp       + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         Matrixメニューからの画面呼出し。
196         *              A gamenId="jsp"  + result.jsp      + GAMENID=XXXX  ⇒ saveDir + "XXXX/index.htm"            画面QUERYのヘッダーメニュー
197         *              B gamenId="jsp"  + index.jsp       + group=YYYY    ⇒ saveDir + "jsp/indexYYYY.htm"         (存在しないはず)
198         *              C gamenId="menu" + multiMenu.jsp   + GAMENID=XXXX  ⇒ saveDir + "jsp/menuXXXX.htm"          (存在しないはず)
199         *              D gamenId="menu" + multiMenu.jsp   + group=YYYY    ⇒ saveDir + "jsp/menuYYYY.htm"          通常メニューのグループ選択
200         *              E gamenId="menu" + matrixMenu.jsp  + group=YYYY    ⇒ saveDir + "menu/matrixMenuYYYY.htm"   Matrixメニューのグループ選択
201         *   その他             xxxx.jsp    ⇒  "xxxx.htm"
202         *
203         * このメソッドは、フィルタに保持されている(例えば、メモリ、ファイルハンドル、スレッド)
204         * 様々なリソースを開放する機会を与え、 あらゆる永続性の状態が、メモリ上における Filter
205         * の現在の状態と同期しているように注意してください。
206         *
207         * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。
208         * @og.rev 4.3.3.0 (2008/10/01) Matrixメニュー対応
209         * @og.rev 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
210         * @og.rev 5.6.3.4 (2013/04/26) 5.6.3.4 (2013/04/26) command="NEW" + resultXX.jsp ⇒  "forwardXX.htm"。 result.jsp にフレームを使うパターン(3ペイン)
211         * @og.rev 5.6.4.2 (2013/05/17) Matrixメニュー buttonRequest 廃止対応
212         *
213         * @param request ServletRequestオブジェクト
214         *
215         * @return      セーブするファイル名
216         */
217        private String makeFileName( final ServletRequest request ) {
218                HttpServletRequest myReq = (HttpServletRequest) request;
219                String requestURI = myReq.getRequestURI();
220
221                int index2     = requestURI.lastIndexOf( '/' );
222                String jspID   = requestURI.substring( index2+1 );
223                int index1     = requestURI.lastIndexOf( '/',index2-1 );
224                String gamenId = requestURI.substring( index1+1,index2 );
225
226                String file = null;
227
228                if( jspID != null && jspID.endsWith( ".jsp" ) ) {
229                        String cmd = request.getParameter( "command" );
230                        if( cmd != null && jspID.equals( "forward.jsp" ) ) {
231                                if( "NEW".equals( cmd ) ) { file = "forward.htm"; }
232                                else if( "RENEW".equals( cmd ) || "REVIEW".equals( cmd ) ) { file = "renew.htm"; }
233                                else {
234                                        String xferVal = request.getParameter( HybsSystem.NO_XFER_KEY + cmd );
235                                        // 5.5.2.5 (2012/05/21) update.jsp に出力されるファイルを、コマンド名.htm に出力するように機能追加
236                                        if( "update.jsp".equals( xferVal ) ) {
237                                                file = cmd + ".htm" ;
238                                        }
239                                        else if( xferVal != null && xferVal.endsWith( "jsp" ) ) {
240                                                file = xferVal.toLowerCase(Locale.JAPAN).replace( "jsp","htm" );
241                                        }
242                                        else {
243                                                String xferCmd = request.getParameter( HybsSystem.NO_XFER_KEY + cmd + "CMD" );
244                                                if( xferCmd != null ) {
245                                                        file = xferCmd.toLowerCase(Locale.JAPAN) + ".htm";
246                                                }
247                                                else {
248                                                        file = cmd.toLowerCase(Locale.JAPAN) + ".htm";
249                                                }
250                                        }
251                                }
252                        }
253                        else if( "index.jsp".equals( jspID ) && ( "RENEW".equals( cmd ) || "REVIEW".equals( cmd ) ) ) {
254                                file = "indexRNW.htm";
255                        }
256                        else if( "index.jsp".equals( jspID ) && "NEW".equals( cmd ) ) {
257                                file = "indexNW.htm";
258                        }
259                        else if( "query.jsp".equals( jspID ) && "NEW".equals( cmd ) ) {
260                                file = "queryNW.htm";
261                        }
262                        // 5.6.3.4 (2013/04/26) command="NEW" + resultXX.jsp ⇒  "forwardXX.htm"。 result.jsp にフレームを使うパターン(3ペイン)
263//                      else if( jspID.startsWith( "result" ) && jspID.endsWith( ".jsp" ) && "NEW".equals( cmd ) ) {
264                        else if( jspID.startsWith( "result" ) && "NEW".equals( cmd ) ) {
265                                file = "forward" + jspID.substring( 6,jspID.length()-4 ) + ".htm" ;
266                        }
267                        // 5.6.4.2 (2013/05/17) fileDownload.jsp の対応
268                        else if( "fileDownload.jsp".equals( jspID ) ) {
269                                gamenId = request.getParameter( "GAMENID" );    // gamenId(元はフォルダを抽出)をリクエスト変数から取得する。
270                                file    = request.getParameter( "filename" );   // 日本語ファイル名で抽出する場合。
271                                file    = "fileDownload:" + file ;                              // ただし、セーブ時は、UnicodeLittle なので、"fileDownload:" でマーカーする。
272                        }
273                        else {
274                                file = jspID.substring( 0,jspID.length()-4 ) + ".htm" ;
275                        }
276
277                        // 5.6.4.2 (2013/05/17) Matrixメニュー 対応
278                        //    URI分離          URI分離           request取出
279                        // @ gamenId="jsp"  + index.jsp       + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         Matrixメニューからの画面呼出し。
280                        // A gamenId="jsp"  + result.jsp      + GAMENID=XXXX  ⇒ saveDir + "jsp/indexXXXX.htm"         画面QUERYのヘッダーメニュー
281                        // B gamenId="menu" + multiMenu.jsp   + group=YYYY    ⇒ saveDir + "menu/menuYYYY.htm"         通常メニューのグループ選択
282                        // C gamenId="menu" + matrixMenu.jsp  + group=YYYY    ⇒ saveDir + "menu/matrixMenuYYYY.htm"   Matrixメニューのグループ選択
283                        String guiKey = request.getParameter( "GAMENID" );
284                        String group  = request.getParameter( "group" );
285
286                        if( "jsp".equals( gamenId ) && guiKey != null ) {
287                                if( "index.jsp".equals( jspID ) || "result.jsp".equals( jspID ) ) {
288                                        file = "jsp/index" + guiKey + ".htm";                           // @,A
289                                }
290                        }
291                        else if( group != null ) {
292                                if( "multiMenu.jsp".equals( jspID ) ) {
293                                        file = gamenId + "/menu" + group + ".htm";                      // B
294                                }
295                                else if( "matrixMenu.jsp".equals( jspID ) ) {
296                                        file = gamenId + "/matrixMenu" + group + ".htm";        // C
297                                }
298                                gamenId = "jsp" ;                       // トリッキー
299                        }
300
301                        // 5.6.4.2 (2013/05/17) Matrixメニュー buttonRequest 廃止
302//                      // 4.3.3.0 (2008/10/01) Matrixメニュー対応
303//                      //   GAMENID=XXXX&buttonRequest=true + index.jsp      ⇒ "jsp/indexXXXX.htm"
304//                      //   GAMENID=XXXX&buttonRequest=true + multiMenu.jsp  ⇒ "jsp/menuXXXX.htm"
305//                      //   group=YYYY&buttonRequest=true   + matrixMenu.jsp ⇒ "menu/matrixMenuYYYY.htm"
306//                      String guiKey = request.getParameter( "GAMENID" );
307//                      String btnReq = request.getParameter( "buttonRequest" );
308//                      if( "true".equals( btnReq ) ) {
309//                              if( guiKey != null ) {
310//                                      if( "index.jsp".equals( jspID ) ) {
311//                                              file = "jsp/index" + guiKey + ".htm";
312//                                      }
313//                                      else if( "multiMenu.jsp".equals( jspID ) ) {
314//                                              file = "jsp/menu" + guiKey + ".htm";
315//                                              gamenId = "jsp" ;       // トリッキー
316//                                      }
317//                              }
318//                              else {
319//
320//                                      String group = request.getParameter( "group" );
321//                                      if( group  != null ) {
322//                                              if( "matrixMenu.jsp".equals( jspID ) ) {
323//                                                      file = "menu/matrixMenu" + group + ".htm";
324//                                                      gamenId = "jsp" ;       // トリッキー
325//                                              }
326//                                      }
327//                              }
328//                      }
329
330//                      int index1     = requestURI.lastIndexOf( '/',index2-1 );
331//                      String gamenId = requestURI.substring( index1+1,index2 );
332
333                        if( "jsp".equals( gamenId ) ) { file = saveDir + file; }
334                        else { file = saveDir + gamenId + "/" + file; }
335
336//                      File fl = new File( saveDir + gamenId );
337//                      if( !fl.exists() && !fl.mkdirs() ) {
338                        File fl = new File( file ).getParentFile();
339                        if( fl != null && !fl.exists() && !fl.mkdirs() ) {
340                                String errMsg = "所定のフォルダが作成できませんでした。[" + fl + "]" ;
341                                throw new RuntimeException( errMsg );
342                        }
343                }
344
345                return file;
346        }
347}