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.io.ObjectOutputStream;
024    import java.util.Locale;
025    import java.util.MissingResourceException;
026    import java.util.ResourceBundle;
027    
028    /**
029     * This class is not the recommended way to Localize messages. It is provided to provide some level
030     * of compatibility with Log4j 1.x.
031     *
032     * The recommended way to localize messages is to simply log a message id. Log events should
033     * then be recorded without formatting into some kind of data store. The application that is
034     * used to read the events and display them to the user should also localize and format the
035     * messages for the end user.
036     */
037    public class LocalizedMessage implements Message, LoggerNameAwareMessage {
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        private String messagePattern;
050        private String[] stringArgs;
051        private transient Object[] argArray;
052        private String formattedMessage;
053        private transient Throwable throwable;
054    
055        /**
056         * Constructor with message pattern and arguments.
057         *
058         * @param messagePattern the message pattern that to be checked for placeholders.
059         * @param arguments      the argument array to be converted.
060         */
061        public LocalizedMessage(final String messagePattern, final Object[] arguments) {
062            this((ResourceBundle) null, (Locale) null, messagePattern, arguments);
063        }
064    
065        public LocalizedMessage(final String bundleId, final String key, final Object[] arguments) {
066            this(bundleId, (Locale) null, key, arguments);
067        }
068    
069        public LocalizedMessage(final ResourceBundle bundle, final String key, final Object[] arguments) {
070            this(bundle, (Locale) null, key, arguments);
071        }
072    
073        public LocalizedMessage(final String bundleId, final Locale locale, final String key, final Object[] arguments) {
074            this.messagePattern = key;
075            this.argArray = arguments;
076            this.throwable = null;
077            setup(bundleId, null, locale);
078        }
079    
080        public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key,
081                                final Object[] arguments) {
082            this.messagePattern = key;
083            this.argArray = arguments;
084            this.throwable = null;
085            setup(null, bundle, locale);
086        }
087    
088        public LocalizedMessage(final Locale locale, final String key, final Object[] arguments) {
089            this((ResourceBundle) null, locale, key, arguments);
090        }
091    
092        public LocalizedMessage(final String messagePattern, final Object arg) {
093            this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg});
094        }
095    
096        public LocalizedMessage(final String bundleId, final String key, final Object arg) {
097            this(bundleId, (Locale) null, key, new Object[] {arg});
098        }
099    
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    }