001/* 002 * Copyright (c) 2017 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.fukurou.fileexec; 017 018import java.nio.file.Path; 019import java.io.IOException; // 7.2.1.0 (2020/03/13) 020import java.util.List ; // 021import java.util.ArrayList ; // 022import java.util.Arrays ; // 023 024import java.sql.Connection; // 7.2.1.0 (2020/03/13) 025import java.sql.CallableStatement; // 7.2.1.0 (2020/03/13) 026import java.sql.SQLException; // 7.2.1.0 (2020/03/13) 027import java.sql.Types; // 7.2.1.0 (2020/03/13) 028 029import static org.opengion.fukurou.fileexec.AppliExec.GE72.*; // enum のショートカット 030 031/** 032 * RunExec_DBIN は、RunExec インターフェースの実装クラスで、ファイルをデータベースに登録します。 033 * 034 *<pre> 035 * GE72.RUNTYPEが、'1' の場合の処理を行います。 036 * 0:NONE // なにもしない 037 * 1:DBIN // DB入力 038 * 2:PLSQL // PL/SQLコール 039 * 3:BAT // BATファイルコール 040 * 4:JSP // JSPファイルコール(URLコネクション) 041 * 042 * GE72のCLMS(外部カラム指定)は、取り込むファイルのカラム順です。A,B,,D のようにすると、C欄のデータは取り込みません。 043 * このカラムは、TABLE_NAME(テーブル名)で指定したテーブルのカラムと同じである必要があります。 044 * 045 * PARAMS(パラメータ)は、固定値の指定になります。key=val形式です。 046 * FGJ,DYSET,DYUPD,USRSET,USRUPD,PGSET,PGUPD,PGPSET,PGPUPD は、DB共通カラムとしてkeyのみ指定することで 047 * 値を自動設定します。それ以外に、下記のカラムに値が設定されています。 048 * FILE_NAME ファイル名 049 * FULL_PATH ディレクトリを含めたファイルのフルパス 050 * FGTKAN 取込完了フラグ(1:処理中 , 2:済 , 7:デーモンエラー , 8:アプリエラー) 051 * ERRMSG エラーメッセージ 052 * 053 * RUNPG(実行プログラム)は、データを取り込んだ後に実行する PL/SQLです。 054 * GEP1001(?,?,?,?,…) 最低、4つのパラメータ(?)が必要で、それ以降のパラメータは固定値のみ渡せます。(GEP1001はサンプル) 055 * PO_STATUS OUT NUMBER -- ステータス(0:正常 2:異常) 056 * ,PO_ERR_CODE OUT VARCHAR2 -- エラーメッセージ 057 * ,PI_EXECID IN VARCHAR2 -- 処理ID 058 * ,PI_FILE_NAME IN VARCHAR2 -- ファイル名 059 *</pre> 060 * 061 * @og.rev 7.0.0.0 (2017/07/07) 新規作成 062 * 063 * @version 7.0 064 * @author Kazuhiko Hasegawa 065 * @since JDK1.8, 066 */ 067public class RunExec_DBIN implements RunExec { 068 private static final XLogger LOGGER= XLogger.getLogger( RunExec_DBIN.class.getName() ); // ログ出力 069 070 private static final String DEF_ENCODE = "Windows-31J" ; 071 072 /** システム依存の改行記号(String)。 */ 073 public static final String CR = System.getProperty("line.separator"); 074 075 /** 076 * デフォルトコンストラクター 077 * 078 * @og.rev 6.9.7.0 (2018/05/14) PMD Each class should declare at least one constructor 079 */ 080 public RunExec_DBIN() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 081 082 /** 083 * 実際に処理を実行するプログラムのメソッド。 084 * 085 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加 086 * @og.rev 6.9.7.0 (2018/05/14) PMD encode,clms72,skipCnt unreferenced before a possible exit point. 087 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 088 * 089 * @param path 処理するファイルパス 090 * @param ge72Data GE72 テーブルデータ 091 * @return 処理件数(正は成功、マイナスは異常時の行番号) 092 */ 093 public int exec( final Path path , final String[] ge72Data ) { 094 LOGGER.debug( () -> "⑦ exec Path=" + path + " , GE72Data=" + Arrays.toString( ge72Data ) ); 095 096 // 6.9.7.0 (2018/05/14) PMD encode,clms72,skipCnt unreferenced before a possible exit point. 097 final String table = ge72Data[TABLE_NAME.NO]; 098 099 if( table == null || table.isEmpty() ) { 100 // MSG2003 = DBINでは、tableは、必須です。 101 throw MsgUtil.throwException( "MSG2003" ); 102 } 103 104 final String encode = StringUtil.nval( ge72Data[FILE_ENC.NO] , DEF_ENCODE ); // UTF-8 , Windows-31J; 105 final String clms72 = ge72Data[CLMS.NO]; // CLMS (#NAMEの設定) 106 107 // 一旦すべてのデータを読み取ります。よって、大きなファイルには向きません。 108 final List<List<String>> dataList = new ArrayList<>(); // ファイルを読み取った行データごとの分割されたデータ 109 final LineSplitter split = new LineSplitter( encode , clms72 ); 110 split.forEach( path , line -> dataList.add( line ) ); // 1行ごとに、カラムを分割されたListオブジェクト 111 112 final String[] clms = split.getColumns(); // ファイルの#NAME から、カラム列を取り出します。 113 if( clms == null || clms.length == 0 ) { 114 // MSG2004 = DBINでは、カラム列は、必須です。 115 throw MsgUtil.throwException( "MSG2004" ); 116 } 117 118 // 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 119 // key=val , key=val 形式 120 final ConstValsSet cnstValSet = new ConstValsSet( path,ge72Data[PARAMS.NO],ge72Data[EXECID.NO] ); 121 cnstValSet.setConstData(); 122 123 final String[] cnstKeys = cnstValSet.getConstKeys(); 124 final String[] cnstVals = cnstValSet.getConstVals(); 125 126// final String INS_QUERY = DBUtil.getInsertSQL( table,clms,null,null ); 127 final String INS_QUERY = DBUtil.getInsertSQL( table,clms,cnstKeys,cnstVals ); // 7.2.1.0 (2020/03/13) 128 129 final int skipCnt = StringUtil.nval( ge72Data[SKIP_CNT.NO] , 0 ); 130 final List<String[]> dbData = new ArrayList<>(); 131 if( !dataList.isEmpty() ) { 132 for( int row=skipCnt; row<dataList.size(); row++ ) { // 行番号:skipCntの行から取り込む 133 final List<String> line = dataList.get(row); 134 // 7.2.1.0 (2020/03/13) データの設定で、clmsの個数に準拠する。 135 final String[] vals = new String[clms.length]; 136 for( int col=0; col<clms.length; col++ ) { // カラム番号 137 if( col < line.size() ) { 138 vals[col] = line.get(col); 139 } 140 else { 141 vals[col] = "" ; 142 } 143 } 144 dbData.add( vals ); 145// dbData.add( line.toArray( new String[line.size()] ) ); 146 } 147 } 148 149 return DBUtil.execute( INS_QUERY , dbData ); 150 } 151 152 /** 153 * 追加で呼び出す PL/SQL を実行します。 154 * 155 * これは、取り込み処理の実施結果にかかわらず、必ず呼ばれます。 156 * 157 * 第一引数、第二引数は、通常のPL/SQLと異なり、IN/OUT パラメータです。 158 * 結果(STATUS)と内容(ERR_CODE)は、取込時の値をセットし、PL/SQLの結果を返します。 159 * 第三引数は、EXECID(処理ID) 、第四引数は、ファイル名です。 160 * それ以降の引数については、入力(IN)のみですが、自由に設定できます。 161 * ただし、パラメータは使えず、固定値を渡すのみです。 162 * 163 * { call GEP1001( ?,?,?,?,'AAAA','BBBB' ) } 164 * 165 * CREATE OR REPLACE PROCEDURE GEP1001( 166 * PO_KEKKA OUT NUMBER, -- エラー結果(0:正常 1:警告 2:異常) 167 * PO_ERR_CODE OUT VARCHAR2, -- エラーメッセージ文字列 168 * PI_EXECID IN VARCHAR2, -- 処理ID 169 * PI_FILE_NAME IN VARCHAR2, -- ファイル名 170 * PI_PRM1 IN VARCHAR2, -- ユーザー定義引数1 171 * PI_PRM2 IN VARCHAR2 -- ユーザー定義引数2 172 * ); 173 * 174 * @og.rev 7.2.1.0 (2020/03/13) 新規追加 175 * 176 * @param path 処理するファイルパス 177 * @param ge72Data GE72 テーブルデータ 178 * @param fgtkan 取込完了フラグ(0:取込なし , 1:処理中 , 2:済 , 7:デーモンエラー , 8:アプリエラー) 179 * @param errMsg エラーメッセージ 180 */ 181 public void endExec( final Path path , final String[] ge72Data , final String fgtkan , final String errMsg ) { 182 final String runPG = ge72Data[RUNPG.NO]; 183 if( runPG == null || runPG.isEmpty() ) { return; } // 呼出なし 184 185 LOGGER.debug( () -> "⑧ endExec Path=" + path + " , runPG=" + runPG + " , fgtkan=" + fgtkan ); 186 187 final String plsql = "{ call " + runPG + "}"; 188 final String execId = ge72Data[EXECID.NO]; 189 final String fileName = path.getFileName().toString(); 190 191 try( final Connection conn = DBUtil.getConnection() ) { 192 try( final CallableStatement callStmt = conn.prepareCall( plsql ) ) { 193 194 callStmt.setQueryTimeout( 300 ); // DB_MAX_QUERY_TIMEOUT 195 callStmt.setFetchSize( 1001 ); // DB_FETCH_SIZE 196 197 // IN OUT 属性を使い場合は、値をセットします。 198 callStmt.setInt( 1,Integer.parseInt( fgtkan ) ); // IN 結果(STATUS) 199 callStmt.setString( 2,errMsg ); // IN 内容(ERR_CODE) 200 callStmt.registerOutParameter(1, Types.INTEGER); // OUT 結果(STATUS) 201 callStmt.registerOutParameter(2, Types.VARCHAR); // OUT 内容(ERR_CODE) 202 callStmt.setString( 3,execId ); // 処理ID 203 callStmt.setString( 4,fileName ); // ファイル名 204 205 callStmt.execute(); 206 207 final int rtnCode = callStmt.getInt(1); 208 209 if( rtnCode > 0 ) { // 正常以外の場合 210 // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 211 final String outErrMsg = callStmt.getString(2); 212 throw MsgUtil.throwException( "MSG0019" , outErrMsg , "callPLSQL" ); 213 } 214 conn.commit(); 215 LOGGER.debug( () -> "⑨ Path=" + path + " , plsql=" + plsql ); 216 } 217 catch( final SQLException ex ) { 218 conn.rollback(); 219 conn.setAutoCommit(true); 220 throw ex ; 221 } 222 } 223 catch( final SQLException ex ) { 224 final String outErrMsg = "errMsg=[" + ex.getMessage() + "]" + CR 225 + "errCode=[" + ex.getErrorCode() + "] State=[" + ex.getSQLState() + "]" ; 226 227 // MSG0019 = DB処理の実行に失敗しました。メッセージ=[{0}]。\n\tquery=[{1}]\n\tvalues={2} 228 throw MsgUtil.throwException( ex , "MSG0019" , outErrMsg , runPG , execId ); 229 } 230 } 231 232 /** 233 * 固定値を処理する内部クラス 234 * 235 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 236 */ 237 private static final class ConstValsSet { 238 private final Path path ; // ファイルパス 239 private final String params ; // パラメータ(key=val,…形式の固定値) 240 private final String pgset ; // PG名 241 private final String dyset ; // 日付 242 243 private String[] cnstKeys ; 244 private String[] cnstVals ; 245 246 /** 247 * ファイルパスとプログラム名を引数に取るコンストラクター 248 * 249 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 250 * 251 * @param path ファイルパス 252 * @param params 固定値パラメータ 253 * @param pgset PG名 254 */ 255 public ConstValsSet( final Path path , final String params , final String pgset ) { 256 this.path = path; 257 this.params = params; 258 this.pgset = pgset; 259 dyset = StringUtil.getTimeFormat(); 260 } 261 262 /** 263 * 固定値のキー配列と値配列を設定します。 264 * 265 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 266 * 267 */ 268 public void setConstData() { 269 if( params != null && !params.isEmpty() ) { 270 final String[] keysVals = params.split( "," ); 271 if( keysVals != null && keysVals.length > 0 ) { 272 final int len = keysVals.length; 273 cnstKeys = new String[len]; 274 cnstVals = new String[len]; 275 276 for( int col=0; col<len; col++ ) { // 固定値のカラム列 277 final String kv = keysVals[col]; 278 final int ad = kv.indexOf( '=' ); 279 if( ad > 0 ) { 280 cnstKeys[col] = kv.substring(0,ad).trim(); 281 cnstVals[col] = kv.substring(ad+1).trim(); 282 } 283 else { 284 cnstKeys[col] = kv.trim(); 285 cnstVals[col] = getVal( cnstKeys[col] ); // 特定の固定値の値をセットします。 286 } 287 } 288 } 289 } 290 } 291 292 /** 293 * 固定値のキー配列を返します。 294 * 295 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 296 * 297 * @return 固定値のキー配列 298 */ 299 public String[] getConstKeys() { return cnstKeys; } 300 301 /** 302 * 固定値の値配列を返します。 303 * 304 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 305 * 306 * @return 固定値の値配列 307 */ 308 public String[] getConstVals() { return cnstVals; } 309 310 /** 311 * 固定値の設定で、特定のキーの値を返します。 312 * 313 * FGJ,DYSET,DYUPD,USRSET,USRUPD,PGSET,PGUPD,PGPSET,PGPUPD,FILE_NAME,FULL_PATH 314 * 315 * @og.rev 7.2.1.0 (2020/03/13) 固定値(PARAMS)の対応追加 316 * 317 * @param cnstKey 固定値のキー 318 * @return キーに対応した値 319 */ 320 private String getVal( final String cnstKey ) { 321 final String cnstVal ; 322 323 if( "FULL_PATH".equalsIgnoreCase( cnstKey ) ) { // このパスの絶対パス 324 String temp = ""; 325 try { 326 temp = path.toFile().getCanonicalPath() ; 327 } 328 catch( final IOException ex ) { 329 System.out.println( ex ); 330 } 331 cnstVal = temp; 332 } 333 else { 334 switch( cnstKey ) { 335 case "FILE_NAME" : cnstVal = path.getFileName().toString() ; break; // ファイル名 336 case "FGJ" : cnstVal = "1" ; break; // 1:活動中 337 case "DYSET" : cnstVal = dyset ; break; // yyyyMMddHHmmss 338 case "DYUPD" : cnstVal = dyset ; break; 339 case "PGSET" : cnstVal = pgset ; break; // PL/SQLコール 340 case "PGUPD" : cnstVal = pgset ; break; 341 case "PGPSET" : cnstVal = "GE7001"; break; // JSP画面ID 342 case "PGPUPD" : cnstVal = "GE7001"; break; 343 case "USRSET" : cnstVal = "BATCH"; break; // BATCH固定 344 case "USRUPD" : cnstVal = "BATCH"; break; 345 default : cnstVal = "" ; break; 346 } 347 } 348 return cnstVal; 349 } 350 } 351}