20 November 2009

My very own first blog spam

Is my blog famous or what? I have gotten my first blog spam! The first words of the spam is "Web casting, or broadcasting over the internet, is a media file".

The comment was posted on my Windows Media Player and MMS (streaming) problems post. I have removed the post now since the links in the post most likly is some kind of virus.

27 October 2009

Don't disable SPI firewall on Netgear router if using VPN

In a previous post I mentioned that disabling the SPI firewall on a Netgear WGT624 v3 router could increase the throughput speed through NAT. Well Now I have disovered that there is a price for this. I can no longer connect to my company VPN via Windows buildt in VPN client!

22 October 2009

How to improve fan control on HP/Compaq Mini

In a previous post I said that I had solved the fan problem on my Compaq Mini. The problem is that the fan is noisy and the control over the fan is bad. The solution? Build your own fan control!

The first step is to get all the tools. Your computer, a stapler (I used a stapler "gun"), office tape and a pincer of some kind:


Open the stapler and take out the clips. A stapler gun has coarse clips and that is good.


Use the pincer and remove about 2 clips


Use the pincer to bend the clips in a L-shape


Insert the clips into the fan exhaust of your computer:


Put some tape on it to put it in place:


Thats it! Now the fan is in your control! Well, to be specific the fan is not moving. And thats a good thing! For your ears! :)

I have used this construct for 4 months now, and I must say that the solution works! The computer gets a bit warm after a while. Really hot in the sun. I try to stay out of the sun and monitor the temperature with SpeedFan. Despite my solution I have never had any problems with instability.

15 October 2009

Fixing lag/delay problem for Wacom tablet

I tested a Wacom Bamboo Pen & Touch tablet. After installing the tablet and driver I noticed that there was a delay between the movement on the tablet (both using pen and touch) and the movement of the mouse cursor on the screen. It was very noticeably when comparing to a corded mouse.

I read some hints on the internet (this blog post: http://michaelauerswald.de/2008/11/06/wacom-intuos3-on-vista-lag-problem-solved/) that disabling the Wacom Virtual Hid Driver would help. And I think it did! Now I think the delay is almost equal to the corded mouse (and that is good).

In Windows 7:
  • Open Control Panel
  • Under Devives and Printers click Devied Manager
  • Open the node in the tree called Human Interface Devices
  • Right click on Wacom Virtual Hid Driver and choose Disable
  • Reboot



Eclipse debugger tutorial

If you are interested in learning how to debug java programs in Eclipse I can recommend this site: http://eclipsetutorial.sourceforge.net/debugger.html. It has screen cast videos showing how to use the debugger. There are 7 lessons, about 10-20 min each. I consider myself semi-experienced in debugging, and even I found the lessons interesting. There are many hints and shortcuts presented that makes debugging easier.

The first lesson can be watched online, and the others downloaded as flash videos.

12 October 2009

Telnet in Windows 7

I'm using Windows 7 professional. The telnet client is not available by default. But it is easy to add it. Go to Control Panel -> Programs -> Get Programs. Then click "Turn Windows features on or off". Click "Telnet Client" and OK.

09 October 2009

Create ant tasks in build.xml from external (jar) file in project

I will show how to use an ant library with ant tasks without adding the external jar file on the classpath on your computer (for some reason, perhaps so everybody in your team can run them without installment on their local computer).

In this example I will use the Ivy library. It is kind of a silly example since Ivy itself is a tool so no jar files needs to exist in the project directory.

Download and place the external jar file in your project directory. I placed the file ivy-2.1.0-rc2.jar in a directory called lib.

Use a zip-program (for example 7Zip) and look in your jar file to see where the file antlib.xml is. For Ivy this is org/apache/ivy/ant/antlib.xml.

The next step is the make up a namespace. I say makeup, because you can use whatever you like. However it is good to use the form antlib:java package. This way it will work when you one day remove the external library from your project and add in the ant-library directory on your local computer (then you wont need the taskdef). For Ivy the namespace is antlib:org.apache.ivy.ant. For more information see http://ant.apache.org/manual/CoreTypes/antlib.html.

Next is to write the build.xml file:
<project name="ivyhgtest" xmlns:ivy="antlib:org.apache.ivy.ant">
 
  <target name="resolve" description="--> retreive dependencies with ivy">
    <path id="ivy.classpath">
      <fileset dir="lib/" includes="ivy*.jar" />
    </path>
  
    <taskdef resource="org/apache/ivy/ant/antlib.xml"
      classpathref="ivy.classpath"
      uri="antlib:org.apache.ivy.ant" />
 
    <ivy:retrieve />
  </target>
</project>

The content of xmlns:ivy="antlib:org.apache.ivy.ant" must match the uri="antlib:org.apache.ivy.ant". The XML namespace xmlns:ivy is arbitrary but it must match the task namespace <ivy:retrieve />.Choose a good namespace that maches your external feature (only one word).

Note how I used a wildcard to reference the ivy-2.1.0-rc2.jar. It can be good if a new version of the jar is published.

I can now access all the tasks defined in the antlib.xml file with the ivy namespace.

08 October 2009

Mini evaluation of Maven repository managers

If your company is using some kind of dependency management system (for example Ivy or Maven) it is strongly recommended that you use a company-repository. This repository has two purposes:
  • Cache of third party libraries to speed up fetching (it really makes a difference!)
  • Releasesof your own software to it is available to the others in the company.
The simplest repository is just a directory, perhaps mounted via an external file system. But this has limitations. A better alternative is to use a repository manager. Such a manager is a layer above the repository that provides all kinds of features (searching, uploading, auto-purging to save disc space etc).

As of today there are three Maven repository managers (see http://docs.codehaus.org/display/MAVENUSER/Maven+Repository+Manager+Feature+Matrix): Archiva, Artifactory and Nexus.

I have made a small evaluation (I have tried them for about 30 min each). I must say that they are all very similar. They will all probably work for you. They are all web based.

My favorite for now is Nexus. This is because the GUI is rather simple, you can create virtual groups of repositories.

.

07 October 2009

The ultimate project structure

Well I don't think there is an ultimate project structure. But I think the default project structure that is used in Maven is very good. The basic for a java project looks like this:


A common objection of this is why so many levels? Why src/main/java when src is enough? I will try to break it down:

Why main/test?
You probably have both main code and test code. A separation of those is not bad. You will perhaps have documentation. Then you could create a src/doc directory.

Why src/xxxx/java?
You can probably have other languages. Or perhaps things that are not languages for example "resources".

Ivy + Ant versus Maven

I  have tried both Maven and Ivy + Ant enough to make a simple comparison. Ivy is a dependecy management system. It means in other words that you don't have to check in the binary jar files of dependencies you have in your project. Maven is also a dependecy management system but it is much more then that. Maven also has project management and build features.

Maven usage has increased in the open source community. But there are some problems:
  • Maven is complex and hard to learn. This is from my own experiance. Even though it is rather simple to get a basic project to "talk" Maven, it becomes very hard when you have some special thing (for example some strange build task).
  • Documentation is lacking. Many of the features in Maven is not documented good enough on the official Maven page.
  • Complex syntax. This is perhaps of lesser importance when there is a good Eclipse plugin. But it still is a hassle to insert 11 lines in the Maven project descriptor file just to change the Java Compile level.
Ivy also has some problems:
  • You have to write Ant files (or some other script) to build your system. Maven can build and test by a declaration-language.
  • The plugin to find dependencies for Ivy in Eclipse is... well non-existent. Maven has a find and browse dialog to find dependencie.
 If you only need the dependecy management feature I would recommend to go with Ivy. But if you need the other features only found in Maven perhaps that is the way to go.

05 October 2009

Windows Experience Index for "Primary hard disk" with SSD


With my Intel X25-M G2 SSD I get 7,8 out or 7,9 in the Disk data transfer rate test with Windows 7.

  The number does not say that much except that it is fast compared to others on the market.

Tried an SSD disc in my laptop

I have now tried the Intel X25-M G2 80GB SSD disc in my Laptop.


I previously had a Samsung 5400 rpm disc. The difference is notable! On a subjective level the computer feels more responsive. Opening and closing programs goes much faster! On a objective level I compared with my previous disc.

Start Windows 7 (from power on button), login to active directory domain, launch mysql, and skype
X25-M G2: 45 s
Samsung 5400 rpm: 95 s
Worth mentioning is that the BIOS startup process takes 12 s.

Clean and compile a big Java project in Eclipse (first time)
X25-M G2: 50 s
Samsung 5400 rpm: 116 s

Clean and compile a big Java project in Eclipse (second time)
X25-M G2: 22 s
Samsung 5400 rpm: 40 s
Clearly the OS disc cache in RAM has a big impact the second time.

It is clear that disc intensive operations takes half the time. Sometimes even faster. The booting of Windows is such a example (if the BIOS startup process is subtracted). The reason for this (I'm guessing) is that performance degrades on a spinning drive when several processes accesses the disc in parallel.

I hope that Windows 7 can use the TRIM command so that performance of my SSD disk doesn't degrades over time as the disc fills.

21 September 2009

Looking for a replacement for VMWare Server 1

I've been looking for a replacement for VMWare Server 1 for a while now. I'm having serious problem with time drifting in VMWare Server 1. I've previously tried VMWare Server 2 but it was really bad (for reasons I now have partially have forgotten). Oh, I remember that one problem was that not all of the virtual machines was compatible with VMWare Server 2. They just didn't start.

Some of my colleagues have tried Sun VirtualBox. I first though that this was a client-only virtual machine software. But after reading the user manual I have found a way to use it like VMWare Server. VirtualBox can be run on a server computer in a headless mode (read: only command line, no grapchics). For example:

> VBoxHeadless -s "Lennart test" -p 5000

This starts the already created virtual machine registered as "Lennart test" and listens for remote desktop connections on port 5000. Then I use my favorite remote desktop program to connect to the virtual machine (for example the build in Windows Remote Desktop Client).

Creating and managing virtual machines can be performed both with a GUI of command line. If you would like use a GUI and have a Linux server I recommend using some X software (for example the one included in  Cygwin).

I have not tried running graphics intense applications yet but my initial guess is that it will be a bit slower then the VMWare equalivance- VMWare Server Console. But I can be wrong...

18 September 2009

Windows Media Player and MMS (streaming) problems

Windows Media Player has some serious problems playing web radio via Microsoft Media Services (mms://). Sometimes it works and then suddenly "baaam" it doesn't. The problem is not with the server because it works with VLC.

I'm not the only one having problems, I know several other persons that has/had problems with it.

My current problem is with Windows Media Player 11. When opening a mms:// url in the external application the GUI freezes and the CPU gets high. When opening an url with embedded player (in Internet Explorer or Firefox) the player status is "Ready" and nothing happens when pressing the Play button:


I have tried various settings in Windows Media Player -> Tools -> Options -> Network. I have checked and unchecked the checkboxes but nothing works.

Update: I avoided the problem by installing WinAmp. Not only does the MMS streaming work very fast it is also possible to save play-lists with my favorite radio channels.

16 September 2009

JDK breakpoint problem fixed in update 16

As I previously posted I found a break point problem when running JDK 1.6 update 14. According to the release notes of JDK 1.6 update 16 this problem is now fixed. I'm happy that Sun fixed the break point problem and that is was so important so it got it's "own" update.

I have not yet tried the new JDK but I'm going to. I'm quite eager to try out the new Garbage First (G1) Garbage Collector that was introduced in update 14.

07 September 2009

Which license should I choose for an open source project

I was thinking on starting a (very, very, small) open source project. Just to get the hang of how it could work. I was thinking on hosting the project on Google Code. When creating a project there is a list of licenses to choose from. Even though Google Code has narrowed down the list it still is a list of 9 licenses:

So what to choose? Well this blog gave me some thoughts: http://google-opensource.blogspot.com/2008/05/standing-against-license-proliferation.html. It basically narrows downs the list to two: Apache License or GPLv3. And from there it is much easier to choose. As a software developer and not a lawyer I don't have the knowledge (or time!) to understand what the different licenses means. I'm going to have to trust someone else on this.

In my case I think the Apache License is probably the way to go. Since I'm working in a commercial company, I know that any of the GPLv3 and GPLv2 license is a big no-no. So if any company are going to use my software it must be the Apache License. And for me it is ok if another company makes profit on my code without giving back to the community.

06 September 2009

How to get faster interet connection when behind a Netgear router

I have a good internet connection that can devliver about 93 Mbit/s traffic when a computer is directly plugged into the wall. When sitting behind my Netgear WGT624 v3 router my Windows XP computer can get about 45 Mbit/s and my linux computer can get about 60 Mbit/s. I'm guessing the difference of because Linux has a better TCP/IP implementation.

When tring to find my DNS problem I tried some different settings on my router. One setting gave me a speed effect:
 
I disabled the SPI Firewall (the settings is found on the WAN Setup page). Now this is probably not so good from a sercurity perspective...
After this setting my Windows computer increased about 5-10 Mbit/s . There was no clear increase for my Linux computer.

Problem with DNS lookups on my home internet connection

I noticed that when I surfed from my home internet connection I got several timeouts and other problems when surfing. When using nslookup several times in a row (like 20) some of the queries was dropped. This is very frustrating. Here are my theories on what the problem might be:
  1. My ISP DNS
  2. My ISP special DNS-forward page for people who doesn't have an account. For every customer that does not have an account and plugs in a computer in the wall socket in my building a sign-up page is presented in the web browser.
  3. My own router
Number 1 is out of the question because I have also lost queries when using a OpenDNS server (208.67.222.222).

 My best guess so far is alternative number 2. It is a rather complicated feature requiring to catch all packets going to port 53 (I have lost DNS quires for both TCP and UDP). It could also be my router giving me the trouble. But I did not change anything in the router settings since when the problem started.

I have some problem error searching this. For that I need a computer outside my network that I has root access to so I can set up a server on port 53.

05 September 2009

I'm going to give Mercurial a go!

I watched an interesting video from Google IO 2009: http://code.google.com/events/io/sessions/DoYouBelieveUsers.html. They where talking in general on how to get a higher usability (addressed to an engineering audience (read: no Human Computer Interaction designers)). They had a graph where they placed different programs where the left of the graph where users with little experience and to the right users with extensive experience. I cut and pasted the Git vs. Mercurial comparison:
Their point was that (so fare) Mercurial is easier to use than Git.

In my opinion usability is everything! This means that I will give Mercurial a go and see if that fits my needs. If it doesn't I will perhaps give Git a go.

02 September 2009

The quest for a good version control software

One issue that has been bothering me for a couple of year now is version control software. I have experience (more than some hours) with 3: ClearCase, CVS and Subversion (SVN).

CVS
Good things
  • Fast
  • Wide spread (many knows how to use it and it has 3rd party tools (for example Eclipse plugin integrated in Eclipse)).
Bad things
  • Does not version directories. Probably that biggest design flaw.
Subversion
Good things
  • Version directories
  • Relatively wide spread.
Bad things
  • No native branches or tags. I mean what were they thinking? "Everything is a directory"? Is there someone that really likes and uses this so called "feature"? Everybody I know follows the de-facto standard "trunk", "tags", "branches".
  • Late double directed merge tracking (available only after 1.5). A bit too late I might say...
ClearCase
Good things
  • The mother of version control. Has it all. Did it all right.
  • Version tree and merge tracker. Two of the best tools a developer knows.
Bad things
  • Is so expensive that it is ridiculous. 44000 € excluding consumption tax. Can anybody afford it?
  • It has some learning curve to learn how the config spec works

My next version control software?
I first used clear case in 2004. It was some year old then so it has probably been working the way I'm used to a little bit longer. I really cannot understand how the open source world has not been able to create a better (or equally good) version control system in this time.


I have been thinking on which version control software I should use. I really don't know.  I'm leaning towards Git or Mercurial but I'm not sure. Here is my feature wish list
  • Tags and branches should exist natively and all the tools should have good support for them.
  • There should exist a good version tree that can display all the branches and the merges between the branches.
  • There should exist a merge tool so branches can easily be merged
  • It should be free (I don't care if it's open source, It should not cost me any money)
  • It should exist a plugin in Eclipse for it (and preferably in other IDE:s as well).
  • It should be wide spread so there is a high acceptance and support for it.
  • Focus on usability. Usability is so important! And developers has a tendency to forget that...

Open cmd prompt here plugin for Eclipse

A really nice thing in Microsoft Power toys is the Open Command Window Here (found at http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx). Vista has a similar feature built in when shift+right-clicking a folder.

When developing in Eclipse I have missed this feature. But with this plugin: http://code.google.com/p/tarlog-plugins/ it is possible! (Download the jar file and put it in Eclipse dropins-folder and restart Eclipse):
 
 

01 September 2009

Using LDAP as authentication in JAAS with Java

A simple example of how to use LDAP as an authentication back end in Java when using JAAS:
DefaultConfig {
 com.sun.security.auth.module.LdapLoginModule REQUIRED
     userProvider="ldap://myhost:myport/"
     authIdentity="uid={USERNAME},ou=users,ou=system"
     useSSL=true
     debug=true;
};

Change the myhost and myport. The above example assumes that the users exist in the LDAP tree in ou=system,ou=users with the uid set to the user name. I'm not sure on how the file would look like if the LDAP server is a Windows Active Directory (perhaps this will be a future blog entry).

More information on the LDAP JAAS module: http://java.sun.com/javase/6/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/LdapLoginModule.html

How to make Java SSL trust a certificate

I used the LdapLoginModule via JAAS to authenticate a user. All worked fine when using plain text. But I wanted to use SSL to encrypt the trafic to my LDAP server but got this exception:
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

The problem is that my SSL certificate on my LDAP server is self signed and my client (the one running the LdapLoginModule) cannot trust it.

To solve this problem download the certificate (I used Firefox, but Internet Explorer could probably also be used).
  1. Surf to https://myserver:myport/
  2. Press "I understand the risks" (or something like that, I'm using a Swedish firefox)
  3. Press "Add an exception"
  4. Press "Get certificate"
  5. Press "Show"
  6. Press "Details"
  7. Press "Export"
  8. Save it to disc somewhere. For example "mysslcertificate.cer"
Use the keytool command supplied with the JDK:
keytool -import -file mysslcertificate.cer -keystore mykeystore

The certificate is stored in the keystore named mykeystore in the current directory (will be created if it does not exist).

When launching the java application set the following VM parameter: javax.net.ssl.trustStore. For example:
java -Djavax.net.ssl.trustStore=mykeystore MyJavaProgram

Note: The keytool command demand that a password is set for the keystore. But the password is not required to read the certificates in the keystore (probably because certificates are considered public).

The following blog post gave me the initial help of how to do this http://blogs.sun.com/andreas/entry/no_more_unable_to_find.

30 August 2009

A slightly better blogger post editor

One of the bad things about Blogger is the bad post editor. Today I found out I can choose a slightly better editor. Enable it in Basic settings:
The editor is bit better on image handling. But I'm not quite happy with it since I cannot choose 100% image size. 100 % is the only size that screen shoots look nice.

One thing I still miss is a feature to include source code.

19 August 2009

Time to upgrage eclipse when @Override does not work

When using an old version of Eclipse 3.2.1 from September 2006 I got this type of error:
The method actionPerformed(ActionEvent) of type NAME_OF_CLASS_AND_METHOD must override a superclass method

The class had this definition:
@Override
public void actionPerformed(ActionEvent e) {

The problem was solved when upgrading to a newer version of Eclipse (3.5). I'm guessing the support for the @Override annotation was a bit limited.

17 August 2009

Started with Android

I've started to learn to program Android. From about two weeks of programming I have one good thing and and bad thing to say:

Good: The programmer has a great deal of freedom. For example I can create a program to replace the Home/Start screen application. I can also access all the hardware (for example GPS, SMS, sensors).

Bad:
Not to good documented yet. There are many classes in API that lacks documentation and you have to guess how to do things.

More info on the Android developer site.

03 July 2009

Linux or Windows on my Notebook?

A friend had a netbook with Ubuntu Netbook Remix. It looked like a nice OS that simplified the netbook experience.

When I finally bough my own netbook I was more or less convinced that I would throw out the existing operative system and install Linux on it. However I have not felt the need to do so. My notebook is fast enough to run Windows XP Home. With the hibernate mode the netbook is relatively fast to boot/start. In the end I don't feel like going through the hassle of reinstalling anything.

If the it had come pre-installed with Linux my feeling is that I would have kept that OS.

29 June 2009

Micro USB as a standard on mobile phones

Moble Manufacturers argee to use Micro USB interface to connect to new mobile phones. So what is Mirco USB? Well the answer to that was not so simple to find. One of the pictures on Wikipedia that first claimed to show a Micro USB does not look like it...

I downloaded the USB 2.0 specfication and found this picture:


There is also a micro-A plug. An USB A-type plug are often found at places that gives power (for example your computer). A B plug can most often be found at place that draw power (for example your mobile phone).

To confuse things a bit more the connectors in the USB 3.0 specifications looks "half" different. And in the USB 3.0 specification there is also a AB-plug that can take both a A and B. But the USB 3.0 specification uses more wires so it is a completely different story.

Whats the lesson here? First of all don't always trust Wikipedia! Second, the mist behind an article on a new site can take some time to disperse.

27 June 2009

My Compaq Mini

I've bought a Compaq Mini 700 series (730eo) netbook computer. The price was 3000 Swedish kronor (SEK). I've used it for 2 weeks now and gotten myself an opinion of how it is so here is my review.

Pros:
  • Small and light. I put my computer on my kitchen scale and it was 1066 grams. The trend now is bigger netbooks but this is still in the smaller range.
  • Good keyboard. I've tested a bunch of netbooks at my local store and this had one of the best keyboards
  • Good value for money.


Cons:
  • Noisy fan and bad fan control. The fan really makes some noise and the fan control is not that good at adapting the speed of the fan. I have solved this problem and I'll post another blog post about it later
  • Slow hard drive (4200 RPM). But it is good enough for me. I can even develop Java programs in Eclipse with it. So it does not bug me that much.
  • Only a 3 cell battery that lasts 2 hours (3 max). I guess that is a compromise for its small wiegth
  • Low quality web cam. Other netbooks have much better (I tried these in the store as well). But that does not bother me that much.

Here is a picture of the keyboard layout (Swedish):


One thing I really like with a netbook is that I can move it around my apartment so easy. I read websites at the kitchen table, use it at the couch when I watch tv and can have it in my lap when I sit at the balcony.

Update: Here is how I fixed the fan control: http://blog.lesc.se/2009/10/how-to-improve-fan-control-on-hpcompaq.html

26 June 2009

Resize a dialog with a multi line text field that has fixed width

stackoverflow.com is a great forum to ask questions. Sometimes an answer to a question you ask there will trigger yourself to think in new ways so you can answer you own question. This happened to me in the problem with the dialog with the multi line text field with fixed width: http://stackoverflow.com/questions/1048224/get-height-of-multi-line-text-with-fixed-width-to-make-dialog-resize-properly

Base class for ChangeEvent/ChangeListener mangament

If you have written a data model you often want to be able to notify others about the change in the model by using some kind of listener. The Java swing ChangeListener interface is one choice of listener to choose. But the management of a listener list (adding, removing and notification of listeners) is a bit of a hazel to write code for each time you have a model. I searched the Java API but could not find any base class of helper class to solve this. The closest I got was the EventListenerList class. By using this class I've written my own base/helper class:
import javax.swing.event.*;

/**
* Convenience class to notify others of changes
* Can be used as a base class or as an aggregate/helper
*/
public class ChangeEventDispatcher {

protected EventListenerList listenerList = new EventListenerList();
protected ChangeEvent changeEvent = null;

public void addChangeListener(ChangeListener l) {
listenerList.add(ChangeListener.class, l);
}

public void removeFooListener(ChangeListener l) {
listenerList.remove(ChangeListener.class, l);
}

/**
* Called by by child/wrapper class to dispatch a Change event
*
* Code is copied from EventListenerList documentation and adapted to ChangeEvent
*/
public void fireChangeEvent() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {
if (listeners[i]==ChangeListener.class) {
// Lazily create the event:
if (changeEvent == null)
changeEvent = new ChangeEvent(this);
((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
}
}
}
}

An example of how to use this class:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

@SuppressWarnings("serial")
public class ChangeTester extends JFrame {

private JButton increaseButton = new JButton("Increase");
private JLabel valueLabel = new JLabel();

private ValueModel model = new ValueModel();

public ChangeTester() {
setLayout(new FlowLayout());
add(increaseButton);
add(valueLabel);

//Change the model when pressing the button
increaseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
model.increaseValue();
}
});

//The label should be updated when the model is changed
model.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
updateLabelValue();
}
});

updateLabelValue();
pack();
}

private void updateLabelValue() {
valueLabel.setText(Integer.toString(model.getValue()));
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new ChangeTester();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}

/**
* Simple model that extends the ChangeEventDispatcher
* to get listener list handling
*/
public static class ValueModel extends ChangeEventDispatcher {
private int value;

public void increaseValue() {
this.value++;
fireChangeEvent(); //Notify listeners
}

public int getValue() {
return value;
}
}
}

Quick close a dialog by escape key

I Windows there is a convention that a dialog is closed (and canceled) if the user pressed the Esc key. There is also a convention that pressing Enter closes (and confirms) the dialog. In Java there is no default implementation of this.

So we need to implement this ourself. A naive approach would be to add a KeyListener to the dialog. This will not work if the keyboard focus is in a nested component (for example a text field). So how about to add a key listener to both the dialog and all its subcomponents? Well this would work, and I have successfully implemented this using a ContainerListener in the dialog so any components added in the dialog also gets a key listener. But there is a simpler way: the key bindings way!
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

@SuppressWarnings("serial")
public class QuickCloseDialog extends JDialog {

public QuickCloseDialog() {
initCloseListener();
add(new JTextField("A text field"));
pack();
}

private void initCloseListener() {
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE , 0), "close");
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "close");
getRootPane().getActionMap().put("close", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
dispose();
}
});
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JDialog dialog = new QuickCloseDialog();
dialog.setVisible(true);
}
});
}
}

With a key binding you don't have to care where the keyboard focus is.

Update: My solution is probably not thread safe, this alternative solution is: http://stackoverflow.com/questions/642925/swing-how-do-i-close-a-dialog-when-the-esc-key-is-pressed

25 June 2009

Static import and GroupLayout tip for Eclipse

I you like GroupLayout and use Eclipse this might be a good tip:
Eclipse is not that good with static imports but it is possible to make the content assist suggest and automatically add static imports. Go to Preferences -> Java -> Editor -> Content Assist -> Favorites and add the types javax.swing.GroupLayout.*, javax.swing.GroupLayout.Alignment.* and javax.swing.LayoutStyle.ComponentPlacement.*:

It is now possible to use the Content Assist (Ctrl + Space) to get help when writing the GroupLayout properties:

Eclipse 3.5 Galileo - a first impression


I installed Eclipse 3.5 (or Galileo as it is called) today.

My first impression is that it is similar to 3.4. One thing that i noticed is that cancellation of building a Java project is working better in 3.5. The Java editor is updated and writing code is a bit different. For instance I got a bit annoyed by how parenthesis are inserted. But I guess that I'll quickly get used to it.

The "editor" for Team Synchronizing compare view is updated so it now is more like a Java editor. I'm note sure I will edit code in a compare view just now. I still miss the Ctrl + Shift + T shortcut in Team Synchronizing.

24 June 2009

Key bindings is sometimes to prefer over KeyListener

In Java (Swing) there are KeyListener:s and key bindins. KeyListerner is the classical way of catching events from the keyboard. But inn some situations the key bindings feature is preferred:

  • If you want to catch (only) the CTRL key

  • If you want to catch keys regardles of where the user has focus


For example if you are developing a tetris game this code can be used to get the Left arrow key:
        getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
getActionMap().put("left", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
engine.moveBrickLeft();
}
});


To catch the control key use KeyStroke.getKeyStroke("ctrl pressed CONTROL") and KeyStroke.getKeyStroke("released CONTROL")

More information at http://java.sun.com/docs/books/tutorial/uiswing/misc/keybinding.html

04 June 2009

Hint: Set a good comment on your ssh key

Using a ssh key to login into a ssh account is really good. You don't have to know the passwords of the accounts you are logging into, only the password on the ssh key (if any). If you use a password on the key the password can be cached (for example using Pageant).

When creating a ssh key I really recommend that you set a good comment. For example using puttygen enter a "Key comment":


This should be included in the key:
  • The identity of you (for example your e-mail)

  • The computer you used to create it on (for example your Windows computer name)

  • The date you created the key

This way it will be so much easier when someone should clean up the keys from .ssh/authorized_keys. The identity is good if there are many users for the same ssh account (common in some corporations). The computer name and date is useful when you know that you have lost the key (for example because of hard drive crash), of when a security exploit has been published and the key is considered unsafe.

03 June 2009

Clean your laptop from dust periodically

A colleague of mine had some problems with very high temperatures in a laptop. The temperatures was about 71 degrees Celsius at idle and 91 degrees Celsius a full load. (It was a HP laptop with some kind of dual core). I opened the laptop up and took out the fan. In the space between the outlet of the fan and the heat pipe there was a rather thick layer of dust. I cleaned it and now the temperatures where about 5 degrees lower.

I my colleague's case there is probably something else wrong with the fan (or computer), but it is clear that a little bit of dust defensively can have an effect on the temperature (and sound level).

02 June 2009

Breakpoint problem with Eclipse 3.4 and JDK 1.6 update 14

I had problems that some break points did not take in my Eclipse 3.4.1 when I updated from JDK 1.6 update 12 to JDK 1.6. update 14. Well some breakpoints took, but none in the AWT thread. It could be a coincidence, I don't know.

I updated to Eclipse 3.4.2 but that did not solve the problem.

I had to downgrade to JDK 1.6 update 13 to solve the problem. It was enough to choose another JDK for my specific project. Eclipse itself could run update 14.



I searched the Internet but could not find any official bug either in Java or Eclipse. Anybody out there got more information?

Update: the problem is now fixed: http://blog.lesc.se/2009/09/jdk-breakpoint-problem-fixed-in-update.html

01 June 2009

Java VisualVM, a better JConsole

A new tool is available for Java developers: VisualVM. JDK 1.6 update 7 and later comes bundled with this new tool. The executable (jvisualvm) can be found in bin or the tools directory.

The first thing I recommend doing is to install the MBean plugin. With this plugin VisualVM gets the mbean feature found in JConsole. In fact I never uses JConsole anymore since VisualVM has all the features, and more.

To install the plugin select Tools -> Plugins -> Available Plugins. Check the VisualVM-MBeans and press install:


Now attach to a process by clicking on it in the list. If the process is on another computer use the Remote-node.

This is how the MBeans tab looks like:


This is how the monitor tab looks like:

Btrace – a debugging tool with bugs

I tried Btrace (http://btrace.dev.java.net/) it is a debugging tool for Java. I have often been in the situation that a bug has been found in a production system. The production system must run even as I try to find the cause of the bug. That means the (java) program cannot be restarted to add some remote debugging ports or some other start-up options. This is where Btrace comes in. With Btrace you can write small script-like java code fragments so the program can be traced. Btrace accomplishes this by dynamically instrumenting the code.

Let’s take an example. Let’s say your production system tries to find numbers in the Fibonacci sequence:
import java.util.ArrayList;
import java.util.List;

public class FibonacciService {
private List<Integer> list = new ArrayList<Integer>();

public FibonacciService() {
//First two numbers in Fibonacci sequence.
list.add(0);
list.add(1);

new Thread(new Runnable() {
public void run() {
while (true) {
calculateNext();

try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "calc-thread").start();
}

private void calculateNext() {
int oldOld = list.get(list.size() - 2);
int old = list.get(list.size() - 1);

int newNumber = nextFibonacci(oldOld, old);
list.add(newNumber);
System.out.println(newNumber);
}

private static int nextFibonacci(int oldOld, int old) {
return oldOld + old;
}

public static void main(String args[]) {
new FibonacciService();
}
}
This Btrace code fragment will print out a trace string to the Btrace console every time the method calculateNext is entered:
  @OnMethod(
clazz="se.lesc.btrace_example1.FibonacciService",
method="calculateNext"
)
public static void onStartCalc() {
println("Starting to calculate new number...");
}
Btrace code fragments can be attached to running java processes in two ways. Either use the command line “btrace” command or use a plugin to Suns Java VisualVM. Java VisualVM is like a Jconsole on steroids. It s is included in the JDK bundle since 1.6 update 7 (or something like that). You will find it in the JDK installation directory (either in tools, or in bin). Follow these instructions to add Btrace capabilities to VisualVM: http://btrace.dev.java.net/visualvm_uc.html.

When installed, right click on the process and select “Trace application…”.


Then paste (or write) the code fragment and press Start. When pressing Start it takes a few seconds for the code to be instrumented. The output is shown in the console below the code fragment.


A little bit more advanced fragment that will print the method arguments when the method nextFibonacci is called:
  @OnMethod(
clazz="se.lesc.btrace_example1.FibonacciService",
method="nextFibonacci"
)
public static void onNextFibonacci(int oldOld, int old) {
print("Input values are ");
print(str(oldOld));
print(" and " );
print(str(old));
println();
}
A fragment that prints out the return value when the nextFibonacci method is returned:
  @OnMethod(
clazz="se.lesc.btrace_example1.FibonacciService",
method="nextFibonacci",
location=@Location(Kind.RETURN)
)
public static void onNextFibonacci(int returnValue) {
print("Result is: ");
print(str(returnValue));
println();
}
There are some restrictions on how advanced the fragments can be. Because the code must never get stuck loops are not allowed. Creating objects arbitrary is not allowed nor calling any methods (except from the BTraceUtils class).

Everything is not good about Btrace:
  • Slow web page. Perhaps not Btrace fault. The entire *.dev.java.net servers are so slow that it is annoying.

  • There is always a risk when instrumenting the code. Out of about 30 runs I had 3 unexpected crashes of the JVM that I can remember. It is with other words somewhat of a risk to run Btrace in a production environment.

  • Some features don’t work. For example I tried to use a regular expression to match a method but every time I got java.lang.VerifyError (Edit: I've now submitted a bug report for it: https://btrace.dev.java.net/issues/show_bug.cgi?id=41). Another example is that I tried to get a reference to the current object in side a method, but it did not work.

  • The documentation is need improvment. For example Btrace uses some kind of signature matching to find methods with arguments. How this matching is done is not documented. Such a central feature is really important to have documented.

  • The project does not look to be actively developed. The last implemented ticket was submitted the 22th of February in 2009. Even critical and easy to fix problems is not fixed. For example in my opinion the bug about that Btrace cannot be run on Windows when there exist a space in the path should really be fixed. Edit: the project apparently has active development, based on the comments on this blog entry. :)

26 May 2009

Work with multiple workspaces in Eclipse

A workspace in eclipse consists of some projects and settings (for example code formatting). If you are working on different things it is useful to have multiple workspaces. My recommendation is that you create a shortcut for each workspace. In Windows create a new shortcut and set the target to:
"C:\Program Files\eclipse\eclipse.exe"
-data "C:\Documents and Settings\lennartsc\My Documents\workspace_test"
-showlocation Test
-vm "C:\Program Files\Java\jdk1.6.0_12\bin\javaw" -vmargs -Xmx900M

The data parameter is the location of your workspace. The showlocation parameter is useful when you Alt-Tab between different Eclipse instances so it is possible to distinguish between them. The vm parameter is not needed, unless you want to specify the JVM (useful if you are using Maven). The last parameter, Xmx is good is you want Eclipse to have more memory.

29 April 2009

Ask a programming related question on stackoverflow.com

One of the best "programmer-asks-questions-formus" I know is stackoverflow.com. It is just like the old experts-exchange, except it is free and it contains no commercials. It is also very simple to ask questions. Just log in with your openID and ask a question!

I have been a reader for some months now but today I finally asked my first question: http://stackoverflow.com/questions/802612/how-to-write-a-null-safe-compare-in-pure-sql.

27 April 2009

Bouncing balls and collision detection

One of the first assignments on the first Java course on my computing science program was to create a program with bouncing balls. The balls should be able to bounce of the walls. There was no requirement for the balls to bounce of each other.

Is there a simple model for bouncing two balls? One approach is to only look at one ball at a time and consider the other ball as a rigid object. The in angle should be the same as the out angle:


The result: a very simple ball simulation program:

There are of course some bugs. Like that the balls can escape... :) Try it out! If the balls get stuck drag them into a new position. Balls version 0.0.1

21 April 2009

GroupLayout vs JFrame - the content pane problem

I really like the "new" GroupLayout that was introduced in JDK 1.6. It originates from Netbeans GUI editor. It is a layout that groups components in logical groups. For a tutorial of GroupLayout see http://java.sun.com/docs/books/tutorial/uiswing/layout/group.html.

The JFrame is a sneaky Swing implementation. It has a content pane where the main things of your application should be in. But is has convenience methods (for example add() and setLauout()) that actually invokes call in the content pane. This might make you believe you can use is just as a normal container when using GroupLayout. The naive approach is to do this:
public class MyFrame extends JFrame {
GroupLayout layout = new GroupLayout(this);
...

This will unfortunately not work. The GroupLayout is fooled to control the JFrame, but in fact it should control the content pane inside the JFrame. The solution is instead to to this:

import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class GroupLayoutJFrameExample extends JFrame {

private GroupLayout layout = new GroupLayout(this.getContentPane());

public GroupLayoutJFrameExample() {
initLayout();
pack();
}

private void initLayout() {
this.getContentPane().removeAll();
this.getContentPane().setLayout(layout);

JLabel helloLabel = new JLabel("Hello!");

layout.setVerticalGroup(layout.createSequentialGroup()
.addComponent(helloLabel)
);

layout.setHorizontalGroup(layout.createParallelGroup()
.addComponent(helloLabel)
);
}

public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new GroupLayoutJFrameExample();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

16 April 2009

How to merge-copy a Java bean

Problem: you have one template object and one target object. They are of the same class and is a standard Java bean. You would like to copy and merge all of the properties from the template object to the target object, but only for attributes that is null in the target.

Solution:
  public <M> void merge(M target, M destination) throws Exception {
BeanInfo beanInfo = Introspector.getBeanInfo(target.getClass());

// Iterate over all the attributes
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {

// Only copy writable attributes
if (descriptor.getWriteMethod() != null) {
Object originalValue = descriptor.getReadMethod()
.invoke(target);

// Only copy values values where the destination values is null
if (originalValue == null) {
Object defaultValue = descriptor.getReadMethod().invoke(
destination);
descriptor.getWriteMethod().invoke(target, defaultValue);
}

}
}
}


Test code:
  @Test
public void testMerge() throws Exception {
Person templatePerson = new Person("Template", 20);
Person targetPerson = new Person("Johan", null);
merge(targetPerson, templatePerson);

assertEquals("Johan", targetPerson.getName());
assertEquals(20, targetPerson.getAge());
}

private static class Person {
private String name;
private Integer age;

public Person(String name, Integer age) {
this.name = name;
this.age = age;
}

public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
}

09 April 2009

Tetris in Java (J2ME) MIDP 1.0

I found my old Tetris game i developed in 2004. At that time I had a Ericsson 610i phone. It supported MIDP 1.0. Nowdays the specification for J2ME have evloved quite some much. I tried to play the game on my 3 years old SonyEricsson W810. It still works! Well sort of, I noticed that there is bug so that exiting is a bit harder than it used to...


If you want to play my Tetris direct your phone to this URL: http://lesc.se/tetris/Tetris.jad.

05 April 2009

A simple under clock experiment

I have one computer in a closet. It is a server that is always on. I did a little experiment to see how under clocking the CPU would affect temperature and power consumption.

The CPU is a AMD Atholon 3500+ that clocks in a 2,2 GHz. During the test I had 2 Gb of memory and one Samsung 80 Gb, 2 platter 2,5 inch hard drive (a notebook hard drive).







SpeedPower consumptionTemperature (high fan speed)Temperature (low fan speed)
2,2 GHzLoad36 W5260
Idle26 W3940
1,8 GHzLoad35 W4653
Idle25 W3739
1,0 GHzLoad30 W4244
Idle25 W3739

I did not modify the VCore anything during these tests, I let the motherboard decide that. My guess is that with more tuning of the VCore I would have gotten better numbers.

31 March 2009

BeanTableModel as a TableModel for Swing

I've written lots of models for JTable's in Swing. Often I have to re-learn how Swing TableModel work each time. There is also often the case that one row in the table represents a Java object with bean getters and setters. Inspired by the Spring bean configuration I wrote TableModel that uses a bean class as a base.

It makes it easy to create a table and sets up the default CellEditors automatically and can get and set data from the cell in the GUI to the Java object.


BeanTableModel:
package se.lesc.beantablemodel;

import java.beans.*;
import java.util.*;

import javax.swing.table.AbstractTableModel;

/**
* A table model where each row represents one instance of a Java bean.
* When the user edits a cell the model is updated.
*
* @author Lennart Schedin
*
* @param <M> The type of model
*/
@SuppressWarnings("serial")
public class BeanTableModel<M> extends AbstractTableModel {
private List<M> rows = new ArrayList<M>();
private List<BeanColumn> columns = new ArrayList<BeanColumn>();
private Class<?> beanClass;

public BeanTableModel(Class<?> beanClass) {
this.beanClass = beanClass;
}

public void addColumn(String columnGUIName, String beanAttribute,
EditMode editable) {
try {
PropertyDescriptor descriptor = new PropertyDescriptor(beanAttribute,
beanClass);
columns.add(new BeanColumn(columnGUIName, editable, descriptor));
} catch (Exception e) {
e.printStackTrace();
}
}

public void addColumn(String columnGUIName, String beanAttribute) {
addColumn(columnGUIName, beanAttribute, EditMode.NON_EDITABLE);
}

public void addRow(M row) {
rows.add(row);
}

public void addRows(List<M> rows) {
for (M row : rows) {
addRow(row);
}
}

public int getColumnCount() {
return columns.size();
}

public int getRowCount() {
return rows.size();
}

public Object getValueAt(int rowIndex, int columnIndex) {
BeanColumn column = columns.get(columnIndex);
M row = rows.get(rowIndex);

Object result = null;
try {
result = column.descriptor.getReadMethod().invoke(row);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}

public void setValueAt(Object value, int rowIndex, int columnIndex) {
M row = rows.get(rowIndex);
BeanColumn column = columns.get(columnIndex);

try {
column.descriptor.getWriteMethod().invoke(row, value);
} catch (Exception e) {
e.printStackTrace();
}
}

public Class<?> getColumnClass(int columnIndex) {
BeanColumn column = columns.get(columnIndex);
Class<?> returnType = column.descriptor.getReadMethod().getReturnType();
return returnType;
}

public String getColumnName(int column) {
return columns.get(column).columnGUIName;
}


public boolean isCellEditable(int rowIndex, int columnIndex) {
return columns.get(columnIndex).editable == EditMode.EDITABLE;
}

public List<M> getRows() {
return rows;
}

public enum EditMode {
NON_EDITABLE,
EDITABLE;
}

/** One column in the table */
private static class BeanColumn {
private String columnGUIName;
private EditMode editable;
private PropertyDescriptor descriptor;

public BeanColumn(String columnGUIName, EditMode editable,
PropertyDescriptor descriptor) {
this.columnGUIName = columnGUIName;
this.editable = editable;
this.descriptor = descriptor;
}
}
}

A test class:
package se.lesc.beantablemodel;

import javax.swing.*;

import se.lesc.beantablemodel.BeanTableModel.EditMode;

@SuppressWarnings("serial")
public class TableTester extends JTable {

public TableTester() {
BeanTableModel<Person> model = new BeanTableModel<Person>(Person.class);
model.addColumn("Social Security Number", "socialSecurityNumber");
model.addColumn("Name", "name", EditMode.EDITABLE);
model.addColumn("Age", "age", EditMode.EDITABLE);
model.addColumn("Heigt (in cm)", "height", EditMode.EDITABLE);
model.addColumn("Has empoyment", "employed", EditMode.EDITABLE);

model.addRow(new Person("1", "David", 20, 170.5, true));
model.addRow(new Person("2", "Susan", 26, 162.0, false));
model.addRow(new Person("3", "Mark", 42, 180.5, false));
model.addRow(new Person("4", "Anna", 54, 168.0, true));
model.addRow(new Person("5", "Johan", 5, 120.5, false));

setModel(model);
}

public static class Person {
private String socialSecurityNumber;
private String name;
private Integer age;
private Double height;
private Boolean employed;
private String hiddenField;

public Person(String socialSecurityNumber, String name, Integer age,
Double height, Boolean isMale) {
this.socialSecurityNumber = socialSecurityNumber;
this.name = name;
this.age = age;
this.height = height;
this.employed = isMale;
}

public String getSocialSecurityNumber() {
return socialSecurityNumber;
}
public void setSocialSecurityNumber(String socialSecurityNumber) {
this.socialSecurityNumber = socialSecurityNumber;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getHeight() {
return height;
}
public void setHeight(Double height) {
this.height = height;
}
public Boolean isEmployed() {
return employed;
}
public void setEmployed(Boolean employed) {
this.employed = employed;
}

public String getHiddenField() {
return hiddenField;
}
public void setHiddenField(String hiddenField) {
this.hiddenField = hiddenField;
}
}

public static void main(String args[]) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

TableTester table = new TableTester();
JScrollPane scollPane = new JScrollPane(table);
frame.setContentPane(scollPane);

frame.pack();
frame.setVisible(true);

}
}

11 March 2009

Where are the numerical-less keyboards?

I want more keyboards that are full size without a numerical keyboard. Why? It is a purely physical requirement! A person that uses the mouse in the right hand does not have to use as wide angle of the arm if the keyboard is narrower. Even a slight change in angle of the arm increases the risk of mouse arm syndrome. A human person arms are its most comfortable when angling into the direction of the body. A user of the classical finger position on the keaboard (“asdf jklö” on my keyboard) has a long way to the mouse on the right side, probably risking to angle the arm outside the body.


The only keyboard that I have found is the Logitech Dinovo cordless desktop for notebooks (it is no difference between the “for notesbooks” and “laser” except the mouse). It now looks like Logitech is going to stop producing this keyboard (it can only be found in the support section on www.logitech.com).

Use Launchy to start applications

A nice program to start applications is Launchy. It is almost like the start field in Vista, except this is a standalone program. I recommend the Putty plugin to Launchy. With this, it has never been easier to start a new ssh session. Just press Alt-Space to start Launcy and then enter "ssh" to enable the putty plugin. After that enter a saved Putty session or a IP-number or host to connect to a new ssh server.



This is how it looks like. Launch can start almost all your programs (and with parameters)!

Escape illegal characters with JAXB XML serialization

The XML 1.0 specification says that some characters are illegal in XML (http://www.w3.org/TR/REC-xml/).

When performing my JAXB marshal I had an ASCII control character in my Java object. This character was written into the XML file and everything looked okay... Until I tried to make an XSLT transformation. My transformer engine could not transform the XML because of the character.

I searched the internet and found this thread: http://www.nabble.com/Escaping-illegal-characters-during-marshalling-td20090044.html. This code can escape special characters. I've made a small modification to the code so that is worked better. I changed it so it work with only JDK API and added the UTF-8 parameter so it also can handle latin-1 characters regardless on which locale the Java VM is executing.

There is one drawback with this: You don't get a nice indented code. All the marshaller.setProperty(...) will probably not work anymore.

I'm a bit disappointed with the Sun implementation of the JAXB marshaller that it cannot handle this problem.

import java.util.HashSet;

import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

/**
* Delegating {@link XMLStreamWriter} that filters out UTF-8 characters that
* are illegal in XML.
*
* @author Erik van Zijst (small change by Lennart Schedin)
*/
public class EscapingXMLStreamWriter implements XMLStreamWriter {

private final XMLStreamWriter writer;
public static final char substitute = '\uFFFD';
private static final HashSet<Character> illegalChars;

static {
final String escapeString = "\u0000\u0001\u0002\u0003\u0004\u0005" +
"\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012" +
"\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C" +
"\u001D\u001E\u001F\uFFFE\uFFFF";

illegalChars = new HashSet<Character>();
for (int i = 0; i < escapeString.length(); i++) {
illegalChars.add(escapeString.charAt(i));
}
}

public EscapingXMLStreamWriter(XMLStreamWriter writer) {

if (null == writer) {
throw new IllegalArgumentException("null");
} else {
this.writer = writer;
}
}

private boolean isIllegal(char c) {
return illegalChars.contains(c);
}

/**
* Substitutes all illegal characters in the given string by the value of
* {@link EscapingXMLStreamWriter#substitute}. If no illegal characters
* were found, no copy is made and the given string is returned.
*
* @param string
* @return
*/
private String escapeCharacters(String string) {

char[] copy = null;
boolean copied = false;
for (int i = 0; i < string.length(); i++) {
if (isIllegal(string.charAt(i))) {
if (!copied) {
copy = string.toCharArray();
copied = true;
}
copy[i] = substitute;
}
}
return copied ? new String(copy) : string;
}

public void writeStartElement(String s) throws XMLStreamException {
writer.writeStartElement(s);
}

public void writeStartElement(String s, String s1) throws XMLStreamException {
writer.writeStartElement(s, s1);
}

public void writeStartElement(String s, String s1, String s2)
throws XMLStreamException {
writer.writeStartElement(s, s1, s2);
}

public void writeEmptyElement(String s, String s1) throws XMLStreamException {
writer.writeEmptyElement(s, s1);
}

public void writeEmptyElement(String s, String s1, String s2)
throws XMLStreamException {
writer.writeEmptyElement(s, s1, s2);
}

public void writeEmptyElement(String s) throws XMLStreamException {
writer.writeEmptyElement(s);
}

public void writeEndElement() throws XMLStreamException {
writer.writeEndElement();
}

public void writeEndDocument() throws XMLStreamException {
writer.writeEndDocument();
}

public void close() throws XMLStreamException {
writer.close();
}

public void flush() throws XMLStreamException {
writer.flush();
}

public void writeAttribute(String localName, String value) throws XMLStreamException {
writer.writeAttribute(localName, escapeCharacters(value));
}

public void writeAttribute(String prefix, String namespaceUri, String localName, String value)
throws XMLStreamException {
writer.writeAttribute(prefix, namespaceUri, localName, escapeCharacters(value));
}

public void writeAttribute(String namespaceUri, String localName, String value)
throws XMLStreamException {
writer.writeAttribute(namespaceUri, localName, escapeCharacters(value));
}

public void writeNamespace(String s, String s1) throws XMLStreamException {
writer.writeNamespace(s, s1);
}

public void writeDefaultNamespace(String s) throws XMLStreamException {
writer.writeDefaultNamespace(s);
}

public void writeComment(String s) throws XMLStreamException {
writer.writeComment(s);
}

public void writeProcessingInstruction(String s) throws XMLStreamException {
writer.writeProcessingInstruction(s);
}

public void writeProcessingInstruction(String s, String s1)
throws XMLStreamException {
writer.writeProcessingInstruction(s, s1);
}

public void writeCData(String s) throws XMLStreamException {
writer.writeCData(escapeCharacters(s));
}

public void writeDTD(String s) throws XMLStreamException {
writer.writeDTD(s);
}

public void writeEntityRef(String s) throws XMLStreamException {
writer.writeEntityRef(s);
}

public void writeStartDocument() throws XMLStreamException {
writer.writeStartDocument();
}

public void writeStartDocument(String s) throws XMLStreamException {
writer.writeStartDocument(s);
}

public void writeStartDocument(String s, String s1)
throws XMLStreamException {
writer.writeStartDocument(s, s1);
}

public void writeCharacters(String s) throws XMLStreamException {
writer.writeCharacters(escapeCharacters(s));
}

public void writeCharacters(char[] chars, int start, int len)
throws XMLStreamException {
writer.writeCharacters(escapeCharacters(new String(chars, start, len)));
}

public String getPrefix(String s) throws XMLStreamException {
return writer.getPrefix(s);
}

public void setPrefix(String s, String s1) throws XMLStreamException {
writer.setPrefix(s, s1);
}

public void setDefaultNamespace(String s) throws XMLStreamException {
writer.setDefaultNamespace(s);
}

public void setNamespaceContext(NamespaceContext namespaceContext)
throws XMLStreamException {
writer.setNamespaceContext(namespaceContext);
}

public NamespaceContext getNamespaceContext() {
return writer.getNamespaceContext();
}

public Object getProperty(String s) throws IllegalArgumentException {
return writer.getProperty(s);
}
}


Here is the test class:
import static org.junit.Assert.*;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;

import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.stream.*;

import org.junit.Test;

/**
* Test class to escape special characters from XML
*
* @author Lennart Schedin
*/
public class EscapeJaxb {
@Test
public void testEvilXml() throws Exception {
//Store the serialized data in memory
ByteArrayOutputStream out = new ByteArrayOutputStream();

//Serialize the test XML class
JAXBContext jaxbContext = JAXBContext.newInstance(EvilXml.class);
Marshaller marshaller = jaxbContext.createMarshaller();
XMLStreamWriter xmlStreamWriter =
XMLOutputFactory.newInstance().createXMLStreamWriter(out, "UTF-8");
EscapingXMLStreamWriter filter = new EscapingXMLStreamWriter(xmlStreamWriter);
marshaller.marshal(new EvilXml(), filter);

assertEquals(59, out.size());

//Check that the latin-1 char is intact and the control char substituted
String expectedXmlString =
"<?xml version=\"1.0\" ?><evilXml>" +
new EvilXml().content +
"</evilXml>";
expectedXmlString = expectedXmlString.replace('\u0007',
EscapingXMLStreamWriter.substitute);
String xmlString = new String(out.toByteArray(), Charset.forName("UTF8"));
assertEquals(expectedXmlString, xmlString);
}

@XmlRootElement
public static class EvilXml {
@XmlValue
/* Illegal control ASCII character and a latin-1 A with a ring above */
private String content = "Hello World \u0007 \u00c5";
}
}