Skip to content

Does Swing Need Saving?

21
Feb
2007

There’s been some discussion lately regarding various scripting languages and if they are (or aren’t) the salvation for the “dying” Swing API (here, here and here). However, all of these blog entries assume one critical fact: Swing is dead or at least dying. I call that assumption into question.

Actually, I was kind of surprised that none of the Sun fan-boys beat me to the punch. Over the years, I’ve shot my mouth of quite a bit against Swing. So it surprises me greatly that I’d be the first to step forward and try to defend Swing. In fact, if you had talked to me a year ago, likely I would have been leading the charge to bury Swing and move on. Apparently, some things have changed…

The first post to hit the blogosphere, JRuby can save Swing summed up the core assumptions of all three pretty quickly:

  • Swing apps are slow to build
  • Swing layout managers suck
  • Swing apps are hard to maintain
  • Swing is too powerful
  • No native features
  • Swing apps have a bad history

To start with, assuming that using Swing from within a scripting language solves any of these problems smacks of dynamically-typed delirium. When I first read the JRuby cannot save Swing post, I assumed that it would mention this minor fact, but instead it simply berated Swing further and proposed that it is “beyond saving”. Somehow, this seems a little bit of an extreme sentiment towards one of Java’s most established frameworks. Let’s look at item number one…

Swing apps are slow to build

That depends greatly on a number of factors, not the least of which is the capabilities of the developer in question. I can build a non-trivial Swing UI in a couple hours. I’m sure Romain Guy could do it in half that. Compare that to some developers who would take hours to build a simple, three element form. It’s all relative.

Some of the blog posts propose that using a dynamic language would help with this problem. Let’s take a look:

public class TestClass {
    public static void main(String... args) {
        JFrame frame = new JFrame("Test Frame");
 
        JLabel label = new JLabel("This is the header");
        label.setFont(label.getFont().deriveFont(label.getFont().getSize2D() + 10));
        frame.add(label, BorderLayout.NORTH);
 
        JPanel buttonPanel = new JPanel();
        frame.add(buttonPanel);
 
        buttonPanel.add(new JButton("Button 1"));
        buttonPanel.add(new JButton("Button 2"));
        buttonPanel.add(new JButton("Button 3"));
 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 200);
        frame.setVisible(true);
    }
}

This took me about 3 minutes to whip together. Obviously this is a toy example, but it does illustrate the point that Swing isn’t that cryptic or bulky. Now, let’s take a look at the corresponding JRuby code:

require 'java'
 
JFrame = javax.swing.JFrame
JLabel = javax.swing.JLabel
JPanel = javax.swing.JPanel
JButton = javax.swing.JButton
 
BorderLayout = java.awt.BorderLayout
 
frame = JFrame.new 'Test Frame'
 
label = JLabel.new 'This is the header'
label.font = label.font.deriveFont(label.font.size_2D + 10)
frame.add(label, BorderLayout::NORTH)
 
button_panel = JPanel.new
frame.add button_panel
 
button_panel.add JButton.new 'Button 1'
button_panel.add JButton.new 'Button 2'
button_panel.add JButton.new 'Button 3'
 
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.setSize(400, 200)
frame.visible = true

Now, this took me just as long to put together as the example in Java. And I had already built the UI! Ruby is 1) a nice dynamic language, and 2) I had already done the work. Shouldn’t this have gone much faster? Also, this doesn’t seem too much clearer to me than the Java example. I mean, it’s all Swing.

One of the posts proposed that a generic (possibly XML based) overlay be created on top of Swing which would simplify development by only using Swing indirectly. Well, people have already done this (it’s called XUL), and it isn’t very popular outside of Mozilla.org But let’s move on…

Swing layout managers suck

Well maybe… It greatly depends on the type of UI you’re trying to create. If you’re trying to create a form-based UI with lots of elements then I will concede the point. The reason being that most forms are laid out in a grid-like fashion. For example, something like this:


It’s very hard to do something like this in Swing because the only two grid-based layout managers are GridLayout and GridBagLayout. GridLayout is extremely limiting, requiring all cells to be exactly the same dimensions, and GridBagLayout is powerful to a fault (as well as having some glaring weaknesses, like no absolute cell sizes).

However, this single shortcoming in the Swing layout manager menagerie (lack of a good form layout) has been rectified in Java 6. More specifically, the problem was fixed with the introduction of GroupLayout, which is the layout manager used by the NetBeans GUI builder (formerly Matisse). Although GroupLayout’s API is somewhat cryptic (not to mention Java 6 only), it’s still a very powerful layout manager, even more than GridBagLayout. There are also a whole host of third-party layout managers available for Swing which excel at form based layouts. JGoodies’s FormLayout is a popular one. I myself have even gone so far as to port a layout manager from SWT, GridLayout (which resembles GridBagLayout in power without the complexity). This brings me to my next point…

Even if there were no solid layout managers available for Swing, it’s still very easy to create your own. In fact, this process is so simple, and so revealing about the way Swing layout managers work, that I would recommend anyone who’s serious about developing with Swing to pursue custom layout managers. I’ve written several layout managers over the years. While I only use one of them on a regular basis, I greatly value the lessons learned from each and every one of them. And honestly, Swing makes the task of layout manager creation significantly easier than many other GUI toolkits (such as SWT).

Swing apps are hard to maintain

Not true. Badly designed code is hard to maintain. Badly designed code is written by bad programmers. Bad programmers are incapable of using Swing properly (and even if they did, the rest of their code would be atrocious). In fact, Swing encourages best-practice coding in a lot of ways (a highly MVC based architecture for one). If anything, Swing-based apps are easier to maintain than those created using other APIs.

Swing is too powerful

This is a problem?

No native features

Well, it depends on what you mean when you say “no native features”. Does every element in a Swing UI correspond with a native peer? No. Are Swing elements indistinguishable from their native counterparts? Eh, depends on the platform and how good your eyes are. On Windows XP, the answer is often yes. On Vista, less often. On Mac the differences are acceptable, but on Linux the GTKLookAndFeel is terrible. Don’t get me wrong, it’s better than it was, but it still needs a lot of work before it’s truly production quality.

The “native features” case in which Swing falls drastically short is in integration with the native platform’s desktop features. For example:

  • Determining and firing native apps for a particular file association
  • Retrieving corresponding icons for a certain file association
  • Determining file associations in the first place
  • Access to any sort of platform-specific feature
  • …and so on

I won’t deny that SWT is miles ahead of Swing in this regard. However, there is some dissention over a) are these really the responsibility of the GUI toolkit? and b) what’s wrong with separate libraries like JDIC? Oh well, we’ll still chalk this up as a point for “the other side” (whoever that may be).

Swing apps have a bad history

This is flat out undeniable. Swing apps have a terrible reputation. I still walk into random IT departments, mention I’m a Java developer and get lectures about how slow, ugly and unreliable Java is; when in fact, these comments are really directed at legacy Swing applications.

Swing is a hard API to use properly. This is true mainly because it is an API worth using. This difficulty that programmers have with Swing is amplified by the fact that back in the day, many GUIs were designed visually with the aid of tools like Delphi or Visual Basic’s form editor. Obviously, these tools make it a lot easier to get a “cheap and dirty” UI out the door quickly, but the result really isn’t a GUI worth maintaining. Also, it led to a lot of bad programmers thinking that they were good programmers because they were able to create mediocre prototypes. Refer to “Swing apps are hard to maintain” for more on this point.

Also, until recently, the look and feels available for use with Swing were either highly esoteric and non-native or flat out ugly (or both). Without a solid look and feel to render the components well, Swing looks terrible. However, as of Java 5 Swing got a vastly improved Luna (Windows XP) look and feel. Java 6 introduced a half-way-there GTKLookAndFeel on linux, as well as sub-pixel rendering. Swing fonts have been the focal point of a lot of criticisms primarily because they (until recently) lacked sub-pixel rendering. (sub-pixel rendering allows fonts to look “smoother” on most screens) I think that you could make an argument that Swing on Windows XP looks just as good as any native application.

To Conclude…

Swing is in no need of a rescue from more “hip” languages like Ruby or Groovy. Nor is Swing languishing in the pit of unattractiveness. Rather, Swing is alive and well, and getting stronger by the day. Swing doesn’t need saving, what Swing needs is to be given another chance by the world.

JRuby Event Proxy

18
Feb
2007

One of the things which really which annoys me about JRuby is the lack of a clean syntax corresponding to Java’s anonymous inner classes. Now, Ruby has a neat little syntax trick called blocks. It really would be the ultimate solution if you could use Ruby blocks in things like Java event listeners instead of subclasses. Here’s a quick example:

test3.rb

require 'java'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
class CustomActionListener < ActionListener
    def actionPerformed(e)
        puts 'Button Clicked'
    end
end
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
button.addActionListener CustomActionListener.new
frame.content_pane.add button
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

Notice anything annoying about the above code? We’re using this really nice, clean Ruby syntax with this incredibly ugly listener syntax (the custom class). Now, Ruby does have a slightly cleaner recourse called anonymous classes which is a little nicer, but still fairly ugly:

test2.rb

require 'java'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
frame.content_pane.add button
 
listener = Class.new(ActionListener).new
def listener.actionPerformed(e)
    puts 'Button Clicked'
end
button.addActionListener listener
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

As you see, this is a little nicer primarily because there’s no extraneous named-class to be defined. However, it still lacks a truly clean syntax. What would be really nice is if we could write code like the following:

test.rb

require 'java'
require 'jruby_utils'
 
JFrame = javax.swing.JFrame
JButton = javax.swing.JButton
FlowLayout = java.awt.FlowLayout
ActionListener = java.awt.event.ActionListener
 
frame = JFrame.new 'Test Frame'
frame.layout = FlowLayout.new
 
button = JButton.new 'Click Me'
button.addActionListener proxy_method(ActionListener, 'actionPerformed') { |e|
    puts 'Button Clicked'
}
frame.content_pane.add button
 
frame.setSize(200, 75)
frame.default_close_operation = JFrame::EXIT_ON_CLOSE
frame.visible = true

I think that this is pretty clean. There’s no class definition anywhere. In fact, it’s all done in a block with the ActionEvent instance passed as a parameter.

This is cute and all, but it doesn’t seem to be very useful. To make this work, we’re utilizing a fictional method: “proxy_method”. Intuitively, this method should take a Class and a String defining a method name to override as parameters (along with a block), and then reflectively wrap the block into an overriding method within a subclass of the specified Class. In Java, this would be absolutely impossible – not to mention unnecessary and somewhat bizarre since Java a) defines no such “block” syntax, and b) Java already has anonymous inner classes, so there really isn’t a problem to solve. However, Ruby has a far more dynamic nature and it is actually possible for us to write proxy_method. The implementation looks something like this:

jruby_utils.rb

require 'java'
 
def proxy_method(superclass, name, &block)
    clazz = Class.new(superclass)
    instance = clazz.new
    instance.instance_eval do
        @name = name
        @block = block
 
        def method_missing(missing, *args)
            if missing.id2name == @name
                @block.call args
            else
                super
            end
        end
    end
    instance
end

As you can see, this is pretty much doing reflectively what we worked out mentally above. proxy_method accepts two parameters, a Class and a String. The third parameter is a special “block parameter” which is assigned by Ruby to the specified block wrapped into a Proc instance. (the specified block being the block used in conjunction with the proxy_method invocation in the example above) Then, the method dynamically creates an anonymous class with the specified superclass and assigns it to the clazz variable. clazz (which really represents a bona fide type) is instantiated and a dynamic code block is invoked. (a dynamic instance block is effectively a reflective body to a class) The name and block parameters are assigned to instance fields within the dynamic block and a new method is defined (or rather overridden).

Now, in Ruby, the method_missing method is a special method which is invoked by Ruby in the case of a method invocation which cannot be resolved against the specified instance. The default implementation (in Object) raises an exception, but by overriding this method, developers can easily add arbitrary methods to instances dynamically. In this case, we check to see if the missing method’s name matches the method name we were passed as a parameter of proxy_method. If the names do indeed match, then we invoke the block we were passed in the first place, passing all of the parameters which were intended for the missing method. Then, the value returned from the block is returned from method_missing. (though, in the case of an event listener this really isn’t necessary)

The a vitally important part of our code is the else statement within method_missing. This call passes the method missing invocation up the hierarchy if it doesn’t match the method name with which we are concerned. This is important because this allows either the runtime to throw an exception (in case of a truly missing method), or another method_missing implementation in a super-class to also check for method to reflectively invoke. In our case, the method_missing call will get sent on to the JavaObject class (defined by JRuby) which will check for any corresponding methods in the associated Java class. If none are found, it too will send the method_missing invocation up the hierarchy where it will eventually reach Object, which will raise an exception.

Just as an aside, proxy_method is also suitable for usage in other situations such as creating Java threads directly in JRuby. Consider the following example:

test4.rb

require 'java'
require 'jruby_utils'
 
JThread = java.lang.Thread
thread = proxy_method(JThread, 'run') { puts '...in another thread' }
thread.start

This code will print “…in another thread” once to stdout. But the cool bit is this is actually equivalent to the following code:

test4.rb

require 'java'
 
JThread = java.lang.Thread
class ThreadImpl < JThread
    def run
        puts '...in another thread'
    end
end
 
thread = ThreadImpl.new
thread.start

Notice how much cleaner it is with proxy_method? :-)

Hopefully, you find this to be a useful overview of Ruby method reflection in general, and more specifically, a useful method which can greatly simplify any JRuby code using Java APIs designed for use with anonymous inner classes.

Rapid Prototyping with JRuby

16
Feb
2007

For this entry, let’s consider a situation which arises fairly frequently in real world development: the need to quickly prototype something. Now, when I say prototype, I mean something even more atomic than building a demo of an application or feature. What I’m talking about is trying out a certain aspect of an API to make sure it works as expected; or possibly something even more trivial like quickly retrieving a system property.

Now, Java as a language just doesn’t lend itself very well to this kind of development. I’ve heard it said that “in a world of lightweight tools, Java is a sledge-hammer.” While this analogy is somewhat derogatory, it’s true to a certain extent. The way the Java language is structured syntactically, coupled with the sheer overly-designed bulkiness of most Java APIs, makes Java utterly unsuitable for quickly trying something with a minimum of fuss. For example:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
 
import javax.swing.JComponent;
import javax.swing.JFrame;
 
/*
 * Created: Feb 16, 2007
 */
 
/**
 * TODO	add description
 *
 * @author Daniel Spiewak
 */
public class TestClass {
    public static void main(String... args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                final GradientPaint paint = new GradientPaint(0, 0, Color.BLACK, 0, 200, Color.WHITE);
 
                JFrame frame = new JFrame("Test Frame");
                frame.setSize(200, 200);
 
                JComponent component = new JComponent() {
                    @Override
                    protected void paintComponent(Graphics g) {
                        Graphics2D g2 = (Graphics2D) g;
 
                        g2.setPaint(paint);
                        g2.fillRect(0, 0, getWidth(), getHeight());
                    }
                };
                frame.getContentPane().add(component);
 
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setVisible(true);
            }
        });
    }
}

This example could be code produced by someone trying to test the Swing gradient functionality. Here, however, is the corresponding JRuby code:

require 'java'
 
JFrame = javax.swing.JFrame
JComponent = javax.swing.JComponent
GradientPaint = java.awt.GradientPaint
Color = java.awt.Color
 
class CustomComponent < JComponent
    @paint = GradientPaint.new(0, 0, Color::BLACK, 0, 200, Color::WHITE)
 
    def initialize
    end
 
    def paintComponent(g)
        g.paint = @paint
        g.fillRect(0, 0, getWidth, getHeight)
    end
end
 
frame = JFrame.new 'Test Frame'
frame.setSize(200, 200)
 
frame.add CustomComponent.new
 
frame.defaultCloseOperation = JFrame::EXIT_ON_CLOSE
frame.visible = true

There, isn’t that ever so much cleaner? :-) (actually, readers who haven’t fallen asleep yet will notice I cheated in the JRuby version by not wrapping everything into the EDT, but that’s another post…) Either way, the result is the same (pictured right), a simple JFrame showing a full gradient from black to white. Not a terribly impressive example, but one which does demonstrate the atomic nature of a lot of modern prototyping needs. But let’s look at something smaller for a moment…

Let’s say that you’re building some code which needs to (among other things) detect the exact operating system upon which it is running and perform different operations based on those facts. This is a fairly common scenario among desktop applications. In fact, I came across it myself just a few days ago. Now, Java provides a system property which returns a string representation of the operating system name. (it’s the os.name property) While this is a good starting place, the property itself doesn’t follow a standardized format. And since the only way to really determine the OS name is to perform string manipulation on the value – which is in a non-standardized format – we’re a bit up the creek.

The only way to be sure we’re running our tests properly is to actually examine the value of the os.name property. This would be ever-so-easy if Java provided a nice mechanism to peer into the standardized system properties, but alas, no such mechanism exists. The only way to get a property value, in fact, is to actually run some code which retrieves the value and prints it out. Unfortunately, due to Java’s rather verbose syntax, such a program would be ridiculously long (given the problem). A better way is to use the JIRB utility, an interactive JRuby console:

Microsoft Windows [Version 6.0.6000]
Copyright (c) 2006 Microsoft Corporation.  All rights reserved.

C:\\Users\\Daniel Spiewak>jirb
irb(main):001:0> require 'java'
=> true
irb(main):002:0> System = java.lang.System
=> System
irb(main):003:0> System.getProperty 'os.name'
=> "Windows Vista"
irb(main):004:0> exit

C:\\Users\\Daniel Spiewak>

And it’s just that simple. In fact, I have JRuby installed on all of my test systems for precisely this reason. It’s so easy to do really quick, highly atomic prototyping in JRuby. Things (such as the OS name example) which would ordinarily take five to ten minutes of typing (in Java) require mere seconds in JIRB.