Skip to main content

Swing, JAX-WS, and JavaBeans

Posted by rbair on June 26, 2006 at 1:29 PM PDT

On the SwingLabs Forums we have been discussing Web Services and Swing. In particular, I want to start/retrofit a project on java.net where we can build both visual (like the Aerith map viewer) and non visual components. Some examples of non visual JavaBean components could include a Flickr bean, YahooSearch bean, YahooWeather bean, and so on.

During the discussion, Adam expressed some concern about focusing on potentially gimicky web services like Flickr at the expense of more mainstream services such as those that would be developed in house at your local enterprise.

I decided to take a shot at that situation and see what the development landscape looked like from the enterprise angle, especially when using the latest JEE 5 features. What I found was surprisingly easy, and very... uh... addicting.

There's no webstart demo, because I don't have a webserver ready to handle the server side tasks. I guess you'll just have to trust me that it works. Or better yet, try it yourself :-)

Setup

JAX-WS can be daunting. The most difficult aspect of it is the setup and configuation of the build environment. This will become a bit more clear as we go along. I've chosen to sidestep the whole issue and just try NetBeans 5.5 with the Enterprise development pack This really made the setup process a lot easier for a lowly desktop guy like myself.

I went to the NetBeans 5.5 download area and downloaded both the NetBeans IDE 5.5 Beta Installer and the NetBeans Enterprise Pack 5.5 EA Installer. I then downloaded and installed NetBeans 5.5, followed by the Enterprise Pack. I chose to install the SJAS 9 application server as part of the Enterprise Pack install. This was to make sure I had a Java EE 5 compatible server handy to test the new featuers. Note, I'm running on an Intel Mac with Mustang, and was surprised that everything *almost* worked. The only thing I had to do special was configure my netbeans.conf file to point to the JDK. This may have been because I've been messing with my JDK settings on MacOSX to get Mustang running.

Finally, after installing everything and starting NetBeans, I had to enter the Server Manager and configure SJAS 9 as a web server. This is important, because later when we create our web application a Java EE 5 compatible server needs to be available.

Now that I had everything installed and configured, I created two projects: Swing-WS-Website and Swing-WS. Swing-WS-Website is a normal NetBeans Web Application project type. The only special requirement was to make sure SJAS 9 was selected as the target server, and that JEE 5 is selected as the target.

The Swing-WS project was just a normal Java Application project type. Nothing special.

Creating the Web Service

I chose a really simple and pointless web service as an example -- a random number generator. In NetBeans, I created a new Web Service. I altered the code to look like this:

package org.jdesktop.swingws.services;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;

/**
*
* @author rbair
*/
@WebService()
public class RandomNumbers {
    @WebMethod
    public List generateRandomNumbers(Integer count) {
        Random rand = new Random(System.currentTimeMillis());
        List results = new ArrayList();
        for (int i=0; i<count; i++) {
            results.add(rand.nextInt());
        }
        return results;
    }
}

There really isn't much to this code. I simply marked the RandomNumbers class with the @WebService annotation, and then marked the generateRandomNumbers method with the @WebMethod annotation. By creating this class with the NetBeans wizard, it made sure that the appropriate build configuration was done so that at this point, all I have to do is deploy.

In SJAS 9 I can test my web service by going to the admin site. By default this is installed at http://localhost:4848. I won't go into any more detail on this product. I've found it very easy to use, and you can find info on the net for it.

Writing the Client

Now that I knew my service was deployed and working, I was ready to write the client. First, I had to make the client project aware of the web service. When doing this by hand, this is where the nasty part is.

All SOAP web services (which is what NetBeans created and deployed for me) have a WSDL file. This file describes the service in a language neutral manner. The same WSDL can be used by .NET clients or Java clients, for instance. In Java EE 5, the wsimport command line program reads WSDL files and generates java source files. You can then call these Java classes from within your client.

I've tried doing all this by hand in the past and found it to be a huge pain. Tool support makes this experience much, much better. In NetBeans, I opened the New File wizard and selected Web Service Client. I then pointed it at my RandomNumbers web service (note: in the IDE I didn't even have to deal with the WSDL directly, NB hid all this mess from me). NetBeans automatically called wsimport and created the source files for the web service for me.

The only problem I had was that I never saw these source files. Even though I'd specified a source package. As it turns out, NB 5.5 puts the source files in the "build" directory (which normally just contains class files). I guess they're trying to be clever and hide the sources from your source tree (no point cluttering it up). I'd have preferred that they generated a jar file and put it on my classpath, or at least told me what was going on. It took a while to figure out what was happening. Now that I know, it isn't so bad.

Once the web service was imported into the project, I wrote a quick main method to test it out:

    public static void main(String[] args) {
        try {
            RandomNumbersService rns = new RandomNumbersService();
            System.out.println(rns.getRandomNumbersPort().generateRandomNumbers(250));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

Before running the sample I opened the SJAS admin webapp in my browser to check the logs and see what happened when the webservice was called. I then ran the client. Bingo. Worked perfectly.

So that's cool, but now how do I write a Swing application with this knowledge? Here's what I'd like it to look like (please Romain, don't laugh ;-))


Those familiar with me know that I'm into JavaBeans. For this exercise, imagine that you are the desktop guy tasked with writing applications as fast as your hot little hands can type. Wouldn't it be nice if the gal who wrote the webservice also provided a fancy little JavaBean you could use in the UI? The story goes something like this:

Sally is a server side super-engineer. She dreams in XML. Not just any XML, but we're talking namespace-declared, Schema-validated, fully robust XML. She's written a few web services to make available to the masses within her company. To help facilitate their work, she bundles the generated client stubs (generated by wsimport) along with a set of simple JavaBeans.

Clint is a client side desktop kind of guy. He has more work to do than time to do it. All departments in the company want him to write their apps because he gets them done fast, and they work pretty well. Clint needs to access Sally's web services. No problem. He gets her web service bundle (including the client side stubs and JavaBeans) and adds them to his project. He takes the JavaBeans and adds them to his Visual editor's palette. He then creates a new form, adds the WS JavaBeans to the palette, configures them (and the rest of the UI) in his editor, compiles, tests, and deploys. All as fast as his little hands can type... er, click, since he uses the visual designer as much as possible to simplify his life.

Funny enough, Wally the Web App guru also needs to write some clients. He also takes advantage of Sally's hard work and imports the JavaBeans into his JSF visual environment. Since I don't know enough about writing webapps, I'll stop the story here. Except to say that there is no fundemental difference between writing desktop and web applications when it comes to these simple JavaBeans.

Ok, enough of the somewhat silly story, but perhaps you can see a bit of the vision. So, how do we write these JavaBeans? For this example, it is really quite simple. Basically, my RandomNumbersWS bean needs to allow the developer to specify the count -- the number of random numbers to return. In a real example it may take your Yahoo application id and search params. But that's another blog. Here's the code:

package org.jdesktop.swingws.beans;

import java.util.List;
import org.jdesktop.swingws.client.RandomNumbers;
import org.jdesktop.swingws.client.RandomNumbersService;
import org.jdesktop.swingx.JavaBean;

/**
*
* @author rbair
*/
public class RandomNumbersWS extends JavaBean {
    private int count = 10;
    private RandomNumbers port = null;
   
    /** Creates a new instance of RandomNumbersWS */
    public RandomNumbersWS() {
    }
   
    public void setCount(int count) {
        if (count < 0) {
            throw new IllegalArgumentException("Count must be 0 or a positive whole number");
        }
        int old = getCount();
        this.count = count;
        firePropertyChange("count", old, getCount());
    }
   
    public int getCount() {
        return count;
    }
   
    public List generateRandomNumbers() {
        if (port == null) {
            port = new RandomNumbersService().getRandomNumbersPort();
        }
        return port.generateRandomNumbers(count);
    }
}

JavaBean is a convenience class in SwingX that I can extend. It makes writing these non visual beans a lot easier because it handles the infrastructure for a lot of the property change notification. You'll notice that this bean isn't doing much: it's simply a wrapper for the RandomNumbers client side proxy (called a "port" in JAX-WS). Now, let's see how to use it.

I created my Swing GUI with Matisse -- took a minute, maybe two (Note for Scott: I didn't get any baseline support for JSpinner). I then wrote this code in the actionPerformed event handler for the generate button:

    private void generateButtonActionPerformed(java.awt.event.ActionEvent evt) {                                               
        randomNumbersWS.setCount((Integer)countSpinner.getValue());
        final List results = randomNumbersWS.generateRandomNumbers();
        resultsList.setModel(new AbstractListModel() {
            public Object getElementAt(int index) {
                return results.get(index);
            }
            public int getSize() {
                return results.size();
            }
        });
    }                                             

If bean binding was ready to go, this would have been even simpler. But as it is, this is just normal Swing code. I set the count on the ws bean, then asked it for the random numbers. I then put the resulting List into the JList component.

Ok, so why bother with the bean? Seems like extra overhead here. Yes, this is a trivial example and it doesn't show off the point to a Bean very well. However, if I were using JSR 295: Bean Binding then having a Bean to bind to would have been really useful. Further, more complicated web services may benefit more from the beans approach.

There is another subtle benefit to using the JavaBean in this case. It insulates the UI developer from the web service implementation. Both Clint and Wally won't have to change anything is Sally decides to switch to XML-RPC instead of SOAP and redistributes a new bean. From the UI perspective, they are dealing with the Web Service on the semantic level, rather than the level of the implementation.

Once I learned how to use NB to execute web services from the client, it was a very easy process to get these calls into the UI. Those paying close attention will notice that the call blocks, though, so I have some more work to go before this is a genuinely rich rich client application. Gotta get rid of those blocking calls!

Related Topics >>