    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.fukurou.xml;
    017
    018import org.opengion.fukurou.system.OgRuntimeException;                          // 6.4.2.0 (2016/01/29)
    019import org.opengion.fukurou.system.ThrowUtil;                                           // 6.4.2.0 (2016/01/29)
    020import org.opengion.fukurou.util.FileUtil;                                                      // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理します。
    021import org.opengion.fukurou.system.Closer;
    022import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.1.0.0 (2014/12/26) refactoring
    023
    024import java.sql.Connection;
    025import java.sql.SQLException;
    026
    027// import java.io.Reader;
    028import java.io.BufferedReader;
    029import java.io.InputStreamReader;
    030// import java.io.FileInputStream;
    031import java.io.InputStream;
    032import java.io.IOException;
    033import java.io.File;
    034import java.io.UnsupportedEncodingException;
    035import java.io.Writer;                                                                                          // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理します。
    036
    037import java.util.Arrays;
    038import java.util.List;
    039import java.util.Map;
    040import java.util.ArrayList;
    041import java.util.Enumeration;
    042import java.util.jar.JarFile;
    043import java.util.jar.JarEntry;
    044import java.net.URL;
    045
    046/**
    047 * ORACLE XDK 形式のXMLﾌｧｲﾙを読み取って、ﾃﾞｰﾀﾍﾞｰｽに登録します。
    048 *
    049 * これは、Ver5の時は、org.opengion.hayabusa.common.InitFileLoader として
    050 * 使用されていたｸﾗｽを改造したものです。
    051 * InitFileLoader は、Ver6 では廃止されていますので、ご注意ください。
    052 *
    053 * 登録の実行有無の判断は、ﾌｧｲﾙの更新時刻より判断します。(useTimeStamp=true の場合)
    054 * これは、読み取りﾌｧｲﾙの更新時刻が、0でない場合、読み取りを行います。
    055 * 読み取りが完了した場合は、更新時刻を 0 に設定します。
    056 * 読み取るﾌｧｲﾙは、ｸﾗｽﾛｰﾀﾞｰのﾘｿｰｽや、指定のﾌｫﾙﾀﾞ以下のﾌｧｲﾙ、そして、
    057 * zip 圧縮されたﾌｧｲﾙの中から、拡張子が xml で、UTF-8でｴﾝｺｰﾄﾞされている
    058 * 必要があります。通常は、ﾌｧｲﾙ名がﾃｰﾌﾞﾙ名と同一にしておく必要がありますが、
    059 * ROWSETのtable属性にﾃｰﾌﾞﾙ名をｾｯﾄしておくことも可能です。
    060 * ﾌｧｲﾙの登録順は、原則、ｸﾗｽﾛｰﾀﾞｰの検索順に、見つかった全てのﾌｧｲﾙを
    061 * 登録します。ﾃﾞｰﾀそのものは、INSERT のみ対応していますので、原則登録順は無視されます。
    062 * ただし、拡張XDK 形式で、EXEC_SQL ﾀｸﾞを使用した場合は、登録順が影響する可能性があります。
    063 * 例：GE12.xml GE12 ﾃｰﾌﾞﾙに登録するXMLﾌｧｲﾙ
    064 * 登録時に、既存のﾃﾞｰﾀの破棄が必要な場合は、拡張XDK 形式のXMLﾌｧｲﾙを
    065 * 作成してください。これは、EXEC_SQL ﾀｸﾞに書き込んだSQL文を実行します。
    066 * 詳細は、{@link org.opengion.fukurou.xml.HybsXMLHandler HybsXMLHandler} ｸﾗｽを参照してください。
    067 *
    068 *   &lt;ROWSET tableName="XX" &gt;
    069 *       &lt;EXEC_SQL&gt;                    最初に記載して、初期処理(ﾃﾞｰﾀｸﾘｱ等)を実行させる。
    070 *           delete from GEXX where YYYYY
    071 *       &lt;/EXEC_SQL&gt;
    072 *       &lt;ROW num="1"&gt;
    073 *           &lt;ｶﾗﾑ1&gt;値1&lt;/ｶﾗﾑ1&gt;
    074 *             ･･･
    075 *           &lt;ｶﾗﾑn&gt;値n&lt;/ｶﾗﾑn&gt;
    076 *       &lt;/ROW&gt;
    077 *        ･･･
    078 *       &lt;ROW num="n"&gt;
    079 *          ･･･
    080 *       &lt;/ROW&gt;
    081 *       &lt;EXEC_SQL&gt;                    最後に記載して、項目の設定(整合性登録)を行う。
    082 *           update GEXX set AA='XX' , BB='XX' where YYYYY
    083 *       &lt;/EXEC_SQL&gt;
    084 *   &lt;ROWSET&gt;
    085 *
    086 * @og.rev 4.0.0.0 (2004/12/31) 新規作成(org.opengion.hayabusa.common.InitFileLoader)
    087 * @og.rev 6.0.0.0 (2014/04/11) ﾊﾟｯｹｰｼﾞ、ｸﾗｽﾌｧｲﾙ変更
    088 *
    089 * @version     6.0
    090 * @author      Kazuhiko Hasegawa
    091 * @since       JDK7.0,
    092 */
    093public final class XMLFileLoader {
    094        private static final String ENCODE = "UTF-8";                   // 8.0.0.1 (2021/10/08) cloud対応
    095
    096        private final Connection connection ;
    097        private final boolean useTimeStamp ;
    098
    099        // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理します。
    100        private Writer log = FileUtil.getLogWriter( "System.out" );
    101
    102        // 6.0.0.0 (2014/04/11) ﾀｲﾑｽﾀﾝﾌﾟのｾﾞﾛｸﾘｱ対象のﾌｧｲﾙを管理するﾘｽﾄ
    103        private final List<File> fileList = new ArrayList<>();
    104
    105        // 6.0.0.0 (2014/04/11) setAfterMap ﾒｿｯﾄﾞの対応
    106        /** 6.4.3.1 (2016/02/12) 作成元のMapを、HashMap から ConcurrentHashMap に置き換え。 */
    107        private Map<String,String> afterMap     ;
    108
    109        // 6.0.0.0 (2014/04/11) 追加,更新,削除,実行 の各実行時のｶｳﾝﾄの総数
    110        private final int[] crudCnt = new int[] { 0,0,0,0 };    // CRUD ｶｳﾝﾄですが、配列の順番は追加,更新,削除,実行
    111
    112        /** getCRUDCount() で返される ｶｳﾝﾄ数の配列番号 {@value} */
    113        public static final int INS = 0;
    114        /** getCRUDCount() で返される ｶｳﾝﾄ数の配列番号 {@value} */
    115        public static final int DEL = 1;
    116        /** getCRUDCount() で返される ｶｳﾝﾄ数の配列番号 {@value} */
    117        public static final int UPD = 2;
    118        /** getCRUDCount() で返される ｶｳﾝﾄ数の配列番号 {@value} */
    119        public static final int DDL = 3;
    120
    121        /**
    122         * ｺﾈｸｼｮﾝを引数にする、ｺﾝｽﾄﾗｸﾀｰです。
    123         * classPath="resource" で初期化された XMLFileLoader を作成します。
    124         * useTimeStamp 属性を true に設定すると、このﾌｧｲﾙを読み取る都度
    125         * ﾀｲﾑｽﾀﾝﾌﾟを、ｸﾘｱします。
    126         * また、ﾀｲﾑｽﾀﾝﾌﾟがｸﾘｱされたﾌｧｲﾙは読み込みませんので、機能的に
    127         * 一度しか読み込まないという事になります。
    128         *
    129         * @param       conn                    登録用ｺﾈｸｼｮﾝ
    130         * @param       useTimeStamp    ﾀｲﾑｽﾀﾝﾌﾟの管理を行うかどうか[true:行う/false:行わない]
    131         */
    132        public XMLFileLoader( final Connection conn , final boolean useTimeStamp ) {
    133                connection                      = conn ;
    134                this.useTimeStamp       = useTimeStamp;
    135        }
    136
    137        /**
    138         * ﾛｸﾞ出力を行う 内部ﾛｸﾞ(Writer) を指定します。
    139         *
    140         * 内部ﾛｸﾞ(Writer) の初期値は、null とします。
    141         * 内部ﾛｸﾞ(Writer)が null の場合は、なにもしません。
    142         *
    143         * @og.rev 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理します。
    144         *
    145         * @param       log     Writerｵﾌﾞｼﾞｪｸﾄ
    146         */
    147        public void setLogWriter( final Writer log ) {
    148                this.log = log ;
    149        }
    150
    151        /**
    152         * XMLﾌｧｲﾙを読み取った後で指定するｶﾗﾑと値のﾍﾟｱ(ﾏｯﾌﾟ)情報をｾｯﾄします。
    153         *
    154         * このｶﾗﾑと値のﾍﾟｱのﾏｯﾌﾟは、ｵﾌﾞｼﾞｪｸﾄ構築後に設定される為、
    155         * XMLﾌｧｲﾙのｷｰの存在に関係なく、Mapのｷｰと値が使用されます。(Map優先)
    156         * null を設定した場合は、なにも処理されません。
    157         *
    158         * @og.rev 6.0.0.0 (2014/04/11) 新規追加
    159         *
    160         * @param       map     後設定するｶﾗﾑﾃﾞｰﾀﾏｯﾌﾟ
    161         */
    162        public void setAfterMap( final Map<String,String> map ) {
    163                afterMap = map;
    164        }
    165
    166        /**
    167         * XMLﾌｧｲﾙを登録後の 追加,更新,削除,実行 のｶｳﾝﾄ配列を返します。
    168         *
    169         * 簡易的に処理したいために、配列に設定しています。
    170         * 順番に、追加,更新,削除,実行 のｶｳﾝﾄ値になります。
    171         *
    172         * @og.rev 6.0.0.0 (2014/04/11) 新規追加
    173         *
    174         * @return      追加,更新,削除,実行 のｶｳﾝﾄ配列
    175         * @og.rtnNotNull
    176         */
    177        public int[] getCRUDCount() {
    178                // 6.0.2.5 (2014/10/31) refactoring
    179                return crudCnt.clone();
    180        }
    181
    182        /**
    183         * 対象となるﾌｧｲﾙ群を ClassLoader の指定ﾊﾟｽから、検索します。
    184         *
    185         * 対象ﾌｧｲﾙは、指定ﾌｫﾙﾀﾞに ﾃｰﾌﾞﾙ名.xml 形式で格納しておきます。
    186         * このﾌｫﾙﾀﾞのﾌｧｲﾙをﾋﾟｯｸｱｯﾌﾟします。
    187         * useTimeStamp 属性を true に設定すると、このﾌｧｲﾙを読み取る都度
    188         * ﾀｲﾑｽﾀﾝﾌﾟを、ｸﾘｱします。
    189         * また、ﾀｲﾑｽﾀﾝﾌﾟがｸﾘｱされたﾌｧｲﾙは読み込みませんので、機能的に
    190         * 一度しか読み込まないという事になります。
    191         *
    192         * @og.rev 8.5.0.0 (2023/04/21) 対象外の resource を読込んでいた為、判定条件を変更
    193         *
    194         * @param       path    対象となるﾌｧｲﾙ群を検索するｸﾗｽﾊﾟｽ
    195         */
    196        public void loadClassPathFiles( final String path ) {
    197                loadClassPathFiles( path, "" );
    198        }
    199
    200        /**
    201         * 対象となるﾌｧｲﾙ群を ClassLoader の指定ﾊﾟｽから、検索します。
    202         *
    203         * 対象ﾌｧｲﾙは、指定ﾌｫﾙﾀﾞに ﾃｰﾌﾞﾙ名.xml 形式で格納しておきます。
    204         * このﾌｫﾙﾀﾞのﾌｧｲﾙをﾋﾟｯｸｱｯﾌﾟします。
    205         * useTimeStamp 属性を true に設定すると、このﾌｧｲﾙを読み取る都度
    206         * ﾀｲﾑｽﾀﾝﾌﾟを、ｸﾘｱします。
    207         * また、ﾀｲﾑｽﾀﾝﾌﾟがｸﾘｱされたﾌｧｲﾙは読み込みませんので、機能的に
    208         * 一度しか読み込まないという事になります。
    209         *
    210         * @og.rev 6.0.0.0 (2014/04/11) 新規追加
    211         * @og.rev 6.4.0.4 (2015/12/26) Writer(ﾛｸﾞ)のCloseは、ここでは行わない。
    212         * @og.rev 6.8.5.1 (2018/01/15) ﾌｧｲﾙ名は、##ﾊﾞｰｼﾞｮﾝ番号を変換しておく必要がある。
    213         * @og.rev 8.5.0.0 (2023/04/21) 対象外の resource を読込んでいた為、判定条件を変更
    214         *
    215         * @param       path    対象となるﾌｧｲﾙ群を検索するｸﾗｽﾊﾟｽ
    216         * @param       keyword 検索対象ﾌｧｲﾙのｷｰﾜｰﾄﾞ
    217         */
    218//      public void loadClassPathFiles( final String path ) {
    219        public void loadClassPathFiles( final String path,final String keyword ) {      // 8.5.0.0 (2023/04/21) Modify
    220                try {
    221                        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
    222                        final Enumeration<URL> enume = loader.getResources( path );
    223                        while( enume.hasMoreElements() ) {
    224                                final URL url = enume.nextElement();
    225                                // jar:file:/実ディレクトリ または、file:/実ディレクトリ
    226//                              final String dir = url.getFile();
    227                                final String dir = url.getFile().replaceAll( "%23%23","##" );   // 6.8.5.1 (2018/01/15)
    228
    229                                if( keyword == null || keyword.isEmpty() || dir.contains( keyword ) ){  // 8.5.0.0 (2023/04/21) Add
    230                                        println( "      XMLFileLoader Scan:[ " + url + " ]" );                          // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理
    231
    232                                        if( "jar".equals( url.getProtocol() ) ) {
    233                                                // dir = file:/C:/webapps/gf/WEB-INF/lib/resource2.jar!/resource 形式です。
    234                                                final String jar = dir.substring(dir.indexOf( ':' )+1,dir.lastIndexOf( '!' ));
    235                                                // jar = /C:/webapps/gf/WEB-INF/lib/resource2.jar 形式に切り出します。
    236
    237                                                // 5.6.6.1 (2013/07/12) jarファイルも、タイムスタンプ管理の対象
    238                                                loadJarFile( new File( jar ) );
    239                                        }
    240                                        else {
    241                                                // 5.3.6.0 (2011/06/01) 実フォルダの場合、フォルダ階層を下る処理を追加
    242                                                // dir = /C:/webapps/gf/WEB-INF/classes/resource/ 形式です。
    243                                                loadXMLDir( new File( dir ) );
    244                                        }
    245                                }                                                                                                                                               // 8.5.0.0 (2023/04/21) Add
    246                        }
    247                        Closer.commit( connection );
    248                }
    249                catch( final SQLException ex ) {
    250                        final String errMsg = "SQL実行時にエラーが発生しました。"
    251                                        + CR + ex.getMessage();
    252                        Closer.rollback( connection );
    253                        throw new OgRuntimeException( errMsg,ex );
    254                }
    255                catch( final IOException ex ) {
    256                        final String errMsg = "XMLファイル読み取り時にエラーが発生しました。"
    257                                        + CR + ex.getMessage();
    258                        throw new OgRuntimeException( errMsg,ex );
    259                }
    260                finally {
    261                        // 6.4.0.4 (2015/12/26) Writer(ﾛｸﾞ)のCloseは、ここでは行わない。
    262                        setZeroTimeStamp();                             // 6.0.0.0 (2014/04/11) ﾀｲﾑｽﾀﾝﾌﾟの書き換えをﾒｿｯﾄﾞ化
    263                }
    264        }
    265
    266        /**
    267         * 対象となるﾌｧｲﾙ群を ﾌｧｲﾙ単体、ﾌｫﾙﾀﾞ階層以下、ZIPﾌｧｲﾙ から、検索します。
    268         *
    269         * 対象ﾌｧｲﾙは、ﾃｰﾌﾞﾙ名.xml 形式で格納しておきます。
    270         * この処理では、ﾌｧｲﾙ単体(*.xml)、ﾌｫﾙﾀﾞ階層以下、ZIPﾌｧｲﾙ(*.jar , *.zip) は混在できません。
    271         * 最初に判定した形式で、個々の処理に振り分けています。
    272         *
    273         * @og.rev 6.0.0.0 (2014/04/11) 新規追加
    274         * @og.rev 6.4.0.4 (2015/12/26) Writer(ﾛｸﾞ)のCloseは、ここでは行わない。
    275         *
    276         * @param       fileObj 読取元のﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
    277         * @see         #loadClassPathFiles( String )
    278         */
    279        public void loadXMLFiles( final File fileObj ) {
    280                try {
    281                        // nullでなく、ﾌｧｲﾙ/ﾌｫﾙﾀﾞが存在することが前提
    282                        if( fileObj != null && fileObj.exists() ) {
    283                                println( "      XMLFileLoader Scan:[ " + fileObj + " ]" );      // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理
    284                                loadXMLDir( fileObj );                  // ﾌｧｲﾙかﾃﾞｨﾚｸﾄﾘ
    285                        }
    286                        Closer.commit( connection );
    287                }
    288                catch( final SQLException ex ) {
    289                        final String errMsg = "SQL実行時にエラーが発生しました。"
    290                                        + CR + ex.getMessage();
    291                        Closer.rollback( connection );
    292                        throw new OgRuntimeException( errMsg,ex );
    293                }
    294                catch( final IOException ex ) {
    295                        final String errMsg = "XMLファイル読み取り時にエラーが発生しました。"
    296                                        + CR + ex.getMessage();
    297                        throw new OgRuntimeException( errMsg,ex );
    298                }
    299                finally {
    300                        // 6.4.0.4 (2015/12/26) Writer(ﾛｸﾞ)のCloseは、ここでは行わない。
    301                        setZeroTimeStamp();                             // 6.0.0.0 (2014/04/11) ﾀｲﾑｽﾀﾝﾌﾟの書き換えをﾒｿｯﾄﾞ化
    302                }
    303        }
    304
    305        /**
    306         * XMLﾌｫﾙﾀﾞ/ﾌｧｲﾙを読み取り、ﾃﾞｰﾀﾍﾞｰｽに追加(INSERT)するﾒｿｯﾄﾞをｺｰﾙします。
    307         *
    308         * ここでは、ﾌｫﾙﾀﾞ階層を下るための再起処理を行っています。
    309         * XMLﾌｧｲﾙは、ORACLE XDK拡張ﾌｧｲﾙです。ﾃｰﾌﾞﾙ名を指定することで、
    310         * XMLﾌｧｲﾙをﾃﾞｰﾀﾍﾞｰｽに登録することが可能です。
    311         * ORACLE XDK拡張ﾌｧｲﾙや、EXEC_SQLﾀｸﾞなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave}
    312         * を参照願います。
    313         *
    314         * @og.rev 5.3.6.0 (2011/06/01) 実ﾌｫﾙﾀﾞの場合、ﾌｫﾙﾀﾞ階層を下る処理を追加
    315         * @og.rev 8.0.0.1 (2021/10/08) cloud対応
    316         *
    317         * @param       jarObj  読取元のJarﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
    318         * @throws      SQLException,IOException        ﾃﾞｰﾀﾍﾞｰｽｱｸｾｽｴﾗｰ、または、ﾃﾞｰﾀ入出力ｴﾗｰ
    319         */
    320        private void loadJarFile( final File jarObj ) throws SQLException,IOException {
    321                if( ! useTimeStamp || jarObj.lastModified() > 0 ) {
    322                        JarFile jarFile = null;
    323                        try {
    324                                jarFile = new JarFile( jarObj );
    325                                final Enumeration<JarEntry> flEnum = jarFile.entries() ;                // 4.3.3.6 (2008/11/15) Generics警告対応
    326                                while( flEnum.hasMoreElements() ) {
    327                                        final JarEntry ent = flEnum.nextElement();                                      // 4.3.3.6 (2008/11/15) Generics警告対応
    328                                        final String file = ent.getName();
    329                                        if( ! ent.isDirectory() && file.endsWith( ".xml" ) ) {
    330                                                // 5.6.6.1 (2013/07/12) jarファイルの中身のタイムスタンプは見ない。
    331                                                final String table = file.substring( file.lastIndexOf('/')+1,file.lastIndexOf('.') );
    332                                                InputStream stream = null;
    333                                                try {
    334                                                        stream = jarFile.getInputStream( ent ) ;
    335                                                        // 8.0.0.1 (2021/10/08) cloud対応
    336//                                                      loadXML( stream,table,file );
    337                                                        final BufferedReader reader = new BufferedReader( new InputStreamReader( stream,ENCODE ) );
    338                                                        loadXML( reader,table,file );
    339                                                }
    340                                                finally {
    341                                                        Closer.ioClose( stream );
    342                                                }
    343                                        }
    344                                }
    345                                fileList.add( jarObj );                 // 5.6.6.1 (2013/07/12) jarﾌｧｲﾙも、ﾀｲﾑｽﾀﾝﾌﾟ管理の対象
    346                        }
    347                        finally {
    348                                Closer.zipClose( jarFile );             // 5.5.2.6 (2012/05/25) findbugs対応
    349                        }
    350                }
    351        }
    352
    353        /**
    354         * XMLﾌｫﾙﾀﾞ/ﾌｧｲﾙを読み取り、ﾃﾞｰﾀﾍﾞｰｽに追加(INSERT)するﾒｿｯﾄﾞをｺｰﾙします。
    355         *
    356         * ここでは、ﾌｫﾙﾀﾞ階層を下るための再起処理を行っています。
    357         * XMLﾌｧｲﾙは、ORACLE XDK拡張ﾌｧｲﾙです。ﾃｰﾌﾞﾙ名を指定することで、
    358         * XMLﾌｧｲﾙをﾃﾞｰﾀﾍﾞｰｽに登録することが可能です。
    359         * ORACLE XDK拡張ﾌｧｲﾙや、EXEC_SQLﾀｸﾞなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave}
    360         * を参照願います。
    361         *
    362         * @og.rev 5.3.6.0 (2011/06/01) 実ﾌｫﾙﾀﾞの場合、ﾌｫﾙﾀﾞ階層を下る処理を追加
    363         * @og.rev 8.0.0.1 (2021/10/08) cloud対応
    364         *
    365         * @param       fileObj 読取元のﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
    366         * @throws      SQLException,IOException        ﾃﾞｰﾀﾍﾞｰｽｱｸｾｽｴﾗｰ、または、ﾃﾞｰﾀ入出力ｴﾗｰ
    367         */
    368        private void loadXMLDir( final File fileObj ) throws SQLException,IOException {
    369                if( fileObj.isDirectory() ) {
    370                        final File[] list = fileObj.listFiles();
    371                        // 6.3.9.0 (2015/11/06) null になっている可能性がある(findbugs)
    372                        if( list != null ) {
    373                                Arrays.sort( list );
    374                                for( final File file : list ) {
    375                                        loadXMLDir( file );
    376                                }
    377                        }
    378                }
    379                else if( ! useTimeStamp || fileObj.lastModified() > 0 ) {
    380                        final String name = fileObj.getName() ;
    381                        if( name.endsWith( ".xml" ) ) {
    382                                final String table = name.substring( name.lastIndexOf('/')+1,name.lastIndexOf('.') );
    383        //                      InputStream stream = null;
    384        //                      try {
    385                                        println( "        " + fileObj );                // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理
    386                                        // 8.0.0.1 (2021/10/08) cloud対応
    387//                                      stream = new FileInputStream( fileObj ) ;
    388//                                      loadXML( stream,table,fileObj.getPath() );
    389                                        final BufferedReader reader = FileUtil.getBufferedReader( fileObj,ENCODE );
    390                                        loadXML( reader,table,fileObj.getPath() );
    391                                        fileList.add( fileObj );                                // 正常に処理が終われば、ﾘｽﾄに追加します。
    392        //                      }
    393        //                      finally {
    394        //                              Closer.ioClose( stream );
    395        //                      }
    396                        }
    397                        else if( name.endsWith( ".zip" ) || name.endsWith( ".jar" ) ) {
    398                                loadJarFile( fileObj );
    399                        }
    400                }
    401        }
    402
    403        /**
    404         * XMLﾌｧｲﾙを読み取り、ﾃﾞｰﾀﾍﾞｰｽに追加(INSERT)します。
    405         *
    406         * XMLﾌｧｲﾙは、ORACLE XDK拡張ﾌｧｲﾙです。ﾃｰﾌﾞﾙ名を指定することで、
    407         * XMLﾌｧｲﾙをﾃﾞｰﾀﾍﾞｰｽに登録することが可能です。
    408         * ORACLE XDK拡張ﾌｧｲﾙや、EXEC_SQLﾀｸﾞなどの詳細は、{@link org.opengion.fukurou.xml.HybsXMLSave}
    409         * を参照願います。
    410         *
    411         * @og.rev 5.6.6.1 (2013/07/12) 更新ｶｳﾝﾄ数も取得します。
    412         * @og.rev 5.6.7.0 (2013/07/27) HybsXMLSave の DDL（ﾃﾞｰﾀ定義言語：Data Definition Language）の処理件数追加
    413         * @og.rev 5.6.9.2 (2013/10/18) EXEC_SQL のｴﾗｰ時に Exception を発行しない。
    414         * @og.rev 6.2.0.0 (2015/02/27) try ～ finally 構文
    415         * @og.rev 7.3.2.0 (2021/03/19) isExecErr でfalseを指定した場合に、ｴﾗｰ内容の文字列を取り出します。
    416         * @og.rev 8.0.0.1 (2021/10/08) cloud対応
    417         *
    418//       * @param       stream  XMLﾌｧｲﾙを読み取るInputStream
    419         * @param       reader  XMLﾌｧｲﾙを読み取るBufferedReader(cloud対応)
    420         * @param       table   ﾃｰﾌﾞﾙ名(ROWSETﾀｸﾞのtable属性が未設定時に使用)
    421         * @param       file    ﾛｸﾞ出力用のﾌｧｲﾙ名
    422         * @see org.opengion.fukurou.xml.HybsXMLSave
    423         */
    424//      private void loadXML( final InputStream stream, final String table, final String file )
    425        private void loadXML( final BufferedReader reader, final String table, final String file )
    426                                                                        throws SQLException,UnsupportedEncodingException {
    427                // 6.2.0.0 (2015/02/27) try ～ finally 構文
    428//              Reader reader = null;
    429                try {
    430//                      // InputStream より、XMLﾌｧｲﾙを読み取り、table に追加(INSERT)します。
    431//                      reader = new BufferedReader( new InputStreamReader( stream,"UTF-8" ) );
    432                        final HybsXMLSave save = new HybsXMLSave( connection,table );
    433                        save.onExecErrException( false );               // 5.6.9.2 (2013/10/18) falseで、EXEC_SQL のｴﾗｰ時に Exception を発行しない。
    434                        save.setAfterMap( afterMap );                   // 6.0.0.0 (2014/04/11) 新規追加
    435                        save.insertXML( reader );
    436
    437                        final int insCnt = save.getInsertCount();
    438                        final int delCnt = save.getDeleteCount();
    439                        final int updCnt = save.getUpdateCount();               // 5.6.6.1 (2013/07/12) 更新ｶｳﾝﾄ数も取得
    440                        final int ddlCnt = save.getDDLCount();                  // 5.6.7.0 (2013/07/27) DDL処理件数追加
    441
    442                        crudCnt[INS] += insCnt ;
    443                        crudCnt[DEL] += delCnt ;
    444                        crudCnt[UPD] += updCnt ;
    445                        crudCnt[DDL] += ddlCnt ;
    446
    447                        final String tableName = save.getTableName() ;
    448
    449                        // 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理
    450                        println( "          File=[" + file + "] TABLE=[" + tableName + "] DEL=["+ delCnt +"] INS=[" + insCnt + "] UPD=[" + updCnt + "] DDL=[" + ddlCnt + "]" );
    451
    452                        // 7.3.2.0 (2021/03/19) isExecErr でfalseを指定した場合に、ｴﾗｰ内容の文字列を取り出します。
    453                        final String errMsg = save.getErrorMessage() ;
    454                        if( !errMsg.isEmpty() ) {
    455                                println( errMsg );
    456                        }
    457                }
    458                finally {
    459                        Closer.ioClose( reader );
    460                }
    461        }
    462
    463        /**
    464         * 指定のﾘｽﾄのﾌｧｲﾙのﾀｲﾑｽﾀﾝﾌﾟをｾﾞﾛに設定します。
    465         * useTimeStamp=true の時に、XMLﾌｧｲﾙのﾛｰﾄﾞに成功したﾌｧｲﾙの
    466         * ﾀｲﾑｽﾀﾝﾌﾟをｾﾞﾛに設定することで、2回目の処理が避けられます。
    467         *
    468         * @og.rev 6.0.0.0 (2014/04/11) 新規追加
    469         */
    470        private void setZeroTimeStamp() {
    471                if( useTimeStamp ) {
    472                        for( final File file : fileList ) {
    473                                if( !file.setLastModified( 0L ) ) {
    474                                        final String errMsg = "タイムスタンプの書き換えに失敗しました。"
    475                                                                        + "file=" + file ;
    476                                        System.err.println( errMsg );
    477                                }
    478                        }
    479                }
    480        }
    481
    482        /**
    483         * 登録されている ﾛｸﾞ(Writer) に、ﾒｯｾｰｼﾞを書き込みます。
    484         * ﾒｯｾｰｼﾞの最後に改行を挿入します。
    485         *
    486         * 内部ﾛｸﾞ(Writer)が null の場合は、なにもしません。
    487         * 内部ﾛｸﾞ(Writer) の初期値は、標準出力(System.out) から作成された Writerです。
    488         *
    489         * @og.rev 6.0.0.0 (2014/04/11) ﾛｸﾞ関係を Writer で管理
    490         * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
    491         *
    492         * @param       msg     書き出すﾒｯｾｰｼﾞ
    493         */
    494        private void println( final String msg ) {
    495                if( log != null ) {
    496                        try {
    497                                log.write( msg );
    498                                log.write( CR );
    499                        }
    500                        catch( final IOException ex ) {
    501                                // ファイルが書き込めなかった場合
    502                                final String errMsg = msg + " が、書き込めませんでした。"
    503                                                                        + ex.getMessage() ;
    504                                System.err.println( errMsg );
    505                                System.err.println( ThrowUtil.ogStackTrace( ex ) );                             // 6.4.2.0 (2016/01/29)
    506                        }
    507                }
    508        }
    509}