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