The exception listener on JMS connections is never called

Since we were having a few issues with memory leaks while handling JMS connections, we wanted to setup am Exception Listener (using the setExceptionListener method of the connection) to handle a connection failure and reconnect. Especially, if you consume messages asynchronously, this seems like a good way to learn that your connection has failed and reconnect.

Unfortunately, we never were notified although we could clearly see that the connection failed.

The problem is that we were in a JBoss container and as the J2EE specification says:

This method must not be used in a Java EE web or EJB application. Doing so may cause a JMSException to be thrown though this is not guaranteed.

This is actually true not only for setExceptionListener but for all the following methods of the JMS Connection class:

  • createConnectionConsumer
  • createSharedConnectionConsumer
  • createDurableConnectionConsumer
  • createSharedDurableConnectionConsumer
  • setClientID
  • setExceptionListener
  • stop

The first four of them are not allowed because of restrictions on the use of threads in the container. The other ones because they may interfere with the
connection management functions of the container.

In the past this was not always enforced by J2EE container vendors but in order to pass the compatibility test suites, they have started really enforcing this policy. So with many containers, you will get an IllegalStateException or a JMSException when invoking setExceptionListener (which states that you’re not allowed to call it).

Generate icons for checkboxes

Instead of using this square check boxes you can also set your own images using the three following methods of JCheckBox:

  • setIcon to set the default icon.
  • setSelectedIcon to set the icon displayed when the box is checked.
  • setDisabledIcon to set the icon used when the box is disabled.

Now I mostly need the same image as the default image for selected and disabled icon with either an outline when it’s selected or grayed out if disabled.
So that I don’t have to set those three icons manually each time, I’ve subclassed JCheckBox so that the setIcon sets all three icons:

public void setIcon(Icon defaultIcon) {
	super.setIcon(defaultIcon);
	// New icon should have the same size
	int height = defaultIcon.getIconHeight();
	int width = defaultIcon.getIconWidth();
	// Get an image with the icon
	BufferedImage image = new BufferedImage(width, height,
			BufferedImage.TYPE_INT_ARGB);
	Graphics2D g2 = image.createGraphics();
	// First paint the icon
	defaultIcon.paintIcon(this, g2, 0, 0);
	// Buffer for the new image
	BufferedImage selected = new BufferedImage(width, height,
			BufferedImage.TYPE_INT_ARGB);
	g2 = selected.createGraphics();
	// Draw the original icon
	g2.drawImage(image, null, 0, 0);
	// Create the stroke for the outline
	g2.setColor(UIManager.getColor("CheckBox.outlineColor"));
	int strokeSize = (int) (.15 * width);
	g2.setStroke(new BasicStroke(strokeSize, BasicStroke.CAP_ROUND,
			BasicStroke.JOIN_ROUND));
	// Then draw the outline
	g2.drawRoundRect(0, 0, width, height, height, height);
	// And create an ImageIcon to use as selected icon
	setSelectedIcon(new ImageIcon(selected));
	// For the disabled icon we just apply a gray filter
	ImageFilter filter = new GrayFilter(false, 0);
	// Apply the filter to the original image
	Image disabled = createImage(new FilteredImageSource(image.getSource(),
			filter));
	// And create an ImageIcon to use as disabled icon
	setDisabledIcon(new ImageIcon(disabled));
}

Now I just need to set one of them and the others are created automatically. The result looks like this:
checkboxes
Of course the outline shouldn’t be so thick and the icon already should provide space for the outline.

Java: Vertical Label in Swing

This is an article I’ve published over 8 years ago on jroller. I’ve now republished it on my current blog because I was asked to clarify the license terms (see next paragraph) and I found it better to do it here than to update a post on a blog I’m not maintaining anymore.

License clarification: You can use the code of any article on this blog as you wish. I’ve just published this code in the hope to be helpful and do not expect anything in return. So from my point of view you can consider it being released under the WTFPL license terms.

Today, let’s see how to implement a vertical label in Swing. This component should extend JLabel and provide the possibility to rotate 90° to the right or to the left.

Like normal JLabel, it should work whether it contains an icon, a text or both.

Here’s a screenshot of how it should look like:

vertical label

The label with a rotation to the left, no rotation and a rotation to the right.

So that the label painted according to the current look and feel, I didn’t want to paint it myself but to delegate it and rotate it.

In order to do this, I had to rotate and translate the graphics object. The remaining problem is that the UI delegate uses some methods of the label to get the size of the component and the insets. So we have to trick the UI delegate into thinking that the component is horizontal and not vertical.

  public Insets getInsets(Insets insets) {
        insets = super.getInsets(insets);
        if (painting) {
            if (rotation == ROTATE_LEFT) {
                int temp = insets.bottom;
                insets.bottom = insets.left;
                insets.left = insets.top;
                insets.top = insets.right;
                insets.right = temp;
            }
            else if (rotation == ROTATE_RIGHT) {
                int temp = insets.bottom;
                insets.bottom = insets.right;
                insets.right = insets.top;
                insets.top = insets.left;
                insets.left = temp;
            }
        }
        return insets;
    }
    public Insets getInsets() {
        Insets insets = super.getInsets();
        if (painting) {
            if (rotation == ROTATE_LEFT) {
                int temp = insets.bottom;
                insets.bottom = insets.left;
                insets.left = insets.top;
                insets.top = insets.right;
                insets.right = temp;
            }
            else if (rotation == ROTATE_RIGHT) {
                int temp = insets.bottom;
                insets.bottom = insets.right;
                insets.right = insets.top;
                insets.top = insets.left;
                insets.left = temp;
            }
        }
        return insets;
    }
    public int getWidth() {
        if ((painting) && (isRotated()))
            return super.getHeight();
        return super.getWidth();
    }
    public int getHeight() {
        if ((painting) && (isRotated()))
            return super.getWidth();
        return super.getHeight();
    }

The painting variable is set in the paintComponent method just before calling the method of the super class:

protected void paintComponent(Graphics g) {
	Graphics2D g2d = (Graphics2D) g;

	if (isRotated())
		g2d.rotate(Math.toRadians(90 * rotation));
	if (rotation == ROTATE_RIGHT)
		g2d.translate(0, -this.getWidth());
	else if (rotation == ROTATE_LEFT)
		g2d.translate(-this.getHeight(), 0);
	painting = true;

	super.paintComponent(g2d);

	painting = false;
	if (isRotated())
		g2d.rotate(-Math.toRadians(90 * rotation));
	if (rotation == ROTATE_RIGHT)
		g2d.translate(-this.getWidth(), 0);
	else if (rotation == ROTATE_LEFT)
		g2d.translate(0, -this.getHeight());
}

Now one remaining problem is that a normal label uses the icon and the text to compute it’s preferred, minimum and maximum size assuming it’s layed out horizontally. Now the layour managers use this methods to layout the components. So we need to return sizes based on a vertical layout. Since we do not want to compute the sizes ourselves, we have to let the super class compute it and switch height and width (when there’s a rotation):

public Dimension getPreferredSize() {
	Dimension d = super.getPreferredSize();
	if (isRotated()) {
		int width = d.width;
		d.width = d.height;
		d.height = width;
	}
	return d;
}

public Dimension getMinimumSize() {
	Dimension d = super.getMinimumSize();
	if (isRotated()) {
		int width = d.width;
		d.width = d.height;
		d.height = width;
	}
	return d;
}

public Dimension getMaximumSize() {
	Dimension d = super.getMaximumSize();
	if (isRotated()) {
		int width = d.width;
		d.width = d.height + 10;
		d.height = width + 10;
	}
	return d;
}

That’s it, now our component behaves exactly like a JLabel except that it can be rotated to the left or to the right. The whole source code here for your reference:

package org.jroller.henribenoit.swing;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;

public class VerticalLabel extends JLabel {
    public final static int ROTATE_RIGHT = 1;

    public final static int DONT_ROTATE = 0;

    public final static int ROTATE_LEFT = -1;

    private int rotation = DONT_ROTATE;

    private boolean painting = false;

    public VerticalLabel() {
        super();
    }

    public VerticalLabel(Icon image, int horizontalAlignment) {
        super(image, horizontalAlignment);
    }

    public VerticalLabel(Icon image) {
        super(image);
    }

    public VerticalLabel(String text, Icon icon, int horizontalAlignment) {
        super(text, icon, horizontalAlignment);
    }

    public VerticalLabel(String text, int horizontalAlignment) {
        super(text, horizontalAlignment);
    }

    public VerticalLabel(String text) {
        super(text);
    }

    public int getRotation() {
        return rotation;
    }

    public void setRotation(int rotation) {
        this.rotation = rotation;
    }

    public boolean isRotated() {
        return rotation != DONT_ROTATE;
    }

    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;

        if (isRotated())
            g2d.rotate(Math.toRadians(90 * rotation));
        if (rotation == ROTATE_RIGHT)
            g2d.translate(0, -this.getWidth());
        else if (rotation == ROTATE_LEFT)
            g2d.translate(-this.getHeight(), 0);
        painting = true;

        super.paintComponent(g2d);

        painting = false;
        if (isRotated())
            g2d.rotate(-Math.toRadians(90 * rotation));
        if (rotation == ROTATE_RIGHT)
            g2d.translate(-this.getWidth(), 0);
        else if (rotation == ROTATE_LEFT)
            g2d.translate(0, -this.getHeight());
    }

    public Insets getInsets(Insets insets) {
        insets = super.getInsets(insets);
        if (painting) {
            if (rotation == ROTATE_LEFT) {
                int temp = insets.bottom;
                insets.bottom = insets.left;
                insets.left = insets.top;
                insets.top = insets.right;
                insets.right = temp;
            }
            else if (rotation == ROTATE_RIGHT) {
                int temp = insets.bottom;
                insets.bottom = insets.right;
                insets.right = insets.top;
                insets.top = insets.left;
                insets.left = temp;
            }
        }
        return insets;
    }

    public Insets getInsets() {
        Insets insets = super.getInsets();
        if (painting) {
            if (rotation == ROTATE_LEFT) {
                int temp = insets.bottom;
                insets.bottom = insets.left;
                insets.left = insets.top;
                insets.top = insets.right;
                insets.right = temp;
            }
            else if (rotation == ROTATE_RIGHT) {
                int temp = insets.bottom;
                insets.bottom = insets.right;
                insets.right = insets.top;
                insets.top = insets.left;
                insets.left = temp;
            }
        }
        return insets;
    }

    public int getWidth() {
        if ((painting) && (isRotated()))
            return super.getHeight();
        return super.getWidth();
    }

    public int getHeight() {
        if ((painting) && (isRotated()))
            return super.getWidth();
        return super.getHeight();
    }

    public Dimension getPreferredSize() {
        Dimension d = super.getPreferredSize();
        if (isRotated()) {
            int width = d.width;
            d.width = d.height;
            d.height = width;
        }
        return d;
    }

    public Dimension getMinimumSize() {
        Dimension d = super.getMinimumSize();
        if (isRotated()) {
            int width = d.width;
            d.width = d.height;
            d.height = width;
        }
        return d;
    }

    public Dimension getMaximumSize() {
        Dimension d = super.getMaximumSize();
        if (isRotated()) {
            int width = d.width;
            d.width = d.height + 10;
            d.height = width + 10;
        }
        return d;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new FlowLayout());
        VerticalLabel label = new VerticalLabel("Testing something");
        VerticalLabel label2 = new VerticalLabel("Testing something");
        VerticalLabel label3 = new VerticalLabel("Testing something");
        label.setIcon(new ImageIcon("shortcut.png"));
        label2.setIcon(new ImageIcon("shortcut.png"));
        label3.setIcon(new ImageIcon("shortcut.png"));
        label.setRotation(VerticalLabel.ROTATE_LEFT);
        label2.setRotation(VerticalLabel.DONT_ROTATE);
        label3.setRotation(VerticalLabel.ROTATE_RIGHT);
        frame.getContentPane().add(label);
        frame.getContentPane().add(label2);
        frame.getContentPane().add(label3);
        frame.pack();
        frame.setVisible(true);
    }
}

 

Java: Importing a .cer certificate into a java keystore

First let’s have a short look at what those certificates are and what you need them for. A certificate is basically a public key together with some additional identification information (e.g. country, location, company…). The certificate is signed by a Certificate Authority (CA) which guarantees that the information attached to the certificate are true. The .cer files are files containing a certificate.

Additionally to the certificate, you also need a private key. The receiver of the certificate will use the public key in the certificate to decipher the encrypted text sent you are sending. You will encrypt the text using the corresponding private key. The public key in the certificate is publicly available. But you are the only one having access to the private key (that’s why the keystore containing your private key is protected by a password). This allows everybody to check whether sent information really comes from you.

While developing your software you will most probably be working with self-generated certificates. These certificates do not allow the client application to check whether you are really who you say are but they allow you to test most certificate related functionality. You can generate such a certificate like this:

$JAVA_HOME/bin/keytool -genkey -alias ws_client -keyalg RSA -keysize 2048 -keypass YOUR_KEY_PASSWORD \
         -keystore PATH_TO_KEYSTORE/ws_client.keystore \
         -storepass YOUR_KEYSTORE_PASSWORD -dname "cn=YOUR_FQDN_OR_IP, ou=YOUR_ORG_UNIT, o=YOUR_COMPANY, c=DE" \
         -validity 3650 -J-Xmx256m

Note that the backslashes you see in there are only required so that this command is recognized as a multiline command. If you write it all on one line, you won’t need them.

The certificate generated above is valid for almost 10 years (3650 days).

The -J parameter is just in there so that you do not get such an error invoking keytool:

Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

Now, when you go into production, you’ll want to have a “real” certificate so that your users do not get more or less scary messages saying that your identity cannot be verified (i.e. has not been created by a trusted certificate authority). You’ll have to buy such a certificate or have your customer generate one if they can.

This is how you can display the certificates currently installed in your keystore:

$JAVA_HOME/bin/keytool -list \
         -keystore PATH_TO_KEYSTORE/ws_client.keystore \
         -storepass YOUR_KEYSTORE_PASSWORD -J-Xmx256m

It will return something like:

Keystore type: JKS
Keystore provider: SUN

Your keystore contains 1 entry

ws_client, Apr 9, 2014, PrivateKeyEntry,

Certificate fingerprint (MD5): 4A:B5:07:64:A3:FF:16:E4:B9:28:A3:D9:BE:9D:7D:E6

You can export this certificate like this:

$JAVA_HOME/bin/keytool -exportcert -rfc -alias ws_client -file CER_FILE_PATH \
         -keystore PATH_TO_KEYSTORE/ws_client.keystore \
         -storepass YOUR_KEYSTORE_PASSWORD -J-Xmx256m

The rfc option means that the certificate will not be exported in binary form but as shown below.

The exported file looks like this:

-----BEGIN CERTIFICATE-----
MIICJzCCAZCgAwIBAgIEU0VlpTANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJERTETMBEGA1UE
ChMKU0lFTUVOUyBBRzEaMBgGA1UECxMRTWVkaWNhbCBTb2x1dGlvbnMxGDAWBgNVBAMTDzE5Mi4x
NjguMTkwLjIwMDAeFw0xNDA0MDkxNTIyMTNaFw0yNDA0MDYxNTIyMTNaMFgxCzAJBgNVBAYTAkRF
MRMwEQYDVQQKEwpTSUVNRU5TIEFHMRowGAYDVQQLExFNZWRpY2FsIFNvbHV0aW9uczEYMBYGA1UE
AxMPMTkyLjE2OC4xOTAuMjAwMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCRt0f7K7LRjHQI
WN2XWoTAu48ldycigmEyjZ9alsj+ci1I44Q5KCFBNadkGP7ecHmbDJTADKzzJ4O0x+pSzffUScfp
1iHi4HNJcSXE1vXg3a3q/w4B5d+42P/RRHvbnkLLgamOGv9JpGoFFul4R4y2DwuzyN1V8O7nvKs8
TfBsZwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEqolhr8ae0WpB+q7G+trJiZ40j6xmadAMZ9/24G
eisxgIXDFDLcc8MQ+1txMvBI8GXt3JM+wIbKjBtePzFAoNxnVg4DjLINsWRpcuepnXgCTqwuIh6m
+6oGNiiNYUuoW7UbhgYMCEHNc2r09xGhj7Zunz5JJs1Qk0Plh2hPdbQ/
-----END CERTIFICATE-----

In order to do get a certificate, you’ll have to provide the certifying authorities of the customer with a certificate request. This can be done using the keytool command like this:

$JAVA_HOME/bin/keytool -certreq -alias ws_client -file CSR_FILE_PATH -keypass YOUR_KEY_PASSWORD \
         -keystore PATH_TO_KEYSTORE/ws_client.keystore \
         -storepass YOUR_KEYSTORE_PASSWORD -J-Xmx256m

The certificate request file looks like this:

-----BEGIN NEW CERTIFICATE REQUEST-----
MIIBmDCCAQECAQAwWDELMAkGA1UEBhMCREUxEzARBgNVBAoTClNJRU1FTlMgQUcxGjAYBgNVBAsT
EU1lZGljYWwgU29sdXRpb25zMRgwFgYDVQQDEw8xOTIuMTY4LjE5MC4yMDAwgZ8wDQYJKoZIhvcN
AQEBBQADgY0AMIGJAoGBAJG3R/srstGMdAhY3ZdahMC7jyV3JyKCYTKNn1qWyP5yLUjjhDkoIUE1
p2QY/t5weZsMlMAMrPMng7TH6lLN99RJx+nWIeLgc0lxJcTW9eDdrer/DgHl37jY/9FEe9ueQsuB
qY4a/0mkagUW6XhHjLYPC7PI3VXw7ue8qzxN8GxnAgMBAAGgADANBgkqhkiG9w0BAQUFAAOBgQCA
wOaXDF1siyqldzF/5IN/z0VS77nm6hD/JrxpSQi7E+SCT+G/+2I6HBhNba2FTGRqkIkcJ1eG9ZTA
kxMMEO2TI9eZ01xHXP5yUWhOozjHZAFHESpEbP+f7lVLS/EpiLUCCNaSRSMsXqOpi1sEX2v9GrCE
HiU8uypX+jFW/J5REg==
-----END NEW CERTIFICATE REQUEST-----

This certificate request file can then be sent to the person providing the certificate. Using this certificate request, he/she will generate a certificate which can then be imported this way:

$JAVA_HOME/bin/keytool -importcert -alias ws_client -file CER_FILE_PATH \
         -keystore PATH_TO_KEYSTORE/ws_client.keystore \
         -storepass YOUR_KEYSTORE_PASSWORD -J-Xmx256m 

You will need to answer y when prompted whether you trust this certificate:

Owner: CN=benohead.com, OU=Blog, O=amazingweb GmbH, C=DE
Issuer: CN=benohead.com, OU=HenriCA, O=amazingweb GmbH, C=DE
Serial number: 534565a5
Valid from: Wed Apr 09 17:22:13 CEST 2014 until: Sat Apr 06 17:22:13 CEST 2024
Certificate fingerprints:
         MD5:  4A:B5:07:64:A3:FF:16:E4:B9:28:A3:D9:BE:9D:7D:E6
         SHA1: 69:C5:C9:9D:08:AE:17:37:2E:58:F6:77:C9:7B:59:59:E3:29:49:74
         Signature algorithm name: SHA1withRSA
         Version: 3
Trust this certificate? [no]:  yes
Certificate was added to keystore

Note that whether you use a self-generated certificate or one generated by a trusted CA, you will need to reference the keystore file and provide the keystore password in the configuration of your servlet container or application server (e.g. in jbossweb.sar/server.xml for JBoss).

Java: Could not find main class. Program will exit.

In order to make a runnable (executable) JAR file which can be started by double clicking the JAR file, you just need to add a manifest file to the JAR file which references the main class to be executed. To do this, just create a file called manifest.txt and add the following contents:

Main-Class: com.benohead.app.Main

Replace com.benohead.app.Main by the fully qualified name of your main class (the class containing the static main method to be executed). Make sure that there is a newline after the fully qualified name since it could make problems later on if it is missing. You can then make the JAR file using your class files and the manifest file:

jar -cvfm MyJarFile.jar manifest.txt com/benohead/app/*.class

A manifest.mf file created and added instead of the manifest.txt file. When you double click on the create JAR file, the main class will be automatically executed. Now, you may be able to run the JAR file like this on some machine but it might also fail on other machines, with the following error message:

Could not find main class. Program will exit.

You may also notice that the following works on some machines where the double click doesn’t work:

java -jar MyJarFile.jar

The problem is probably that you compiled your code with a new version of the JDK (e.g. JDK 6) but an old version of JRE (e.g. JRE 5) is used on double click. You may wonder why it is a problem when you double click but not when you run it from the command line. The answer is that you might be using different versions on the JRE when double clicking and when call java from the command line. To check this, you can execute the following two commands. To check the version used on double click:

ftype | find "jarfile"

It will return something like this:

jarfile=”C:\Program Files\Java\jre1.5.0_14\bin\javaw.exe” -jar “%1” %*

This shows that JRE 5 is used on double click. If you compiled your code using JDK 6 it will be a problem. To check the version used from the command line:

java -version

It will return something like this:

java version “1.6.0_20” Java(TM) SE Runtime Environment (build 1.6.0_20-b02) Java HotSpot(TM) Client VM (build 16.3-b01, mixed mode)

In this case it is using JRE 6 which is the reason why it worked from the command line. So how do you fix it ? There are two ways to do it:

  1. Reinstall the new JRE. It should then fix the file association in the OS
  2. Fix the file association manually

For the second one, you can execute the following in a command prompt:

ftype jarfile="C:\Program Files\Java\jre6\bin\javaw.exe" -jar "%1" %*

Of course you need to find the path to the new JRE and use it instead of the path above. Depending on your operating you can use the command below to find it:

where javaw

(where is some kind of Windows equivalent to the which command under Linux and is available from Windows 2003 Server onwards, so including Windows Vista, Windows 7 and Windows 8) Note that to check whether the problem is really a java version issue, you can also start the JAR file from the command line using the same version as used in the double click scenario e.g.:

"C:\Program Files\Java\jre1.5.0_14\bin\javaw.exe" -jar MyJarFile.jar

If you have a version issue, you should get an exception along the lines:

“Exception in thread “main” java.lang.UnsupportedClassVersionError: com/benohead/app/Main (Unsupported major.minor version 50.0)”).

Here’s for your reference a mapping of java versions to major.minor versions:

Java Version Major Version
Java 1.1          45.0
Java 1.2          46.0
Java 1.3          47.0
Java 1.4          48.0
Java 5             49.0
java 6             50.0
java 7             51.0
java 8             52.0

System.TypeLoadException: Method does not have an implementation.

I’m working on a C# project where I have a WCF service using an external library and providing some functionality through a REST interface. For some reason, it broke at some point in time and I was getting the following error message:

System.TypeLoadException: Method ‘XXX’ in type ‘YYY’ from assembly ‘ZZZ’ does not have an implementation.

First, since I had the code for the external library (I actually built it from source), I checked the code. The method XXX was actually available with an implementation in type YYY.

Here is how the different assemblies look like:

Assemblies

After a few hours I did figure out that the problem was that I had referenced an old version of the interfaces assembly when building the implementation assembly and a new version of the interfaces assembly when building the services assembly. So the implementation was actually there. It was just not marked as being the implementation of the method defined in the interface but was just marked as an additional method present in the implementation. So accessing the method through the interface in the service failed.

After recompiling the implementation assembly with a reference to the newest interfaces assembly solved the problem.

Since I have over 15 years of experience on the Java platform and about 4 years of experience on the .NET platform, I tend to always relate everything I see on the .NET platform to the way it works in Java. So after finding the solution to this issue, I decided to check how the behavior in Java would be in this case.

I’ve first created an interface:

public interface IMyInterface {
	public void myFirstMethod();
}

Then I’ve created an implementation:

public class MyImplementation implements IMyInterface {
	public void myFirstMethod() {
		System.out.println("myFirstMethod");
	}

	public void mySecondMethod() {
		System.out.println("mySecondMethod");
	}
}

Finally, I’ve implemented a service instantiating the implementation and calling the first method through the interface:

public class MyService {
	public static void main(String[] args) {
		IMyInterface myInt = new MyImplementation();
		myInt.myFirstMethod();
	}
}

Running the service displays:

myFirstMethod

Then I’ve updated the interface adding the second method:

public interface IMyInterface {
	public void myFirstMethod();
	public void mySecondMethod();
}

I’ve then added a call to the second method in the service without recompiling the implementation with the new interface:

public class MyService {
	public static void main(String[] args) {
		IMyInterface myInt = new MyImplementation();
		myInt.myFirstMethod();
		myInt.mySecondMethod();
	}
}

Compiling the service again and running it displays:

myFirstMethod
mySecondMethod

So I didn’t get the same issue here.

Note that this post is not about which platform is better and whether the Java behavior makes more sense than the .NET behavior or not.
With the way Java behaves I wouldn’t have run into this problem. On the other hand, I wouldn’t have noticed that I built the implementation classes against the wrong version of the interface. If the implementation actually didn’t implement mySecondMethod, I wouldn’t have noticed it either and would have got an exception like:

Exception in thread "main" java.lang.AbstractMethodError: MyImplementation.mySecondMethod()V
at MyService.main(MyService.java:5)

Also if I had handled versioning properly on the .NET side, I would probably not wasted that much time. I guess it’s always the thing with versioning: it’s easier to assume that there is only one version of a given interface and that all components use the same one, than handling multiple versions and having different assemblies built against different versions of another assembly. On the other hand, assuming this often just means that if you do not have a build system making sure that all components are recompiled against the latest version of an interface, you’ll get some nice exceptions at runtime.

Java: Character sets and encoding

When you need to communicate with an external system or application and are not located in the US, it is very important to use the right character set. If all involved system are first class Unicode citizens, it is pretty easy. But it is not always the case especially when you work with older systems. As long as you stay in the Java world, everything is UTF-16 so no problem. And when you leave this safe Unicode world, you need to encode your string using an appropriate character set.

Often, you will want to convert your Java strings to a sequence of byte representing this string in a given character set. For this you have the getBytes method of the String class:

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException

and:

public byte[] getBytes(Charset charset)

The second one is great if you already know in advance which character set you will be using. But if your software can be used in many countries world-wide and you do not know in advance which character set you will be needing, you’ll have to make the character set used configurable and go for the first method.

The first method takes the name of a character set as parameter. Unfortunately, it is not so easy to find a list of supported character sets and especially to find out how they are named.

This method basically java.lang.StringCoding class to lookup an instance of the Charset class from the provided character set name and uses a StringEncoder to transform the UTF-16 String to a byte array.

Note that this lookup of the Charset instance is not so expensive as the responses are cached since it is expected that most programs use the same character set multiple times.

If the character set is not already in the cache, a lookup is done in three lists of character sets:

  1. Standard character sets
  2. Extended character sets
  3. Character set from providers found via the application class loader

Note that there are many entries in the lists below which refer to the same character set. Java has an additional layer in there which are character set aliases. So each character set can have multiple aliases e.g. UTF-16 can be specified with any of the following aliases:

  • UTF_16
  • utf16
  • unicode
  • UnicodeBig

Standard character sets

Here is a list of standard character sets. This list should be complete but it might be different in different versions of the JRE.

iso-ir-6
ANSI_X3.4-1986
ISO_646.irv:1991
ASCII
ISO646-US
us
IBM367
cp367
csASCII
default
646
iso_646.irv:1983
ANSI_X3.4-1968
ascii7
UTF8
unicode-1-1-utf-8
UTF_16
utf16
unicode
UnicodeBig
UTF_16BE
ISO-10646-UCS-2
X-UTF-16BE
UnicodeBigUnmarked
UTF_16LE
X-UTF-16LE
UnicodeLittleUnmarked
UnicodeLittle
UTF_32
UTF32
UTF_32LE
X-UTF-32LE
UTF_32BE
X-UTF-32BE
UTF_32LE_BOM
UTF-32LE-BOM
UTF_32BE_BOM
UTF-32BE-BOM
iso-ir-100
ISO_8859-1
latin1
l1
IBM819
cp819
csISOLatin1
819
IBM-819
ISO8859_1
ISO_8859-1:1987
ISO_8859_1
8859_1
ISO8859-1
iso8859_2
8859_2
iso-ir-101
ISO_8859-2
ISO_8859-2:1987
ISO8859-2
latin2
l2
ibm912
ibm-912
cp912
912
csISOLatin2
iso8859_4
iso8859-4
8859_4
iso-ir-110
ISO_8859-4
ISO_8859-4:1988
latin4
l4
ibm914
ibm-914
cp914
914
csISOLatin4
iso8859_5
8859_5
iso-ir-144
ISO_8859-5
ISO_8859-5:1988
ISO8859-5
cyrillic
ibm915
ibm-915
cp915
915
csISOLatinCyrillic
iso8859_7
8859_7
iso-ir-126
ISO_8859-7
ISO_8859-7:1987
ELOT_928
ECMA-118
greek
greek8
csISOLatinGreek
sun_eu_greek
ibm813
ibm-813
813
cp813
iso8859-7
iso8859_9
8859_9
iso-ir-148
ISO_8859-9
ISO_8859-9:1989
ISO8859-9
latin5
l5
ibm920
ibm-920
920
cp920
csISOLatin5
iso8859_13
8859_13
iso_8859-13
ISO8859-13
ISO_8859-15
8859_15
ISO-8859-15
ISO8859_15
ISO8859-15
IBM923
IBM-923
cp923
923
LATIN0
LATIN9
L9
csISOlatin0
csISOlatin9
ISO8859_15_FDIS
koi8_r
koi8
cskoi8r
koi8_u
cp1250
cp5346
cp1251
cp5347
ansi-1251
cp1252
cp5348
cp1253
cp5349
cp1254
cp5350
cp1257
cp5353
cp437
ibm437
ibm-437
437
cspc8codepage437
windows-437
cp737
ibm737
ibm-737
737
cp775
ibm775
ibm-775
775
cp850
ibm-850
ibm850
850
cspc850multilingual
cp852
ibm852
ibm-852
852
csPCp852
cp855
ibm-855
ibm855
855
cspcp855
cp857
ibm857
ibm-857
857
csIBM857
cp858
ccsid00858
cp00858
858
cp862
ibm862
ibm-862
862
csIBM862
cspc862latinhebrew
cp866
ibm866
ibm-866
866
csIBM866
cp874
ibm874
ibm-874
874

Extended character sets

Extended character sets are more exotic character sets. it e.g. contains asian character sets like Big5, GBK, GB18030, ISO-2022-JP and ISO-2022-KR.
Here is a list of those character sets. This list should also be complete but it might be different in different versions of the JRE.

Big5
csBig5
x-MS950-HKSCS
x-windows-950
windows-950
x-windows-874
ms-874
x-EUC-TW
euctw
cns11643
EUC-TW
Big5-HKSCS
big5hk
big5-hkscs
big5-hkscs:unicode3.0
x-Big5-Solaris
GBK
windows-936
CP936
GB18030
gb18030-2000
GB2312
gb2312
gb2312-80
gb2312-1980
euc-cn
euccn
x-mswin-936
ms_936
Shift_JIS
shift_jis
shift-jis
ms_kanji
x-sjis
csShiftJIS
windows-31j
windows-932
csWindows31J
JIS_X0201
JIS_X0201
X0201
csHalfWidthKatakana
x-JIS0208
JIS_C6226-1983
iso-ir-87
x0208
JIS_X0208-1983
csISO87JISX0208
JIS_X0212-1990
jis_x0212-1990
x0212
iso-ir-159
csISO159JISX02121990
EUC-JP
eucjis
eucjp
Extended_UNIX_Code_Packed_Format_for_Japanese
csEUCPkdFmtjapanese
x-euc-jp
x-eucjp
x-euc-jp-linux
euc-jp-linux
x-eucjp-open
eucJP-open
x-PCK
ISO-2022-JP
jis
csISO2022JP
jis_encoding
csjisencoding
ISO-2022-JP-2
csISO2022JP2
iso2022jp2
x-windows-50221
cp50221
x-windows-50220
cp50220
x-windows-iso2022jp
x-JISAutoDetect
EUC-KR
ksc5601
euckr
ks_c_5601-1987
ksc5601-1987
ksc5601_1987
ksc_5601
csEUCKR
5601
x-windows-949
windows949
windows-949
ms_949
x-Johab
ksc5601-1992
ksc5601_1992
ms1361
ISO-2022-KR
csISO2022KR
ISO-2022-CN
csISO2022CN
x-ISO-2022-CN-CNS
ISO-2022-CN-CNS
x-ISO-2022-CN-GB
ISO-2022-CN-GB
x-ISCII91
iscii
ST_SEV_358-88
iso-ir-153
csISO153GOST1976874
ISO-8859-3
8859_3
ISO_8859-3:1988
iso-ir-109
ISO_8859-3
ISO8859-3
latin3
l3
ibm913
ibm-913
cp913
913
csISOLatin3
ISO-8859-6
8859_6
iso-ir-127
ISO_8859-6
ISO_8859-6:1987
ISO8859-6
ECMA-114
ASMO-708
arabic
ibm1089
ibm-1089
cp1089
1089
csISOLatinArabic
ISO-8859-8
8859_8
iso-ir-138
ISO_8859-8
ISO_8859-8:1988
ISO8859-8
cp916
916
ibm916
ibm-916
hebrew
csISOLatinHebrew
x-ISO-8859-11
iso-8859-11
iso8859_11
TIS-620
tis620.2533
windows-1255
windows-1256
windows-1258
x-IBM942
ibm942
ibm-942
942
x-IBM942C
ibm942C
ibm-942C
942C
x-IBM943
ibm943
ibm-943
943
x-IBM943C
ibm943C
ibm-943C
943C
x-IBM948
ibm948
ibm-948
948
x-IBM950
ibm950
ibm-950
950
x-IBM930
ibm930
ibm-930
930
x-IBM935
ibm935
ibm-935
935
x-IBM937
ibm937
ibm-937
937
x-IBM856
ibm-856
ibm856
856
IBM860
ibm860
ibm-860
860
csIBM860
IBM861
ibm861
ibm-861
861
csIBM861
cp-is
IBM863
ibm863
ibm-863
863
csIBM863
IBM864
ibm864
ibm-864
864
csIBM864
IBM865
ibm865
ibm-865
865
csIBM865
IBM868
ibm868
ibm-868
868
cp-ar
csIBM868
IBM869
ibm869
ibm-869
869
cp-gr
csIBM869
x-IBM921
ibm921
ibm-921
921
x-IBM1006
ibm1006
ibm-1006
1006
x-IBM1046
ibm1046
ibm-1046
1046
IBM1047
ibm-1047
1047
x-IBM1098
ibm1098
ibm-1098
1098
IBM037
ibm037
ebcdic-cp-us
ebcdic-cp-ca
ebcdic-cp-wt
ebcdic-cp-nl
csIBM037
cs-ebcdic-cp-us
cs-ebcdic-cp-ca
cs-ebcdic-cp-wt
cs-ebcdic-cp-nl
ibm-037
ibm-37
cpibm37
037
x-IBM1025
ibm1025
ibm-1025
1025
IBM1026
ibm1026
ibm-1026
1026
x-IBM1112
ibm1112
ibm-1112
1112
x-IBM1122
ibm1122
ibm-1122
1122
x-IBM1123
ibm1123
ibm-1123
1123
x-IBM1124
ibm1124
ibm-1124
1124
IBM273
ibm273
ibm-273
273
IBM277
ibm277
ibm-277
277
IBM278
ibm278
ibm-278
278
ebcdic-sv
ebcdic-cp-se
csIBM278
IBM280
ibm280
ibm-280
280
IBM284
ibm284
ibm-284
284
csIBM284
cpibm284
IBM285
ibm285
ibm-285
285
ebcdic-cp-gb
ebcdic-gb
csIBM285
cpibm285
IBM297
ibm297
ibm-297
297
ebcdic-cp-fr
cpibm297
csIBM297
IBM420
ibm420
ibm-420
ebcdic-cp-ar1
420
csIBM420
IBM424
ibm424
ibm-424
424
ebcdic-cp-he
csIBM424
IBM500
ibm500
ibm-500
500
ebcdic-cp-ch
ebcdic-cp-bh
csIBM500
x-IBM834
cp834
ibm834
ibm-834
IBM-Thai
ibm838
ibm-838
838
IBM870
ibm870
ibm-870
870
ebcdic-cp-roece
ebcdic-cp-yu
csIBM870
IBM871
ibm871
ibm-871
871
ebcdic-cp-is
csIBM871
x-IBM875
ibm875
ibm-875
875
IBM918
ibm-918
918
ebcdic-cp-ar2
x-IBM922
ibm922
ibm-922
922
x-IBM1097
ibm1097
ibm-1097
1097
x-IBM949
ibm949
ibm-949
949
x-IBM949C
ibm949C
ibm-949C
949C
x-IBM939
ibm939
ibm-939
939
x-IBM933
ibm933
ibm-933
933
x-IBM1381
ibm1381
ibm-1381
1381
x-IBM1383
ibm1383
ibm-1383
1383
x-IBM970
ibm970
ibm-970
ibm-eucKR
970
x-IBM964
ibm964
ibm-964
964
x-IBM33722
ibm33722
ibm-33722
33722
IBM01140
ccsid01140
cp01140
1140
IBM01141
ccsid01141
cp01141
1141
IBM01142
ccsid01142
cp01142
1142
IBM01143
ccsid01143
cp01143
1143
IBM01144
ccsid01144
cp01144
1144
IBM01145
ccsid01145
cp01145
1145
IBM01146
ccsid01146
cp01146
1146
IBM01147
ccsid01147
cp01147
1147
IBM01148
ccsid01148
cp01148
1148
IBM01149
ccsid01149
cp01149
1149
x-MacRoman
x-MacCentralEurope
x-MacCroatian
x-MacGreek
x-MacCyrillic
x-MacUkraine
x-MacTurkish
x-MacArabic
x-MacHebrew
x-MacIceland
x-MacRomania
x-MacThai
x-MacSymbol
x-MacDingbat

Additional character set providers

If you need to work with other character sets, not supported by default, you can write your own character set provider.

Note that it might also be useful, if you want to do some character substitution. Instead of configuring iso-latin-1 as character set, you could write your own character set encoder/decoder and replace e.g. strange characters produced by the autocompletion feature of Microsoft Word by corresponding characters which exist in the target character set.

In order to do that you need to extend java.nio.charset.spi.CharsetProvider and make your class available in the application classpath. Additionally, you will need to create a new class extending java.nio.charset.Charset.

CharsetProvider

In this class, you need to implement two methods:

charsetForName

This class basically gets a String representing a character set name and returns a Charset object or null if this particular provider doesn’t support the provided character set.

charsets

This returns an iterator over the character sets supported by this provider.

Charset

This is the class where the whole work is done. You have to implement quite a few methods. Most of the work is done by an Encoder and a Decoder.

Decoder

To implement a decoder you have to implement the decodeLoop method. It basically does what its name says: it decodes bytes to characters. So it will iterate through the bytes and see whether it’s the first byte of a multi-byte character, if not it will add a character to the output otherwise, it will read more bytes before adding a character to the output character buffer.

Encoder

Similarly, to implement an encoder, you need to implement the encodeLoop method. It takes a character buffer as input, iterates through the character buffer, convert each of them to one or multiple bytes and writes the bytes to the Byte buffer.

How to use SVN 1.8 with Eclipse

In this post I’m using Subclipse. I am not too sure whether you’d have the same problem with Subversive or whether it can be fix in the same way. Also I am working on a Mac. Of course the actual solution to this problem is not Mac OS X specific but would also work with Linux. So if you are using Eclipse and Subversion, just ignore the Mac specifics (e.g. Homebrew) and keep reading.

I installed subversion 1.8 using Homebrew some time ago but Eclipse kept using the old svn version so I didn’t have to upgrade my working copies. Now I tried to update a working copy from the command line (it contains documents and not code. That’s why I didn’t do it in Eclipse) and saw that I hadn’t upgraded it yet:

$ svn up
svn: E155036: Please see the 'svn upgrade' command
svn: E155036: The working copy at '/Users/henribenoit/workspace/kanban'
is too old (format 29) to work with client version '1.8.0 (r1490375)' (expects format 31). You need to upgrade the working copy first.

No big deal, I just upgraded all working copies:

$ svn upgrade

Now I could update them:

$ svn up
Updating '.':
At revision 303.

But now I couldn’t synchronize the working copies with the repository using Eclipse any more because eclipse was using the old svn version:

Problems reported while synchronizing SVNStatusSubscriber. 0 of 1 resources were synchronized.
An error occurred synchronizing /kanban: Error getting status for resource P/kanban org.apache.subversion.javahl.ClientException: svn: E155021: This client is too old to work with the working copy at
‘/Users/henribenoit/workspace/kanban’ (format ’31’).
Error getting status for resource P/kanban org.apache.subversion.javahl.ClientException: svn: E155021: This client is too old to work with the working copy at
‘/Users/henribenoit/workspace/kanban’ (format ’31’).
org.apache.subversion.javahl.ClientException: svn: E155021: This client is too old to work with the working copy at
‘/Users/henribenoit/workspace/kanban’ (format ’31’).
org.apache.subversion.javahl.ClientException: svn: E155021: This client is too old to work with the working copy at
‘/Users/henribenoit/workspace/kanban’ (format ’31’).

Actually I had already installed subversion 1.8 but I didn’t install it with the Java language bindings (which I did when I installed svn 1.7). So to get a clean system, I removed svn:

$ brew remove subversion
Uninstalling /usr/local/Cellar/subversion/1.8.0...

And wanted to install it again with the java bindings and as universal binaries (with 32bits and 64bits support), but got an error message:

$ brew install --universal --java subversion
Error: subversion dependency serf not installed with:
--universal

So I had to remove serf so that it can be installed also as a universal binary:

$ brew remove serf
Uninstalling /usr/local/Cellar/serf/1.2.1...

And after serf, came sqlite:

$ brew install --universal --java subversion
Error: subversion dependency sqlite not installed with:
--universal
$ brew remove sqlite
Uninstalling /usr/local/Cellar/sqlite/3.8.0...

Now I could install subversion but it didn’t manage to create all the links because some links were left over from an older installation:

$ brew install --universal --java subversion
==> Installing subversion dependency: sqlite
...
==> Installing subversion dependency: serf
...
==> Installing subversion
...
Warning: Could not link subversion. Unlinking...
Error: The `brew link` step did not complete successfully
The formula built, but is not symlinked into /usr/local
You can try again using `brew link subversion'

Possible conflicting files are:
...

It’s easy to fix by using the –overwrite option with brew link:

$ brew link --overwrite subversion
Linking /usr/local/Cellar/subversion/1.8.0... 108 symlinks created

Now svn 1.8 is installed properly (including the Java language binding for the Subversion API i.e. JavaHL). But when I tried in Eclipse to synchronize with an svn repository, I got the following error message:

Incompatible JavaHL library loaded. Subversion 1.7.x required.

This basically means that this version of JavaHL is not supported by the installed version of subclipse. In fact, the Subversion Wiki also gives you this info:

Subclipse Version SVN/JavaHL Version
1.10.x 1.8.x
1.8.x 1.7.x
1.6.x 1.6.x
1.4.x 1.5.x
1.2.x 1.4.x
1.0.x 1.4.x

You can check which version of Subclipse is installed in the “About Eclipse” menu item (click there on “Installation details” and search for Subclipse). If you see e.g. that Subclipse 1.8.1 is installed, the table tells you it will only work with svn 1.7. So we need a newer version of Subclipse. My first idea was to run an update in Eclipse. But it updated some other software but nothing related to subclipse. Then I notice that the update site I had used to install subclipse was: http://subclipse.tigris.org/update_1.8.x

This means that it will provide minor updates like 1.8.2, 1.8.3 and so on but no major update like the version 1.10.x that we need for svn 1.8. So I added a new update site for Subclipse 1.10.x: http://subclipse.tigris.org/update_1.10.x/

Then installed Sublipse 1.10.2 from this update site and after my sync with the svn repository was successful !

Eclipse and subversion: Failed to Load JavaHL Library

I haven’t used eclipse for some time (I recently spent more time upgrading, securing servers and writing shell scripts than actually programming). Now I wanted to check what the status of one of my latest projects was. But when I started Eclipse and synchronized with the subversion repository, I got the following error message:

Failed to load JavaHL Library.
These are the errors that were encountered:
no libsvnjavahl-1 in java.library.path
no svnjavahl-1 in java.library.path
no svnjavahl in java.library.path
java.library.path = .:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java

I then searched for this library in the library path (was not sure it was really missing there), and found something:

$ ls -l /Library/Java/Extensions/libsvnjavahl-1.*
lrwxr-xr-x  1 root  admin  35 Mar 22  2013 /Library/Java/Extensions/libsvnjavahl-1.dylib -> /usr/local/lib/libsvnjavahl-1.dylib

I somehow remembered there were two files last time I had issues with the subversion integration. So I followed the linked and checked what was in /usr/local/lib:

$ ls -l /usr/local/lib/libsvnjavahl-1.*
lrwxr-xr-x  1 henribenoit  admin  53 Jul 24 23:35 /usr/local/lib/libsvnjavahl-1.0.dylib -> ../Cellar/subversion/1.7.8/lib/libsvnjavahl-1.0.dylib
lrwxr-xr-x  1 henribenoit  admin  47 Jul 24 23:35 /usr/local/lib/libsvnjavahl-1.a -> ../Cellar/subversion/1.7.8/lib/libsvnjavahl-1.a
lrwxr-xr-x  1 henribenoit  admin  51 Jul 24 23:35 /usr/local/lib/libsvnjavahl-1.dylib -> ../Cellar/subversion/1.7.8/lib/libsvnjavahl-1.dylib
lrwxr-xr-x  1 henribenoit  admin  52 Jul 24 23:35 /usr/local/lib/libsvnjavahl-1.jnilib -> ../Cellar/subversion/1.7.8/lib/libsvnjavahl-1.jnilib

So I have a link to the .dylib file in /Library/Java/Extensions/ but I miss a link to the .jnilib. Easy to fix:

$ ln -s /usr/local/lib/libsvnjavahl-1.jnilib /Library/Java/Extensions/libsvnjavahl-1.jnilib

After that I could syncronize with the svn repository in Eclipse !

Another solution I tried is to move away from the jni based svn client adapter and use the pure Java SVNKit adapter. To change this setting, go to the Eclipse preferences and write svn in the filter:

SVN interface client

I’ve resized the window to only show the relevant setting, this dialog actually has many more settings. In the displayed select box you can choose between JavaHL (JNI) and SVNKit (Pure Java). If you use SVNKit, you don’t need to have access to the native library shown above.

When it comes to choosing between JavaHL and SVNKit: When a new version of subversion is available, JavaHL is usually available earlier. So you can upgrade earlier. But it is often more difficult to install and integrate than SVNKit.

jmap: Could not reserve enough space for object heap

If you get the following error message when using jmap to get a Java heap dump:

# $JAVA_HOME/bin/jmap -dump:file=/tmp/dump.hprof PID
Error occurred during initialization of VM
Could not reserve enough space for object heap
Could not create the Java virtual machine.

it’s due to the default heap size used in the server VM so you just need to set some values for the Xmx and Xms parameters. With jmap you also need to prefix it with a -J meaning that this parameter will be transferred to the underlying JVM:

 # $JAVA_HOME/bin/jmap -F -J-Xms16m -J-Xmx128m -dump:file=/tmp/dump.hprof 24613
Attaching to process ID 24613, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 20.4-b02
Dumping heap to /tmp/dump.hprof ...
Finding object size using Printezis bits and skipping over...
...
Finding object size using Printezis bits and skipping over...
Heap dump file created

If you create multiple files, it makes sense to have a timestamp in the filename e.g.:

 # $JAVA_HOME/bin/jmap -F -J-Xms16m -J-Xmx128m -dump:file=/tmp/dump.`date '+%Y%m%d%H%M%S'`.hprof 24613