    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 * これは、読み取りファイルの更新時刻が、０でない場合、読み取りを行います。
    046 * 読み取りが完了した場合は、更新時刻を ０ に設定します。
    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 *   &lt;ROWSET tableName="XX" &gt;
    060 *       &lt;EXEC_SQL&gt;                    最初に記載して、初期処理(データクリア等)を実行させる。
    061 *           delete from GEXX where YYYYY
    062 *       &lt;/EXEC_SQL&gt;
    063 *       &lt;ROW num="1"&gt;
    064 *           &lt;カラム1&gt;値1&lt;/カラム1&gt;
    065 *             ･･･
    066 *           &lt;カラムn&gt;値n&lt;/カラムn&gt;
    067 *       &lt;/ROW&gt;
    068 *        ･･･
    069 *       &lt;ROW num="n"&gt;
    070 *          ･･･
    071 *       &lt;/ROW&gt;
    072 *       &lt;EXEC_SQL&gt;                    最後に記載して、項目の設定(整合性登録)を行う。
    073 *           update GEXX set AA='XX' , BB='XX' where YYYYY
    074 *       &lt;/EXEC_SQL&gt;
    075 *   &lt;ROWSET&gt;
    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 に設定し、１件ごとの処理は行いません。
    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() が２回呼ばれるのを防ぐため。
    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         * @og.rev 5.10.15.1 (2019/09/06) listがnullの場合はそのままリターン
    303         *
    304         * @param       fileObj                 読取元のファイルオブジェクト
    305         * @param       useTimeStamp    タイムスタンプの管理を行うかどうか[true:行う/false:行わない]
    306         * @param       fileList                処理したファイルを保管しておくListオブジェクト
    307         * @throws SQLException,IOException  データベースアクセスエラー、または、データ入出力エラー
    308         */
    309        private void loadXMLDir( final File fileObj , final boolean useTimeStamp , final List<File> fileList )
    310                                                                                                                                                                throws SQLException,IOException {
    311                // dir = /C:/webapps/gf/WEB-INF/classes/resource/ 形式です。
    312                File[] list = fileObj.listFiles();
    313        //      Arrays.sort( list );
    314                if( list == null ) { return; } // 5.10.15.1 (2019/09/06)
    315                for( int i=0; i<list.length; i++ ) {
    316                        String file = list[i].getName() ;
    317                        if( list[i].isFile() && file.endsWith( ".xml" ) ) {
    318                                if( ! useTimeStamp || list[i].lastModified() > 0 ) {
    319                                        String table = file.substring( file.lastIndexOf('/')+1,file.lastIndexOf('.') );
    320                                        InputStream stream = null;
    321                                        try {
    322                                                System.out.println( "        " + list[i] );
    323                                                stream = new FileInputStream( list[i] ) ;
    324                                                loadXML( stream,connection,table );
    325                                        }
    326//                                      catch( IOException ex ) {
    327//                                              String errMsg = "dir の XMLファイル読み取り時にエラーが発生しました。"
    328//                                                              + HybsSystem.CR + ex.getMessage();
    329//                                              throw new RuntimeException( errMsg,ex );
    330//                                      }
    331                                        finally {
    332                                                Closer.ioClose( stream );
    333                                        }
    334                                        if( fileCommit ) {
    335                                                connection.commit();
    336                                        }
    337                                        fileList.add( list[i] );
    338                                }
    339                        }
    340                        else {
    341                                loadXMLDir( list[i],useTimeStamp,fileList );
    342                        }
    343                }
    344        }
    345
    346        /**
    347         * XMLファイルを読み取り、データベースに追加(INSERT)します。
    348         *
    349         * XMLファイルは、ORACLE XDK拡張ファイルです。テーブル名を指定することで、
    350         * XMLファイルをデータベースに登録することが可能です。
    351         * ORACLE XDK拡張ファイルや、EXEC_SQLタグなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave}
    352         * を参照願います。
    353         *
    354         * @param       stream  読み取るストリーム
    355         * @param       conn    DB接続のコネクション
    356         * @param       table   追加するテーブル名
    357         *
    358         * @og.rev 5.6.6.1 (2013/07/12) 更新カウント数も取得します。
    359         * @og.rev 5.6.7.0 (2013/07/27) HybsXMLSave の DDL（データ定義言語：Data Definition Language）の処理件数追加
    360         * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
    361         *
    362         * @return      追加された件数
    363         * @see org.opengion.fukurou.xml.HybsXMLSave
    364         */
    365        private int loadXML( final InputStream stream, final Connection conn,final String table )
    366                throws SQLException,UnsupportedEncodingException {
    367
    368                // InputStream より、XMLファイルを読み取り、table に追加(INSERT)します。
    369                Reader reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) );
    370                HybsXMLSave save = new HybsXMLSave( conn,table );
    371                save.onExecErrException( false );               // 5.6.9.2 (2013/10/18) EXEC_SQL のエラー時に Exception を発行しない。
    372                save.insertXML( reader );
    373                int insCnt = save.getInsertCount();
    374                int delCnt = save.getDeleteCount();
    375                int updCnt = save.getUpdateCount();             // 5.6.6.1 (2013/07/12) 更新カウント数も取得
    376                int ddlCnt = save.getDDLCount();                // 5.6.7.0 (2013/07/27) DDL処理件数追加
    377                String tableName = save.getTableName() ;
    378
    379//              System.out.println( "          TABLE=[" + tableName + "]  DELETE=["+ delCnt +"] INSERT=[" + insCnt + "]" );
    380//              System.out.println( "          TABLE=[" + tableName + "]  DELETE=["+ delCnt +"] INSERT=[" + insCnt + "] UPDATE=[" + updCnt + "]" );
    381                System.out.println( "          TABLE=[" + tableName + "]  DELETE=["+ delCnt +"] INSERT=[" + insCnt + "] UPDATE=[" + updCnt + "] DDL=[" + ddlCnt + "]" );
    382                return insCnt;
    383        }
    384}