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.util.ResourceBundle; 019// import java.util.PropertyResourceBundle; 020import java.util.Locale; 021import java.util.Arrays; 022import java.text.MessageFormat; 023 024// import java.io.InputStream; 025// import java.io.InputStreamReader; 026// import java.io.BufferedReader; 027// import java.io.IOException; 028// import java.net.URL; 029// import java.net.URLConnection; 030 031// import static java.nio.charset.StandardCharsets.UTF_8; 032 033/** 034 * MsgUtilは、共通的に使用されるリソースからメッセージを作成する、ユーティリティークラスです。 035 * 036 *<pre> 037 * 現状は、{@value OMIT_BASE} 以下の message.properties ファイルをリソースとして使用します。 038 * このリソースファイルを、各言語別に作成することで、アプリケーションのメッセージを国際化できます。 039 * 通常のリソース変換以外に、キーワードと引数で、RuntimeException を返す簡易メソッドも提供します。 040 * 041 *</pre> 042 * @og.rev 7.0.0.0 (2017/07/07) 新規作成 043 * 044 * @version 7.0 045 * @author Kazuhiko Hasegawa 046 * @since JDK1.8, 047 */ 048public final class MsgUtil { 049 private static final XLogger LOGGER= XLogger.getLogger( MsgUtil.class.getSimpleName() ); // ログ出力 050 051// /** 初期設定されているリソースバンドルのbaseName {@value} */ 052// public static final String F_BS_NM = "org.opengion.fukurou.message" ; 053 /** 初期設定されているクラス名のキーワード {@value} */ 054 public static final String OMIT_BASE = "org.opengion.fukurou" ; 055 056 private static final int BUFFER_MIDDLE = 200 ; 057 private static final int STACKTRACE_COUNT = 5 ; 058 private static final String CR_TAB = "\n\tat " ; 059 060 private static final ResourceBundle PARENT // 7.2.5.0 (2020/06/01) 061 = ResourceBundle.getBundle( OMIT_BASE+".message" , Locale.getDefault() ); 062 063 private static ResourceBundle resource ; // 7.2.5.0 (2020/06/01) 外部設定のリソース 064 private static String omitName = "DummyName"; // 7.2.5.0 (2020/06/01) 外部設定のリソース 065 066 /** 067 * デフォルトコンストラクターをprivateにして、 068 * オブジェクトの生成をさせないようにする。 069 */ 070 private MsgUtil() {} 071 072 /** 073 * リソースの取得元のベースとなるパッケージ文字列を指定します。 074 * リソースは、keyで指定するパッケージの直下に、 075 * "message_ja_JP.properties" 形式のファイルで用意しておきます。 076 * 077 * @param key リソースのベースとなるパッケージ文字列。 078 */ 079 public static void setResourceKey( final String key ) { 080 omitName = key; 081 resource = ResourceBundle.getBundle( omitName+".message" , Locale.getDefault() ); 082 } 083 084 /** 085 * リソースから取得するメッセージを文字列で返します。 086 * 087 * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した 088 * 文字列を返します。 089 * 親リソースとして、"org.opengion.fukurou.message" で定義されたリソースバンドルを 090 * 読み込んでいます。 091 * 092 * @og.rev 6.4.3.1 (2016/02/12) 新規追加 093 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加 094 * @og.rev 7.2.5.0 (2020/06/01) ResourceBundleは、native2asciiなしで(ResourceBundle.Controlも不要)使用できる。 095 * 096 * @param id リソースのキーとなるID。 097 * @param args リソースを、MessageFormat.format で加工する場合の引数。 098 * 099 * @return MessageFormat.formatで加工された文字列 100 */ 101 public static String getMsg( final String id , final Object... args ) { 102 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 103 String rtn; 104 105// // リソースバンドルのすべてがキャッシュに格納される・・・はず。 106// final ResourceBundle resource = ResourceBundle.getBundle( F_BS_NM , Locale.getDefault() , UTF8_CONTROL ); 107 108 try { 109// return id + ":" + MessageFormat.format( resource.getString( id ) , args ); 110 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ).append( id ).append( ':' ); 111 112 if( resource != null && resource.containsKey( id ) ) { 113 buf.append( MessageFormat.format( resource.getString( id ) , args ) ); 114 } 115 else if( PARENT.containsKey( id ) ) { 116 buf.append( MessageFormat.format( PARENT.getString( id ) , args ) ); 117 } 118 else { 119 buf.append( Arrays.toString( args ) ); 120 } 121 122// return buf.toString(); 123 rtn = buf.toString(); 124 } 125 catch( final RuntimeException ex ) { 126 final String errMsg = id + "[" + Arrays.toString ( args ) + "]" ; 127 LOGGER.warning( ex , () -> "【WARNING】 " + errMsg ); 128// return errMsg ; 129 rtn = errMsg ; 130 } 131 return rtn; 132 } 133 134 /** 135 * メッセージを作成して、RuntimeExceptionの引数にセットして、throw します。 136 * 137 * @og.rev 6.4.3.1 (2016/02/12) 新規追加 138 * 139 * @param id リソースのキーとなるID。 140 * @param args リソースを、MessageFormat.format で加工する場合の引数。 141 * @return メッセージを書き込んだ、RuntimeException 142 * 143 * @see #getMsg( String,Object... ) 144 * @see #throwException( Throwable,String,Object... ) 145 */ 146 public static RuntimeException throwException( final String id , final Object... args ) { 147 return throwException( null , id , args ); 148 } 149 150 /** 151 * メッセージを作成して、RuntimeExceptionの引数にセットして、throw します。 152 * 153 * @og.rev 6.4.3.1 (2016/02/12) 新規追加 154 * @og.rev 6.8.1.5 (2017/09/08) LOGGER.debug 情報の追加 155 * 156 * @param th 発生元のThrowable( null値は許容されます ) 157 * @param id リソースのキーとなるID。 158 * @param args リソースを、MessageFormat.format で加工する場合の引数。 159 * @return メッセージを書き込んだ、RuntimeException 160 * 161 * @see #getMsg( String,Object... ) 162 * @see #throwException( String,Object... ) 163 */ 164 public static RuntimeException throwException( final Throwable th , final String id , final Object... args ) { 165 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 166 .append( getMsg( id , args ) ); 167 168 if( th != null ) { 169 buf.append( "\n\t" ).append( th.getMessage() ); 170 } 171 172 // ラムダ式で、Exception が throw された場合、上位にアップされない。(非検査例外(RuntimeException系)なら、スローできる・・・はず) 173 // 原因がわかるまで、とりあえず、printStackTrace しておきます。 174 final String errMsg = buf.toString(); 175 final RuntimeException ex = new RuntimeException( errMsg , th ); 176 LOGGER.warning( ex , () -> "【WARNING】 " + errMsg ); 177 return ex; 178 } 179 180 /** 181 * エラーメッセージを作成して、文字列を返します。 182 * 183 * @og.rev 6.4.3.1 (2016/02/12) 新規追加 184 * 185 * @param id リソースのキーとなるID。 186 * @param args リソースを、MessageFormat.format で加工する場合の引数。 187 * @return 作成されたエラーメッセージ文字列 188 * 189 * @see #getMsg( String,Object... ) 190 */ 191 public static String errPrintln( final String id , final Object... args ) { 192 return errPrintln( null , id , args ); 193 } 194 195 /** 196 * Throwable付きのエラーメッセージを作成して、LOGGER で出力します。 197 * 198 * @og.rev 6.4.3.1 (2016/02/12) 新規追加 199 * @og.rev 7.2.5.0 (2020/06/01) ネットワークパスのチェックを行います。 200 * 201 * @param th 発生元のThrowable( null値は許容されます ) 202 * @param id リソースのキーとなるID。 203 * @param args リソースを、MessageFormat.format で加工する場合の引数。 204 * @return 作成されたエラーメッセージ文字列 205 * 206 * @see #getMsg( String,Object... ) 207 */ 208 public static String errPrintln( final Throwable th , final String id , final Object... args ) { 209 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ) 210 .append( getMsg( id , args ) ); 211 212 if( th != null ) { 213 buf.append( "\n\t" ).append( th.getMessage() ); // 7.2.5.0 (2020/06/01) エラーに含める 214 215 int cnt = 0; 216 for( final StackTraceElement stEle : th.getStackTrace() ) { 217 final String clnNm = stEle.getClassName(); 218 if( clnNm.contains( "MsgUtil" ) ) { continue; } 219 220// if( clnNm.contains( "org.opengion.fukurou" ) || cnt < STACKTRACE_COUNT ) { 221 // omitName が未設定の場合でも、ダミーの値を入れています。 222 if( clnNm.contains( OMIT_BASE ) || clnNm.contains( omitName ) || cnt < STACKTRACE_COUNT ) { 223// buf.append( "\n\t" ).append( stEle.toString() ); 224 final String eleStr = stEle.toString(); // 1.4.0 (2019/10/01) 225 if( buf.indexOf( eleStr ) < 0 ) { 226 buf.append( CR_TAB ).append( eleStr ); 227 } 228 else { 229 buf.append( CR_TAB ).append( "………" ); 230 } 231 cnt++; 232 } 233 } 234 } 235 236 LOGGER.warning( () -> "【WARNING】 " + buf.toString() ); 237 238 return buf.toString(); 239 } 240 241// Java 9 でようやくResourceBundle のデフォルト文字コードが UTF-8に 242// http://yanok.net/2017/07/java-9-resourcebundle-utf-8.html 243// とりあえず、native2ascii なしで、propertiesファイルを記述できます。 244// 245// /** 246// * ResourceBundle.Controlは、バンドル・ロード処理中にResourceBundle.getBundleファクトリによって呼び出される一連のコールバック・メソッドを定義します。 247// * 248// * @og.rev 6.4.3.1 (2016/02/12) 新規追加 249// */ 250// private static final ResourceBundle.Control UTF8_CONTROL = new ResourceBundle.Control() { 251// /** 252// * 指定された形式とロケールを持つ指定されたバンドル名のリソース・バンドルを、指定されたクラス・ローダーを必要に応じて使用してインスタンス化します。 253// * 254// * 指定されたパラメータに対応する使用可能なリソース・バンドルが存在しない場合、このメソッドはnullを返します。 255// * 予想外のエラーが発生したためにリソース・バンドルのインスタンス化が行えない場合には、単純にnullを返す代わりに、 256// * ErrorまたはExceptionをスローすることでエラーを報告する必要があります。 257// * reloadフラグがtrueの場合、それは、以前にロードされたリソース・バンドルの有効期限が切れたためにこのメソッドが呼び出されたことを示します。 258// * 259// * @og.rev 6.4.3.1 (2016/02/12) 新規追加 260// * 261// * @param baseName リソース・バンドルの基底バンドル名。完全指定クラス名 262// * @param locale リソース・バンドルのインスタンス化対象となるロケール 263// * @param format ロードされるリソース・バンドルの形式 264// * @param loader バンドルをロードするために使用するClassLoader 265// * @param reload バンドルの再ロードを示すフラグ。有効期限の切れたリソース・バンドルを再ロードする場合はtrue、それ以外の場合はfalse 266// * 267// * @return ResourceBundle.Controオブジェクト 268// * 269// * @throws NullPointerException bundleName、locale、format、またはloaderがnullの場合、またはtoBundleNameからnullが返された場合 270// * @throws IllegalArgumentException formatが不明である場合、または指定されたパラメータに対して見つかったリソースに不正なデータが含まれている場合。 271// * @throws ClassCastException ロードされたクラスをResourceBundleにキャストできない場合 272// * @throws IllegalAccessException クラスまたはその引数なしのコンストラクタにアクセスできない場合。 273// * @throws InstantiationException クラスのインスタンス化が何かほかの理由で失敗する場合。 274// * @throws ExceptionInInitializerError このメソッドによる初期化に失敗した場合。 275// * @throws SecurityException セキュリティ・マネージャが存在し、新しいインスタンスの作成が拒否された場合。詳細は、Class.newInstance()を参照してください。 276// * @throws IOException 何らかの入出力操作を使ってリソースを読み取る際にエラーが発生した場合 277// */ 278// @Override 279// public ResourceBundle newBundle( final String baseName, 280// final Locale locale, 281// final String format, 282// final ClassLoader loader, 283// final boolean reload ) throws IllegalAccessException, InstantiationException, IOException { 284// // The below is a copy of the default implementation. 285// final String bundleName = toBundleName( baseName , locale ); 286// final String resourceName = toResourceName( bundleName, "properties" ); 287// InputStream stream = null; 288// if( reload ) { 289// final URL url = loader.getResource( resourceName ); 290// if( url != null ) { 291// final URLConnection urlConn = url.openConnection(); 292// if( urlConn != null ) { 293// urlConn.setUseCaches( false ); 294// stream = urlConn.getInputStream(); 295// } 296// } 297// } else { 298// stream = loader.getResourceAsStream( resourceName ); 299// } 300// 301// ResourceBundle bundle = null; 302// if( stream != null ) { 303// try { 304// // Only this line is changed to make it to read properties files as UTF-8. 305// bundle = new PropertyResourceBundle( new BufferedReader( new InputStreamReader( stream,UTF_8 ) ) ); 306// } finally { 307// stream.close(); 308// } 309// } 310// return bundle; 311// } 312// }; 313}