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 */ 017package org.apache.logging.log4j.message; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.ObjectOutputStream; 022import java.util.Locale; 023import java.util.MissingResourceException; 024import java.util.ResourceBundle; 025 026import org.apache.logging.log4j.status.StatusLogger; 027 028/** 029 * Provides some level of compatibility with Log4j 1.x and convenience but is not the recommended way to Localize 030 * messages. 031 * <p> 032 * The recommended way to localize messages is to log a message id. Log events should then be recorded without 033 * formatting into a data store. The application that is used to read the events and display them to the user can then 034 * localize and format the messages for the end user. 035 * </p> 036 */ 037public class LocalizedMessage implements Message, LoggerNameAwareMessage { 038 private static final long serialVersionUID = 3893703791567290742L; 039 040 private String baseName; 041 042 // ResourceBundle is not Serializable. 043 private transient ResourceBundle resourceBundle; 044 045 private final Locale locale; 046 047 private transient StatusLogger logger = StatusLogger.getLogger(); 048 049 private String loggerName; 050 private String key; 051 private String[] stringArgs; 052 private transient Object[] argArray; 053 private String formattedMessage; 054 private transient Throwable throwable; 055 056 /** 057 * Constructor with message pattern and arguments. 058 * 059 * @param messagePattern the message pattern that to be checked for placeholders. 060 * @param arguments the argument array to be converted. 061 */ 062 public LocalizedMessage(final String messagePattern, final Object[] arguments) { 063 this((ResourceBundle) null, (Locale) null, messagePattern, arguments); 064 } 065 066 public LocalizedMessage(final String baseName, final String key, final Object[] arguments) { 067 this(baseName, (Locale) null, key, arguments); 068 } 069 070 public LocalizedMessage(final ResourceBundle bundle, final String key, final Object[] arguments) { 071 this(bundle, (Locale) null, key, arguments); 072 } 073 074 public LocalizedMessage(final String baseName, final Locale locale, final String key, final Object[] arguments) { 075 this.key = key; 076 this.argArray = arguments; 077 this.throwable = null; 078 this.baseName = baseName; 079 this.resourceBundle = null; 080 this.locale = locale; 081 } 082 083 public LocalizedMessage(final ResourceBundle bundle, final Locale locale, final String key, 084 final Object[] arguments) { 085 this.key = key; 086 this.argArray = arguments; 087 this.throwable = null; 088 this.baseName = null; 089 this.resourceBundle = bundle; 090 this.locale = locale; 091 } 092 093 public LocalizedMessage(final Locale locale, final String key, final Object[] arguments) { 094 this((ResourceBundle) null, locale, key, arguments); 095 } 096 097 public LocalizedMessage(final String messagePattern, final Object arg) { 098 this((ResourceBundle) null, (Locale) null, messagePattern, new Object[] {arg}); 099 } 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}