3333import java .util .Map ;
3434import java .util .Properties ;
3535import java .util .concurrent .ConcurrentHashMap ;
36+ import java .util .function .Function ;
3637
3738/**
3839 * Singleton class to manage OpenAPI i18n error templates using FreeMarker.
4142 * This class is thread-safe and uses a cache to store configurations for different locales.
4243 */
4344public final class I18nErrorTemplate {
44-
45- private static final I18nErrorTemplate INSTANCE = new I18nErrorTemplate ();
46-
45+ private static final @ NotNull Logger LOGGER = LoggerFactory .getLogger (I18nErrorTemplate .class );
4746 private final @ NotNull Map <String , Configuration > configurationMap ;
48- private final @ NotNull Logger logger ;
47+ private final @ NotNull Function < Locale , String > resourceNameFunction ;
4948
50- private I18nErrorTemplate () {
49+ public I18nErrorTemplate (final @ NotNull Function < Locale , String > resourceNameFunction ) {
5150 configurationMap = new ConcurrentHashMap <>();
52- logger = LoggerFactory . getLogger ( getClass ()) ;
51+ this . resourceNameFunction = resourceNameFunction ;
5352 }
5453
55- public static @ NotNull I18nErrorTemplate getInstance () {
56- return INSTANCE ;
57- }
58-
59- private Configuration createConfiguration (final @ NotNull Locale locale ) {
54+ private Configuration createConfiguration (final @ NotNull Locale locale ) throws IOException {
6055 final Configuration configuration = new Configuration (Configuration .VERSION_2_3_34 );
61- configuration .setDefaultEncoding (StandardCharsets .UTF_8 .name ());
6256 final StringTemplateLoader stringTemplateLoader = new StringTemplateLoader ();
57+ configuration .setDefaultEncoding (StandardCharsets .UTF_8 .name ());
6358 configuration .setTemplateLoader (stringTemplateLoader );
6459 configuration .setLocale (locale );
60+ final Properties properties = new Properties ();
61+ final String resourceName = resourceNameFunction .apply (locale );
62+ try (final StringReader stringReader = new StringReader (IOUtils .resourceToString (resourceName ,
63+ StandardCharsets .UTF_8 ))) {
64+ properties .load (stringReader );
65+ }
66+ for (final Map .Entry <Object , Object > entry : properties .entrySet ()) {
67+ stringTemplateLoader .putTemplate (String .valueOf (entry .getKey ()), String .valueOf (entry .getValue ()));
68+ }
6569 return configuration ;
6670 }
6771
@@ -71,25 +75,14 @@ private Configuration createConfiguration(final @NotNull Locale locale) {
7175
7276 public @ NotNull String get (final @ NotNull I18nError i18NError , final @ NotNull Map <String , Object > map ) {
7377 final Locale locale = I18nLocaleContext .getLocale ();
74- Configuration configuration = configurationMap .get (locale .toString ());
75- if (configuration == null ) {
76- configuration = createConfiguration (locale );
77- configurationMap .put (locale .toString (), configuration );
78- }
7978 try {
80- final StringTemplateLoader stringTemplateLoader = (StringTemplateLoader ) configuration .getTemplateLoader ();
81- if (stringTemplateLoader .findTemplateSource (i18NError .getKey ()) == null ) {
82- final Properties properties = new Properties ();
83- try (final StringReader stringReader = new StringReader (IOUtils .resourceToString (i18NError .getResourceName (),
84- StandardCharsets .UTF_8 ))) {
85- properties .load (stringReader );
86- }
87- synchronized (configuration ) {
88- for (final Map .Entry <Object , Object > entry : properties .entrySet ()) {
89- stringTemplateLoader .putTemplate (String .valueOf (entry .getKey ()),
90- String .valueOf (entry .getValue ()));
91- }
92- }
79+ Configuration configuration = configurationMap .get (locale .toString ());
80+ if (configuration == null ) {
81+ // The whole configuration creation process is not thread-safe, but creating a new configuration
82+ // multiple times among concurrent threads brings no harm.
83+ // So, we don't use a lock here to improve the overall performance.
84+ configuration = createConfiguration (locale );
85+ configurationMap .put (locale .toString (), configuration );
9386 }
9487 final Template template = configuration .getTemplate (i18NError .getKey ());
9588 try (final StringWriter stringWriter = new StringWriter ()) {
@@ -99,12 +92,12 @@ private Configuration createConfiguration(final @NotNull Locale locale) {
9992 } catch (final TemplateException e ) {
10093 final String errorMessage =
10194 "Error: Template " + i18NError .getKey () + " for " + locale + " could not be processed." ;
102- logger .error (errorMessage , e );
95+ LOGGER .error (errorMessage , e );
10396 return errorMessage ;
10497 } catch (final IOException e ) {
10598 final String errorMessage =
10699 "Error: Template " + i18NError .getKey () + " for " + locale + " could not be loaded." ;
107- logger .error (errorMessage , e );
100+ LOGGER .error (errorMessage , e );
108101 return errorMessage ;
109102 }
110103 }
0 commit comments