This entry is written as a reaction to Chris Schalke's article, Building Custom JSF UI Components on the serverside. It shows how to obtain the same functionality with different technology: a custom Stock Quote component using Wicket.

The discussed files are avialable through ViewCVS on:

Intended audience: anyone seeking a better way of developing web applications. I trust you have Java development experience and know your HTML. Other than that.... well... just see. You might want to take a look at some Wicket Examples to see what Wicket is all about.

In Wicket building custom components is very easy. A normal web application will contain many, tailor made components for your application. In fact, each Page you create is a Component and could be put in a jar, and reused in another application.

Creating a Stock Quote component

The Stock Quote component should take a symbol and display the value of the stock obtained through a webservice in its body. I've used the same webservice as Chris did, and found some code on devx.com for calling the webservice. In order to keep it simple, I've not included this code here, but you can see it in our CVS. The webservice calling code is not the prettiest around, but it suffices.

So: Price of IBM goes here should replace the body contents with the actual value of IBM stock. Here's the Java code for the component (there is no markup file associated with it):

public class StockQuoteLabel extends WebComponent
{
    public StockQuoteLabel(String id, String symbol)
    {
        super(id, new Model(symbol));
    }
    public StockQuoteLabel(String id, IModel model)
    {
        super(id, model);
    }
    protected void onComponentTagBody(
              final MarkupStream markupStream
            , final ComponentTag openTag)
    {
        String symbol = getModelObjectAsString();
        String quote = getQuote(symbol);
        replaceComponentTagBody(markupStream, openTag, quote);
    }
    private String getQuote(String symbol)
    {
        // do SOAP stuff
    }
}

The first constructor adds the symbol string as a Model to the component. Models are what bind your business application logic to components, they are the glue between your world and the Wicket components. The first constructor is a convenience constructor, allowing you to create the component quickly with a stock symbol such as "IBM". The second constructor allows you to retrieve the stock symbol from somewhere else, such as a property from a POJO.
The first line in the onComponentTagBody method returns the contents of the model as a String. The second line replaces the text between the span tags with the stock quote retrieved from the web service.

So how would you use this component? Let's first take a look at the markup (StockQuote.html):


StockQuote

    

The quote for IBM is: foo bar

The idea is to replace the "foo bar" text with the actual value of IBM stock. In the corresponding Java file for this web page the component is added to the page like (StockQuote.java):

public class StockQuote extends WebPage {
     public StockQuote() {
        add(new StockQuoteLabel("stockIBM", "IBM"));
     }
}

That is it.

Using a Form to get the symbol

Now lets use user input for dynamically retrieving the stockquote. The page alters like so:


StockQuote

    
Enter symbol:

The quote for symbol is: quote

As you can see, I've only added the form and an input field, and I've added a label to render the symbol in, and renamed the stock quote component. In the Java this looks like:

public class StockQuote extends WebPage {
    private static class Quote implements Serializable {
        private String symbol;
        public Quote() {}
        public String getSymbol() { return symbol; }
        public void setSymbol(String s) { symbol = s; }
    }

    private final Quote quote = new Quote();

    public StockQuote() {
        Form form = new Form("form");
        IModel model = new PropertyModel(quote, "symbol");
        form.add(new TextField("symbol", model));
        add(form);
        add(new Label("symbol", model));
        add(new StockQuoteLabel("quote", model));
    }
}

Here I've wired the page such that the text field, label and stock quote label all use the same instance of the Quote class. The property model uses the OGNL library to get and set the value of the property that is bound to the model, in this case the symbol property of the Quote object.

In order to run this example we need to create an application object and tell it what our home page is:

public class StockQuoteApplication extends WebApplication {
    public StockQuoteApplication() {
        getPages().setHomePage(StockQuote.class);
    }
}

Lastly we need to adjust the web.xml to make Wicket available to the servlet container:


<!DOCTYPE web-app
          PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
          "http://java.sun.com/dtd/web-app_2_3.dtd">


    Wicket Examples
    
        StockQuoteApplication
        wicket.protocol.http.WicketServlet
        
            applicationClassName
            wicket.examples.stockquote.StockQuoteApplication
        
    
    
        StockQuoteApplication
        /stock/*
    

And we're ready to run.

Conclusion

Wicket makes custom component creation a breeze. In just a few lines of code we've created a component that can play nicely with other Wicket components, and works without having to resort to configuration files, component registries and other fluff.

NB

Normally you wouldn't want to make a custom component for this functionality. The getQuote() soap client would be part of a POJO that looks like:

public class StockQuote {
    private String symbol;
    public StockQuote() {}
    public StockQuote(String s) {symbol = s; }
    public void setSymbol(String s) {symbol = s; }
    public String getSymbol() { return symbol; }
    public String getQuote() { return getQuote(symbol); }
    private String getQuote(String symbol) {
        // do SOAP thingy
    }
}

So all functionality of getting a quote is located in the StockQuote POJO. Now you can
use this directly in your pages, without having to create a new component, just using
the default Label component to display the value of the quote:

quote.setSymbol("IBM");
add(new Label("quote", new PropertyModel(quote, "quote")));