How to read values from a properties file located within a Java jar file

A good programming practice will be to code applications to read from a text file for values which are expected to change often. Java Swing applications are often packed in a single jar file and it can make deployment easier if our Java Swing applications can read from text files embedded within the same jar file that they reside in.

There came a time where I needed to code a Java applet to read i18n labels from some properties files. This post documents my proof of concept before I embarked in coding that Java applet.

The sample scenario

I proved this concept with a hello world applet. This hello world applet reads a message from a properties file contained within the same jar file that it resides in.

Files within my jar file

There are just two files in my jar file:

  • HelloWorldApplet.class
  • msg.properties

And they reside in the same directory within the jar file.

HelloWorldApplet.class is the binary of my applet, whereas msg.properties contains the message to display.

The msg.properties file contains the following entry:

msg.helloworld=Hello world from jar file!

The message "Hello world from jar file!" should be accessible by my HelloWorldApplet with the "msg.helloworld" key.

Codes to realise HelloWorldApplet.class

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ResourceBundle;

import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class HelloWorldApplet extends JApplet {

    private JButton showMessageButton;

    public void init() {
	SwingUtilities.invokeLater(new Runnable() {
	    public void run() {
		initApplet();
	    }
	});
    }

    private void initApplet() {

	JPanel mainPane = new JPanel();

	showMessageButton = new JButton("Show message");
	showMessageButton.addActionListener( new ActionListener() {
	    public void actionPerformed(ActionEvent event) {
		JOptionPane.showMessageDialog(
			HelloWorldApplet.this,
                        ResourceBundle.getBundle("msg").getString("msg.helloworld"),
			"Hello World Applet",
			JOptionPane.INFORMATION_MESSAGE
		);
	    }
	});

	mainPane.add(showMessageButton);

	setContentPane(mainPane);

    }

}

I started off with overriding the init method of my Java applet class to call the initApplet method. This will ensure that my Java applet is initialized with the event dispatcher thread.

Within the initApplet method, I build a javax.swing.JPanel to hold a javax.swing.JButton which is named as "Show message".

I then create and add a java.awt.event.ActionListener to the JButton. Within the actionPerformed method, I call the JOptionPane.showMessageDialog method to display the message.

Using java.util.ResourceBundle.getBundle("msg"), I get the ResourceBundle object that gave my applet code access to the msg.properties file within the jar file. I then use the getString method of the ResourceBundle object to read the message to display with the "msg.helloworld" key.

Lastly I set the JPanel object as the main panel of my applet class.

Why I got a MissingResourceException exception initially

I was being thrown the following exception trace in my face when I try to read the hello world message:

Exception in thread "AWT-EventQueue-2" java.util.MissingResourceException: Can't find bundle for base name msg.properties, locale en_SG
	at java.util.ResourceBundle.throwMissingResourceException(Unknown Source)
	at java.util.ResourceBundle.getBundleImpl(Unknown Source)
	at java.util.ResourceBundle.getBundle(Unknown Source)
	at HelloWorldApplet$2.actionPerformed(HelloWorldApplet.java:37)
	at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
	at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
	at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
	at java.awt.Component.processMouseEvent(Unknown Source)
	at javax.swing.JComponent.processMouseEvent(Unknown Source)
	at java.awt.Component.processEvent(Unknown Source)
	at java.awt.Container.processEvent(Unknown Source)
	at java.awt.Component.dispatchEventImpl(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
	at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
	at java.awt.Container.dispatchEventImpl(Unknown Source)
	at java.awt.Component.dispatchEvent(Unknown Source)
	at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
	at java.awt.EventQueue.access$200(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.awt.EventQueue$3.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.awt.EventQueue$4.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
	at java.awt.EventQueue.dispatchEvent(Unknown Source)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
	at java.awt.EventDispatchThread.run(Unknown Source)

It turned out to be that I had supplied "msg.properties" as the parameter value to the ResourceBundle.getBundle method initially. Note that we must exclude the .properties extension when we want to get a ResourceBundle object to read from a properties file.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.