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.