001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements. See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache license, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License. You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the license for the specific language governing permissions and
015     * limitations under the license.
016     */
017    package org.apache.logging.log4j.message;
018    
019    import org.apache.logging.log4j.status.StatusLogger;
020    
021    import java.io.IOException;
022    import java.io.ObjectInputStream;
023    import java.util.Locale;
024    import java.util.MissingResourceException;
025    import java.util.ResourceBundle;
026    
027    /**
028     * This class is not the recommended way to Localize messages. It is provided to provide some level
029     * of compatibility with Log4j 1.x.
030     *
031     * The recommended way to localize messages is to simply log a message id. Log events should
032     * then be recorded without formatting into some kind of data store. The application that is
033     * used to read the events and display them to the user should also localize and format the
034     * messages for the end user.
035     */
036    public class LocalizedMessage extends ParameterizedMessage implements LoggerNameAwareMessage
037    {
038        private static final long serialVersionUID = 3893703791567290742L;
039    
040        private String bundleId;
041    
042        private transient ResourceBundle bundle;
043    
044        private Locale locale;
045    
046        private transient StatusLogger logger = StatusLogger.getLogger();
047    
048        private String loggerName;
049    
050        public LocalizedMessage(String messagePattern, String[] stringArgs, Throwable throwable) {
051            super(messagePattern, stringArgs, throwable);
052            setup(null, null, null);
053        }
054    
055    
056        public LocalizedMessage(String bundleId, String key, String[] stringArgs,
057                                Throwable throwable) {
058            super(key, stringArgs, throwable);
059            setup(bundleId, null, null);
060        }
061    
062        public LocalizedMessage(ResourceBundle bundle, String key, String[] stringArgs,
063                                Throwable throwable) {
064            super(key, stringArgs, throwable);
065            setup(null, bundle, null);
066        }
067    
068        public LocalizedMessage(String bundleId, Locale locale, String key, String[] stringArgs,
069                                Throwable throwable) {
070            super(key, stringArgs, throwable);
071            setup(bundleId, null, locale);
072        }
073    
074        public LocalizedMessage(ResourceBundle bundle, Locale locale, String key, String[] stringArgs,
075                                Throwable throwable) {
076            super(key, stringArgs, throwable);
077            setup(null, bundle, locale);
078        }
079    
080        public LocalizedMessage(Locale locale, String key, String[] stringArgs, Throwable throwable) {
081            super(key, stringArgs, throwable);
082            setup(null, null, locale);
083        }
084    
085    
086        /**
087         * <p>This method returns a LocalizedMessage which contains the arguments converted to String
088         * as well as an optional Throwable.</p>
089         * <p/>
090         * <p>If the last argument is a Throwable and is NOT used up by a placeholder in the message
091         * pattern it is returned in LocalizedMessage.getThrowable() and won't be contained in the
092         * created String[].<br/>
093         * If it is used up ParameterizedMessage.getThrowable() will return null even if the last
094         * argument was a Throwable!</p>
095         *
096         * @param messagePattern the message pattern that to be checked for placeholders.
097         * @param arguments      the argument array to be converted.
098         */
099        public LocalizedMessage(String messagePattern, Object[] arguments) {
100            super(messagePattern, arguments);
101            setup(null, null, null);
102        }
103    
104        public LocalizedMessage(String bundleId, String key, Object[] arguments) {
105            super(key, arguments);
106            setup(bundleId, null, null);
107        }
108    
109        public LocalizedMessage(ResourceBundle bundle, String key, Object[] arguments) {
110            super(key, arguments);
111            setup(null, bundle, null);
112        }
113    
114        public LocalizedMessage(String bundleId, Locale locale, String key, Object[] arguments) {
115            super(key, arguments);
116            setup(bundleId, null, locale);
117        }
118    
119        public LocalizedMessage(ResourceBundle bundle, Locale locale, String key, Object[] arguments) {
120            super(key, arguments);
121            setup(null, bundle, locale);
122        }
123    
124        public LocalizedMessage(Locale locale, String key, Object[] arguments) {
125            super(key, arguments);
126            setup(null, null, locale);
127        }
128    
129        public LocalizedMessage(String messagePattern, Object arg) {
130            super(messagePattern, arg);
131            setup(null, null, null);
132        }
133    
134        public LocalizedMessage(String bundleId, String key, Object arg) {
135            super(key, arg);
136            setup(bundleId, null, null);
137        }
138    
139        public LocalizedMessage(ResourceBundle bundle, String key, Object arg) {
140            super(key, arg);
141            setup(null, bundle, null);
142        }
143    
144        public LocalizedMessage(String bundleId, Locale locale, String key, Object arg) {
145            super(key, arg);
146            setup(bundleId, null, locale);
147        }
148    
149        public LocalizedMessage(ResourceBundle bundle, Locale locale, String key, Object arg) {
150            super(key, arg);
151            setup(null, bundle, locale);
152        }
153    
154        public LocalizedMessage(Locale locale, String key, Object arg) {
155            super(key, arg);
156            setup(null, null, locale);
157        }
158    
159        public LocalizedMessage(String messagePattern, Object arg1, Object arg2) {
160            super(messagePattern, arg1, arg2);
161            setup(null, null, null);
162        }
163    
164        public LocalizedMessage(String bundleId, String key, Object arg1, Object arg2) {
165            super(key, arg1, arg2);
166            setup(bundleId, null, null);
167        }
168    
169        public LocalizedMessage(ResourceBundle bundle, String key, Object arg1, Object arg2) {
170            super(key, arg1, arg2);
171            setup(null, bundle, null);
172        }
173    
174        public LocalizedMessage(String bundleId, Locale locale, String key, Object arg1, Object arg2) {
175            super(key, arg1, arg2);
176            setup(bundleId, null, locale);
177        }
178    
179        public LocalizedMessage(ResourceBundle bundle, Locale locale, String key, Object arg1,
180                                Object arg2) {
181            super(key, arg1, arg2);
182            setup(null, bundle, locale);
183        }
184    
185        public LocalizedMessage(Locale locale, String key, Object arg1, Object arg2) {
186            super(key, arg1, arg2);
187            setup(null, null, locale);
188        }
189    
190        /**
191         * Set the name of the Logger.
192         * @param name The name of the Logger.
193         */
194        public void setLoggerName(String name) {
195            this.loggerName = name;
196        }
197    
198        /**
199         * Returns the name of the Logger.
200         * @return the name of the Logger.
201         */
202        public String getLoggerName() {
203            return this.loggerName;
204        }
205    
206        private void setup(String bundleId, ResourceBundle bundle, Locale locale) {
207            this.bundleId = bundleId;
208            this.bundle = bundle;
209            this.locale = locale;
210        }
211    
212        /**
213         * Returns the formatted message after looking up the format in the resource bundle.
214         * @param messagePattern The key for the resource bundle or the pattern if the bundle doesn't contain the key.
215         * @param args The parameters.
216         * @return The formatted message String.
217         */
218        @Override
219        public String formatMessage(String messagePattern, String[] args) {
220            ResourceBundle bundle = this.bundle;
221            if (bundle == null) {
222                if (bundleId != null) {
223                    bundle = getBundle(bundleId, locale, false);
224                } else {
225                    bundle = getBundle(loggerName, locale, true);
226                }
227            }
228            String msgPattern = (bundle == null || !bundle.containsKey(messagePattern)) ?
229                messagePattern : bundle.getString(messagePattern);
230            return format(msgPattern, args);
231        }
232    
233        /**
234         * Override this to use a ResourceBundle.Control in Java 6
235         * @param key The key to the bundle.
236         * @param locale The locale to use when formatting the message.
237         * @param loop If true the key will be treated as a package or class name and a resource bundle will
238         * be located based on all or part of the package name. If false the key is expected to be the exact bundle id.
239         * @return The ResourceBundle.
240         */
241        protected ResourceBundle getBundle(String key, Locale locale, boolean loop) {
242            ResourceBundle rb = null;
243    
244            if (key == null) {
245                return null;
246            }
247            try {
248                if (locale != null) {
249                    rb = ResourceBundle.getBundle(key, locale);
250                } else {
251                    rb = ResourceBundle.getBundle(key);
252                }
253            } catch (MissingResourceException ex) {
254                if (!loop) {
255                    logger.debug("Unable to locate ResourceBundle " + key);
256                    return null;
257                }
258            }
259    
260            String substr = key;
261            int i;
262            while (rb == null && (i = substr.lastIndexOf('.')) > 0) {
263                substr = substr.substring(0, i);
264                try {
265                    if (locale != null) {
266                        rb = ResourceBundle.getBundle(substr, locale);
267                    } else {
268                        rb = ResourceBundle.getBundle(substr);
269                    }
270                } catch (MissingResourceException ex) {
271                    logger.debug("Unable to locate ResourceBundle " + substr);
272                }
273            }
274            return rb;
275        }
276    
277        private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
278            stream.defaultReadObject();
279            bundle = null;
280            logger = StatusLogger.getLogger();
281        }
282    }