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     @Override
147     public void setLoggerName(final String name) {
148         this.loggerName = name;
149     }
150 
151     /**
152      * Returns the name of the Logger.
153      * @return the name of the Logger.
154      */
155     @Override
156     public String getLoggerName() {
157         return this.loggerName;
158     }
159 
160     private void setup(final String bundleId, final ResourceBundle bundle, final Locale locale) {
161         this.bundleId = bundleId;
162         this.bundle = bundle;
163         this.locale = locale;
164     }
165 
166     /**
167      * Returns the formatted message after looking up the format in the resource bundle.
168      * @return The formatted message String.
169      */
170     @Override
171     public String getFormattedMessage() {
172         if (formattedMessage != null) {
173             return formattedMessage;
174         }
175         ResourceBundle bundle = this.bundle;
176         if (bundle == null) {
177             if (bundleId != null) {
178                 bundle = getBundle(bundleId, locale, false);
179             } else {
180                 bundle = getBundle(loggerName, locale, true);
181             }
182         }
183         final String messagePattern = getFormat();
184         final String msgPattern = (bundle == null || !bundle.containsKey(messagePattern)) ?
185             messagePattern : bundle.getString(messagePattern);
186         final Object[] array = argArray == null ? stringArgs : argArray;
187         final FormattedMessage msg = new FormattedMessage(msgPattern, array);
188         formattedMessage = msg.getFormattedMessage();
189         throwable = msg.getThrowable();
190         return formattedMessage;
191     }
192 
193     @Override
194     public String getFormat() {
195         return messagePattern;
196     }
197 
198     @Override
199     public Object[] getParameters() {
200         if (argArray != null) {
201             return argArray;
202         }
203         return stringArgs;
204     }
205 
206     @Override
207     public Throwable getThrowable() {
208         return throwable;
209     }
210 
211     /**
212      * Override this to use a ResourceBundle.Control in Java 6
213      * @param key The key to the bundle.
214      * @param locale The locale to use when formatting the message.
215      * @param loop If true the key will be treated as a package or class name and a resource bundle will
216      * be located based on all or part of the package name. If false the key is expected to be the exact bundle id.
217      * @return The ResourceBundle.
218      */
219     protected ResourceBundle getBundle(final String key, final Locale locale, final boolean loop) {
220         ResourceBundle rb = null;
221 
222         if (key == null) {
223             return null;
224         }
225         try {
226             if (locale != null) {
227                 rb = ResourceBundle.getBundle(key, locale);
228             } else {
229                 rb = ResourceBundle.getBundle(key);
230             }
231         } catch (final MissingResourceException ex) {
232             if (!loop) {
233                 logger.debug("Unable to locate ResourceBundle " + key);
234                 return null;
235             }
236         }
237 
238         String substr = key;
239         int i;
240         while (rb == null && (i = substr.lastIndexOf('.')) > 0) {
241             substr = substr.substring(0, i);
242             try {
243                 if (locale != null) {
244                     rb = ResourceBundle.getBundle(substr, locale);
245                 } else {
246                     rb = ResourceBundle.getBundle(substr);
247                 }
248             } catch (final MissingResourceException ex) {
249                 logger.debug("Unable to locate ResourceBundle " + substr);
250             }
251         }
252         return rb;
253     }
254 
255     private void writeObject(final ObjectOutputStream out) throws IOException {
256         out.defaultWriteObject();
257         getFormattedMessage();
258         out.writeUTF(formattedMessage);
259         out.writeUTF(messagePattern);
260         out.writeUTF(bundleId);
261         out.writeInt(argArray.length);
262         stringArgs = new String[argArray.length];
263         int i = 0;
264         for (final Object obj : argArray) {
265             stringArgs[i] = obj.toString();
266             ++i;
267         }
268     }
269 
270     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
271         in.defaultReadObject();
272         formattedMessage = in.readUTF();
273         messagePattern = in.readUTF();
274         bundleId = in.readUTF();
275         final int length = in.readInt();
276         stringArgs = new String[length];
277         for (int i = 0; i < length; ++i) {
278             stringArgs[i] = in.readUTF();
279         }
280         logger = StatusLogger.getLogger();
281         bundle = null;
282         argArray = null;
283     }
284 }