Message Bundle from DB - Part 1 (J2EE)

This is the first part of a post about message bundles. Having to work on applications requiring support for different languages I had the need to have a message bundle retrieving the translation from a DB. With a  DB was easier to update labels not correctly translated without the need of restarting the application.

We are going to build a service providing the functionality to store and retrieve localized messages from a database. In the second part we will build the same with the Spring Framework.

Source code: https://github.com/oivarc/j2ee-samples.git

The source code you will find on the repository contains a bit more of what is showed here. There is also a simple page used for test that gives the possibility to add/edit the label in different languages.
The code has been tested using NetBeans, Glassfish 4.1, JavaDB. It uses the EclipseLink for the JPA connection and PrimeFaces for the UI.

Let's start introducing the database structure we will utilize to store the localized messages. The table structure is quite simple, but will allow us to store messages in all the languages we need without changing the structure to add new languages.

MYSQL table structure

CREATE TABLE message_bundle (
  id BIGINT(20) NOT NULL AUTO_INCREMENT,
  bundle_key VARCHAR(255) NOT NULL,
  language CHAR(2) NOT NULL,
  label VARCHAR(255) NOT NULL,
  PRIMARY KEY (id),
  KEY IDX_KEY_LANGUAGE (bundle_key,language)
)

Next we need to define an entity mapping the table:

MessageBundle.java
@Entity
@Table(name = "message_bundle")
public class MessageBundle implements Serializable {

    private static final long serialVersionUID = -1L;
    
    @EmbeddedId
    private MessageBundleKey id;

    @Column(name = "label")
    private String label;

    public MessageBundleKey getId() {
        return id;
    }

    public void setId(final MessageBundleKey someId) {
        id = someId;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(final String someLabel) {
        label = someLabel;
    }

}

MessageBundleKey.java

@Embeddable
public class MessageBundleKey implements Serializable {

    private static final long serialVersionUID = -1L;

    @Column(name = "bundle_key")
    private String key;

    @Column(name = "language")
    private String language;

    /**
     * Default constructor.
     */
    public MessageBundleKey() {
    }

    /**
     * Constructor given the bundle key and language.
     * @param someKey The bundle key.
     * @param someLanguage The language.
     */
    public MessageBundleKey(final String someKey, final String someLanguage) {
        key = someKey;
        language = someLanguage;
    }

    public String getKey() {
        return key;
    }

    public void setKey(final String someKey) {
        key = someKey;
    }

    public String getLanguage() {
        return language;
    }

    public void setLanguage(final String someLanguage) {
        language = someLanguage;
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        MessageBundleKey that = (MessageBundleKey) o;

        if (!key.equals(that.key)) {
            return false;
        }
        if (!language.equals(that.language)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = key.hashCode();
        result = 31 * result + language.hashCode();
        return result;
    }
}

Now we can define the bean that will act as our ResourceBundle. The name of the bean is the one we will use in the JSF pages to access the resource bundle. In the bean is injected a MessageBundleService that contains the methods to retrieve the messages from the database. For brevity I will not report the full code of the service here as you can find it following the link to the code.

DBMessageBundle.java

@Named(value = "msg")
public class DBMessageBundle extends ResourceBundle {

    private static final Logger LOGGER = LoggerFactory.getLogger(DBMessageBundle.class);

    @Inject
    private MessageBundleService messageBundleService; 

    public DBMessageBundle() {
        super();
    }

    @Override
    protected Object handleGetObject(final String key) {
        FacesContext context = FacesContext.getCurrentInstance();
        Locale locale = context.getViewRoot().getLocale();
        LOGGER.debug("Getting message {} for language {} from DB", key, locale.getLanguage());
        return messageBundleService.findByKeyAndLanguage(key, locale.getLanguage()).getLabel();
    }

    @Override
    public Enumeration<String> getKeys() {
        return Collections.enumeration(messageBundleService.findAllKey());
    }

}


MessageBundleService.java

public interface MessageBundleService {

    String BEAN_NAME = "messageBundleService";

    Page<MessageBundle> findPaginated(PageRequest somePageRequest);

    List<String> findAllKey();

    List<MessageBundle> findByKey(String someKey);

    MessageBundle findByKeyAndLanguage(String someKey, String someLanguage);

    MessageBundle saveOrUpdate(MessageBundle someMessageBundle);

    void delete(MessageBundle someMessageBundle);
}

The service allows also the loading of the messages in paginated mode using the Page and PageRequest classes. These classes should be familiar to who is used to the Spring repository.

Page.java


public class Page<T> implements Serializable {



    /**

     * The content of the page.

     */
    private List<T> content;

    /**
     * The total amount of entities.
     */
    private int count;

...

}

PageRequest.java

public class PageRequest implements Serializable {

    private int start;

    private int pageSize;

    private SortOrder sortOrder;

    private String sortField;

    private Map<String, String> filters = new HashMap<>();

...

}


Ok, now that we have our custom ResourceBundle let's see how we use it in our JSF pages. The example is a PrimeFaces output label. 

 <p:outputLabel for="pageLanguage" value="#{msg['messagebundleSample.pageLanguage']}" />


That's all for now. 

In the second part I will show what to do to implement the same with Spring, providing a working example. 

Comments

Popular posts from this blog

WebSphere stdout/stderr logging redirect

JSF2 View Scope with Spring Core

Spring Data Repositories: Query by Specification