jsfjsf-2resourcebundlefacescontext

Difference between by Application#getResourceBundle() and ResourceBundle#getBundle() in JSF 2.0


In order to retreive Strings from the resourse bundle, i am trying to compare the result from this two methods, below an example for the code:

First Example:

baseName: The fully qualified name of the resource bundle (<base-name> in <resource-bundle>).

FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
ResourceBundle bundle = app.getResourceBundle(context, baseName);

Second Example:

varName: is the String representing the <var></var> in <resource-bundle>

FacesContext context = FacesContext.getCurrentInstance();
Locale locale = context .getViewRoot().getLocale();
ClassLoader loader = Thread.currentThread().getContextClassLoader();
ResourceBundle bundle = ResourceBundle.getBundle(varName, locale, loader);

What is the difference between these tow examples? if no difference, what would be the best practice to get ResourceBundle (to use Application#getMessageBundle() or ResourceBundle#getBundle()) ?


Solution

  • First of all, you mixed up the varName/baseName of the approaches. The actual approaches are:

    Application#getResourceBundle()

    varName: is the String representing the <resource-bundle><var> in faces-config.xml

    FacesContext context = FacesContext.getCurrentInstance();
    Application application = context.getApplication();
    ResourceBundle bundle = application.getResourceBundle(context, varName);
    

    ResourceBundle#getBundle()

    baseName: is the fully qualified name of the resource bundle, like <resource-bundle><base-name>

    FacesContext context = FacesContext.getCurrentInstance();
    Locale locale = context.getViewRoot().getLocale();
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, loader);
    

    The former obtains it via JSF Application, which would under the covers also use UIViewRoot#getLocale() (with a fallback to Locale#getDefault()), while the latter obtains it directly.

    As to the technical and the end result, there's no difference. You'll get exactly the same bundle in both cases (provided that locale is right). However, as to maintainability, it's definitely different. Resource bundles fall under "configuration" and it must be externalized (in faces-config.xml).

    Hardcoding a FQN as in baseName is a poor practice. You can't easily quickly change the FQN without recompiling and rebuilding all the code again. If it was in a 3rd party JAR file, it would be even more troublesome. You could otherwise just override it with another <resource-bundle> on same <var> from inside your webapp. Also, JSF component/utility libraries may provide an own Application wrapper which could possibly decorate getResourceBundle() call in order to do some awesomeness. That wouldn't be possible if you obtained it directly via ResourceBundle#getBundle().


    There's by the way a third approach: just inject it.

    In a JSF managed bean, provided a <var>text</var>:

    @ManagedProperty("#{text}")
    private ResourceBundle text;
    

    Or in a CDI managed bean:

    @Inject
    private PropertyResourceBundle text;
    

    With this producer:

    public class BundleProducer {
    
        @Produces
        public PropertyResourceBundle getBundle() {
            FacesContext context = FacesContext.getCurrentInstance();
            return context.getApplication().evaluateExpressionGet(context, "#{text}", PropertyResourceBundle.class);
        }
    
    }
    

    Note: EL evaluation of #{text} bundle uses under the covers Application#getResourceBundle().