In this post we looked at adding String resource bundles to our JSF applications to move our string constants into external resources that we can define for different locales. Now I want to extend that example to show how you can expand on that by using injection to access those resources.

We finished off with a MessageProvider class that was responsible for fetching the resource bundle and had methods for fetching values from that bundle. We created this class manually and accessed the string resources from it. The problem here is that we need to keep on recreating the provider in each piece of code that needs it.

Doing things the CDI way

You could construct an instance of the MessageProvider each time you need it, but instead, we should look at how we can change our existing code to make it more CDI like. We can make the method that fetches the bundle a producer method and then inject the resource bundle where needed. If we give it a long enough scope, we can re-use it in the same request.

First we’ll write a new Qualifier for the bundle so we can uniquely identify this bundle in a type safe manner. We may want to produce and inject multiple resources that will all be of the same type (ResourceBundle) so using a qualifier is a probably a good idea.

@Qualifier
@Target({ TYPE, METHOD, PARAMETER, FIELD })
@Retention(RUNTIME)
@Documented
public @interface MessageBundle {

}

We’ll add a @Produces annotation to the getBundle() method.

public class MessageProvider {

     @Produces @MessageBundle 
     public ResourceBundle getBundle() {
		if (bundle == null) {
			FacesContext context = FacesContext.getCurrentInstance();
			bundle = context.getApplication().getResourceBundle(context, "msgs");
		}
		return bundle;
	}
	...
        ...
        ...
}

Now we just need to inject this into our bean and use the injected bundle instead of constructing our own instance of the MessageProvider class.

@Named
@RequestScoped
public class SomeBean {

	@Inject @MessageBundle
	private ResourceBundle bundle;
			
	public String getMessage() {
		String msg = null;
		String key = "someMessage";
		try {
			msg = bundle.getString(key);
		} catch (MissingResourceException e) {
			msg = "??"+key+"?? is missing!";
		}
		return MessageFormat.format(msg, "SomeValue");				
	}
}

While this is more CDI-ish, it does have the problem that it consists of more code since we re-produce the exception handling code each time. So we probably need to use something that consists of more than just the bundle, say maybe the whole MessageProvider class with the methods to fetch key values. The beauty here is that we we don’t need to do anything to achieve this except give the MessageProvider class a scope :

@RequestScoped
public class MessageProvider {
    ....
    ....
}

No we just add an injection point for the MessageProvider class in our bean. We’ll create a new bean for this called AnotherBean :

@Named
@RequestScoped
public class AnotherBean {

	@Inject
	private MessageProvider provider;
	
	public String getFirstNameCaption() {
		return provider.getValue("firstName");
	}
}

In our JSF page we add a reference to the property to display the value :

First Name Caption = #{anotherBean.firstNameCaption}

This results in the following text being displayed :

First Name Caption = First Name

You might notice that our old bean is still using the resource bundle from the producer method, and everything is still working. This is because beans can be injectable and still contain producer methods. You will also find that across the request, the injected instance of the bundle is always the same as the one in provider.getBundle(). This is because every time the bundle is produced, the MessageProvider is constructed and put into request scope and the bundle value is returned. This bundle value is retained in the MessageProvider instance. No matter how many times we inject the bundle, the same one is used, even though we didn’t give it a request scope. This is actually a useful thing because the resource bundle cannot be proxied by CDI so it cannot be put into request scope itself, but it can be put into request scope if it is contained in another bean (in this case, the MessageProvider).

This shows not only how flexible CDI is in making objects available to your applications, but how easy it is to tweak how those objects are made available, all without changing the underlying code.

Next time we’ll look at using event handling to notify us of missing resources.

You can download the Maven source code for this project and run it locally by unzipping it and running mvn clean jetty:run in the command line and going to http://localhost:8080/resourcedemo/.