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 org.apache.logging.log4j.status.StatusLogger;
20  
21  import java.io.IOException;
22  import java.io.ObjectInputStream;
23  import java.io.ObjectOutputStream;
24  import java.util.Locale;
25  import java.util.MissingResourceException;
26  import java.util.ResourceBundle;
27  
28  /**
29   * This class is not the recommended way to Localize messages. It is provided to provide some level
30   * of compatibility with Log4j 1.x.
31   *
32   * The recommended way to localize messages is to simply log a message id. Log events should
33   * then be recorded without formatting into some kind of data store. The application that is
34   * used to read the events and display them to the user should also localize and format the
35   * messages for the end user.
36   */
37  public class LocalizedMessage implements Message, LoggerNameAwareMessage {
38      private static final long serialVersionUID = 3893703791567290742L;
39  
40      private String bundleId;
41  
42      private transient ResourceBundle bundle;
43  
44      private Locale locale;
45  
46      private transient StatusLogger logger = StatusLogger.getLogger();
47  
48      private String loggerName;
49      private String messagePattern;
50      private String[] stringArgs;
51      private transient Object[] argArray;
52      private String formattedMessage;
53      private transient Throwable throwable;
54  
55      /**
56       * Constructor with message pattern and arguments.
57       *
58       * @param messagePattern the message pattern that to be checked for placeholders.
59       * @param arguments      the argument array to be converted.
60       */
61      public LocalizedMessage(final String messagePattern, final Object[] arguments) {
62          this((ResourceBundle) null, (Locale) null, messagePattern, arguments);
63      }
64  
65      public LocalizedMessage(final String bundleId, final String key, final Object[] arguments) {
66          this(bundleId, (Locale) null, key, arguments);
67      }
68  
69      public LocalizedMessage(final ResourceBundle bundle, final String key, final Object[] arguments) {
70          this(bundle, (Locale) null, key, arguments);
71      }
72  
73      public LocalizedMessage(final String bundleId, final Locale locale, final String key, final Object[] arguments) {
74          this.messagePattern = key;
75          this.argArray = arguments;
76          this.throwable = null;
77          setup(bundleId, null, locale);
78      }
79  
80      public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key,
81                              final Object[] arguments) {
82          this.messagePattern = key;
83          this.argArray = arguments;
84          this.throwable = null;
85          setup(null, bundle, locale);
86      }
87  
88      public LocalizedMessage(final Locale locale, final String key, final Object[] arguments) {
89          this((ResourceBundle) null, locale, key, arguments);
90      }
91  
92      public LocalizedMessage(final String messagePattern, final Object arg) {
93          this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
94      }
95  
96      public LocalizedMessage(final String bundleId, final String key, final Object arg) {
97          this(bundleId, (Locale) null, key, new Object[] {arg});
98      }
99  
100     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg) {
101         this(bundle, (Locale) null, key, new Object[] {arg});
102     }
103 
104     public LocalizedMessage(final String bundleId, final Locale locale, final String key, final Object arg) {
105         this(bundleId, locale, key, new Object[] {arg});
106     }
107 
108     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg) {
109         this(bundle, locale, key, new Object[] {arg});
110     }
111 
112     public LocalizedMessage(final Locale locale, final String key, final Object arg) {
113         this((ResourceBundle) null, locale, key, new Object[] {arg});
114     }
115 
116     public LocalizedMessage(final String messagePattern, final Object arg1, final Object arg2) {
117         this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg1, arg2});
118     }
119 
120     public LocalizedMessage(final String bundleId, final String key, final Object arg1, final Object arg2) {
121         this(bundleId, (Locale) null, key, new Object[] {arg1, arg2});
122     }
123 
124     public LocalizedMessage(final ResourceBundle bundle, final String key, final Object arg1, final Object arg2) {
125         this(bundle, (Locale) null, key, new Object[] {arg1, arg2});
126     }
127 
128     public LocalizedMessage(final String bundleId, final Locale locale, final String key, final Object arg1,
129                             final Object arg2) {
130         this(bundleId, locale, key, new Object[] {arg1, arg2});
131     }
132 
133     public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, final Object arg1,
134                             final Object arg2) {
135         this(bundle, locale, key, new Object[] {arg1, arg2});
136     }
137 
138     public LocalizedMessage(final Locale locale, final String key, final Object arg1, final Object arg2) {
139         this((ResourceBundle) null, locale, key, new Object[] {arg1, arg2});
140     }
141 
142     /**
143      * Set the name of the Logger.
144      * @param name The name of the Logger.
145      */
146     public void setLoggerName(final String name) {
147         this.loggerName = name;
148     }
149 
150     /**
151      * Returns the name of the Logger.
152      * @return the name of the Logger.
153      */
154     public String getLoggerName() {
155         return this.loggerName;
156     }
157 
158     private void setup(final String bundleId, final ResourceBundle bundle, final Locale locale) {
159         this.bundleId = bundleId;
160         this.bundle = bundle;
161         this.locale = locale;
162     }
163 
164     /**
165      * Returns the formatted message after looking up the format in the resource bundle.
166      * @return The formatted message String.
167      */
168     public String getFormattedMessage() {
169         if (formattedMessage != null) {
170             return formattedMessage;
171         }
172         ResourceBundle bundle = this.bundle;
173         if (bundle == null) {
174             if (bundleId != null) {
175                 bundle = getBundle(bundleId, locale, false);
176             } else {
177                 bundle = getBundle(loggerName, locale, true);
178             }
179         }
180         final String messagePattern = getFormat();
181         final String msgPattern = (bundle == null || !bundle.containsKey(messagePattern)) ?
182             messagePattern : bundle.getString(messagePattern);
183         final Object[] array = argArray == null ? stringArgs : argArray;
184         final FormattedMessage msg = new FormattedMessage(msgPattern, array);
185         formattedMessage = msg.getFormattedMessage();
186         throwable = msg.getThrowable();
187         return formattedMessage;
188     }
189 
190     public String getFormat() {
191         return messagePattern;
192     }
193 
194     public Object[] getParameters() {
195         if (argArray != null) {
196             return argArray;
197         }
198         return stringArgs;
199     }
200 
201     public Throwable getThrowable() {
202         return throwable;
203     }
204 
205     /**
206      * Override this to use a ResourceBundle.Control in Java 6
207      * @param key The key to the bundle.
208      * @param locale The locale to use when formatting the message.
209      * @param loop If true the key will be treated as a package or class name and a resource bundle will
210      * be located based on all or part of the package name. If false the key is expected to be the exact bundle id.
211      * @return The ResourceBundle.
212      */
213     protected ResourceBundle getBundle(final String key, final Locale locale, final boolean loop) {
214         ResourceBundle rb = null;
215 
216         if (key == null) {
217             return null;
218         }
219         try {
220             if (locale != null) {
221                 rb = ResourceBundle.getBundle(key, locale);
222             } else {
223                 rb = ResourceBundle.getBundle(key);
224             }
225         } catch (final MissingResourceException ex) {
226             if (!loop) {
227                 logger.debug("Unable to locate ResourceBundle " + key);
228                 return null;
229             }
230         }
231 
232         String substr = key;
233         int i;
234         while (rb == null && (i = substr.lastIndexOf('.')) > 0) {
235             substr = substr.substring(0, i);
236             try {
237                 if (locale != null) {
238                     rb = ResourceBundle.getBundle(substr, locale);
239                 } else {
240                     rb = ResourceBundle.getBundle(substr);
241                 }
242             } catch (final MissingResourceException ex) {
243                 logger.debug("Unable to locate ResourceBundle " + substr);
244             }
245         }
246         return rb;
247     }
248 
249     private void writeObject(final ObjectOutputStream out) throws IOException {
250         out.defaultWriteObject();
251         getFormattedMessage();
252         out.writeUTF(formattedMessage);
253         out.writeUTF(messagePattern);
254         out.writeUTF(bundleId);
255         out.writeInt(argArray.length);
256         stringArgs = new String[argArray.length];
257         int i = 0;
258         for (final Object obj : argArray) {
259             stringArgs[i] = obj.toString();
260             ++i;
261         }
262     }
263 
264     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
265         in.defaultReadObject();
266         formattedMessage = in.readUTF();
267         messagePattern = in.readUTF();
268         bundleId = in.readUTF();
269         final int length = in.readInt();
270         stringArgs = new String[length];
271         for (int i = 0; i < length; ++i) {
272             stringArgs[i] = in.readUTF();
273         }
274         logger = StatusLogger.getLogger();
275         bundle = null;
276         argArray = null;
277     }
278 }