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.taglib; 017 018import org.opengion.hayabusa.common.HybsSystem; 019import org.opengion.hayabusa.common.HybsSystemException; 020import org.opengion.fukurou.util.StringUtil; 021import static org.opengion.fukurou.util.StringUtil.nval ; 022 023import org.apache.commons.codec.binary.Base64 ; 024import java.nio.charset.Charset; // 5.5.2.6 (2012/05/25) 025 026/** 027 * Cookie を読み書きするタグです。 028 * 029 * Cookie は少量の情報を Servlet から Web ブラウザに送り、 ブラウザにそれを 030 * 維持しもらい、以降のアクセスでサーバに送り返してもらう仕組です。 031 * Cookie の値はクライアントを一意に識別できるようになっているので一般に 032 * セッション管理に用いられています。 033 * 034 * Cookie には名前と値が一つありますが、他にコメントやパス、ドメイン、 035 * 最長存続期間、バージョンといったオプショナルな属性もあります。 036 * Web ブラウザの中にはオプショナルな属性の扱いにバグがあるものがあります。 037 * このため、Servlet の相互運用性を高めるためにはあまり使わないほうがいいでしょう。 038 * 039 * 標準の JavaScript で登録機能はサポートしていましたが、メモリのみで、かつ 040 * 画面単位の書き込みのみでした。 041 * 今回の cookie タグでは、永続化(maxAge)の設定や、システム内(CONTEXT_NAME以下) 042 * での共有(デフォルト)や、その変更、ドメインを指定しての共有(domain)などの 043 * 機能を持っています。 044 * また、漢字コードでの読み書き(useBase64)にも対応しています。 045 * 読み込みに関しては、漢字を指定しなければ、{@SYS.COOKIE.カラム名}で、使用可能です。 046 * 複数の読み込み、また、漢字コードを含むクッキーの場合は、読み込み(action="LOAD") 047 * してください。指定のキー以外に、別名に読み込む(aliasNames)事も可能です。 048 * 049 * @og.formSample 050 * ●形式: 051 * <og:cookie 052 * action = "SAVE" Cookie に対するアクションを指定します。(SAVE|LOAD|DELETE) 053 * keys = "AAA,BBB" キーをCSV形式で複数指定できます。 054 * vals = "VAL1,VAL2" 値をCSV形式で複数指定できます。 055 * path = "/ge" クライアントがこの Cookie を返さなくてはいけないパスを指定します。 056 * domain = ".foo.com" この Cookie がどこで生成されたかを表すドメインを指定します。 057 * maxAge = "3600" Cookie の最長存続期間を秒単位で設定します。 058 * useBase64 = "false" 漢字等の2Byte文字を使用する場合に、BASE64で処理します。[true/false] 059 * > 060 * ●body:なし 061 * 062 * ●Tag定義: 063 * <og:cookie 064 * action ○【TAG】アクション(SAVE,LOAD,DELETE)をセットします(必須)。 065 * keys ○【TAG】クッキーのキーをCSV形式で複数指定します(必須)。 066 * vals 【TAG】keys属性に対応する値をCSV形式で複数指定します 067 * aliasNames 【TAG】クッキーのキーの別名をCSV形式で複数指定します 068 * path 【TAG】クライアントがこの Cookie を返さなくてはいけないパスを指定します(初期値:/+CONTEXT_NAME) 069 * domain 【TAG】この Cookie がどこで生成されたかを表すドメインを指定します(初期値:付与したサーバ) 070 * maxAge 【TAG】Cookie の最長存続期間を秒単位で設定します(初期値: -1 ) 071 * useBase64 【TAG】漢字等の文字を扱う場合に、BASE64で処理を行うかどうか[true/false]を設定します(初期値:false ) 072 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 073 * /> 074 * 075 * ●使用例 076 * 例1)設定:複数キーを同時に書き込むことが可能です。 077 * <og:cookie 078 * action="SAVE" keys="CDJ,FG_NAME" vals="{@CDJ},{@NAME}" 079 * /> 080 * 081 * 例2)取得:cookieタグで取得すると、それ以降では {@CDJ} や {@NAME} で扱えます。 082 * aliasNames 属性を使わない場合は、keys に指定した変数にセットされます。 083 * <og:cookie 084 * action="LOAD" keys="CDJ,FG_NAME" aliasNames="CDJ,NAME" 085 * /> 086 * 087 * 例3)取得:SYS パラメータでの取得も可能です。 088 * {@SYS.COOKIE.CDJ} 089 * 090 * 例4) QUERY画面では値の表示(LOAD)を行い、RESULT画面で値の設定(SAVE)を行うケース 091 * 092 * QUERY画面 093 * <og:cookie action="LOAD" useBase64="true" 094 * keys="CLM,NAME" aliasNames="CLM,LABEL_NAME" /> 095 * 096 * <og:column name="CLM" defaultVal="{@CLM}" /> 097 * <og:column name="LABEL_NAME" defaultVal="{@LABEL_NAME}"/> 098 * 099 * RESULT画面 100 * <og:cookie action="SAVE" maxAge="360000" useBase64="true" 101 * keys="CLM,NAME" vals="{@CLM},{@LABEL_NAME}" /> 102 * 103 * 例5) QUERY画面では、{@SYS.COOKIE.カラム名} で取得。 104 * RESULT画面では、ムラタ内全システム共通に使える値をセット。 105 * 106 * QUERY画面 107 * <og:column name="SYSTEM_ID" defaultVal="{@SYS.COOKIE.SYSTEM_ID}" /> 108 * 109 * RESULT画面 110 * <og:cookie action="SAVE" maxAge="360000" domain=".opengion.org" 111 * keys="SYSTEM_ID" vals="{@SYSTEM_ID}" /> 112 * 113 * @og.rev 3.8.0.2 (2005/06/30) 新規作成 114 * @og.group 画面制御 115 * 116 * @version 0.9.0 2000/10/17 117 * @author Kazuhiko Hasegawa 118 * @since JDK5.0, 119 */ 120public class CookieTag extends CommonTagSupport { 121 //* このプログラムのVERSION文字列を設定します。 {@value} */ 122 private static final String VERSION = "5.5.2.6 (2012/05/25)" ; 123 124 private static final long serialVersionUID = 552620120525L ; 125 126 /** 127 * プラットフォーム依存のデフォルトの Charset です。 128 * プラットフォーム依存性を考慮する場合、エンコード指定で作成しておく事をお勧めします。 129 * 130 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応 131 */ 132 private static final Charset DEFAULT_CHARSET = Charset.defaultCharset() ; 133 134 /** action 引数に渡す事の出来る アクション 設定 {@value} */ 135 public static final String ACT_SAVE = "SAVE" ; 136 /** action 引数に渡す事の出来る アクション 取得 {@value} */ 137 public static final String ACT_LOAD = "LOAD" ; 138 /** action 引数に渡す事の出来る アクション 削除 {@value} */ 139 public static final String ACT_DELETE = "DELETE" ; 140 141 /** action 引数に渡す事の出来る アクション リスト */ 142 private static final String[] ACTION_LIST = new String[] { ACT_SAVE , ACT_LOAD , ACT_DELETE }; 143 144 private String action = null; 145 private String[] keys = null; 146 private String[] vals = null; 147 private String[] aliasNames = null; 148 private String path = "/" + HybsSystem.sys( "CONTEXT_NAME" ); 149 private String domain = null; 150 private int maxAge = -1; 151 private boolean useBase64 = false; 152 153 /** 154 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 155 * 156 * @return 後続処理の指示(EVAL_PAGE) 157 */ 158 @Override 159 public int doEndTag() { 160 debugPrint(); // 4.0.0 (2005/02/28) 161 actionExec( action ); 162 163 return EVAL_PAGE ; 164 } 165 166 /** 167 * タグリブオブジェクトをリリースします。 168 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 169 * 170 */ 171 @Override 172 protected void release2() { 173 super.release2(); 174 action = null; 175 keys = null; 176 vals = null; 177 aliasNames = null; 178 path = "/" + HybsSystem.sys( "CONTEXT_NAME" ); 179 domain = null; 180 maxAge = -1; 181 useBase64 = false; 182 } 183 184 /** 185 * アクションを実行します。 186 * 187 * アクションは action 属性で指定します。 188 * 189 * @param action アクション(public static final 宣言されている文字列) 190 */ 191 private void actionExec( final String action ) { 192 193 if( ACT_SAVE.equals( action ) ) { 194 saveCookies( maxAge ); 195 } 196 else if( ACT_LOAD.equals( action ) ) { 197 loadCookies(); 198 } 199 else if( ACT_DELETE.equals( action ) ) { 200 saveCookies( 0 ); // maxAge にゼロをセットすると削除されます。 201 } 202 else { 203 String errMsg = "指定のアクションは実行できません。アクションエラー" 204 + HybsSystem.CR 205 + "action=[" + action + "] " 206 + HybsSystem.CR 207 + StringUtil.array2csv( ACTION_LIST ) ; 208 throw new HybsSystemException( errMsg ); 209 } 210 } 211 212 /** 213 * SAVE アクションを実行します。 214 * 215 * クッキーに書き込みを行います。 216 * 217 * @param maxAge 最長存続期間( 0 なら削除 ) 218 */ 219 private void saveCookies( final int maxAge ) { 220 // BASE64Encoder encoder = null; 221 for( int i=0; i<keys.length; i++ ) { 222 String value = nval (vals[i],"" ); 223 if( useBase64 && value.length() > 0 ) { 224 // if( encoder == null ) { encoder = new BASE64Encoder(); } 225 // value = encoder.encode( value.getBytes() ) ; 226 227 byte[] encoded = Base64.encodeBase64( value.getBytes( DEFAULT_CHARSET ) ); // 5.5.2.6 (2012/05/25) findbugs対応 228 value = new String( encoded,DEFAULT_CHARSET ); // 5.5.2.6 (2012/05/25) findbugs対応 229 } 230 setCookie( keys[i], value, maxAge ); 231 } 232 } 233 234 /** 235 * LOAD アクションを実行します。 236 * 237 * クッキーから読み込みを行います。 238 * 239 */ 240 private void loadCookies() { 241 // BASE64Decoder decoder = null; 242 if( aliasNames == null ) { aliasNames = keys; } 243 244 // try { 245 for( int i=0; i<keys.length; i++ ) { 246 String value = getCookie( keys[i] ); 247 if( useBase64 && value != null && value.length() > 0 ) { 248 // if( decoder == null ) { decoder = new BASE64Decoder(); } 249 // byte[] decoded = decoder.decodeBuffer( value ); 250 byte[] decoded = Base64.decodeBase64( value.getBytes( DEFAULT_CHARSET ) ); // 5.5.2.6 (2012/05/25) findbugs対応 251 value = new String( decoded,DEFAULT_CHARSET ); // 5.5.2.6 (2012/05/25) findbugs対応 252 } 253 if( value != null ) { 254 setRequestAttribute( aliasNames[i],value ); 255 } 256 } 257 // } 258 // catch( IOException ex ) { 259 // String errMsg = "BASE64Decoder エラー " + ex.toString(); 260 // throw new HybsSystemException( errMsg ); 261 // } 262 } 263 264 /** 265 * 【TAG】アクション(SAVE,LOAD,DELETE)をセットします。 266 * 267 * @og.tag 268 * アクションは,HTMLから(get/post)指定されますので,ACT_xxx で設定される 269 * フィールド定数値のいづれかを、指定できます。 270 * 無指定の場合は、なにもしません。 271 * 272 * <table border="1" frame="box" rules="all" > 273 * <caption>アクションの説明</caption> 274 * <tr><th>action </th><th>名称</th><th>機能</th></tr> 275 * <tr><td>SAVE </td><td>登録</td><td>指定の keys のキーに vals の値をセットします。</td></tr> 276 * <tr><td>LOAD </td><td>取得</td><td>指定の keys のクッキーを(リクエスト中に)取得します。</td></tr> 277 * <tr><td>DELETE </td><td>削除</td><td>指定の keys のクッキーを削除します。</td></tr> 278 * </table> 279 * 280 * @param act アクション(public static final 宣言されている文字列) 281 * @see <a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.CookieTag.ACT_DELETE">アクション定数</a> 282 */ 283 public void setAction( final String act ) { 284 action = nval( getRequestParameter( act ),action ); 285 286 if( action != null && !check( action, ACTION_LIST ) ) { 287 String errMsg = "指定のアクションは実行できません。アクションエラー" 288 + HybsSystem.CR 289 + "action=[" + action + "] " 290 + HybsSystem.CR 291 + StringUtil.array2csv( ACTION_LIST ) ; 292 throw new HybsSystemException( errMsg ); 293 } 294 } 295 296 /** 297 * 【TAG】クッキーのキーをCSV形式で複数指定します。 298 * 299 * @og.tag 300 * クッキーにセットするときのキーを指定します。カンマ区切りで複数指定できます。 301 * vals 属性には、キーに対応する値を、設定してください。 302 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 303 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 304 * 305 * @param key クッキーのキー 306 */ 307 public void setKeys( final String key ) { 308 keys = getCSVParameter( key ); 309 } 310 311 /** 312 * 【TAG】クッキーのキーの別名をCSV形式で複数指定します。 313 * 314 * @og.tag 315 * クッキーから値を取得する(action="LOAD")場合に、読み込みキー(keys)に対応する 316 * 別名を指定することで、別名の変数に読み込んだ値を登録することが出来ます。 317 * 別名を指定しない場合は、keys に指定された名前が、使用されます。 318 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 319 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 320 * 321 * @param names クッキーの別名 322 */ 323 public void setAliasNames( final String names ) { 324 aliasNames = getCSVParameter( names ); 325 } 326 327 /** 328 * 【TAG】keys属性に対応する値をCSV形式で複数指定します。 329 * 330 * @og.tag 331 * キーに設定した値を、カンマ区切り文字で複数して出来ます。 332 * 指定順序は、キーと同じにしておいて下さい。 333 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 334 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 335 * 336 * @param val keys属性に対応する値 337 */ 338 public void setVals( final String val ) { 339 vals = getCSVParameter( val ); 340 } 341 342 /** 343 * 【TAG】クライアントがこの Cookie を返さなくてはいけないパスを指定します(初期値:/+CONTEXT_NAME)。 344 * 345 * @og.tag 346 * この値を指定すると Cookie が該当するディレクトリ内、さらに、 347 * サブディレクトリに存在する全てのページから参照できるようになります。 348 * Cookie のパスには Cookie をセットした Servlet が含まれていなければなりません。 349 * 例えば、/catalog を指定したとします。このとき、 サーバの /catalog 以下の全ての 350 * ディレクトリから Cookie が見えるようになります。 351 * 初期値は、"/" + CONTEXT_NAME です。 352 * 353 * Cookie のパス名指定についての詳細は RFC2109 を参照してください。 354 * 355 * @param uri パスを指定するString 356 */ 357 public void setPath( final String uri ) { 358 path = nval( getRequestParameter( uri ),path ); 359 } 360 361 /** 362 * 【TAG】この Cookie がどこで生成されたかを表すドメインを指定します(初期値:付与したサーバ)。 363 * 364 * @og.tag 365 * ドメイン名の形式は RFC2109 で指定されています。 366 * ドメイン名は (.foo.com のように) ドットで始まります。 このように設定すると、 367 * Cookie は指定された Domain Name System (DNS) のゾーン内のサーバから見える 368 * ようになります(例えば、www.foo.com) からは見えるけれど、a.b.foo.com からは 369 * 見えないというようにです)。 デフォルトでは Cookie を付与したサーバにしか送り返しません。 370 * 371 * @param pattern この Cookie が見えてもよいドメイン名を指定するString 372 */ 373 public void setDomain( final String pattern ) { 374 domain = nval( getRequestParameter( pattern ),domain ); 375 } 376 377 /** 378 * 【TAG】Cookie の最長存続期間を秒単位で設定します(初期値: -1 )。 379 * 380 * @og.tag 381 * 正の値が指定されると Cookie はある秒数が過ぎた後、削除されます。 382 * この値は、Cookie の有効期限が切れる 最長 存続期間であることに注意してください。 383 * Cookie の現在までの存続期間ではありません。 384 * 385 * 負の値は Cookie が永続的に保存されないことを意味しています。 この場合、 386 * Web ブラウザが終了すると Cookie も削除されます。 0 という値を指定すると 387 * Cookie が削除されることになります。 388 * 初期値は、-1(永続的に保存されない)です。 389 * 390 * @param expiry Cookie の最長存続期間を秒単位で指定する整数値。 負の値は Cookie を保存しない、 0 なら Cookie を削除する意味となる。 391 */ 392 public void setMaxAge( final String expiry ) { 393 maxAge = nval( getRequestParameter( expiry ),maxAge ); 394 } 395 396 /** 397 * 【TAG】漢字等の文字を扱う場合に、BASE64で処理を行うかどうか[true/false]を設定します(初期値:false )。 398 * 399 * @og.tag 400 * クッキーへの読み書きは、ASCII に限られます。漢字等のコードを書き込む場合は、 401 * BASE64でエンコードして書き込む必要があります。読み込む場合も同様です。 402 * ただし、一般のASCIIは、BASE64 ではエンコードしないため、外部で指定する必要があります。 403 * BASE64 で書き込んだ場合ば、{@SYS.COOKIE.CDJ} での取得はできませんので、 404 * action="LOAD" で、取得してください。 405 * 初期値は、false(使用しない)です。 406 * 407 * @param flag 漢字等の文字を扱う場合に、BASE64で処理を行うかどうか[true/false] 408 */ 409 public void setUseBase64( final String flag ) { 410 useBase64 = nval( getRequestParameter( flag ),useBase64 ); 411 } 412 413 /** 414 * このオブジェクトの文字列表現を返します。 415 * 基本的にデバッグ目的に使用します。 416 * 417 * @return このクラスの文字列表現 418 */ 419 @Override 420 public String toString() { 421 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 422 .println( "VERSION" ,VERSION ) 423 .println( "action" ,action ) 424 .println( "keys" ,keys ) 425 .println( "vals" ,vals ) 426 .println( "aliasNames" ,aliasNames ) 427 .println( "path" ,path ) 428 .println( "domain" ,domain ) 429 .println( "maxAge" ,maxAge ) 430 .println( "useBase64" ,useBase64 ) 431 .println( "Other..." ,getAttributes().getAttribute() ) 432 .fixForm().toString() ; 433 } 434}