Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Comment lines by Scott Johnson: Loading Java resource bundles via HTTP

Scott Johnson (scottjoh@us.ibm.com), Software Engineer, WebSphere Business Monitor, IBM
author photo
Scott Johnson has been a software developer for 26 years. Before joining IBM he worked in the areas of banking, cash management, survey research, and healthcare scheduling and staffing. He joined IBM in 2000 as the JavaServer Pages component lead for WebSphere Application Server. He currently uses Java, Javascript, Dojo and other technologies to develop widgets for WebSphere Business Monitor.

Summary:  Learn how to load a Java™ resource bundle that resides in a different Web application. Before Java Platform Standard Edition (Java SE) 6, support for accomplishing this was not part of the JDK. But with the addition of the ResourceBundle.Control class in Java SE 6, loading a "remote" resource bundle became possible by simply extending the Control class and adding some custom code. This content is part of the IBM WebSphere Developer Technical Journal.

Date:  30 Sep 2009
Level:  Intermediate PDF:  A4 and Letter (73KB | 13 pages)Get Adobe® Reader®
Also available in:   Chinese  Portuguese

Activity:  17334 views
Comments:  

Another way to use resource bundles

Imagine this scenario: You must provide a display widget that pulls message keys and message replacement parameters from a database, looks up the keys in Java resource bundles, and then formats and displays the messages. The only problem is that the resource bundles reside in Web applications other than the one in which your widget lives, and, in fact, the Web applications live on different servers.

This is not the typical use-case for Java resource bundles and it could be kind of a nightmare. How do you accomplish this? Rather than explain this out of the gate, it will be easier to show you, and to help with that, I have included a sample application for you to download and run.


Sample apps and source files

Working samples for this situation are provided for IBM® WebSphere® Application Server V7 and Apache Tomcat 6 running with Java Platform Standard Edition (Java SE) 6. Java SE 6 is required because the sample uses the Java class java.util.ResourceBundle.Control to accomplish bundle loading via HTTP, and that class is not available before Java SE 6.

Installing the sample application

The sample material included with this article for download include:

  • sj_Tomcat.zip contains
    • RemoteResourceBundle.war
    • AutoParts.war
    • AutoSales.war
  • sj_WAS.zip contains:
    • RemoteResourceBundleEAR.ear
    • AutoPartsEAR.ear
    • AutoSalesEAR.ear
  • sj_Source.zip contains the Java source and other files that comprise the sample. Sources used in RemoteResourceBundle.war:
    • src/remote/bundle/example/RemoteResourceBundleLoader.java
    • src/remote/bundle/example/ProductContextRoots.java
    • src/remote/bundle/example/MessagesBundle.java
    • src/remote/bundle/example/MessagesBean.java
    • src/remote/bundle/example/productContextRoots.properties
    • WebContent/RemoteBundleDisplay.jsp

Sources used in AutoParts.war: WebContent/remote/bundle/example/autoparts.properties

Sources used in AutoSales.war: WebContent/remote/bundle/example/autosales.properties

To install the sample materials:

  1. Deploy the Tomcat WAR files

    Unzip sj_Tomcat.zip to the root of your Tomcat installation’s webapps directory. If your Tomcat server is started, it will deploy the applications automatically. If your Tomcat server is not started, start it now and the applications will be automatically deployed.

  2. Deploy the WebSphere Application Server EAR files
    1. Unzip sj_WAS.zip to a convenient temp directory.
    2. Log in to the Integrated Solutions Console, and from the menu, select Applications => New Application => New Enterprise Application.
    3. Click the Browse button and locate the temp directory where you extracted sj_WAS.zip and choose RemoteResourceBundleEAR.ear.
    4. Click the Next button until you are presented with the Summary page (accept all defaults on all the panels) and then click the Finish button to start installing the application.
    5. When the application has been installed, click the Save link to save the application to the master configuration.
    6. Repeat these steps for AutoPartsEAR.ear and AutoSalesEAR.ear.
    7. Once the three applications are installed, navigate to Applications => Application Types => WebSphere enterprise applications.
    8. In the list of applications you should see the three applications you just installed, with a red X in the Application Status column next to each application. Select the checkbox to the left of each of the three applications and click the Start button.
    9. When the applications have started, you will see a green arrow in the Application Status column next to each application, indicating the applications have been started.
  3. Run the sample

    In your browser’s address bar, type http://<host>:<port>/ RemoteResourceBundle/RemoteBundleDisplay.jsp, where <host>:<port> are the host and port of the running server in which you installed the sample applications. For example, in WebSphere Application Server this might look like:

    http://localhost:9080/RemoteResourceBundle/RemoteBundleDisplay.jsp

    or in Tomcat this might look like:

    http://localhost:8080/RemoteResourceBundle/RemoteBundleDisplay.jsp

    The sample’s very simple browser output is shown in Figure 1.



    Figure 1. The sample’s display output
    Figure 1. The sample’s display output

The interesting thing about this output is that RemoteBundleDisplay.jsp, which lives in the RemoteResourceBundle Web application, was able to retrieve resource bundles from two totally separate Web applications -- AutoParts and AutoSales -- and format messages from those bundles for display.


How the sample application works

Using Figure 2 for reference, below is an overview of how the sample application works:


Figure 2. Overview of the sample
Figure 2. Overview of the sample
  1. RemoteBundleDisplay.jsp is requested. It instantiates the class MessagesBean and accesses the MessageBean’s property, called messagesMap, which will return formatted messages for the JSP to display.
  2. MessagesBean’s method getMessagesMap() iterates over some hardcoded message keys, and for each key it calls the private method getString().
  3. MessagesBean’s getString() method calls the static Java method ResourceBundle.getBundle() in order to retrieve the resource bundle for the current message key. One of the arguments to ResourceBundle.getBundle() is an instance of the sample class RemoteResourceBundleLoader, which extends the Java class ResourceBundle.Control and implements the method newBundle().
  4. RemoteResourceBundleLoader.newBundle() creates an HttpURLConnection for retrieving the required resource bundle from a Web application. The resource bundle is located and is loaded into a new instance of the sample class called MessagesBundle. This MessagesBundle instance is returned by RemoteResourceBundleLoader. (If the bundle was retrieved by a previous call to ResourceBundle.getBundle(), the Java ResourceBundle class will return a cached version of the bundle.)
  5. The MessagesBundle instance is used by MessagesBean’s getString() method to retrieve the string for the message key being processed. MessagesBean formats the string and stores it in a map which, when all messages have been retrieved, is returned to the JSP.
  6. The JSP displays the formatted messages.

How bundle loading via HTTP works

The key is the Java class ResourceBundle.Control. The class java.util.ResourceBundle.Control was introduced in Java SE 6. The Control class gives you more control over resource bundle loading than was available in any previous Java release.

Look at the sample class RemoteResourceBundleLoader, which is found in the downloadable sj_Source.zip file in the package remote.bundle.example (Listing 1).

The getFormats() method is used by ResourceBundle to determine what format(s) will be used for the resource bundles. In the sample application, you only want to load bundles of type properties; for example, autoparts.properties.


Listing 1. Specifying the format for the resource bundle
private static String propertiesType = "properties";

// Only "properties" files are used (e.g., autoparts.properties)
   public List<String> getFormats(String baseName) {
   return Collections.singletonList(propertiesType);

The newBundle() method in RemoteResourceBundleLoader (Listing 2) is called by the factory method ResourceBundle.getBundle() to instantiate a ResourceBundle for the base bundle name, a locale, and a format. (See the Javadoc for ResourceBundle for a complete description of the bundle loading process.)


Listing 2. The newBundle() method in RemoteResourceBundleLoader
public ResourceBundle newBundle(String baseName, 
                                    Locale locale, 
                                    String format,
                                    ClassLoader loader, 
                                    boolean reload) 
                   throws IllegalAccessException, InstantiationException, 
                            IOException {

        ResourceBundle bundle = null;

        if ((baseName == null) || 
            (locale == null) || 
            (format == null) || 
            (loader == null)) {
            return null;
        }
        
        // format must be '.properties'
        if (!format.equals(propertiesType)) {
            return null;
        }

        // Create bundle name from baseName and locale (e.g., "autoparts" 
        //  (no locale), autoparts_fr (French)
        String bundleName = toBundleName(baseName, locale);
        
        // Create resource name (e.g., "autoparts.properties", 
        //                             "autoparts_fr.properties"
        String resourceName = toResourceName(bundleName, format);
        
        // get product context roots, if not already obtained
        Properties productContextRoots=ProductContextRoots.getProductContextRoots();
        if (productContextRoots==null) {
            ProductContextRoots pe = new ProductContextRoots();
            pe.loadProductContextRoots();
            productContextRoots = ProductContextRoots.getProductContextRoots();
            if (productContextRoots==null) {
                return null;
            }
        }
        // The last segment of the baseName indicates product 
        // (i.e., 'autosales', 'autoparts').
        // Use this string to find the product context root in productContextRoots.
        int dotIndex = baseName.lastIndexOf('.');
        if (dotIndex==-1) {
            return null;
        }
        String productName= baseName.substring(dotIndex+1);
        
        // Find product context root using productName
        String productContextRoot = productContextRoots.getProperty(productName);
        if (productContextRoot==null) {
            return null;
        }

	// Create the full name for this resource 
        String fullResourceName;
        if (!productContextRoot.startsWith("http")) {
        	if (this.refererHeader != null && this.refererHeader.length()>0) {
            	    // Create full resource name using Referer, 
                   //  context root and resource name:
            	    fullResourceName=this.refererHeader+
                   productContextRoot+resourceName;
        	} else {
	            // Create full resource name using scheme, 
                    // host, port, context root and resource name:
	            fullResourceName=this.scheme+"://"+
                    this.host+":"+this.port+productContextRoot+resourceName;
        	}
        } else {
        	// Create full resource name using context root and resource name:
        	fullResourceName=productContextRoot+resourceName;
        }

        // Create HttpURLConnection for the resource file
        URL proxy=new URL(fullResourceName);
        HttpURLConnection httpProxy = (HttpURLConnection)proxy.openConnection();
        if (httpProxy == null) {
          return null;
        }
        if (reload) {
          httpProxy.setUseCaches(false);
        }

        // Instantiate the input stream
        InputStream stream = httpProxy.getInputStream();
        if (stream == null) {
          return null;
        }
        BufferedInputStream bis = null;

        // Instantiate the bundle with the stream.
        try {
            bis = new BufferedInputStream(stream);
            bundle = new MessagesBundle(bis);
            bis.close();
        }finally{
            if (bis != null) try { bis.close (); } catch (Throwable ignore) {}
        }
        return bundle;
}


Creating a URL from a message key and base bundle name

How do you know which Web application contains the resource bundle for a particular message key? The sample code in Listing 2 implements a simple scheme for building the URLs it needs for retrieving resource bundles from Web applications. The scheme is:

  • Message keys contain product identifiers

    The message keys contain a product identifier in the first segment of the key name. For example: autoparts.parts.received. In this message key, autoparts is the Web application that contains the resource bundle.

  • Base bundle names include a fixed string plus a product identifier

    All the sample resource bundles use the same base string for their names: remote.bundle.example

    When the MessagesBean calls ResourceBundle.getBundle(), it passes in this fixed string appended with the product identifier from the message key, for example:

    remote.bundle.example.autoparts

    This string is known as the baseName argument to ResourceBundle.getBundle().

  • Product context roots

    A properties file called productContextRoots.properties contains the context roots of the Web applications that contain the resource bundles that will be loaded. The contents of the properties file are:

    autoparts = /AutoParts/
    autosales = /AutoSales/

    The method RemoteResourceBundleLoader.newBundle() reads this properties file one time, the first time the method is invoked.

  • Mapping a bundle baseName to a product context root

    When the method RemoteResourceBundleLoader.newBundle() is called to load a new resource bundle, it takes the last segment of the baseName argument and uses it as a key to finding the context root of the Web application where the bundle resides.

    For example, the last segment of the bundle baseName:

    remote.bundle.example.autoparts

    will be used to find the context root for the autoparts Web application in productContextRoots.properties. In this example, the context root will be:

    /AutoParts/


Filling in the gaps

As you recall from the overview of the sample application, RemoteBundleDisplay.jsp instantiates the class MessagesBean. Listing 3 shows the JSP code.


Listing 3. RemoteBundleDisplay.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1" import="remote.bundle.example.MessagesBean"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    
<jsp:useBean id="messagesBean" scope="page" 
	class="remote.bundle.example.MessagesBean"></jsp:useBean> 
<jsp:setProperty name="messagesBean" property="locale" 
	value="${pageContext.request.locale}"></jsp:setProperty>    
<jsp:setProperty name="messagesBean" property="scheme" 
	value="${pageContext.request.scheme}"></jsp:setProperty>    
<jsp:setProperty name="messagesBean" property="port" 
	value="${pageContext.request.serverPort}"></jsp:setProperty>    
<jsp:setProperty name="messagesBean" property="host" 
	value="${pageContext.request.serverName}"></jsp:setProperty>    
<jsp:setProperty name="messagesBean" property="refererHeader" 
	value='${header["Referer"]}'></jsp:setProperty>    
<html>  
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 
<title>Displays messages from resource bundles located in other Web 
Applications</title>
</head>
<body>
<p>The Messages:</p>

<ul>
<c:forEach var="msg" items="${messagesBean.messagesMap}">
  <li>${msg.value}</li>
</c:forEach>
</ul>
</body>
</html>

The JSP sets several properties on the MessagesBean instance, and then loops over the messagesMap created by MessagesBean and displays the formatted messages (Listing 4).


Listing 4. MessagesBean.java
public Map<String,String> getMessagesMap(){
        messages = new HashMap<String, Object[]>();
        // The message keys and the arguments are stored in
        // a HashMap for the purposes of this sample.

        // In the real world, both the message keys and their arguments could be
        // persisted to a database by event emitters living in many different
        // Web Applications which could be deployed on different hosts.
        // A single, centralized display widget, living on any server anywhere,
        // could retrieve the message keys and their arguments from the database, 
        // get the messages from the appropriate Web Application resource bundles, 
        // and then format and display the messages.
        messages.put("autoparts.parts.received", 
            new Object[]{"Detroit"});
        messages.put("autoparts.parts.shipped", 
            new Object[]{"Cleveland"});
        messages.put("autosales.invoice.received", 
            new Object[]{"TopNotch Motors", 10});
        messages.put("autosales.shipment.received", 
            new Object[]{"TopNotch Motors", "Lamborghini"});

        Set<String> messagesKeys = messages.keySet();
        Iterator<String> messagesIter = messagesKeys.iterator();
        messagesMap = new HashMap<String,String>();
        while(messagesIter.hasNext()) {
            String key = messagesIter.next();
            Object[] values = messages.get(key);
            int dotIndex = key.indexOf('.');
            if (dotIndex>1) {
                String productName= key.substring(0, dotIndex);
                String msg = getString(key,productName, this.locale );
                String formattedParams = MessageFormat.format(msg, values);

                messagesMap.put(key, formattedParams);
            }
        }
        return messagesMap;
    }

    /**
     * Returns the string in a resource bundle for key, product and locale
     *
     * @param messageKey a key
     * @param product the identifier for a specific resource bundle
     * @param locale the locale of the client
     * @return String a value which is assigned to the key
     */
    public String getString(String messageKey, String product, String locale) {
        try {
            // Load bundle using RemoteResourceBundleLoader.
            ResourceBundle bundle = ResourceBundle.getBundle(
                    BASE_BUNDLE_PACKAGE+"."+product,
                    new Locale(locale),
                    new RemoteResourceBundleLoader(this.scheme, this.host, 
                        this.port, this.refererHeader));
            String messageString=bundle.getString(messageKey);
            return messageString;
        } catch (MissingResourceException e) {
            return null;
        }
}

When the JSP accesses the bean’s messagesMap property, the bean calls getString() for each of the hardcoded message keys.

In JDK 6, the ResourceBundle.getBundle() method accepts a ResourceBundle.Control class as an argument. In the getString() method in Listing 4, you pass a new instance of the class RemoteResourceBundleLoader as the third argument to ResourceBundle.getBundle(). RemoteResourceBundleLoader extends ResourceBundle.Control. The call to ResourceBundle.getBundle() returns a resource bundle. That bundle is used to get the message for the message key by calling bundle.getString(messageKey).

After you’ve gotten the message, you use MessageFormat.format() to format the message, and then you put the formatted message in the messagesMap, which is used by the JSP.


Summary

This article showed you how the loading of Java resource bundles can be done via HTTP. In a real-world scenario, both the message keys and their arguments could be persisted to a database by event emitters living in many different Web applications, which could be deployed on different hosts. A single, centralized display widget, living on any server anywhere, could retrieve the message keys and their arguments from the database, get the messages from the appropriate Web application resource bundles, and then format and display the messages.


Acknowledgements

The author thanks Varad Ramamoorthy for his technical review of this article.



Downloads

DescriptionNameSizeDownload method
Code samplesj_Source.zip17 KBHTTP
Code samplesj_Tomcat.zip289 KBHTTP
Code samplesj_WAS.zip12 KBHTTP

Information about download methods


Resources

About the author

author photo

Scott Johnson has been a software developer for 26 years. Before joining IBM he worked in the areas of banking, cash management, survey research, and healthcare scheduling and staffing. He joined IBM in 2000 as the JavaServer Pages component lead for WebSphere Application Server. He currently uses Java, Javascript, Dojo and other technologies to develop widgets for WebSphere Business Monitor.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, Java technology
ArticleID=431146
ArticleTitle=Comment lines by Scott Johnson: Loading Java resource bundles via HTTP
publish-date=09302009
author1-email=scottjoh@us.ibm.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers