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.common; 017 018import org.opengion.fukurou.xml.HybsXMLSave; 019import org.opengion.fukurou.util.Closer ; 020 021import java.sql.Connection; 022import java.sql.SQLException; 023 024import java.io.Reader; 025import java.io.BufferedReader; 026import java.io.InputStreamReader; 027import java.io.FileInputStream; 028import java.io.InputStream; 029import java.io.IOException; 030import java.io.File; 031import java.io.UnsupportedEncodingException; 032 033import java.util.List; 034import java.util.ArrayList; 035import java.util.Enumeration; 036import java.util.jar.JarFile; 037import java.util.jar.JarEntry; 038import java.net.URL; 039 040/** 041 * ORACLE XDK 形式のXMLファイルを読み取って、データベースに登録します。 042 * 起動(実行)は、コンテキスト読み取り時の初回のみです。GE12パラメータを変更した 043 * 場合は、コンテキストのリロードが必要です。 044 * 登録の実行有無の判断は、ファイルの更新時刻より判断します。(useTimeStamp=true の場合) 045 * これは、読み取りファイルの更新時刻が、0でない場合、読み取りを行います。 046 * 読み取りが完了した場合は、更新時刻を 0 に設定します。 047 * 読み取るファイルは、クラスローダーのリソースとして取得されますので、クラスパスが 048 * 設定されている必要があります。また、ファイルは、拡張子が xml で、UTF-8でエンコード 049 * されている必要があります。通常は、ファイル名がテーブル名と同一にしておく必要が 050 * ありますが、ROWSETのtable属性にテーブル名をセットしておくことも可能です。 051 * ファイルの登録順は、原則、クラスローダーの検索順に、見つかった全てのファイルを 052 * 登録します。データそのものは、INSERT のみ対応していますので、原則登録順は無視されます。 053 * ただし、拡張XDK 形式で、EXEC_SQL タグを使用した場合は、登録順が影響する可能性があります。 054 * 例:GE12.xml GE12 テーブルに登録するXMLファイル 055 * 登録時に、既存のデータの破棄が必要な場合は、拡張XDK 形式のXMLファイルを 056 * 作成してください。これは、EXEC_SQL タグに書き込んだSQL文を実行します。 057 * 詳細は、{@link org.opengion.fukurou.xml.HybsXMLHandler HybsXMLHandler} クラスを参照してください。 058 * 059 * <ROWSET tableName="XX" > 060 * <EXEC_SQL> 最初に記載して、初期処理(データクリア等)を実行させる。 061 * delete from GEXX where YYYYY 062 * </EXEC_SQL> 063 * <ROW num="1"> 064 * <カラム1>値1</カラム1> 065 * ・・・ 066 * <カラムn>値n</カラムn> 067 * </ROW> 068 * ・・・ 069 * <ROW num="n"> 070 * ・・・ 071 * </ROW> 072 * <EXEC_SQL> 最後に記載して、項目の設定(整合性登録)を行う。 073 * update GEXX set AA='XX' , BB='XX' where YYYYY 074 * </EXEC_SQL> 075 * <ROWSET> 076 * 077 * @og.rev 4.0.0.0 (2004/12/31) 新規作成 078 * @og.group 初期化 079 * 080 * @version 4.0 081 * @author Kazuhiko Hasegawa 082 * @since JDK5.0, 083 */ 084public final class InitFileLoader { 085 private final String CLASSPATH ; 086 private final Connection connection ; 087 private boolean fileCommit = false; // ファイル毎にコミット処理を行うかどうか(true:行う/false:行わない) 088 089 /** 090 * コネクションを引数にする、コンストラクターです。 091 * classPath="resource" で初期化された InitFileLoader を作成します。 092 * 093 * @param conn 登録用コネクション 094 */ 095 public InitFileLoader( final Connection conn ) { 096 this( conn,"resource" ); 097 } 098 099 /** 100 * コネクションと検索パスを指定して構築する、コンストラクターです。 101 * 対象ファイルは、classPath で指定された場所を、クラスローダーで検索します。 102 * ここで見つかったパス以下の XMLファイル(拡張子は小文字で、.xml )を検索 103 * します。このファイル名は テーブル名.xml 形式で格納しておきます。 104 * 105 * @param conn 登録用コネクション 106 * @param classPath 対象となるファイル群を検索する、クラスパス 107 */ 108 public InitFileLoader( final Connection conn,final String classPath ) { 109 connection = conn ; 110 CLASSPATH = classPath; 111 } 112 113 /** 114 * ファイル毎にコミット処理を行うかどうか指定します(初期値:false[行わない])。 115 * 対象ファイル毎に、データベースへの登録を完了(commit)するかどうかを指定します。 116 * 通常、Connection は、autoCommit を false に設定し、1件ごとの処理は行いません。 117 * さらに、XMLファイルにも相互関連がある場合があるため、複数ファイルを取り込む場合は、 118 * それらを一群として処理したいケースもあります。また、各ファイルが独立している 119 * 場合は、他のファイル取り込み時にエラーが発生しても、それまでの分は、取り込みたい 120 * ケースがあります。 121 * ここでは、ファイル毎にコミットを発行するかどうかを指定できます。 122 * 初期値は、false[行わない]です。 123 * ※ true に設定した場合でも、途中でエラーが発生した場合は、それ以降の処理は、 124 * 継続しません。それ以前の処理が、登録されているだけです。 125 * 126 * @param fileCmt ファイル毎にコミット処理を行うかどうか [true:行う/false:行わない] 127 */ 128 public void setFileCommit( final boolean fileCmt ) { 129 fileCommit = fileCmt ; 130 } 131 132 /** 133 * 対象となるファイル群を検索します。 134 * 対象ファイルは、resource フォルダに テーブル名.xml 形式で格納しておきます。 135 * このフォルダのファイルをピックアップします。 136 * useTimeStamp 属性を true に設定すると、このファイルのタイムスタンプを、 137 * システムパラメータ定義(GE12) にセットします。それ以降、このタイムスタンプと 138 * ファイルを比較して、変更がなければ、登録処理を行いません。 139 * 140 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。 141 * @og.rev 5.3.6.0 (2011/06/01) 実フォルダの場合、フォルダ階層を下る処理を追加 142 * @og.rev 5.5.2.6 (2012/05/25) JarFile を、Closer#zipClose( ZipFile ) メソッドを利用して、close します。 143 * @og.rev 5.6.6.1 (2013/07/12) jarファイルの場合も、タイムスタンプ管理の対象とします。 144 * 145 * @param useTimeStamp タイムスタンプの管理を行うかどうか[true:行う/false:行わない] 146 */ 147 public void loadInitFiles( final boolean useTimeStamp ) { 148 List<File> fileList = new ArrayList<File>(); 149 150 // 5.5.2.6 (2012/05/25) findbugs対応 151 JarFile jarFile = null; 152 try { 153 // System.out.println( " ==========================" ); 154 155 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 156 Enumeration<URL> enume = loader.getResources( CLASSPATH ); // 4.3.3.6 (2008/11/15) Generics警告対応 157 while( enume.hasMoreElements() ) { 158 URL url = enume.nextElement(); // 4.3.3.6 (2008/11/15) Generics警告対応 159 // jar:file:/実ディレクトリ または、file:/実ディレクトリ 160 System.out.println( " InitFileLoader Scan:[ " + url + " ]" ); // 5.6.6.1 (2013/07/12) メッセージ出力 161 String dir = url.getFile(); 162 if( "jar".equals( url.getProtocol() ) ) { 163 // dir = file:/C:/webapps/gf/WEB-INF/lib/resource2.jar!/resource 形式です。 164 String jar = dir.substring(dir.indexOf( ':' )+1,dir.lastIndexOf( '!' )); 165 // jar = /C:/webapps/gf/WEB-INF/lib/resource2.jar 形式に切り出します。 166 167 // 5.6.6.1 (2013/07/12) jarファイルも、タイムスタンプ管理の対象 168 File jarObj = new File( jar ); 169 if( useTimeStamp && jarObj.lastModified() <= 0 ) { // fileObj は、jarファイルのこと 170 continue; 171 } 172 173// JarFile jarFile = new JarFile( jar ); 174 jarFile = new JarFile( jar ); 175 Enumeration<JarEntry> flEnum = jarFile.entries() ; // 4.3.3.6 (2008/11/15) Generics警告対応 176 while( flEnum.hasMoreElements() ) { 177 JarEntry ent = flEnum.nextElement(); // 4.3.3.6 (2008/11/15) Generics警告対応 178 String file = ent.getName(); 179 if( ! ent.isDirectory() && file.endsWith( ".xml" ) ) { 180 // // 5.6.6.1 (2013/07/12) jarファイルの中身のタイムスタンプは見ない。 181// if( ! useTimeStamp || ent.getTime() > 0 ) { 182 String table = file.substring( file.lastIndexOf('/')+1,file.lastIndexOf('.') ); 183 InputStream stream = null; 184 try { 185// System.out.println( " " + url + file ); 186 System.out.println( " " + file ); // 5.6.6.1 (2013/07/12) メッセージ変更 187 stream = jarFile.getInputStream( ent ) ; 188 loadXML( stream,connection,table ); 189 } 190// catch( IOException ex ) { 191// String errMsg = "jar の XMLファイル読み取り時にエラーが発生しました。" 192// + HybsSystem.CR + ex.getMessage(); 193// throw new RuntimeException( errMsg,ex ); 194// } 195 finally { 196 Closer.ioClose( stream ); 197 } 198 if( fileCommit ) { 199 connection.commit(); 200 } 201// } 202 } 203 } 204 fileList.add( jarObj ); // 5.6.6.1 (2013/07/12) jarファイルも、タイムスタンプ管理の対象 205 Closer.zipClose( jarFile ); // 5.5.2.6 (2012/05/25) findbugs対応 206 jarFile = null; // 正常終了時に、close() が2回呼ばれるのを防ぐため。 207 } 208 else { 209// // dir = /C:/webapps/gf/WEB-INF/classes/resource/ 形式です。 210// File fileObj = new File( dir ); 211// File[] list = fileObj.listFiles(); 212// for( int i=0; i<list.length; i++ ) { 213// String file = list[i].getName() ; 214// if( list[i].isFile() && file.endsWith( ".xml" ) ) { 215// if( ! useTimeStamp || list[i].lastModified() > 0 ) { 216// String table = file.substring( file.lastIndexOf('/')+1,file.lastIndexOf('.') ); 217// InputStream stream = null; 218// try { 219// stream = new FileInputStream( list[i] ) ; 220// System.out.println( " " + url + file ); 221// loadXML( stream,connection,table ); 222// } 223// catch( IOException ex ) { 224// String errMsg = "dir の XMLファイル読み取り時にエラーが発生しました。" 225// + HybsSystem.CR + ex.getMessage(); 226// throw new RuntimeException( errMsg,ex ); 227// } 228// finally { 229// Closer.ioClose( stream ); 230// } 231// if( fileCommit ) { 232// connection.commit(); 233// if( !list[i].setLastModified( 0L ) ) { 234// String errMsg = "タイムスタンプの書き換えに失敗しました。" 235// + "file=" + file ; // 5.1.8.0 (2010/07/01) errMsg 修正 236// System.out.println( errMsg ); 237// } 238// } 239// else { 240// fileList.add( list[i] ); 241// } 242// } 243// } 244// } 245 // 5.3.6.0 (2011/06/01) 実フォルダの場合、フォルダ階層を下る処理を追加 246 // dir = /C:/webapps/gf/WEB-INF/classes/resource/ 形式です。 247 File fileObj = new File( dir ); 248 loadXMLDir( fileObj,useTimeStamp,fileList ); 249 } 250 } 251 connection.commit(); 252 // System.out.println( " ==========================" ); 253 } 254 catch( SQLException ex ) { 255 String errMsg = "SQL実行時にエラーが発生しました。" 256 + HybsSystem.CR + ex.getMessage(); 257 Closer.rollback( connection ); 258// if( connection != null ) { 259// Closer.rollback( connection ); 260// try { connection.rollback(); } 261// catch( SQLException ex2 ) { errMsg += ex2.getMessage(); } 262// } 263 throw new RuntimeException( errMsg,ex ); 264 } 265 catch( IOException ex ) { 266 String errMsg = "XMLファイル読み取り時にエラーが発生しました。" 267 + HybsSystem.CR + ex.getMessage(); 268 throw new RuntimeException( errMsg,ex ); 269 } 270 finally { 271 Closer.zipClose( jarFile ); // 5.5.2.6 (2012/05/25) findbugs対応 272 273 // 5.3.6.0 (2011/06/01) finally 処理で、タイムスタンプの書き換えを行う。 274 275 // commit が成功した場合のみ、ファイルのタイムスタンプの書き換えを行う。 276// if( ! fileCommit ) { 277// File[] files = fileList.toArray( new File[fileList.size()] ); 278// for( int i=0; i<files.length; i++ ) { 279 // 5.6.6.1 (2013/07/12) useTimeStamp=true の場合のみ、書き換えます。 280 if( useTimeStamp ) { 281 for( File file : fileList ) { 282 if( !file.setLastModified( 0L ) ) { 283 String errMsg = "タイムスタンプの書き換えに失敗しました。" 284 + "file=" + file ; // 5.1.8.0 (2010/07/01) errMsg 修正 285 System.out.println( errMsg ); 286 } 287 } 288 } 289 } 290 } 291 292 /** 293 * XMLフォルダ/ファイルを読み取り、データベースに追加(INSERT)するメソッドをコールします。 294 * 295 * ここでは、フォルダ階層を下るための再起処理を行っています。 296 * XMLファイルは、ORACLE XDK拡張ファイルです。テーブル名を指定することで、 297 * XMLファイルをデータベースに登録することが可能です。 298 * ORACLE XDK拡張ファイルや、EXEC_SQLタグなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave} 299 * を参照願います。 300 * 301 * @og.rev 5.3.6.0 (2011/06/01) 実フォルダの場合、フォルダ階層を下る処理を追加 302 * 303 * @param fileObj 読取元のファイルオブジェクト 304 * @param useTimeStamp タイムスタンプの管理を行うかどうか[true:行う/false:行わない] 305 * @param fileList 処理したファイルを保管しておくListオブジェクト 306 * @throws SQLException,IOException データベースアクセスエラー、または、データ入出力エラー 307 */ 308 private void loadXMLDir( final File fileObj , final boolean useTimeStamp , final List<File> fileList ) 309 throws SQLException,IOException { 310 // dir = /C:/webapps/gf/WEB-INF/classes/resource/ 形式です。 311 File[] list = fileObj.listFiles(); 312 // Arrays.sort( list ); 313 for( int i=0; i<list.length; i++ ) { 314 String file = list[i].getName() ; 315 if( list[i].isFile() && file.endsWith( ".xml" ) ) { 316 if( ! useTimeStamp || list[i].lastModified() > 0 ) { 317 String table = file.substring( file.lastIndexOf('/')+1,file.lastIndexOf('.') ); 318 InputStream stream = null; 319 try { 320 System.out.println( " " + list[i] ); 321 stream = new FileInputStream( list[i] ) ; 322 loadXML( stream,connection,table ); 323 } 324// catch( IOException ex ) { 325// String errMsg = "dir の XMLファイル読み取り時にエラーが発生しました。" 326// + HybsSystem.CR + ex.getMessage(); 327// throw new RuntimeException( errMsg,ex ); 328// } 329 finally { 330 Closer.ioClose( stream ); 331 } 332 if( fileCommit ) { 333 connection.commit(); 334 } 335 fileList.add( list[i] ); 336 } 337 } 338 else { 339 loadXMLDir( list[i],useTimeStamp,fileList ); 340 } 341 } 342 } 343 344 /** 345 * XMLファイルを読み取り、データベースに追加(INSERT)します。 346 * 347 * XMLファイルは、ORACLE XDK拡張ファイルです。テーブル名を指定することで、 348 * XMLファイルをデータベースに登録することが可能です。 349 * ORACLE XDK拡張ファイルや、EXEC_SQLタグなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave} 350 * を参照願います。 351 * 352 * @param stream 読み取るストリーム 353 * @param conn DB接続のコネクション 354 * @param table 追加するテーブル名 355 * 356 * @og.rev 5.6.6.1 (2013/07/12) 更新カウント数も取得します。 357 * @og.rev 5.6.7.0 (2013/07/27) HybsXMLSave の DDL(データ定義言語:Data Definition Language)の処理件数追加 358 * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。 359 * 360 * @return 追加された件数 361 * @see org.opengion.fukurou.xml.HybsXMLSave 362 */ 363 private int loadXML( final InputStream stream, final Connection conn,final String table ) 364 throws SQLException,UnsupportedEncodingException { 365 366 // InputStream より、XMLファイルを読み取り、table に追加(INSERT)します。 367 Reader reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) ); 368 HybsXMLSave save = new HybsXMLSave( conn,table ); 369 save.onExecErrException( false ); // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。 370 save.insertXML( reader ); 371 int insCnt = save.getInsertCount(); 372 int delCnt = save.getDeleteCount(); 373 int updCnt = save.getUpdateCount(); // 5.6.6.1 (2013/07/12) 更新カウント数も取得 374 int ddlCnt = save.getDDLCount(); // 5.6.7.0 (2013/07/27) DDL処理件数追加 375 String tableName = save.getTableName() ; 376 377// System.out.println( " TABLE=[" + tableName + "] DELETE=["+ delCnt +"] INSERT=[" + insCnt + "]" ); 378// System.out.println( " TABLE=[" + tableName + "] DELETE=["+ delCnt +"] INSERT=[" + insCnt + "] UPDATE=[" + updCnt + "]" ); 379 System.out.println( " TABLE=[" + tableName + "] DELETE=["+ delCnt +"] INSERT=[" + insCnt + "] UPDATE=[" + updCnt + "] DDL=[" + ddlCnt + "]" ); 380 return insCnt; 381 } 382}