Internationalization with MessageFormat

In this post I want to show a quick example of how to internationalize your application in a easy simple way by taking advantage of MessageFormat class.

The design is fairly simple: for each single locale/language we have a properties file with translated messages. At the start we load the properties into a MessageGenerator object and then use it’s methods to get the formatted messages and use them in the code. To encapsulate the loading we have a MessageGeneratorFactory with static methods creating appropriate MessageGenerator objects.

The following are examples of properties files that contain translated messages. Notice that each line has a message identifying label and a translated text with placeholders for values. The English properties (translation_en.properties):

ImportCsv=Import csv files into database
MapTypeError=Couldn't map type for table='{0}' column='{1}'
MonitorAsOf=Monitor (as of {0,date,full})

The same messages in French (translation_fr.properties):

ImportCsv=Importer des fichiers csv dans la base de données
MapTypeError=Impossible de faire correspondre le type \
             pour la colonne {1} de la table {0}
MonitorAsOf=Moniteur (au {0,date,full})

The class providing the localized messages in the application is MessageGenerator. See that for each single message there is a separate get method. This way not only we get type checking of provided arguments but also separate the way messages are prepared from their usage. Internally MessageGenerator uses precompiled MessageFormat objects – they are prepared in class constructor and based on properies file loaded by ResourceBundle.

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
public class MessageGenerator {
    // For each of the available messages MessageGenerator
    // keeps the precompiled MessageFormat object, a identifying
    // String label and a 'get' method.
    private final String importCsvLabel = "ImportCsv";
    private final MessageFormat importCsv;

    private final String mapTypeErrorLabel = "MapTypeError";
    private final MessageFormat mapTypeError;

    private final String monitorAsOfLabel = "MonitorAsOf";
    private final MessageFormat monitorAsOf;

    MessageGenerator(ResourceBundle bundle, Locale locale) {
        // Load and precompile each of the MessageFormat objects
        importCsv = new MessageFormat(
                bundle.getString(importCsvLabel), locale);
        mapTypeError = new MessageFormat(
                bundle.getString(mapTypeErrorLabel), locale);
        monitorAsOf = new MessageFormat(
                bundle.getString(monitorAsOfLabel), locale);
    }

    public String getImportCsvMessage() {
        return importCsv.format(new Object[] {});
    }

    public String getMapTypeErrorMessage(String tableName,
                String columnName) {
        return mapTypeError.format(new Object[] {tableName, columnName});
    }

    public String getMonitorAsOfMessage(Date date) {
        return monitorAsOf.format(new Object[] {date});
    }
}

See that in the code above the constructor is made package-private. This is because we do not want the user to freely construct MessageGenerator objects, but to use a static factory provided by us. This way we can keep only one instance of this object for each locale (notice that it is immutable).

Now the code of MessageGeneratorFactory. See that to load properties files we use ResourceBundle class.

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
public class MessageGeneratorFactory {
    // This is a static factory, so no constructor is needed
    private MessageGeneratorFactory() {
    }

    private static MessageGenerator englishMessages = null;
    private static MessageGenerator frenchMessages = null;

    // Methods creating/returning MessageGenerator objects
    // for specific languages/localisations:

    public static MessageGenerator getEnglishMessages() throws IOException {
        if (englishMessages == null)
            englishMessages = initMessages(Locale.ENGLISH);
        return englishMessages;
    }

    public static MessageGenerator getFrenchMessages() throws IOException {
        if (frenchMessages == null)
            frenchMessages = initMessages(Locale.FRENCH);
        return frenchMessages;
    }

    // Helper method loading the Properties and creating MessageGenerator
    private static MessageGenerator initMessages(Locale locale) {
        // We use ResourceBundle to load the properties files.
        // It assumes specific file naming. For instance we expect
        // the French translations to be in 'i18n/translation_fr.properties'
        ResourceBundle resourceBundle =
            ResourceBundle.getBundle("i18n/translation", locale);
        return new MessageGenerator(resourceBundle, locale);
    }
}

Now lets use both of the classes shown above and have fun with a piece of code that prints out some of the messages to the console:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
public class MainProgram {

    public static void printSomeLocalizedMessages(
            MessageGenerator generator) {
        System.out.println(generator.getImportCsvMessage());
        System.out.println(generator.getMapTypeErrorMessage("X", "Y"));
        System.out.println(generator.getMonitorAsOfMessage(new Date()));
        System.out.println();
    }

    public static void main(String[] args) throws Exception {
        // Prepare 2 generators: one French and one English
        MessageGenerator englishGenerator = MessageGeneratorFactory
                .getEnglishMessages();

        MessageGenerator frenchGenerator = MessageGeneratorFactory
                .getFrenchMessages();

        // Test the generators by printing some of the messages
        printSomeLocalizedMessages(englishGenerator);
        printSomeLocalizedMessages(frenchGenerator);
    }
}

If you are curious what will be printed out go ahead, copy-paste this code and compile it yourself! You can also download the slightly more complicated version of the code above here.

It is important to mention that there are many things that can be improved in this design – this is a very simple code to be used as an example, feel free to use it and improve it yourself. For instance one of the improvements could be loading properties with translations from XML files (see an example how to do that on www.codewrapper.com)

Scripting in Java

Did you know that you can compile and run scripting languages like JavaScript, Python, Ruby and many others directly from your Java code? API responsible for it has been created as a result of Java Specification Request 223 and sits in the javax.script package.

Let’s see on a minimalist example how to use the Java Scripting API to run a JavaScript command:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptTest {
    public static void main(String[] args) throws ScriptException {
        // create a script engine manager
        ScriptEngineManager manager =
            new ScriptEngineManager();
        // create a JavaScript engine
        ScriptEngine javaScriptEngine =
            manager.getEngineByName("JavaScript");
        // evaluate JavaScript code from String
        javaScriptEngine.eval(
            "println('Hello World from JavaScript');");
    }
}

This code should compile and run without problems on Java 6. As a result you should get following output:

Hello World from JavaScript

Support for JavaScript comes included in the standard JRE in form of Rhino: JavaScript for Java ScriptEngine. But other languages are not included by default. Getting support for them is however very easy. Let’s see how you could get support for Python.

You will first need to get Jython, which is a Java implementation for Python. You can download it directly from this URL. If it does not work, go to http://www.jython.org/ and get the newest version from section ‘Download’.

Jython comes with a JAR installer (for example jython_installer-2.5.1.jar). Run it with command ‘java -jar jython_installer-2.5.1.jar’ and install in any directory. After the installation you will find file ‘jython.jar’ inside the directory where you installed Jython. You should include this file in the CLASSPATH when running the next example (You could do this in Eclipse by clicking right mouse button on your project, then Properties->Java Build Path->Libraries->Add External Library).

With jython.jar on the CLASSPATH, let’s run the next code:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptTestJython {
    public static void main(String[] args) throws ScriptException {
        // create a script engine manager
        ScriptEngineManager manager =
            new ScriptEngineManager();

        //Printing all available scripting languages
        for (ScriptEngineFactory factory :
            manager.getEngineFactories()) {
            System.out.println("Available language: " +
                factory.getLanguageName());
        }

        // create a Jython engine
        ScriptEngine jythonEngine =
            manager.getEngineByName("python");
        // evaluate Jython code from String
        jythonEngine.eval(
            "print \"Hello World from Jython\\n\"");
    }
}

This time on the beginning we print all the available scripting languages. We expect to see there JavaScript, which is always supported by default, and Jython because we included it in the classpath. After that we run a simple Python’s code. Let’s see what comes out:

Available language: ECMAScript
Available language: python
Hello World from Jython

(If you got, besides the lines above, also a lot of strange info lines in red, don’t be worried. Jython prints them once during initialization. You should not see them by the next run)
Some of you may be surprised by the first line ‘ECMAScript’. It is actually JavaScript engine for which ECMA is a base (you can read more about it here). On the next line you see that our Jython was properly loaded and the last line shows proper execution of Python’s code.

There is a pretty good tutorial showing many useful features of Java Scripting API like loading script from a file, exchanging variables, loading Java classes in the script and much more, available on this Sun’s site. You can see the list of available scripting languages and download JAR files for them on this site.

This article was inspired by a comment from Ricky on one of our previous post. Thanks!

Simple guide to Java Message Service (JMS) using ActiveMQ

JMS let’s you send messages containing for example a String, array of bytes or a serializable Java object, from one program to another. It doesn’t however use a direct connection from program A to program B, instead the message is sent to a JMS provider and put there in a Queue where it waits until the other program receives it.

MessageProducer is a Java program sending a JMS message to a Queue on the JMS Provider. MessageConsumer is another program which receives that message. These two programs can run on separate machines and all they have to know to communicate is the URL of the JMS Provider. The Provider can be for example a Java EE server, like JBoss or Glassfish. But don’t be afraid, you don’t need a full-blown JEE server to send a JMS message. In this article we will use ActiveMQ which is lightweight and easy to use.

First we need to download ActiveMQ. If you are using Linux, you can get it from this link. For Windows you can use this link. In case the links don’t work, you can find the files in ‘Downloads’ section on ActiveMQ’s webpage.

After the download, extract it to any directory and run the ‘activemq’ program from beneath the ‘{path-where-you-extracted-activemq}/bin’ directory:

user@user-laptop:~/activemq/apache-activemq-5.3.0/bin$ ./activemq

You should see a bunch of INFO messages appearing on the terminal:

...
INFO | ActiveMQ Web Demos at http://0.0.0.0:8161/demo
INFO | RESTful file access application at http://0.0.0.0:8161/fileserver
INFO | Started SelectChannelConnector@0.0.0.0:8161

Now the ActiveMQ server is up and running. You can close it any time by pressing Ctrl-C. ActiveMQ has a nice admin console, where you can see a lot of useful informations and change the settings: http://localhost:8161/admin/.

Now that we have a JMS provider running, let’s write our message producer and consumer programs. For that, you will need to put the ActiveMQ’s JAR file on the class path. The file you need is called (for version 5.3.0) ‘activemq-all-5.3.0.jar’ or something similar and is in the extracted ActiveMQ directory. In Eclipse you could click right mouse button on your project and choose Properties->Java Build Path->Libraries->Add External Library.

Here is the code of the program sending (producing) the messages:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
import javax.jms.*;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Producer {
    // URL of the JMS server. DEFAULT_BROKER_URL will just mean
    // that JMS server is on localhost
    private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;

    // Name of the queue we will be sending messages to
    private static String subject = "TESTQUEUE";

    public static void main(String[] args) throws JMSException {
        // Getting JMS connection from the server and starting it
        ConnectionFactory connectionFactory =
            new ActiveMQConnectionFactory(url);
        Connection connection = connectionFactory.createConnection();
        connection.start();

        // JMS messages are sent and received using a Session. We will
        // create here a non-transactional session object. If you want
        // to use transactions you should set the first parameter to 'true'
        Session session = connection.createSession(false,
            Session.AUTO_ACKNOWLEDGE);

        // Destination represents here our queue 'TESTQUEUE' on the
        // JMS server. You don't have to do anything special on the
        // server to create it, it will be created automatically.
        Destination destination = session.createQueue(subject);

        // MessageProducer is used for sending messages (as opposed
        // to MessageConsumer which is used for receiving them)
        MessageProducer producer = session.createProducer(destination);

        // We will send a small text message saying 'Hello' in Japanese
        TextMessage message = session.createTextMessage("こんにちは");

        // Here we are sending the message!
        producer.send(message);
        System.out.println("Sent message '" + message.getText() + "'");

        connection.close();
    }
}

There is a lot going on here. The Connection represents our connection with the JMS Provider – ActiveMQ. Be sure not to confuse it with SQL’s Connection. ‘Destination’ represents the Queue on the JMS Provider that we will be sending messages to. In our case, we will send it to Queue called ‘TESTQUEUE’ (it will be automatically created if it didn’t exist yet).

What you should note is that there is no mention of who will finally read the message. Actually, the Producer does not know where or who the consumer is! We are just sending messages into queue ‘TESTQUEUE’ and what happens from there to the sent messages is not of Producer’s interest any more.

The most interesting for us part in the above code is probably line 46 where we use function ‘.createTextMessage(”こんにちは”);’ to send a text message (in this case to our Japanese friend).

Now let’s see how to receive (consume) the sent message. Here is the code for the Consumer class:

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
import javax.jms.*;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public class Consumer {
    // URL of the JMS server
    private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;

    // Name of the queue we will receive messages from
    private static String subject = "TESTQUEUE";

    public static void main(String[] args) throws JMSException {
        // Getting JMS connection from the server
        ConnectionFactory connectionFactory
            = new ActiveMQConnectionFactory(url);
        Connection connection = connectionFactory.createConnection();
        connection.start();

        // Creating session for seding messages
        Session session = connection.createSession(false,
            Session.AUTO_ACKNOWLEDGE);

        // Getting the queue 'TESTQUEUE'
        Destination destination = session.createQueue(subject);

        // MessageConsumer is used for receiving (consuming) messages
        MessageConsumer consumer = session.createConsumer(destination);

        // Here we receive the message.
        // By default this call is blocking, which means it will wait
        // for a message to arrive on the queue.
        Message message = consumer.receive();

        // There are many types of Message and TextMessage
        // is just one of them. Producer sent us a TextMessage
        // so we must cast to it to get access to its .getText()
        // method.
        if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            System.out.println("Received message '"
                + textMessage.getText() + "'");
        }
        connection.close();
    }
}

As you see, it looks pretty similar to the Producer’s code before. Actually only the part starting from line 35 is substantially different. We produce there a MessageConsumer instead of MessageReceiver and then use it’s .receive() method instead of .send(). You can see also an ugly cast from Message to TextMessage but there is nothing we could do about it, because .receive() method just returns interface Message (TextMessage interface extends Message) and there are no separate methods for receiving just TextMessage’s.

Compile now both programs remembering about adding ActiveMQ’s JAR file to the classpath. Before running them be also sure that the ActiveMQ’s instance is running (for example in a separate terminal). First run the Producer program:

2009/11/14 15:56:37 org.apache.activemq.
transport.failover.FailoverTransport doReconnect
情報: Successfully connected to tcp://localhost:61616
Sent message 'こんにちは'

If you see something similar to the output above (especially the ‘Sent message’ part) then it means that the message was successfully sent and is now inside the TESTQUEUE queue. You can enter the Queues section in the ActiveMQ’s admin console http://localhost:8161/admin/queues.jsp and see that there is one message sitting in TESTQUEUE:

In order to receive that message run now the Consumer program:

2009/11/14 15:58:03 org.apache.activemq.
transport.failover.FailoverTransport doReconnect
情報: Successfully connected to tcp://localhost:61616
Received message 'こんにちは'

If you are getting above input (or something similar) everything went ok. The message was successfully received.

You are now probably thinking “Why would anybody want to do that??”. In fact, the code presented here to transfer just a small text message was pretty big, and you also needed an instance of ActiveMQ running, and dependencies in the classpath and all that…. Instead of using JMS we could use plain TCP/IP with few times less effort. So, what are good points of using JMS compared to simple TCP/IP or Java RMI? Here they are:

  • Communication using JMS is asynchronous. The producer just sends a message and goes on with his business. If you called a method using RMI you would have to wait until it returns, and there can be cases you just don’t want to lose all that time.
  • Take a look at how we run the example above. At the moment we run the Producer to send a message, there was yet no instance of Consumer running. The message was delivered at the moment the Consumer asked ActiveMQ for it. Now compare it to RMI. If we tried to send any request through RMI to a service that is not running yet, we would get a RemoteException. Using JMS let’s you send requests to services that may be currently unavailable. The message will be delivered as soon as the service starts.
  • JMS is a way of abstracting the process of sending messages. As you see in the examples above, we are using some custom Queue with name ‘TESTQUEUE’. The producer just knows it has to send to that queue and the consumer takes it from there. Thanks to that we can decouple the producer from the consumer. When a message gets into the queue, we can do a full bunch of stuff with it, like sending it to other queues, copying it, saving in a database, routing based on its contents and much more. here you can see some of the possibilities.

JMS is widely used as a System Integration solution in big, distributed systems like those of for example banks. There are many books dealing with this huge topic, for example Enterprise Integration Patterns. If you want to learn more about JMS itself you can do it for example on this JMS Tutorial on Sun’s webpage.