Saturday, April 24, 2010

Switching components in Swing with MigLayout

During Swing application development, I had a situation when at one location I had to display one component or another, based on third component value.

Normally, I would have to create some tricks with sizes or remove items from layout, but with MigLayout this is fantastically easy.

There is magic parameter "hidemode". If you set this value to 2 it will automatically shrink element's sizes to 0 when they are hidden.

So complete solution is to put all switchable parameters to same cell and set this "hidemode" to 2.

panel.add(inputField, "split 2,hidemode 2");
panel.add(comboField, "hidemode 2");

Now, when you need to show one element, you just hide other and vice versa.

inputField.setVisible(true);
comboField.setVisible(false);

Writing binary data between Java and Python

I have small project where server-side is done in Python and client is J2ME application and they have to talk to each other. At one time I had JSON to do it, it is quite easy on both sides, there are nice libs both for Java and Python. But recently, as traffic was growing and JSON performance on J2ME is not suitable for my tasks, I decided to try Java serialization with data streams.

For Java it is trivial:

DataOutputStream daos = new DataOutputStream(baos);
daos.writeLong(value1);
daos.writeInt(value2);
daos.writeBoolean(value3);
daos.writeUTF(value4);

In Python there is no such thing, but there is nice utility package called struct. It basically allows to read and write binary data and convert it into normal data. It is configurable and with small experimenting it is possible to find matching reading and writing functions for Java counterparts.

For example to parse example that is written above:

result = struct.unpack('>II', input1)
value1 = (result[0] << 32) + result[1]
result = struct.unpack('>i', input2)   // result[0] becomes value2
result = struct.unpack('>b', input3)   // result[0] is 1 for true and 0 for false


With strings it is a little bit trickier, because with method writeUTF, Java writes string length as 2 byte number and UTF-8 string. So in Python it is:

result = struct.unpack('>H', input4)
result = input5 // it is already acceptable for Python, just calculate result[0] bytes from input

Similar is also writing from Python to Java; same data formats are good for Java reading.

There is nice trick in Python, it allows to unpack and pack a lot of parameters simultaneously, for example this example above can be shortened to:

result = struct.unpack('>IIibH', input6)  // result array now contains all unpacked values except string 

Thursday, April 8, 2010

Logging in Java Web Start

It is quite easy to use logging in Java Web Start applications - normal Java logging and System.out works just fine.

Basically, to work it first should be enabled from "Java Control Panel" and later logs can be found at specific folders.

It is described in details at http://java.sun.com/j2se/1.5.0/docs/guide/deployment/deployment-guide/tracing_logging.html.

Disable cookies in Java Web Start

I was creating application for Java Web Start that works with URLConnection. At some point, I have noticed strange behavior: for same requests I was receiving different responses. Digging deeper, I found that it was sending cookies that I was creating in browser by regular site browsing.

This became interesting and I googled it and found interesting document. So using browser cookies is actually a feature and was intentionally done and worked in Java Web Start and Java Plug-in applications.

For my case it was totally undesirable and I wanted to turn it off. Fortunately, it can be done quite easily (though more complicated then it should be):

       CookieManager cookieManager = new CookieManager();
       CookieHandler.setDefault(cookieManager);
       cookieManager.setCookiePolicy( CookiePolicy.ACCEPT_NONE);

Input Validation in Swing

Swing provides built-in text field validation. JComponent has method "setInputVerifier" and this method accepts implementations of abstract class called InputVerifier. InputVerifier has only one method that developer has to implement:

boolean verify(JComponent input)

This method must return true if input is valid and false otherwise. It cann't be easier.

However there are some glitches. For example, it seems that it only hijacks focus for text components, because checkboxes and buttons remain clickable and fully functional. To overcome this problem (and also for usability) I started to show JOptionPane on invalid inputs. Now, when my text field tries to loose focus, JOptionPane hijacks it from targeted buttons or checkboxes and shows error, and after disappearing text field is again focused.

    port.setInputVerifier(new InputVerifier() {
      public boolean verify(JComponent input) {
        String value = port.getText();
        int port = -1;
        try {
          port = Integer.parseInt(value);
        } catch (NumberFormatException e) {
        }
        if ((port > 0) && (port < 65535)) {
          return true;
        } else {
          JOptionPane.showMessageDialog(settings, "Port must be number between 0 and 65535", "Input error", JOptionPane.ERROR_MESSAGE);
          return false;
        }
      }
    });


So it works fully as expected. Basically, this simple use case is exactly what I needed, so I am quite happy with this InputVerifier. However, for some more advanced or trickier situations there maybe still necessary to create some little custom framework.