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 static org.opengion.fukurou.util.StringUtil.*; 019 020import java.io.File; 021import java.io.FileOutputStream; 022import java.io.IOException; 023import java.io.ObjectInputStream; 024import java.io.ObjectOutputStream; 025import java.io.PrintWriter; 026import java.sql.CallableStatement; 027import java.sql.Connection; 028import java.sql.ResultSet; 029import java.sql.ResultSetMetaData; 030import java.sql.SQLException; 031import java.sql.Statement; 032import java.sql.Types; 033import java.util.Locale ; 034import java.util.Map; 035import java.util.zip.ZipEntry; 036import java.util.zip.ZipOutputStream; 037 038import org.opengion.fukurou.db.Transaction; 039import org.opengion.fukurou.db.TransactionReal; 040import org.opengion.fukurou.model.FileOperation; 041import org.opengion.fukurou.util.Closer ; 042import org.opengion.fukurou.util.ErrorMessage; 043import org.opengion.fukurou.util.FileUtil; 044import org.opengion.fukurou.util.StringUtil; 045import org.opengion.hayabusa.common.HybsSystem; 046import org.opengion.hayabusa.common.HybsSystemException; 047import org.opengion.hayabusa.db.DBErrMsg; 048import org.opengion.hayabusa.io.HybsFileOperationFactory; 049import org.opengion.hayabusa.resource.GUIInfo; 050import org.opengion.hayabusa.resource.ResourceManager; 051 052import oracle.jdbc.OracleCallableStatement; // CURSOR が残る 053import oracle.jdbc.OracleTypes; // CURSOR が残る 054// import java.sql.Array; // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応。oracle.sql.ARRAY の置き換え 055import oracle.sql.ARRAY; // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応 056import oracle.sql.ArrayDescriptor; // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応 057// import oracle.jdbc.OracleConnection; // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ) 対応 058 059/** 060 * SELECT文を直接実行して、指定のファイルに出力するタグです。 061 * 062 * 中間の、データ(DBTableModel)を作成しないため、余計なメモリを取らず、 063 * 高速にデータを抜き出すことが可能です。 064 * 一方、抜き出すデータは生データのため、データの再利用等、システム的な 065 * 使用を想定しています。 066 * JDBCErrMsg 形式のPL/SQL をコールして、その検索結果(カーソル)を抜きこともできます。 067 * 068 * ※ このタグは、Transaction タグの対象です。 069 * 070 * @og.formSample 071 * ●形式:<og:directWriteTable filename="[・・・]" ・・・ >SELECT * FROM ZYXX </og:directWriteTable > 072 * ●body:あり(EVAL_BODY_BUFFERED:BODYを評価し、{@XXXX} を解析します) 073 * 074 * ●Tag定義: 075 * <og:directWriteTable 076 * fileURL 【TAG】保存先ディレクトリ名を指定します (初期値:FILE_URL[=filetemp/]) 077 * filename 【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME) 078 * zipFilename 【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip") 079 * encode 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします (初期値:FILE_ENCODE[=UnicodeLittle]) 080 * fileAppend 【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[通常モード]) 081 * zip 【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false) 082 * separator 【TAG】可変長ファイルを作成するときの項目区切り文字をセットします (初期値:TAB_SEPARATOR[= ]) 083 * useHeader 【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true) 084 * displayMsg 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0033[ 件検索しました]) 085 * notfoundMsg 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした]) 086 * fetchSize 【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:100) 087 * names 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します 088 * queryType 【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC}) 089 * dbid 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT) 090 * useNumber 【TAG】行番号を出力するかどうか(初期値:true) 091 * storageType 【TAG】保存先ストレージタイプを指定します(初期値:CLOUD_TARGET) 092 * bucketName 【TAG】保存先バケット名を指定します(初期値:CLOUD_BUCKET) 093 * debug 【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false) 094 * > ... Body ... 095 * </og:directWriteTable> 096 * 097 * ●使用例 098 * <og:directWriteTable 099 * dbid = "ORCL" 接続データベースID(初期値:DEFAULT) 100 * separator = "," ファイルの区切り文字(初期値:タブ) 101 * fileURL = "{@USER.ID}" 保存先ディレクトリ名 102 * filename = "{@filename}" 保存ファイル名 103 * encode = "UnicodeLittle" 保存ファイルエンコード名 104 * useHeader = "true" 保存ファイルにヘッダーを出力するかどうか 105 * zip = "true" ZIPファイルに圧縮するかどうか 106 * zipFilename = "Sample.zip" ZIPファイルのファイル名 107 * fileAppend = "true" ファイルを追加モードで登録するかどうか 108 * displayMsg = "MSG0033" 実行後の表示メッセージ 109 * fetchSize = "200" DB検索する場合のフェッチするサイズ 110 * storageType = "aws" 保存先のストレージタイプを指定します(初期値:CLOUD_STORAGE) 111 * bucketName = "mybucket001" 保存先のバケット名を指定します(初期値:CLOUD_BUCKET) 112 * > 113 * SELECT * FROM ZYXX 114 * </og:directWriteTable > 115 * 116 * <og:directWriteTable 117 * fileURL = "{@USER.ID}" 保存先ディレクトリ名 118 * filename = "{@filename}" 保存ファイル名 119 * names = "AAA,BBB,CCC,・・・" 指定のキーに対応するリクエスト値を ARG_ARRAY にセットします。 120 * queryType = "JDBCErrMsg" JDBCErrMsg 形式のPL/SQL をコールします。 121 * > 122 * { call PL/SQL(?,?,?,? ) } 123 * </og:directWriteTable > 124 * 125 * @og.rev 3.5.6.0 (2004/06/18) 新規作成 126 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)の実行を追加 127 * @og.rev 5.10.9.0 (2019/03/01) oota クラウドストレージ対応。(Fileクラスを拡張) 128 * 129 * @og.group ファイル出力 130 * 131 * @version 4.0 132 * @author Kazuhiko Hasegawa 133 * @since JDK5.0, 134 */ 135public class DirectWriteTableTag extends CommonTagSupport { 136 //* このプログラムのVERSION文字列を設定します。 {@value} */ 137 private static final String VERSION = "5.7.2.3 (2014/01/31)" ; 138 139 private static final long serialVersionUID = 572320140131L ; 140 141 private static final String TAB_SEPARATOR = "\t" ; 142 private static final String errMsgId = HybsSystem.ERR_MSG_KEY; 143 144 private final int DB_MAX_QUERY_TIMEOUT = HybsSystem.sysInt( "DB_MAX_QUERY_TIMEOUT" ) ; 145// private static final String ARG_ARRAY = HybsSystem.sys( "ARG_ARRAY" ) ; 146// private static final String ERR_MSG = HybsSystem.sys( "ERR_MSG" ) ; 147// private static final String ERR_MSG_ARRAY = HybsSystem.sys( "ERR_MSG_ARRAY" ) ; 148 private static final String ARG_ARRAY = "ARG_ARRAY" ; 149 private static final String ERR_MSG = "ERR_MSG" ; 150 private static final String ERR_MSG_ARRAY = "ERR_MSG_ARRAY" ; 151 152 // 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 153// private String dbid = "DEFAULT"; 154 private String dbid = null; 155 private String separator = TAB_SEPARATOR; // 項目区切り文字 156 private boolean useHeader = true; // ヘッダーの使用可否 157 private String fileURL = HybsSystem.sys( "FILE_URL" ); 158 private String filename = HybsSystem.sys( "FILE_FILENAME" ); // ファイル名 159 private String zipFilename = null; // ZIPファイル名 160 private String sql = null; 161 private String encode = HybsSystem.sys( "FILE_ENCODE" ); // ファイルエンコーディング "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS" 162 private boolean fileAppend = false; // ファイルをAPPENDモードで出力するか 163 private boolean zip = false; // ファイルをZIPするか 164// private String displayMsg = "MSG0033"; // 件検索しました。 165 private String displayMsg = HybsSystem.sys( "VIEW_DISPLAY_MSG" ); 166 private String notfoundMsg = "MSG0077"; // 対象データはありませんでした。 167 private long dyStart = 0; // 実行時間測定用のDIV要素を出力します。 168 private int fetchSize = 100 ; // フェッチする行数(初期値:100) 169 private boolean useNumber = true; // 5.5.7.1(2012/10/05) 行番号出力 170 171 // 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 172 private boolean queryType = true; // ノーマルは、true/ JDBCErrMsg の時は、false 173 private String names = null; // 指定のリクエスト変数を、ARG_ARRAY にセットします。 174 private int errCode = ErrorMessage.OK; 175 private transient ErrorMessage errMessage = null; 176 private String storageType = null; // 5.10.9.0 (2019/03/01) クラウドストレージタイプ 177 private String bucketName = null; // 5.10.9.0 (2019/03/01) バケット名 178 179 /** 180 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。 181 * 182 * @return 後続処理の指示( EVAL_BODY_BUFFERED ) 183 */ 184 @Override 185 public int doStartTag() { 186 dyStart = System.currentTimeMillis(); // 時間測定用 187 return( EVAL_BODY_BUFFERED ); // Body を評価する。( extends BodyTagSupport 時) 188 } 189 190 /** 191 * Taglibのタグ本体を処理する doAfterBody() を オーバーライドします。 192 * 193 * @og.rev 3.8.6.3 (2006/11/30) SQL 文の前後のスペースを取り除きます。 194 * 195 * @return 後続処理の指示(SKIP_BODY) 196 */ 197 @Override 198 public int doAfterBody() { 199 sql = getBodyString(); 200 if( sql == null || sql.length() == 0 ) { 201 String errMsg = "BODY 部の検索用 Select文は、必須です。"; 202 throw new HybsSystemException( errMsg ); 203 } 204 sql = sql.trim(); 205 return(SKIP_BODY); // Body を評価しない 206 } 207 208 /** 209 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。 210 * 211 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 212 * @og.rev 4.0.0.0 (2007/10/18) メッセージリソース統合( getResource().getMessage ⇒ getResource().getLabel ) 213 * @og.rev 5.10.9.0 (2019/03/01) クラウドストレージ対応。 214 * 215 * @return 後続処理の指示 216 */ 217 @Override 218 public int doEndTag() { 219 debugPrint(); // 4.0.0 (2005/02/28) 220 221 PrintWriter pw = null; 222 final int executeCount; 223 try { 224 if( zip ) { 225 String directory = HybsSystem.url2dir( fileURL ); 226 227 if( zipFilename == null ) { zipFilename = filename + ".zip"; } 228 ZipOutputStream gzip = null; 229 try { 230 gzip = new ZipOutputStream( 231 new FileOutputStream( 232 StringUtil.urlAppend( directory,zipFilename ))); 233 gzip.putNextEntry( new ZipEntry( filename ) ); 234 pw = new PrintWriter( gzip ); 235 executeCount = create( pw ) ; 236 237 pw.flush(); 238 gzip.closeEntry(); 239 gzip.finish() ; 240 } 241 finally { 242 Closer.ioClose( gzip ); // 4.0.0 (2006/01/31) close 処理時の IOException を無視 243 } 244 } 245 else { 246 pw = getPrintWriter(); 247 executeCount = create( pw ); 248 } 249 } catch( IOException ex ) { 250 String errMsg = "Error in DirectWriteTableTag: " + toString(); 251 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 252 } finally { 253 Closer.ioClose( pw ); // 4.0.0 (2006/01/31) close 処理時の IOException を無視 254 } 255 256 // 5.10.9.0 (2019/03/01) ADD クラウドストレージ利用の場合はアップロードする 257 String directory = HybsSystem.url2dir( fileURL ); 258 String trgFileName; 259 if(zip) { 260 trgFileName = filename + ".zip"; 261 }else { 262 trgFileName = filename; 263 } 264 FileOperation cloudFile = HybsFileOperationFactory.create(storageType, bucketName, directory, trgFileName); 265 if(!cloudFile.isLocal()) { 266 File localFile = new File(directory, trgFileName); 267 FileUtil.copy(localFile, cloudFile); 268 localFile.delete(); 269 } 270 271 // 3.6.1.0 (2005/01/05) 検索結果の件数を、"DB.COUNT" キーでリクエストにセットする。 272 setRequestAttribute( "DB.COUNT" , String.valueOf( executeCount ) ); 273 setRequestAttribute( "DB.ERR_CODE", String.valueOf( errCode ) ); 274 275 StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_SMALL ); 276 277 // 実行件数の表示 278// boolean useStatusBar = HybsSystem.sysBool( "VIEW_USE_DISPLAY_MSG" ); 279// if( useStatusBar && executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) { 280 if( executeCount > 0 && displayMsg != null && displayMsg.length() > 0 ) { 281 buf.append( executeCount ); 282// buf.append( getResource().getMessage( displayMsg ) ); 283 buf.append( getResource().getLabel( displayMsg ) ); 284 buf.append( HybsSystem.BR ); 285 } 286 else if( executeCount == 0 && notfoundMsg != null && notfoundMsg.length() > 0 ) { 287// buf.append( getResource().getMessage( notfoundMsg ) ); 288 buf.append( getResource().getLabel( notfoundMsg ) ); 289 buf.append( HybsSystem.BR ); 290 } 291 292 // 3.6.1.0 (2005/01/05) TaglibUtil.makeHTMLErrorTable メソッドを利用 293 String err = TaglibUtil.makeHTMLErrorTable( errMessage,getResource() ); 294 if( err != null && err.length() > 0 ) { 295 buf.append( err ); 296 setSessionAttribute( errMsgId,errMessage ); 297 } 298 else { 299 removeSessionAttribute( errMsgId ); 300 } 301 302 jspPrint( buf.toString() ); 303 304 // 時間測定用の DIV 要素を出力 305 long dyTime = System.currentTimeMillis()-dyStart; 306 jspPrint( "<div id=\"queryTime\" value=\"" + (dyTime) + "\"></div>" ); // 3.5.6.3 (2004/07/12) 307 308 // 3.6.1.0 (2005/01/05) 警告時に停止していましたが、継続処理させます。 309 int rtnCode = EVAL_PAGE; 310 if( errCode >= ErrorMessage.NG ) { // 異常 311 rtnCode = SKIP_PAGE; 312 } 313 314 // 4.0.0 (2005/01/31) セキュリティチェック(データアクセス件数登録) 315 GUIInfo guiInfo = (GUIInfo)getSessionAttribute( HybsSystem.GUIINFO_KEY ); 316 if( guiInfo != null ) { guiInfo.addReadCount( executeCount,dyTime,sql ); } 317 318 return( rtnCode ); 319 } 320 321 /** 322 * タグリブオブジェクトをリリースします。 323 * キャッシュされて再利用されるので、フィールドの初期設定を行います。 324 * 325 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 326 * @og.rev 4.0.0.0 (2007/10/10) dbid の初期値を、"DEFAULT" から null に変更 327 * @og.rev 5.5.7.1 (2012/10/05) useNumber追加 328 * @og.rev 5.10.9.0 (2019/03/01) storageType,bucketName属性を追加 329 */ 330 @Override 331 protected void release2() { 332 super.release2(); 333 separator = TAB_SEPARATOR; // 項目区切り文字 334 fileURL = HybsSystem.sys( "FILE_URL" ); 335 filename = HybsSystem.sys( "FILE_FILENAME" ); // ファイル名 336 zipFilename = null; // ZIPファイル名 337 sql = null; 338 encode = HybsSystem.sys( "FILE_ENCODE" ); // ファイルエンコーディング "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS" 339 fileAppend = false; // ファイルをAPPENDモードで出力するか 340 zip = false; // ファイルをZIPするか 341// displayMsg = "MSG0033"; // 件検索しました。 342 displayMsg = HybsSystem.sys( "VIEW_DISPLAY_MSG" ); 343 notfoundMsg = "MSG0077"; // 対象データはありませんでした。 344// dbid = "DEFAULT"; 345 dbid = null; 346 fetchSize = 100 ; // フェッチする行数(初期値:0 参考にしない) 347 dyStart = 0; 348 queryType = true; // ノーマルは、true/ JDBCErrMsg の時は、false 349 names = null; // 指定のリクエスト変数を、ARG_ARRAY にセットします。 350 errCode = ErrorMessage.OK; 351 errMessage = null; 352 useNumber = true; // 5.5.7.1 (2012/10/05) 353 storageType = null; // 5.10.9.0 (2019/03/01) 354 bucketName = null; // 5.10.9.0 (2019/03/01) 355 } 356 357 /** 358 * 実オブジェクトを生成して,OutputStream に書き込みます。 359 * 360 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 361 * @og.rev 3.8.6.0 (2006/09/29) ヘッダーにラベルを出力するように修正 362 * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定 363 * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応 364 * @og.rev 5.1.9.0 (2010/08/01) Transaction 対応 365 * @og.rev 5.2.2.0 (2010/11/01) 改行を含む場合は、ダブルクオートを強制的に前後に追加する。 366 * @og.rev 5.2.2.0 (2010/11/01) ダブルクオートを含む場合は、その直前にダブルクオートを強制的に追加する。 367 * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し 368 * @og.rev 5.3.7.0 (2011/07/01) TransactionReal の引数変更 369 * @og.rev 5.5.7.1 (2012/10/05) useNumberの追加 370 * 371 * @param out PrintWriterオブジェクト 372 * 373 * @return 検索件数 374 */ 375 private int create( final PrintWriter out ) { 376 final int executeCount; 377 Statement stmt = null; 378 CallableStatement callStmt = null; // 4.3.4.3 (2008/12/22) 379 ResultSet resultSet = null ; 380 boolean errFlag = true; 381// Connection conn = null; 382 Transaction tran = null; // 5.1.9.0 (2010/08/01) Transaction 対応 383 try { 384 // 5.1.9.0 (2010/08/01) Transaction 対応 385 TransactionTag tranTag = (TransactionTag)findAncestorWithClass( this,TransactionTag.class ); 386 if( tranTag == null ) { 387// tran = new TransactionReal( dbid,getApplicationInfo() ); 388 tran = new TransactionReal( getApplicationInfo() ); // 5.3.7.0 (2011/07/01) 引数変更 389 } 390 else { 391 tran = tranTag.getTransaction(); 392 } 393// conn = ConnectionFactory.connection( dbid,getApplicationInfo() ); // 3.8.7.0 (2006/12/15) 394 395 Connection conn = tran.getConnection( dbid ); // 5.1.9.0 (2010/08/01) Transaction 対応 396 // 3.6.1.0 (2005/01/05) 397 if( queryType ) { // JDBC 通常の SELECT 文 398 stmt = conn.createStatement(); 399 if( fetchSize > 0 ) { stmt.setFetchSize( fetchSize ); } 400 resultSet = stmt.executeQuery( sql ); 401 } 402 else { // PL/SQL Call 文 403 String[] values = null; 404 if( names != null ) { 405 String[] nameArray = StringUtil.csv2Array( names ); 406 values = getRequest( nameArray ); 407 } 408 callStmt = conn.prepareCall( sql ); 409// resultSet = executeCall( conn,callStmt,sql,values ); // 4.3.4.3 (2008/12/22) 410 resultSet = executeCall( conn,callStmt,values ); // 5.3.0.0 (2010/12/01) 411 } 412 if( resultSet == null ) { return 0; } 413 414 ResultSetMetaData metaData = resultSet.getMetaData(); 415 int numberOfColumns = metaData.getColumnCount(); 416 417 // ヘッダー部の出力 418 if( useHeader && numberOfColumns > 0 ) { 419 StringBuilder headName = new StringBuilder(); 420 StringBuilder headLabel = new StringBuilder(); 421 headName.append( "#Name" ); 422 headLabel.append( "#Label" ); 423 String clm ; 424 ResourceManager resource = getResource(); 425 for(int column = 1; column <= numberOfColumns; column++) { 426 clm = (metaData.getColumnLabel(column)).toUpperCase(Locale.JAPAN); 427 headName.append( TAB_SEPARATOR ).append( clm ); 428 headLabel.append( TAB_SEPARATOR ).append( resource.getLabel( clm ) ); 429 } 430 out.println( headName.toString() ); 431 out.println( headLabel.toString() ); 432 } 433 434 int rowNo = 0; 435 Object obj ; 436 while( resultSet.next() ) { 437 if( useNumber ){ // 5.5.7.1 (2012/10/05) 438 out.print( rowNo ); // 行番号 439 } 440 for(int column = 1; column <= numberOfColumns; column++) { 441 if( column == 1 && !useNumber && !useHeader ){ // 5.5.7.1 (2012/10/05) 442 //この場合だけセパレータ出力しない 443 } 444 else{ 445 out.print( separator ); 446 } 447 obj = resultSet.getObject(column); 448 if( obj != null ) { 449// out.print( obj ); 450 // 5.2.2.0 (2010/11/01) 改行、ダブルクオート等の処理 451 String sval = obj.toString(); 452 if( sval.indexOf( '"' ) >= 0 ) { sval = sval.replaceAll( "\"" ,"\"\"" ) ; } 453 if( sval.indexOf( HybsSystem.CR ) >= 0 ) { 454 sval = "\"" + sval + "\"" ; 455 } 456 out.print( sval ); 457 } 458 } 459 out.println(); 460 rowNo++ ; 461 } 462 executeCount = rowNo ; 463 errFlag = false; // エラーではない 464 } 465 catch ( SQLException ex ) { // 3.6.1.0 (2005/01/05) 466 String errMsg = "データベース処理を実行できませんでした。" 467 + HybsSystem.CR + stmt + HybsSystem.CR 468 + "err=[" + ex.getSQLState() + "]" 469 + ex.getMessage(); 470 throw new HybsSystemException( errMsg,ex ); 471 } 472 finally { 473 Closer.resultClose( resultSet ); 474 Closer.stmtClose( stmt ); 475 Closer.stmtClose( callStmt ); // 4.3.4.3 (2008/12/22) 476 if( tran != null ) { // 5.5.2.6 (2012/05/25) findbugs対応 477 tran.close( errFlag ); // 5.1.9.0 (2010/08/01) Transaction 対応 478 } 479// if( errFlag ) { ConnectionFactory.remove( conn,dbid ); } // 削除 480// else { ConnectionFactory.close( conn,dbid ); } // 返却 481// conn = null; 482 } 483 484 return executeCount ; 485 } 486 487 /** 488 * 引数配列付のクエリーを実行します。 489 * 処理自体は, #execute() と同様に、各サブクラスの実装に依存します。 490 * これは、CallableStatement を用いて、データベース検索処理を行います。 491 * {call TYPE3B01.TYPE3B01(?,?,?,?)} で、4番目の引数には、 492 * names で指定したリクエスト情報が、ARG_ARRAY 配列に順次セットされます。 493 * 使用する場合は、一旦わかり易い変数に受けて利用してください。 494 * 呼び出す PL/SQL では、検索系PL/SQL です。 495 * 496 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 497 * @og.rev 4.3.4.3 (2008/12/22) (Oracle11gDriver対応)PL/SQLコールの場合に、"クローズされた文です。"のエラーが発生する問題に対応 498 * @og.rev 5.3.0.0 (2010/12/01) executeCall メソッドの引数見直し 499 * @og.rev 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 500 * @og.rev 5.7.2.3 (2014/01/31) Oracle11g(11.2.0.3のドライバ)対応は、Ver5 では行わない(戻す)。 501 * 502 * @param conn コネクション 503 * @param callStmt コーラブルステートメント 504 * @param args オブジェクトの引数配列 505 * 506 * @return 結果オブジェクト 507 */ 508// private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String sql,final String[] args ) throws SQLException { 509 private ResultSet executeCall( final Connection conn,final CallableStatement callStmt,final String[] args ) throws SQLException { 510// CallableStatement callStmt = null ; // 4.3.4.3 (2008/12/22) 511 ResultSet resultSet = null; 512// try { 513// callStmt = conn.prepareCall( sql ); 514 callStmt.setQueryTimeout( DB_MAX_QUERY_TIMEOUT ); 515 if( fetchSize > 0 ) { callStmt.setFetchSize( fetchSize ); } 516 Map<String,Class<?>> map = conn.getTypeMap(); 517 try { 518 map.put( ERR_MSG,Class.forName( "org.opengion.hayabusa.db.DBErrMsg" ) ); 519 } 520 catch( ClassNotFoundException ex ) { 521 String errMsg = "org.opengion.hayabusa.db.DBErrMsg クラスが見つかりません。" + HybsSystem.CR 522 + ex.getMessage(); // // 5.1.8.0 (2010/07/01) errMsg 修正 523 throw new HybsSystemException( errMsg,ex ); // 3.5.5.4 (2004/04/15) 引数の並び順変更 524 } 525 526 // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 http://docs.oracle.com/cd/E28389_01/web.1111/b60995/thirdparty.htm 527 ArrayDescriptor sd = ArrayDescriptor.createDescriptor( ARG_ARRAY, conn ); 528 ARRAY newArray = new ARRAY( sd,conn,StringUtil.rTrims( args ) ); 529// Array newArray = ((OracleConnection)conn).createOracleArray( ARG_ARRAY, StringUtil.rTrims( args )); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 530 531 callStmt.registerOutParameter(1, Types.INTEGER); 532 callStmt.registerOutParameter(2, OracleTypes.ARRAY,ERR_MSG_ARRAY); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 533// callStmt.registerOutParameter(2, Types.ARRAY,ERR_MSG_ARRAY); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 534 callStmt.registerOutParameter(3, OracleTypes.CURSOR); 535 ((OracleCallableStatement)callStmt).setARRAY( 4,newArray ); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 536// callStmt.setArray( 4,newArray ); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 537 538 callStmt.execute(); 539 540 errCode = callStmt.getInt(1); 541 542 if( errCode < ErrorMessage.NG ) { // 異常以外の場合 543 resultSet = ((OracleCallableStatement)callStmt).getCursor(3); 544 } 545 if( errCode > ErrorMessage.OK ) { // 正常以外の場合 546 ARRAY rtn3 = ((OracleCallableStatement)callStmt).getARRAY(2); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 547// Array rtn3 = callStmt.getArray(2); // 5.7.2.2 (2014/01/24) Oracle11g(11.2.0.3のドライバ)対応 548 Object[] rtnval3 = (Object[])rtn3.getArray(); 549 errMessage = new ErrorMessage( "Query_JDBCErrMsg Error!!" ); 550 for( int i=0; i<rtnval3.length; i++ ) { 551 DBErrMsg er = (DBErrMsg)rtnval3[i]; 552 if( er == null ) { break; } 553 errMessage.addMessage( er.getErrMsg() ); 554 } 555 } 556// } 557// finally { 558// Closer.stmtClose( callStmt ); 559// callStmt = null; 560// } 561 return resultSet; 562 } 563 564 /** 565 * PrintWriter を取得します。 566 * 567 * ここでは、一般的なファイル出力を考慮した PrintWriter を作成します。 568 * 569 * @og.rev 3.7.1.1 (2005/05/23) フォルダがない場合は、複数階層分のフォルダを自動で作成します。 570 * @og.rev 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。 571 * @og.rev 5.6.1.0 (2013/02/01) 3.7.1.1のコメントに入っているが対応されていないのでフォルダ作成追加 572 * @og.rev 5.10.9.0 (2019/03/01) クラウドストレージ対応を追加。 573 * 574 * @return 出力用PrintWriterオブジェクト 575 */ 576 private PrintWriter getPrintWriter() { 577 if( filename == null ) { 578 String errMsg = "ファイル名がセットされていません。"; 579 throw new HybsSystemException( errMsg ); 580 } 581 String directory = HybsSystem.url2dir( fileURL ); 582 583 // 5.6.1.0 (2013/02/01) 584 // 5.10.9.0 (2019/03/01) MODIFY 585 // File dir = new File(directory); 586 File dir = HybsFileOperationFactory.create(storageType, bucketName, directory); 587 if( ! dir.exists() && ! dir.mkdirs() ) { 588 String errMsg = "ディレクトリの作成に失敗しました。[" + directory + "]"; 589 throw new HybsSystemException( errMsg ); 590 } 591 592 593 // ※ 注意 StringUtil.urlAppend を組み込んでいる意図が不明。一旦削除していますが、注意 594 // 3.8.0.0 (2005/06/07) FileUtil#getPrintWriter を利用。 595 // out = FileUtil.getPrintWriter( StringUtil.urlAppend( directory,filename ),fileAppend,encode); 596 597// 処理を簡素化します。 598// PrintWriter out = FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend ); 599// return out ; 600 601 // 5.10.9.0 (2019/03/01) ADD クラウドストレージ利用、fileAppend、かつファイルが存在する場合はダウンロードする。 602 FileOperation cloudFile = HybsFileOperationFactory.create(storageType, bucketName, directory, filename); 603 if(!cloudFile.isLocal() && fileAppend && cloudFile.isFile()) { 604 File localFile = new File(directory, filename); 605 localFile.delete(); 606 FileUtil.copy(cloudFile, localFile); 607 } 608 609 return FileUtil.getPrintWriter( new File( directory,filename ),encode,fileAppend ); 610 } 611 612 /** 613 * 名称配列を元に、リクエスト情報のデータを取得します。 614 * 615 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 616 * 617 * @param nameArray キーとなる名称の配列 618 * 619 * @return そのリクエスト情報 620 */ 621 private String[] getRequest( final String[] nameArray ) { 622 String[] rtn = new String[nameArray.length]; 623 624 for( int i=0; i<rtn.length; i++ ) { 625 rtn[i] = getRequestValue( nameArray[i] ); 626 } 627 628 return rtn; 629 } 630 631 /** 632 * 【TAG】(通常は使いません)検索時のDB接続IDを指定します(初期値:DEFAULT)。 633 * 634 * @og.tag 635 * 検索時のDB接続IDを指定します。初期値は、DEFAULT です。 636 * 637 * @param id データベース接続ID 638 */ 639 public void setDbid( final String id ) { 640 dbid = nval( getRequestParameter( id ),dbid ); 641 } 642 643 /** 644 * 【TAG】可変長ファイルを作成するときの項目区切り文字をセットします 645 * (初期値:TAB_SEPARATOR[={@og.value #TAB_SEPARATOR}])。 646 * 647 * @og.tag 可変長ファイルを作成するときの項目区切り文字をセットします。 648 * (初期値:ローカル定義のTAB_SEPARATOR[={@og.value #TAB_SEPARATOR}])。 649 * 650 * @param sep 項目区切り文字 651 * @see #TAB_SEPARATOR 652 */ 653 public void setSeparator( final String sep ) { 654 separator = nval( getRequestParameter( sep ),TAB_SEPARATOR ); 655 } 656 657 /** 658 * 【TAG】保存先ディレクトリ名を指定します 659 * (初期値:FILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。 660 * 661 * @og.tag 662 * この属性で指定されるディレクトリに、ファイルをセーブします。 663 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、2文字目が、 664 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、 665 * fileURL = "{@USER.ID}" と指定すると、FILE_URL 属性で指定のフォルダの下に、 666 * さらに、各個人ID別のフォルダを作成して、そこにセーブします。 667 * (初期値:システム定数のFILE_URL[={@og.value org.opengion.hayabusa.common.SystemData#FILE_URL}])。 668 * 669 * @og.rev 3.5.4.3 (2004/01/05) 内部処理を、makeFileURL に移動。 670 * @og.rev 4.0.0.0 (2005/01/31) StringUtil.urlAppend メソッドの利用 671 * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。 672 * 673 * @param url 保存先ディレクトリ名 674 * @see org.opengion.hayabusa.common.SystemData#FILE_URL 675 */ 676 public void setFileURL( final String url ) { 677 String furl = nval( getRequestParameter( url ),null ); 678 if( furl != null ) { 679 char ch = furl.charAt( furl.length()-1 ); 680 if( ch != '/' && ch != '\\' ) { furl = furl + "/"; } 681 fileURL = StringUtil.urlAppend( fileURL,furl ); 682 } 683 } 684 685 /** 686 * 【TAG】ファイルを作成するときのファイル名をセットします(初期値:システムパラメータのFILE_FILENAME)。 687 * 688 * @og.tag ファイルを作成するときのファイル名をセットします。 689 * 690 * @param fname ファイル名 691 */ 692 public void setFilename( final String fname ) { 693 filename = nval( getRequestParameter( fname ),filename ); 694 } 695 696 /** 697 * 【TAG】ZIPファイルを作成するときのZIPファイル名をセットします(初期値:filename + ".zip")。 698 * 699 * @og.tag 700 * zip 属性に、true を指定した場合に、ZIPファイル化します。その場合のファイル名を指定します。 701 * なにも指定しない場合は、filename + ".zip" になります。 702 * 703 * @param zipFile ZIPファイル名 704 * @see #setZip( String ) 705 */ 706 public void setZipFilename( final String zipFile ) { 707 zipFilename = nval( getRequestParameter( zipFile ),zipFilename ); 708 } 709 710 /** 711 * 【TAG】ファイルを作成するときのファイルエンコーディング名をセットします 712 * (初期値:FILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。 713 * 714 * @og.tag 715 * "DEFAULT","JISAutoDetect" ,"JIS", "EUC_JP", "MS932", "SJIS" , "Windows-31J" , "Shift_JIS" 716 * (初期値:システム定数のFILE_ENCODE[={@og.value org.opengion.hayabusa.common.SystemData#FILE_ENCODE}])。 717 * 718 * @og.rev 2.2.0.0 (2002/12/17) 中国語(国際化)対応 エンコードの取得方法変更 719 * @og.rev 3.1.3.0 (2003/04/10) FILE_ENCODE から、エンコード情報を取得する。 720 * 721 * @param enc ファイルエンコーディング名 722 * @see <a href="http://www.iana.org/assignments/character-sets">IANA Charset Registry</a> 723 * @see org.opengion.hayabusa.common.SystemData#FILE_ENCODE 724 */ 725 public void setEncode( final String enc ) { 726 encode = nval( getRequestParameter( enc ),encode ); 727 } 728 729 /** 730 * 【TAG】ヘッダーを書き込むかどうか[true/false]を指定します(初期値:true)。 731 * 732 * @og.tag 733 * #Name ・・・・ ヘッダーの書き込みを指定します。 734 * 通常は、書き込み(true)にしておき、使用側でコメントと解釈するように 735 * 処理を行うべきです。コメントのため、append モードで途中に現れても 736 * 無視できます。また、エンジン標準でデータを取り込む場合に、データの配置が 737 * 変更されても取り込みプログラムはそのまま使用できます。 738 * 初期値は、true(書き込む)です。 739 * 740 * @param flag ヘッダーを書き込むかどうか [true:書き込む/false:書き込まない] 741 */ 742 public void setUseHeader( final String flag ) { 743 useHeader = nval( getRequestParameter( flag ),useHeader ); 744 } 745 746 /** 747 * 【TAG】追加モードで書き込むかどうか[true/false]を指定します(初期値:false[通常モード])。 748 * 749 * @og.tag 750 * ファイルを書き込む場合、追加モードで書き込むかどうかをセットします。 751 * 新規モード(true)の場合、既存のファイルが存在し、かつ書き込み許可があれば、 752 * 上書きで新規に作成します。 753 * 初期値は、false(新規モード)です。 754 * 755 * @param flag [true:追加モード/false:新規モード] 756 */ 757 public void setFileAppend( final String flag ) { 758 fileAppend = nval( getRequestParameter( flag ),fileAppend ); 759 } 760 761 /** 762 * 【TAG】結果をファイルに出力するときに、ZIPで圧縮するかどうか[true/false]を指定します(初期値:false)。 763 * 764 * @og.tag 765 * 大量に抜き出す場合、そのまま、サーバーから取り出すだけでも大変です。 766 * zip 属性を、true にすると、GZIP で圧縮したファイルを作成します。 767 * 初期値は、false(圧縮しない)です。 768 * 769 * @param flag ZIPで圧縮 [true:する/それ以外:しない] 770 * @see #setZipFilename( String ) 771 */ 772 public void setZip( final String flag ) { 773 zip = nval( getRequestParameter( flag ),zip ); 774 } 775 776 /** 777 * 【TAG】検索結果を画面上に表示するメッセージリソースIDを指定します(初期値:MSG0033[ 件検索しました])。 778 * 779 * @og.tag 780 * ここでは、検索結果の件数や登録された件数をまず出力し、 781 * その次に、ここで指定したメッセージをリソースから取得して 782 * 表示します。 783 * 表示させたくない場合は, displayMsg = "" をセットしてください。 784 * 初期値は、検索件数を表示します。 785 * 786 * @param id ディスプレイに表示させるメッセージ ID 787 */ 788 public void setDisplayMsg( final String id ) { 789 String ids = getRequestParameter( id ); 790 if( ids != null ) { displayMsg = ids; } 791 } 792 793 /** 794 * 【TAG】検索結果がゼロ件の場合に表示するメッセージリソースIDを指定します(初期値:MSG0077[対象データはありませんでした])。 795 * 796 * @og.tag 797 * ここでは、検索結果がゼロ件の場合のみ、特別なメッセージを表示させます。 798 * 従来は、displayMsg と兼用で、『0 件検索しました』という表示でしたが、 799 * displayMsg の初期表示は、OFF になりましたので、ゼロ件の場合のみ別に表示させます。 800 * 表示させたくない場合は, notfoundMsg = "" をセットしてください。 801 * 初期値は、MSG0077[対象データはありませんでした]です。 802 * 803 * @param id ディスプレイに表示させるメッセージ ID 804 */ 805 public void setNotfoundMsg( final String id ) { 806 String ids = getRequestParameter( id ); 807 if( ids != null ) { notfoundMsg = ids; } 808 } 809 810 /** 811 * 【TAG】(通常は使いません)データのフェッチサイズを指定します(初期値:100)。 812 * 813 * @og.tag 814 * より多くの行が必要なときに、データベースから取り出す必要がある行数に 815 * ついてのヒントを JDBC ドライバに提供します。 816 * 指定された行数は、この Statement を使って作成された結果セットにだけ影響します。 817 * 指定された値が 0 の場合、ヒントは無視されます。 818 * 初期値は、100 です。 819 * 820 * @param size フェッチする行数(初期値:100) 821 */ 822 public void setFetchSize( final String size ) { 823 fetchSize = nval( getRequestParameter( size ),fetchSize ); 824 } 825 826 /** 827 * 【TAG】PL/SQLを利用する場合の引数にセットすべき データの名称をCSV形式で複数指定します。 828 * 829 * @og.tag 830 * 複数ある場合は、カンマ区切り文字で渡します。 831 * PL/SQL を使用しない場合は、無視されます。 832 * 833 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 834 * 835 * @param nm 引数の名称(複数ある場合は、カンマ区切り文字) 836 */ 837 public void setNames( final String nm ) { 838 names = nval( getRequestParameter( nm ),names ); 839 } 840 841 /** 842 * 【TAG】Query を発行する為のクラスID(JDBC,JDBCErrMsg)を指定します({@og.doc03Link queryType 初期値:JDBC})。 843 * 844 * @og.tag 845 * ストアドプロシージャ等を実行する場合に、queryType="JDBCErrMsg" を 846 * 指定する必要があります。(それ以外の指定は、初期値の JDBC になります。) 847 * 初期値は、"JDBC" です。 848 * {@og.doc03Link queryType Query_**** クラス} 849 * 850 * @og.rev 3.6.1.0 (2005/01/05) PL/SQLコール(JDBCErrMsg 形式)への対応 851 * 852 * @param id Query を発行する為の実クラス ID 853 */ 854 public void setQueryType( final String id ) { 855 // 内部的には、JDBCErrMsg:false / それ以外:true で管理しています。 856 queryType = ! "JDBCErrMsg".equalsIgnoreCase( getRequestParameter( id ) ); 857 } 858 859 /** 860 * 【TAG】ファイルに行番号を出力するかどうか(初期値:true) 861 * 862 * @og.tag 863 * ファイルに行番号を出力するかどうかを指定します。 864 * 初期値は、true(出力する)です。 865 * 866 * @og.rev 5.5.7.1 (2012/10/05) 新規追加 867 * @param flag 行番号出力 [true:する/それ以外:しない] 868 */ 869 public void setUseNumber( final String flag ) { 870 useNumber = nval( getRequestParameter( flag ),useNumber ); 871 } 872 873 /** 874 * シリアライズ用のカスタムシリアライズ書き込みメソッド 875 * 876 * @og.rev 4.0.0.0 (2006/09/31) 新規追加 877 * @serialData 一部のオブジェクトは、シリアライズされません。 878 * 879 * @param strm ObjectOutputStreamオブジェクト 880 * @throws IOException シリアライズに関する入出力エラーが発生した場合 881 */ 882 private void writeObject( final ObjectOutputStream strm ) throws IOException { 883 strm.defaultWriteObject(); 884 } 885 886 /** 887 * シリアライズ用のカスタムシリアライズ読み込みメソッド 888 * 889 * ここでは、transient 宣言された内部変数の内、初期化が必要なフィールドのみ設定します。 890 * 891 * @og.rev 4.0.0.0 (2006/09/31) 新規追加 892 * @serialData 一部のオブジェクトは、シリアライズされません。 893 * 894 * @param strm ObjectInputStreamオブジェクト 895 * @see #release2() 896 */ 897 private void readObject( final ObjectInputStream strm ) throws IOException , ClassNotFoundException { 898 strm.defaultReadObject(); 899 } 900 901 /** 902 * 【TAG】保存先ストレージタイプを設定します。 903 * 904 * @og.tag 905 * ファイルを保存するストレージタイプを設定します。 906 * 未設定の場合は、システムリソースの「CLOUD_TARGET」が参照されます。 907 * 自身のサーバを指定する場合は、「default」を設定してください。 908 * 909 * @og.rev 5.10.9.0 (2019/03/01) 新規追加 910 * 911 * @param storage ストレージタイプ 912 */ 913 public void setStorageType( final String storage ) { 914 storageType = nval( getRequestParameter( storage ), storageType ); 915 } 916 917 /** 918 * 【TAG】保存先バケット名を設定します。 919 * 920 * @og.tag 921 * ファイルを保存するバケット名を指定します。 922 * クラウドストレージ利用時のみ有効です。 923 * 未設定の場合は、システムリソースの「CLOUD_BUKET」が参照されます。 924 * 925 * @og.rev 5.10.9.0 (2019/03/01) 新規追加 926 * 927 * @param bucket バケット名 928 */ 929 public void setBucketName( final String bucket ) { 930 bucketName = nval( getRequestParameter( bucket ), bucketName ); 931 } 932 933 /** 934 * このオブジェクトの文字列表現を返します。 935 * 基本的にデバッグ目的に使用します。 936 * 937 * @og.rev 5.10.9.0 (2019/03/01) storageType,bucketNameを出力対象に追加。 938 * 939 * @return このクラスの文字列表現 940 */ 941 @Override 942 public String toString() { 943 return org.opengion.fukurou.util.ToString.title( this.getClass().getName() ) 944 .println( "VERSION" ,VERSION ) 945 .println( "dbid" ,dbid ) 946 .println( "separator" ,separator ) 947 .println( "useHeader" ,useHeader ) 948 .println( "fileURL" ,fileURL ) 949 .println( "filename" ,filename ) 950 .println( "zipFilename" ,zipFilename) 951 .println( "sql" ,sql ) 952 .println( "encode" ,encode ) 953 .println( "fileAppend" ,fileAppend ) 954 .println( "zip" ,zip ) 955 .println( "displayMsg" ,displayMsg ) 956 .println( "dyStart" ,dyStart ) 957 .println( "fetchSize" ,fetchSize ) 958 .println( "queryType" ,queryType ) 959 .println( "names" ,names ) 960 .println( "errCode" ,errCode ) 961 .println( "storageType" ,storageType ) 962 .println( "bucketName" ,bucketName ) 963 .println( "Other..." ,getAttributes().getAttribute() ) 964 .fixForm().toString() ; 965 } 966}