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 }