1 /* 2 * $Id: MessageFactory.java 22721 2011-08-23 15:41:38Z dirk.olmes $ 3 * -------------------------------------------------------------------------------------- 4 * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com 5 * 6 * The software in this package is published under the terms of the CPAL v1.0 7 * license, a copy of which has been included with this distribution in the 8 * LICENSE.txt file. 9 */ 10 11 package org.mule.config.i18n; 12 13 import java.text.MessageFormat; 14 import java.util.Locale; 15 import java.util.MissingResourceException; 16 import java.util.ResourceBundle; 17 18 import org.apache.commons.logging.Log; 19 import org.apache.commons.logging.LogFactory; 20 21 22 public abstract class MessageFactory 23 { 24 /** 25 * Default is {@link ReloadControl.Always}. 26 */ 27 public static final ResourceBundle.Control DEFAULT_RELOAD_CONTROL = new ReloadControl.Always(); 28 29 /** 30 * This error code is used for {@link Message} instances that are not read from a 31 * resource bundles but are created only with a string. 32 */ 33 private static final int STATIC_ERROR_CODE = -1; 34 private static final transient Object[] EMPTY_ARGS = new Object[]{}; 35 36 protected transient Log logger = LogFactory.getLog(getClass()); 37 38 /** 39 * Do not use the default reload control to avoid loading the resource bundle upon each request. 40 * Subclasses can override to provide a different default. 41 */ 42 protected ResourceBundle.Control reloadControl = null; 43 44 /** 45 * Computes the bundle's full path 46 * (<code>META-INF/services/org/mule/i18n/<bundleName>-messages.properties</code>) from 47 * <code>bundleName</code>. 48 * 49 * @param bundleName Name of the bundle without the "messages" suffix and without 50 * file extension. 51 */ 52 protected static String getBundlePath(String bundleName) 53 { 54 return "META-INF.services.org.mule.i18n." + bundleName + "-messages"; 55 } 56 57 /** 58 * Factory method to create a new {@link Message} instance that is filled with the formatted 59 * message with id <code>code</code> from the resource bundle <code>bundlePath</code>. 60 * 61 * @param bundlePath complete path to the resource bundle for lookup 62 * @param code numeric code of the message 63 * @param arg 64 * @see #getBundlePath(String) 65 */ 66 protected Message createMessage(String bundlePath, int code, Object arg) 67 { 68 return createMessage(bundlePath, code, new Object[] {arg}); 69 } 70 71 /** 72 * Factory method to create a new {@link Message} instance that is filled with the formatted 73 * message with id <code>code</code> from the resource bundle <code>bundlePath</code>. 74 * 75 * @param bundlePath complete path to the resource bundle for lookup 76 * @param code numeric code of the message 77 * @param arg1 78 * @param arg2 79 * @see #getBundlePath(String) 80 */ 81 protected Message createMessage(String bundlePath, int code, Object arg1, Object arg2) 82 { 83 return createMessage(bundlePath, code, new Object[] {arg1, arg2}); 84 } 85 86 /** 87 * Factory method to create a new {@link Message} instance that is filled with the formatted 88 * message with id <code>code</code> from the resource bundle <code>bundlePath</code>. 89 * 90 * @param bundlePath complete path to the resource bundle for lookup 91 * @param code numeric code of the message 92 * @param arg1 93 * @param arg2 94 * @param arg3 95 * @see #getBundlePath(String) 96 */ 97 protected Message createMessage(String bundlePath, int code, Object arg1, Object arg2, 98 Object arg3) 99 { 100 return createMessage(bundlePath, code, new Object[] {arg1, arg2, arg3}); 101 } 102 103 /** 104 * Factory method to create a new {@link Message} instance that is filled with the formatted 105 * message with id <code>code</code> from the resource bundle <code>bundlePath</code>. 106 * 107 * <b>Attention:</b> do not confuse this method with 108 * <code>createMessage(String, int, Object)</code>. 109 * 110 * @param bundlePath complete path to the resource bundle for lookup 111 * @param code numeric code of the message 112 * @param arguments 113 * @see #getBundlePath(String) 114 */ 115 protected Message createMessage(String bundlePath, int code, Object... arguments) 116 { 117 String messageString = getString(bundlePath, code, arguments); 118 return new Message(messageString, code, arguments); 119 } 120 121 /** 122 * Factory method to create a new {@link Message} instance that is filled with the formatted 123 * message with id <code>code</code> from the resource bundle <code>bundlePath</code>. 124 * 125 * @param bundlePath complete path to the resource bundle for lookup 126 * @param code numeric code of the message 127 */ 128 protected Message createMessage(String bundlePath, int code) 129 { 130 String messageString = getString(bundlePath, code, null); 131 return new Message(messageString, code, EMPTY_ARGS); 132 } 133 134 /** 135 * Factory method to create a {@link Message} instance that is not read from a resource bundle. 136 * 137 * @param message Message's message text 138 * @return a Messsage instance that has an error code of -1 and no arguments. 139 */ 140 public static Message createStaticMessage(String message) 141 { 142 return new Message(message, STATIC_ERROR_CODE, EMPTY_ARGS); 143 } 144 145 /** 146 * Factory method to read the message with code <code>code</code> from the resource bundle. 147 * 148 * @param bundlePath complete path to the resource bundle for lookup 149 * @param code numeric code of the message 150 * @return formatted error message as {@link String} 151 */ 152 protected String getString(String bundlePath, int code) 153 { 154 return getString(bundlePath, code, null); 155 } 156 157 /** 158 * Factory method to read the message with code <code>code</code> from the resource bundle. 159 * 160 * @param bundlePath complete path to the resource bundle for lookup 161 * @param code numeric code of the message 162 * @param arg 163 * @return formatted error message as {@link String} 164 */ 165 protected String getString(String bundlePath, int code, Object arg) 166 { 167 Object[] arguments = new Object[] {arg}; 168 return getString(bundlePath, code, arguments); 169 } 170 171 /** 172 * Factory method to read the message with code <code>code</code> from the resource bundle. 173 * 174 * @param bundlePath complete path to the resource bundle for lookup 175 * @param code numeric code of the message 176 * @param arg1 177 * @param arg2 178 * @return formatted error message as {@link String} 179 */ 180 protected String getString(String bundlePath, int code, Object arg1, Object arg2) 181 { 182 Object[] arguments = new Object[] {arg1, arg2}; 183 return getString(bundlePath, code, arguments); 184 } 185 186 protected String getString(String bundlePath, int code, Object[] args) 187 { 188 // We will throw a MissingResourceException if the bundle name is invalid 189 // This happens if the code references a bundle name that just doesn't exist 190 ResourceBundle bundle = getBundle(bundlePath); 191 192 try 193 { 194 String m = bundle.getString(String.valueOf(code)); 195 if (m == null) 196 { 197 logger.error("Failed to find message for id " + code + " in resource bundle " + bundlePath); 198 return ""; 199 } 200 201 return MessageFormat.format(m, args); 202 } 203 catch (MissingResourceException e) 204 { 205 logger.error("Failed to find message for id " + code + " in resource bundle " + bundlePath); 206 return ""; 207 } 208 } 209 210 /** 211 * @throws MissingResourceException if resource is missing 212 */ 213 private ResourceBundle getBundle(String bundlePath) 214 { 215 Locale locale = Locale.getDefault(); 216 if (logger.isTraceEnabled()) 217 { 218 logger.trace("Loading resource bundle: " + bundlePath + " for locale " + locale); 219 } 220 final ResourceBundle.Control control = getReloadControl(); 221 ResourceBundle bundle = control != null 222 ? ResourceBundle.getBundle(bundlePath, locale, getClassLoader(), control) 223 : ResourceBundle.getBundle(bundlePath, locale, getClassLoader()); 224 225 return bundle; 226 } 227 228 /** 229 * Override this method to return the classloader for the bundle/module which 230 * contains the needed resource files. 231 */ 232 protected ClassLoader getClassLoader() 233 { 234 final ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 235 // if there's a deployment classloader present, use it for finding resources 236 return ccl == null ? getClass().getClassLoader() : ccl; 237 } 238 239 /** 240 * Subclasses should override to customize the bundle reload control. Implementations must 241 * save the instance in a field for stateful reload control. Return null to fallback to 242 * default JVM behavior (permanent cache). 243 * 244 * @see #DEFAULT_RELOAD_CONTROL 245 */ 246 protected ResourceBundle.Control getReloadControl() 247 { 248 return reloadControl; 249 } 250 } 251 252