View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.message;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.ObjectOutputStream;
22  import java.util.Locale;
23  import java.util.MissingResourceException;
24  import java.util.ResourceBundle;
25  
26  import org.apache.logging.log4j.status.StatusLogger;
27  
28  /**
29   * Provides some level of compatibility with Log4j 1.x and convenience but is not the recommended way to Localize
30   * messages.
31   * <p>
32   * The recommended way to localize messages is to log a message id. Log events should then be recorded without
33   * formatting into a data store. The application that is used to read the events and display them to the user can then
34   * localize and format the messages for the end user.
35   * </p>
36   */
37  public class LocalizedMessage implements Message, LoggerNameAwareMessage {
38      private static final long serialVersionUID = 3893703791567290742L;
39  
40      private String baseName;
41  
42      // ResourceBundle is not Serializable.
43      private transient ResourceBundle resourceBundle;
44  
45      private final Locale locale;
46  
47      private transient StatusLogger logger = StatusLogger.getLogger();
48  
49      private String loggerName;
50      private String key;
51      private String[] stringArgs;
52      private transient Object[] argArray;
53      private String formattedMessage;
54      private transient Throwable throwable;
55  
56      /**
57       * Constructor with message pattern and arguments.
58       *
59       * @param messagePattern the message pattern that to be checked for placeholders.
60       * @param arguments the argument array to be converted.
61       */
62      public LocalizedMessage(final String messagePattern, final Object[] arguments) {
63          this((ResourceBundle) null, (Locale) null, messagePattern, arguments);
64      }
65  
66      public LocalizedMessage(final String baseName, final String key, final Object[] arguments) {
67          this(baseName, (Locale) null, key, arguments);
68      }
69  
70      public LocalizedMessage(final ResourceBundle bundle, final String key, final Object[] arguments) {
71          this(bundle, (Locale) null, key, arguments);
72      }
73  
74      public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object[] arguments) {
75          this.key = key;
76          this.argArray = arguments;
77          this.throwable = null;
78          this.baseName = baseName;
79          this.resourceBundle = null;
80          this.locale = locale;
81      }
82  
83      public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key,
84              final Object[] arguments) {
85          this.key = key;
86          this.argArray = arguments;
87          this.throwable = null;
88          this.baseName = null;
89          this.resourceBundle = bundle;
90          this.locale = locale;
91      }
92  
93      public LocalizedMessage(final Locale locale, final String key, final Object[] arguments) {
94          this((ResourceBundle) null, locale, key, arguments);
95      }
96  
97      public LocalizedMessage(final String messagePattern, final Object arg) {
98          this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
99      }
100 
101     public LocalizedMessage(final String baseName, final String key, final Object arg) {
102         this(baseName, (Locale) null, key, new Object[] {arg});
103     }
104 
105     /**
106      * @since 2.8
107      */
108     public LocalizedMessage(final ResourceBundle bundle, final String key) {
109         this(bundle, (Locale) null, key, new Object[] {});
110     }
111 
112     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg) {
113         this(bundle, (Locale) null, key, new Object[] {arg});
114     }
115 
116     public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object arg) {
117         this(baseName, locale, key, new Object[] {arg});
118     }
119 
120     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg) {
121         this(bundle, locale, key, new Object[] {arg});
122     }
123 
124     public LocalizedMessage(final Locale locale, final String key, final Object arg) {
125         this((ResourceBundle) null, locale, key, new Object[] {arg});
126     }
127 
128     public LocalizedMessage(final String messagePattern, final Object arg1, final Object arg2) {
129         this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg1, arg2});
130     }
131 
132     public LocalizedMessage(final String baseName, final String key, final Object arg1, final Object arg2) {
133         this(baseName, (Locale) null, key, new Object[] {arg1, arg2});
134     }
135 
136     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg1, final Object arg2) {
137         this(bundle, (Locale) null, key, new Object[] {arg1, arg2});
138     }
139 
140     public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object arg1,
141             final Object arg2) {
142         this(baseName, locale, key, new Object[] {arg1, arg2});
143     }
144 
145     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg1,
146             final Object arg2) {
147         this(bundle, locale, key, new Object[] {arg1, arg2});
148     }
149 
150     public LocalizedMessage(final Locale locale, final String key, final Object arg1, final Object arg2) {
151         this((ResourceBundle) null, locale, key, new Object[] {arg1, arg2});
152     }
153 
154     /**
155      * Set the name of the Logger.
156      * 
157      * @param name The name of the Logger.
158      */
159     @Override
160     public void setLoggerName(final String name) {
161         this.loggerName = name;
162     }
163 
164     /**
165      * Returns the name of the Logger.
166      * 
167      * @return the name of the Logger.
168      */
169     @Override
170     public String getLoggerName() {
171         return this.loggerName;
172     }
173 
174     /**
175      * Returns the formatted message after looking up the format in the resource bundle.
176      * 
177      * @return The formatted message String.
178      */
179     @Override
180     public String getFormattedMessage() {
181         if (formattedMessage != null) {
182             return formattedMessage;
183         }
184         ResourceBundle bundle = this.resourceBundle;
185         if (bundle == null) {
186             if (baseName != null) {
187                 bundle = getResourceBundle(baseName, locale, false);
188             } else {
189                 bundle = getResourceBundle(loggerName, locale, true);
190             }
191         }
192         final String myKey = getFormat();
193         final String msgPattern = (bundle == null || !bundle.containsKey(myKey)) ? myKey : bundle.getString(myKey);
194         final Object[] array = argArray == null ? stringArgs : argArray;
195         final FormattedMessage msg = new FormattedMessage(msgPattern, array);
196         formattedMessage = msg.getFormattedMessage();
197         throwable = msg.getThrowable();
198         return formattedMessage;
199     }
200 
201     @Override
202     public String getFormat() {
203         return key;
204     }
205 
206     @Override
207     public Object[] getParameters() {
208         if (argArray != null) {
209             return argArray;
210         }
211         return stringArgs;
212     }
213 
214     @Override
215     public Throwable getThrowable() {
216         return throwable;
217     }
218 
219     /**
220      * Override this to use a ResourceBundle.Control in Java 6
221      * 
222      * @param rbBaseName The base name of the resource bundle, a fully qualified class name.
223      * @param resourceBundleLocale The locale to use when formatting the message.
224      * @param loop If true the key will be treated as a package or class name and a resource bundle will be located
225      *            based on all or part of the package name. If false the key is expected to be the exact bundle id.
226      * @return The ResourceBundle.
227      */
228     protected ResourceBundle getResourceBundle(final String rbBaseName, final Locale resourceBundleLocale,
229             final boolean loop) {
230         ResourceBundle rb = null;
231 
232         if (rbBaseName == null) {
233             return null;
234         }
235         try {
236             if (resourceBundleLocale != null) {
237                 rb = ResourceBundle.getBundle(rbBaseName, resourceBundleLocale);
238             } else {
239                 rb = ResourceBundle.getBundle(rbBaseName);
240             }
241         } catch (final MissingResourceException ex) {
242             if (!loop) {
243                 logger.debug("Unable to locate ResourceBundle " + rbBaseName);
244                 return null;
245             }
246         }
247 
248         String substr = rbBaseName;
249         int i;
250         while (rb == null && (i = substr.lastIndexOf('.')) > 0) {
251             substr = substr.substring(0, i);
252             try {
253                 if (resourceBundleLocale != null) {
254                     rb = ResourceBundle.getBundle(substr, resourceBundleLocale);
255                 } else {
256                     rb = ResourceBundle.getBundle(substr);
257                 }
258             } catch (final MissingResourceException ex) {
259                 logger.debug("Unable to locate ResourceBundle " + substr);
260             }
261         }
262         return rb;
263     }
264 
265     @Override
266     public String toString() {
267         return getFormattedMessage();
268     }
269 
270     private void writeObject(final ObjectOutputStream out) throws IOException {
271         out.defaultWriteObject();
272         getFormattedMessage();
273         out.writeUTF(formattedMessage);
274         out.writeUTF(key);
275         out.writeUTF(baseName);
276         out.writeInt(argArray.length);
277         stringArgs = new String[argArray.length];
278         int i = 0;
279         for (final Object obj : argArray) {
280             stringArgs[i] = obj.toString();
281             ++i;
282         }
283         out.writeObject(stringArgs);
284     }
285 
286     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
287         in.defaultReadObject();
288         formattedMessage = in.readUTF();
289         key = in.readUTF();
290         baseName = in.readUTF();
291         in.readInt();
292         stringArgs = (String[]) in.readObject();
293         logger = StatusLogger.getLogger();
294         resourceBundle = null;
295         argArray = null;
296     }
297 }