This document shows you how to load Struts message resources from a database instead from the default .properties file. Here's what you will learn:

I use EJB and Oracle stored procedures in my example, but you may choose another way to retrieve your message data from your database.

Setup the database

First of all, we need a database-table, that stores our message resource records. Here's the table that i've used in my example. It has four text columns for different languages (german, french, italian, english). The column smr_key is the primary key. It contains the same keys as you would have in an ordinary java .properties file.

STRUTS_MESSAGE_RESOURCES
-----------------------------------
smr_key        varchar2(100)     PK
smr_text_de    varchar2(1000)
smr_text_fr    varchar2(1000)
smr_text_it    varchar2(1000)
smr_text_en    varchar2(1000)

Setup a custom message resources factory

Struts has a very flexible mechanism that allows you, to implement your own mechanism to load message resources from any source. In this example the source is a database. First we need to write two classes:

DBMessageResourcesFactory

There's nothing special about this one. It just gives you an instance of our custom message resources.

package ch.trexx.struts;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;

public class DBMessageResourcesFactory extends MessageResourcesFactory {

	/* (non-Javadoc)
	 * @see org.apache.struts.util.MessageResourcesFactory#createResources(java.lang.String)
	 */
	public MessageResources createResources(String config) {
		return new DBMessageResources(this, config);
	}
}

DBMessageResources

In this class we implement the connection to the database and load the message resources from there. In my example i use an EJB. You may choose another way to retrieve your data from the database (for instance JDBC).

package ch.trexx.struts;

import java.util.HashMap;
import java.util.Locale;

import org.apache.log4j.Logger;
import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;

import ch.trexx.ejb.SBDatabase;
import ch.trexx.ejb.SBDatabaseUtil;
import ch.trexx.ejb.Constants;

/**
 * Lädt die MessageResources über ein Bean aus der Datenbank anstatt
 * aus den normalen Textfiles.
 * @author wegmuelleran
 */
public class DBMessageResources extends MessageResources {

    private final static Logger logger = Logger.getLogger(DBMessageResources.class);

    /**
     * Ist ein Key in einer bestimmten Sprache nicht vorhanden wird
     * der Key der Default Locale zurückgegeben.
     */
    private static final Locale DEFAULT_LOCALE = new Locale(
            Constants.DEFAULT_LANGUANGE, Constants.DEFAULT_COUNTRY);

    private HashMap appText = null;
    
    /**
     * Lädt die Applikationstexte.
     */
    private void loadAppText() {
        try {
            SBDatabaseLocal sbDatabase = SBDatabaseUtil.getLocalHome().create();
            appText = sbDatabase.getAppText();
            logger.info("MessageResources successfully reloaded");
        } catch (Exception e) {
            logger.warn("MessageResources reload failed");
        }
    }
    
    /**
     * Struts initialisiert die Message Resources wenn die init() Methode des
     * ActionServlets aufgerufen wird. Das ActionServlet sorgt dafür, dass die
     * Init-Methode in regelmässigen Abständen aufgerufen wird.
	 * @param factory
	 * @param config
	 */
	public DBMessageResources(MessageResourcesFactory factory, String config) {
		super(factory, config);
        loadAppText();
	}
    
	/* (non-Javadoc)
	 * @see org.apache.struts.util.MessageResources#getMessage(java.util.Locale, java.lang.String)
	 */
	public String getMessage(Locale locale, String key) {
        String message;
        if (appText == null) {
        	message = "[resources not loaded]";
        } else if (appText.containsKey(key)) {
            HashMap text = (HashMap) appText.get(key);
            message = (String) text.get(locale);
            if (message == null) {
            	message = (String) text.get(DEFAULT_LOCALE);
            }
        } else {
            message =  "[key " + key + " is undefined]";
        }
        return message;
    }
}

Struts calls the factory method createResources only once when you (re-)deploy your application, so you don't have to build a caching mechanism. It took me more time to figure out how to reload the resources without redeploying the whole application. Later we see how this can be done.

The constructor of DBMessageResources calls the method loadAppText(). loadAppText() calls an EJB method that returns a nested HashMap containing the database records. The HashMap has the following structure:

HashMap(
'my.text' => HashMap('de' => 'my german text',
                     'fr' => 'my french text',
                     'it' => 'my italian text',
                     'en' => 'my english text'),
'my.message' => HashMap('de' => 'a german message',
                        'fr' => 'a french message',
                        'it' => 'a italian message',
                        'en' => 'a english message'),
...
)

You find a description of the getAppText() method of the EJB in the next Chapter "Retrieve message resources data from a database". Basically it just shows you, how to put the database records into our HashMap structure described.

The method getMessage() is the most important. Struts calls this method when dealing with message resources. For instance the bean:message tag ....

web.xml

You'll have to reference our new message factory in the web.xml. In the action-servlet configuration we add an additional init-param element. The web.xml should look like this afterwards:

<servlet>
    <servlet-name>action</servlet-name>
    <display-name>DuplexWeb</display-name>
    <servlet-class>ch.trexx.struts.ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>factory</param-name>
        <param-value>ch.trexx.struts.DBMessageResourcesFactory</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

Retrieve message resources data from a database