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.hayabusa.resource.GUIInfo; 021import org.opengion.fukurou.db.Transaction; 022import org.opengion.fukurou.db.TransactionReal; 023// import org.opengion.fukurou.util.FileUtil; 024// import org.opengion.fukurou.util.Closer ; 025import org.opengion.fukurou.util.StringUtil; 026// import org.opengion.fukurou.xml.HybsXMLSave ; 027import org.opengion.fukurou.xml.XMLFileLoader; // 6.0.0.0 (2014/04/11) XMLFileLoader を使う様に変更 028 029import static org.opengion.fukurou.util.StringUtil.nval ; 030 031import java.sql.Connection; 032 033import java.io.File; 034// import java.io.BufferedReader; 035import java.io.StringWriter; // 6.0.0.0 (2014/04/11) XMLFileLoader に渡す Log 036import java.util.Map; 037import java.util.HashMap; 038import java.util.Arrays; // 6.0.0.0 (2014/04/11) keys,vals のエラーメッセージ作成用 039 040/** 041 * 指定の拡張XDK形式ファイルを直接データベースに登録するデータ入力タグです。 042 * 043 * このクラスは、オラクル XDKの oracle.xml.sql.dml.OracleXMLSave クラスと 044 * ほぼ同様の目的で使用できる org.opengion.fukurou.xml.HybsXMLSave のラッパークラスです。 045 * 046 * 拡張XDK形式のXMLファイルを読み込み、データベースに INSERT します。 047 * 拡張XDK形式の元となる オラクル XDK(Oracle XML Developer's Kit)については、以下の 048 * リンクを参照願います。 049 * <a href="http://otn.oracle.co.jp/software/tech/xml/xdk/index.html" target="_blank" > 050 * XDK(Oracle XML Developer's Kit)</a> 051 * 052 * このタグでは、keys,vals を登録することにより、XMLファイルに存在しないカラムを 053 * 追加したり、XMLファイルの情報を書き換えることが可能になります。 054 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 055 * 登録するなどです。 056 * 057 * 拡張XDK形式とは、ROW 以外に、SQL処理用タグ(EXEC_SQL)を持つ XML ファイルです。 058 * また、登録するテーブル(table)を ROWSETタグの属性情報として付与することができます。 059 * (大文字小文字に注意) 060 * これは、オラクルXDKで処理する場合、無視されますので、同様に扱うことが出来ます。 061 * この、EXEC_SQL は、それそれの XMLデータをデータベースに登録する際に、 062 * SQL処理を自動的に流す為の、SQL文を記載します。 063 * この処理は、イベント毎に実行される為、その配置順は重要です。 064 * このタグは、複数記述することも出来ますが、BODY部には、1つのSQL文のみ記述します。 065 * 066 * 6.0.0.0 (2014/04/11) 067 * 指定のファイルがフォルダの場合は、以下のファイルすべて(拡張子はxml)を対象に読込-登録します。 068 * また、拡張子が、zip の場合は、zip内部の xml ファイルを読込-登録します。 069 * 070 * ※ このタグは、Transaction タグの対象です。 071 * 072 * <ROWSET tableName="XX" > 073 * <EXEC_SQL> 最初に記載して、初期処理(データクリア等)を実行させる。 074 * delete from GEXX where YYYYY 075 * </EXEC_SQL> 076 * <MERGE_SQL> このSQL文で UPDATEして、結果が0件ならINSERTを行います。 077 * update GEXX set AA=[AA] , BB=[BB] where CC=[CC] 078 * </MERGE_SQL> 079 * <ROW num="1"> 080 * <カラム1>値1</カラム1> 081 * ・・・ 082 * <カラムn>値n</カラムn> 083 * </ROW> 084 * ・・・ 085 * <ROW num="n"> 086 * ・・・ 087 * </ROW> 088 * <EXEC_SQL> 最後に記載して、項目の設定(整合性登録)を行う。 089 * update GEXX set AA='XX' , BB='XX' where YYYYY 090 * </EXEC_SQL> 091 * <ROWSET> 092 * 093 * @og.formSample 094 * ●形式:<og:directXMLSave fileURL="[・・・]" ・・・ /> 095 * ●body:なし 096 * 097 * ●Tag定義: 098 * <og:directXMLSave 099 * dbid 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT) 100 * fileURL 【TAG】読み取り元ディレクトリ名を指定します (初期値:FILE_URL[=filetemp/]) 101 * filename 【TAG】ファイルを作成するときのファイル名をセットします (初期値:null) 102 * displayMsg 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0003[ファイルの登録が完了しました。]) 103 * keys 【TAG】XMLファイルを読み取った後で指定するキーをCSV形式で複数指定します 104 * vals 【TAG】XMLファイルを読み取った後で指定する値をCSV形式で複数指定します 105 * caseKey 【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 106 * caseVal 【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 107 * caseNN 【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:true) 108 * caseNull 【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:true) 109 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 110 * /> 111 * 112 * ●使用例 113 * <og:directXMLSave 114 * dbid = "ORCL" 接続データベースID(初期値:DEFAULT) 115 * fileURL = "{@USER.ID}" 読み取り元ディレクトリ名 116 * filename = "{@filename}" 読み取り元ファイル名 117 * displayMsg = "MSG0003" 登録完了後のメッセージ 118 * /> 119 * 120 * @og.group ファイル入力 121 * @og.rev 4.0.0.0 (2007/03/08) 新規追加 122 * @og.rev 6.0.0.0 (2014/04/11) 単体ファイル以外(フォルダ、ZIPファイル)への対応 123 * 124 * @version 4.0 125 * @author Kazuhiko Hasegawa 126 * @since JDK5.0, 127 */ 128public class DirectXMLSaveTag extends CommonTagSupport { 129 //* このプログラムのVERSION文字列を設定します。 {@value} */ 130 private static final String VERSION = "6.0.0.0 (2014/04/11)" ; 131 132 private static final long serialVersionUID = 600020140411L ; 133 134// private static final String ENCODE = "UTF-8"; // 6.0.0.0 (2014/04/11) XMLFileLoader を使うため、廃止 135 private String dbid = null; 136 private String fileURL = HybsSystem.sys( "FILE_URL" ); // 4.0.0 (2005/01/31) 137// private String filename = HybsSystem.sys( "FILE_FILENAME" ); // ファイル名 138 private String filename = null; // 6.0.0.0 (2014/04/11) 初期値:null 139// private String displayMsg = "MSG0040"; // 件登録しました。 140 private String displayMsg = "MSG0003"; // ファイルの登録が完了しました。 141 private String[] keys = null; 142 private String[] vals = null; 143 private long dyStart = 0; // 実行時間測定用のDIV要素を出力します。 144 145 /** 146 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 147 * 148 * @og.rev 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 149 * 150 * @return 後続処理の指示( SKIP_BODY ) 151 */ 152 @Override 153 public int doStartTag() { 154 // 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 155 if( useTag() ) { 156 dyStart = System.currentTimeMillis(); // 時間測定用 157 } 158 return SKIP_BODY ; // Body を評価しない 159 } 160 161 /** 162 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 163 * 164 * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel ) 165 * @og.rev 4.0.0.1 (2007/12/03) try ~ catch ~ finally をきちんと行う。 166 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 167 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 168 * @og.rev 5.5.2.6 (2012/05/25) findbugs対応。例外経路で null 値を利用することが保証されています。 169 * @og.rev 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 170 * @og.rev 5.6.7.0 (2013/07/27) DDL(データ定義言語:Data Definition Language)の処理件数追加 171 * 172 * @return 後続処理の指示 173 */ 174 @Override 175 public int doEndTag() { 176 debugPrint(); // 4.0.0 (2005/02/28) 177 // 5.6.6.1 (2013/07/12) caseKey 、caseVal 属性対応 178 if( !useTag() ) { return EVAL_PAGE ; } 179 180 StringWriter logW = new StringWriter(); // 6.0.0.0 (2014/04/11) XMLFileLoader で Logをセット 181 182 // 6.0.0.0 (2014/04/11) XMLFileLoader に渡す 読み取り開始ファイルオブジェクト。 183 String directory = HybsSystem.url2dir( fileURL ); 184 File loadFile = ( filename != null ) ? new File( directory,filename ) : new File( directory ); 185 186// BufferedReader reader = null; 187 final int insCnt ; // 追加数だけ記録する。 188// final int updCnt ; 189// final int delCnt ; 190// final int ddlCnt ; // 5.6.7.0 (2013/07/27) DDL処理件数追加 191 boolean errFlag = true; 192 Transaction tran = null; // 5.1.9.0 (2010/08/01) Transaction 対応 193 try { 194 // 5.1.9.0 (2010/08/01) Transaction 対応 195 TransactionTag tranTag = (TransactionTag)findAncestorWithClass( this,TransactionTag.class ); 196 if( tranTag == null ) { 197 tran = new TransactionReal( getApplicationInfo() ); // 5.3.7.0 (2011/07/01) 引数変更 198 } 199 else { 200 tran = tranTag.getTransaction(); 201 } 202 203 Connection conn = tran.getConnection( dbid ); // 5.1.9.0 (2010/08/01) Transaction 対応 204 205 // 6.0.0.0 (2014/04/11) フォルダ一括登録ができるようにします。 206 XMLFileLoader loader = new XMLFileLoader( conn,false ); // リソースコネクションとuseTimeStamp=false 指定 207 if( keys != null ) { loader.setAfterMap( getAfterMap() ); } 208 loader.setLogWriter( logW ); 209 210 loader.loadXMLFiles( loadFile ); 211 212 int[] crudCnt = loader.getCRUDCount(); // 実行結果のカウント数 213 insCnt = crudCnt[XMLFileLoader.INS]; 214 215// HybsXMLSave save = new HybsXMLSave( conn ); 216// if( keys != null ) { save.setAfterMap( getAfterMap() ); } 217// 218// reader = getBufferedReader(); 219// save.insertXML( reader ); 220// insCnt = save.getInsertCount(); 221// updCnt = save.getUpdateCount(); 222// delCnt = save.getDeleteCount(); 223// ddlCnt = save.getDDLCount(); // 5.6.7.0 (2013/07/27) DDL処理件数追加 224 tran.commit(); // 5.1.9.0 (2010/08/01) Transaction 対応 225 errFlag = false; // エラーではない 226 } 227 catch( Throwable ex ) { 228 if( tran != null ) { // 5.5.2.6 (2012/05/25) findbugs対応 229 tran.rollback(); // 5.1.9.0 (2010/08/01) Transaction 対応 230 } 231 throw new HybsSystemException( ex ); 232 } 233 finally { 234 if( tran != null ) { // 5.5.2.6 (2012/05/25) findbugs対応 235 tran.close( errFlag ); 236 } 237// Closer.ioClose( reader ); 238 } 239 240 // 実行件数の表示 241 // 4.0.0 (2005/11/30) 出力順の変更。一番最初に出力します。 242 if( displayMsg != null && displayMsg.length() > 0 ) { 243 StringBuilder buf = new StringBuilder(); 244// buf.append( "INS:" ).append( insCnt ); 245// buf.append( " / UPD:" ).append( updCnt ); 246// buf.append( " / DEL:" ).append( delCnt ); 247// buf.append( " / DDL:" ).append( ddlCnt ); // 5.6.7.0 (2013/07/27) DDL処理件数追加 248 buf.append( "<pre>" ); 249 buf.append( logW.toString() ); // 6.0.0.0 (2014/04/11) XMLFileLoader で Logをセット 250 buf.append( HybsSystem.CR ); 251 buf.append( HybsSystem.getDate() ).append( " " ); 252 buf.append( getResource().getLabel( displayMsg ) ); 253 buf.append( HybsSystem.CR ); 254 buf.append( "</pre>" ); 255 256 jspPrint( buf.toString() ); 257 } 258 259 // 時間測定用の DIV 要素を出力 260 long dyTime = System.currentTimeMillis()-dyStart; 261 jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" ); // 3.5.6.3 (2004/07/12) 262 263 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 264 GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 265// if( guiInfo != null ) { guiInfo.addWriteCount( insCnt,dyTime,fileURL + filename ); } 266 if( guiInfo != null ) { guiInfo.addWriteCount( insCnt,dyTime,loadFile.getPath() ); } 267 268 return EVAL_PAGE ; 269 } 270 271 /** 272 * タグリブオブジェクトをリリースします。 273 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 274 * 275 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 276 * @og.rev 6.0.0.0 (2014/04/11) filename の初期値を、システムプロパティーのFILE_FILENAME から null に変更 277 * @og.rev 6.0.0.0 (2014/04/11) displayMsg の初期値を、MSG0040 から MSG0003 に変更 278 */ 279 @Override 280 protected void release2() { 281 super.release2(); 282 dbid = null; 283 fileURL = HybsSystem.sys( "FILE_URL" ); // 4.0.0 (2005/01/31) 284// filename = HybsSystem.sys( "FILE_FILENAME" ); // ファイル名 285 filename = null; // 6.0.0.0 (2014/04/11) 初期値:null 286// displayMsg = "MSG0040"; // 件登録しました。 287 displayMsg = "MSG0003"; // ファイルの登録が完了しました。 288 keys = null; 289 vals = null; 290 } 291 292 /** 293 * BufferedReader を取得します。 294 * 295 * ここでは、一般的なファイル出力を考慮した BufferedReader を作成します。 296 * 297 * @og.rev 6.0.0.0 (2014/04/11) XMLFileLoader を使うため、廃止 298 * 299 * @return ファイル入力用BufferedReaderオブジェクト 300 */ 301// private BufferedReader getBufferedReader() { 302// if( filename == null ) { 303// String errMsg = "ファイル名がセットされていません。"; 304// throw new HybsSystemException( errMsg ); 305// } 306// String directory = HybsSystem.url2dir( fileURL ); 307// File file = new File( StringUtil.urlAppend( directory,filename ) ); 308// 309// BufferedReader out = FileUtil.getBufferedReader( file,ENCODE ); 310// 311// return out ; 312// } 313 314 /** 315 * XMLファイルを読み取った後で指定するカラムと値のペア(マップ)情報を作成します。 316 * 317 * このカラムと値のペアのマップは、オブジェクト構築後に設定される為、 318 * XMLファイルのキーの存在に関係なく、Mapのキーと値が使用されます。(Map優先) 319 * key が null や ゼロ文字列の場合は、Map に追加しません。 320 * 321 * @og.rev 5.6.6.1 (2013/07/12) key が null や ゼロ文字列の場合は、Map に追加しません。 322 * @og.rev 6.0.0.0 (2014/04/11) keys と vals の個数チェックを追加 323 * 324 * @return カラムと値のペア(マップ)情報 325 */ 326 private Map<String,String> getAfterMap() { 327 // 6.0.0.0 (2014/04/11) keys と vals の個数チェックを追加 328 if( keys != null && keys.length != vals.length ) { 329 String errMsg = "keys と vals の個数が異なります。" 330 + " keys=" + Arrays.toString( keys ) 331 + " vals=" + Arrays.toString( vals ) ; 332 throw new HybsSystemException( errMsg ); 333 } 334 335 Map<String,String> map = new HashMap<String,String>(); 336 for( int i=0; i<keys.length; i++ ) { 337 if( keys[i] != null && keys[i].length() > 0 ) { // 5.6.6.1 (2013/07/12) 338 map.put( keys[i],vals[i] ); 339 } 340 } 341 return map ; 342 } 343 344 /** 345 * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。 346 * 347 * @og.tag 348 * 検索時のDB接続IDを指定します。初期値は、DEFAULT です。 349 * 350 * @param id データベース接続ID 351 */ 352 public void setDbid( final String id ) { 353 dbid = nval( getRequestParameter( id ),dbid ); 354 } 355 356 /** 357 * 【TAG】読み取り元ディレクトリ名を指定します 358 * (初期値:FILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。 359 * 360 * @og.tag 361 * この属性で指定されるディレクトリより、ファイルを読み取ります。 362 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、 363 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、 364 * fileURL = "{@USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、 365 * さらに、各個人ID別のフォルダの下より、読み取ります。 366 * (初期値:システム定数のFILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。 367 * 368 * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。 369 * 370 * @param url ファイルURL 371 * @see org.opengion.hayabusa.common.SystemData#FILE_URL 372 */ 373 public void setFileURL( final String url ) { 374 String furl = nval( getRequestParameter( url ),null ); 375 if( furl != null ) { 376 char ch = furl.charAt( furl.length()-1 ); 377 if( ch != '/' && ch != '\\' ) { furl = furl + "/"; } 378 fileURL = StringUtil.urlAppend( fileURL,furl ); 379 } 380 } 381 382 /** 383 * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:null)。 384 * 385 * @og.tag 386 * ファイルを作成するときのファイル名をセットします。 387 * (初期値:null)。 388 * 389 * @og.rev 6.0.0.0 (2014/04/11) filename の初期値を、システムプロパティーのFILE_FILENAME から null に変更 390 * 391 * @param fname ファイル名 392 */ 393 public void setFilename( final String fname ) { 394 filename = nval( getRequestParameter( fname ),filename ); 395 } 396 397 /** 398 * 【TAG】query の結果を画面上に表示するメッセージIDを指定します(初期値:MSG0003[ファイルの登録が完了しました。])。 399 * 400 * @og.tag 401 * ここでは、検索結果の件数や登録された件数をまず出力し、 402 * その次に、ここで指定したメッセージをリソースから取得して 403 * 表示します。 404 * 表示させたくない場合は, displayMsg = "" をセットしてください。 405 * 初期値は、検索件数を表示します。 406 * ※ この属性には、リクエスト変数({@XXXX})は使用できません。 407 * 408 * @param id ディスプレイに表示させるメッセージ ID 409 */ 410 public void setDisplayMsg( final String id ) { 411 if( id != null ) { displayMsg = id; } 412 } 413 414 /** 415 * 【TAG】XMLファイルを読み取った後で指定するキーをCSV形式で複数指定します。 416 * 417 * @og.tag 418 * XMLファイルを読み取った後で、データを変更できます。 419 * 変更するカラム名(キー)をCSV形式で指定します。 420 * XMLファイルにキーが存在していた場合は、vals で指定の値に書き換えます。 421 * キーが存在していない場合は、ここで指定するキーと値が、データとして 422 * 追加されます。 423 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 424 * 登録するなどの使い方を想定しています。 425 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 426 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 427 * 428 * @param key リンク先に渡すキー 429 * @see #setVals( String ) 430 */ 431 public void setKeys( final String key ) { 432 keys = getCSVParameter( key ); 433 } 434 435 /** 436 * 【TAG】XMLファイルを読み取った後で指定する値をCSV形式で複数指定します。 437 * 438 * @og.tag 439 * XMLファイルを読み取った後で、データを変更できます。 440 * 変更する値をCSV形式で指定します。 441 * XMLファイルにキーが存在していた場合は、vals で指定の値に書き換えます。 442 * キーが存在していない場合は、ここで指定するキーと値が、データとして 443 * 追加されます。 444 * 例えば、登録日や、登録者、または、テンプレートより各システムID毎に 445 * 登録するなどの使い方を想定しています。 446 * 分解方法は、CSV変数を先に分解してから、getRequestParameter で値を取得します。 447 * こうしないとデータ自身にカンマを持っている場合に分解をミスる為です。 448 * 449 * @param val keys属性に対応する値 450 * @see #setKeys( String ) 451 */ 452 public void setVals( final String val ) { 453 vals = getCSVParameter( val ); 454 } 455 456 /** 457 * このオブジェクトの文字列表現を返します。 458 * 基本的にデバッグ目的に使用します。 459 * 460 * @return このクラスの文字列表現 461 */ 462 @Override 463 public String toString() { 464 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 465 .println( "VERSION" ,VERSION ) 466 .println( "dbid" ,dbid ) 467 .println( "fileURL" ,fileURL ) 468 .println( "filename" ,filename ) 469 .println( "displayMsg" ,displayMsg ) 470 .println( "dyStart" ,dyStart ) 471 .println( "Other..." ,getAttributes().getAttribute() ) 472 .fixForm().toString() ; 473 } 474}