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