Latest Tweets

Creative thinking: embedding images inside a java .properties file.

The issue

 An old java Applet, developed around 2007, was using external image files. These files were located inside the source directory holding the entire Applet class hierarchy under /imatges. Its original developer compiled the applet and published it on an old webserver, preparing previously a very trivial html page to run it. In order to load these external image files (gif and jpg file-formats), the developer had implemented the usual call to getResource() to obtain a valid reference to the files. This technique is still working, as long as the applet is ran on a web server and it does not reside in the user’s local file-system. Whenever trying to run this same code locally on a modern JRE environment (7, 8, and so on) under Windows the applet is unable to load the image files. A Java Null Pointer exception is raised, therefore preventing the applet from being ran at all.

Having a quick look at the Java Console whilst running the applet locally, I found out that the null exception was raised because the return of the getResource() call was, well, null! This odd behaviour is well explained here: http://stackoverflow.com/questions/17572293/java-applet-cant-find-resources-in-jar-if-run-locally. Okay, so it seemed quite impossible to keep using the same way of loading external image files if the applet needed to be executed either locally or remotely on a web server.

 Let’s get rid of external image files!

A different approach was mandatory to fix this issue. I have to admit it’s been a while since my last java programming, so I had to spend an obscene amount of time getting acquainted once more with the JDK environment and the java language. That accomplished, I decided that a good way to solve this annoying problem would be to embed all the external image files inside the applet’s code. My first idea was to convert every single image to its byte counterpart and use it directly inside the applet classes. Sort of the old-heady days of programming! I wrote a trivial class called ImageToBytes to do so.  So, having an external image in either gif, png or jpg format, my utility would be translating it into its java’s byte array representation:

java ImageToBytes imatges/ou30.gif imatges/ou30.java img_ou30
Converting imatges/ou30.gif to bytes; output file will be: imatges/ou30.java
>> String Length: 22269, Byte[] length: 2009

That previous command would transform the original image “out30.gif” into a java’s byte array representation ready to be used in a java class source file:

/* Converted from: imatges/ou30.gif */
byte[] img_ou30 = { 
(byte)0x47,(byte)0x49,(byte)0x46,(byte)0x38,(byte)0x39,(byte)0x61,(byte)0x1e,(byte)0x00,(byte)0x2b,(byte)0x00,(byte)0xf7,
(byte)0x00,(byte)0x00,(byte)0x83,(byte)0x77,(byte)0x7d,(byte)0x88,(byte)0x76,(byte)0x78,(byte)0x8a,(byte)0x81,(byte)0x86,
(byte)0x91,(byte)0x7f,(byte)0x81,(byte)0x8f,(byte)0x81,(byte)0x7c,(byte)0x8f,(byte)0x7f,(byte)0x87,(byte)0x8d,(byte)0x81,
...
};

That was an elegant solution … unluckily, if the original image was big, its byte representation would make the final class too large, more than the maximum allowed size for any java class: 64KB. Therefore, I had to come up with something different. Reading through the Oracle JDK documentation, I found out about the properties files. These sort of files can be deployed within a jar file in order to read configuration entries or literals for an applet. Their format is extremely simple: any line starting with # is a comment; a blank line is not parsed and in order to store entries, one must provide a key and its corresponding key value. Sort of that old win.ini files of past-time. This is an example:

username=moron

As it happens, it is feasible to store any kind of data for the key-value. And according to the JDK reference, there is no character limit in the key-value string. That meant I could write anything as a key-value, like, well, the entire image’s bytes as a whole. So I decided to alter my ImageToBytes utility, adding an additional parameter -p, to create a .properties file containing its byte representation counterpart:

java ImageToBytes imatges/ou30.gif imatges/ou30.properties img_ou30 -p
Converting imatges/ou30.gif to bytes; output file will be: imatges/ou30.properties
>> String Length: 4016, Byte[] length: 2009

Running the previous command will generate a new file containing the whole ou30.gif image but in the right format for a .properties file, as shown below:

img_ou30=4749463839611e002b00f7000083777d8876788a8186917f818f817c8f7f878d818297848691868c948889978984948b91a58780988c8d9d8a8ca18b88a3898e9b8f90a08d909990969f8e96b28a7aa69087a1928d9f9394a49193a6918ea79384a29697a7968caa9491a5959ca59792a89597ad958da1989ea59999a09aa5a79b9cb19990aa9c97a69ca2ad9c91ad9a9caa9d9eb19b98ab9fa0b1a095b19e…….

Finally, I concatenated all the resulting properties files in a single one, called images.properties, and put it inside the applet source code directory’s root.

Reading the images from the .properties file

The original Applet code needed to be modified so that any call to ImageIO.read() or to new ImageIcon() took into account that the images where no longer in the images/ directory, but in the images.properties file and in a large hexadecimal string. To begin with, I decompiled the applet binary classes as discussed here, and then I ran the grep command inside the entire Applet source code directory to look for calls involving the imatges/ directory:

grep -R “imatges/” *
Animacio.java:/* 141 */ this.ou = createImageIcon(“imatges/ou30.gif”, “L’ou”);
Animacio.java:/* 142 */ this.pollet = createImageIcon(“imatges/pollet60.gif”, “L’ou”);
Animacio.java:/* 143 */ this.iconherba = createImageIcon(“imatges/terra.jpg”, “El prat”);
Animacio.java:/* 182 */ URL imgURL = jApplet.class.getResource(“imatges/rellotge_num30.gif”);
Animacio.java:/* 188 */ URL imgURL = jApplet.class.getResource(“imatges/semafor_RED.gif”);
Animacio.java:/* 193 */ URL imgURL = jApplet.class.getResource(“imatges/semafor_GREEN.gif”);
PanellAnimacio.java:/* 80 */ this.iconcel = createImageIcon(“imatges/cel.jpg”, “El fons de l’Applet”);
PanellAnimacio.java:/* 81 */ this.iconherba = createImageIcon(“imatges/terra.jpg”, “El prat”);
PannellAnimacio.java:/* 80 */ this.iconcel = createImageIcon(“imatges/cel.jpg”, “El fons de l’Applet”);
PannellAnimacio.java:/* 81 */ this.iconherba = createImageIcon(“imatges/herba_sim.jpg”, “El prat”);
Tren.java:/* 130 */ this.ou = createImageIcon(“imatges/ou30.gif”, “L’ou”);
Tren.java:/* 131 */ this.pollet = createImageIcon(“imatges/pollet60.gif”, “L’ou”);
Tren.java:/* 177 */ URL imgURL = jApplet.class.getResource(“imatges/rellotge19.gif”);
Tren.java:/* 182 */ URL imgURL = jApplet.class.getResource(“imatges/tren.gif”);
Tren.java:/* 187 */ URL imgURL = jApplet.class.getResource(“imatges/semafor_RED.gif”);
Tren.java:/* 192 */ URL imgURL = jApplet.class.getResource(“imatges/semafor_GREEN.gif”);

Before modifying the previous source files, I implemented a new class, called GetImages, in charge of reading the images.properties file, processing a particular image by its key-name, and returning its key-value as a java’s byte array object to its caller. This way it would be extremely easy to read a particular image from the properties file without too much changing the original sources.

Then, a call-translation would be necessary on the original applet sources. The table below summarizes them:

Original Call Translated call
Object = createImageIcon(path…) Object = createImageIcon(id_image…)
Object = ImageIO.read(URL…) Object = ImageIO.read(byte[] imageData…)

Some additional small changes were required, as in the createImageIcon method:

protected /*static*/ ImageIcon createImageIcon(String /*path*/ key, String description){
   byte[] img = this._imgs.getImage(key);
   if(img!=null) return new ImageIcon(img,description);
   else{
     System.out.println("No es pot carregar la imatge amb id: " + key);
     return null;
   }   
}

Not to mention in the way of reading the image on a call to ImageIO.read():

InputStream img_rellotge = new ByteArrayInputStream(this._imgs.getImage("img_rellotge_num30"));
this.rellotge = ImageIO.read(img_rellotge);
InputStream img_semafor_RED = new ByteArrayInputStream(this._imgs.getImage("img_semafor_RED"));
this.semafor_RED = ImageIO.read(img_semafor_RED);
InputStream img_semafor_GREEN = new ByteArrayInputStream(this._imgs.getImage("img_semafor_GREEN"));
this.semafor_GREEN = ImageIO.read(img_semafor_GREEN);

where _imgs being an object of type GetImages, accordingly initialized before using it:

GetImages _imgs;
...
try{
  this._imgs = new GetImages(GetImages.IMAGESFILE);
}catch(Exception ex){
  System.out.println("GetImages() exception: " + ex);
}

Compiling and testing the applet locally

 I compiled the entire applet source code and generated a new jar file containing all the compiled classes and the images.properties file, but this time without the imatges/ directory with all its external image files:

javac -encoding UTF-8 relativitat/*.java

jar cf relativitat.jar relativitat/*.class relativitat/images.properties

The HTML page was modified to run the applet through the jar file just created:

<APPLET code=”relativitat.jApplet” archive=”relativitat.jar” width=982 height=420></APPLET>

On a Windows 7 virtual machine with a Sun Java 8 runtime, I opened the previous HTML file and this time, instead of getting a null URL reference and the applet complaining about not being able to open the image files, it just ran perfectly well, as shown in the next screenshot:

Running the applet locally on a Windows 7 computer with Sun JRE 8.

Running the applet locally on a Windows 7 virtual machine with Sun JRE 8.