View Javadoc

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/&lt;bundleName&gt;-messages.properties</code>) from
47       * <code>bundleName</code>.
48       * 
49       * @param bundleName Name of the bundle without the &quot;messages&quot; 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