Developing Great Software

Thursday, March 31, 2011

Java EE6 & Wicket - Article #6 – A Wrap Up

Welcome to the 6th in a series of detailed articles on Java EE6 & Wicket. If you haven’t already, please read the following previous articles before continuing.

  1. http://jeff-schwartz.blogspot.com/2011/03/java-ee6-wicket.html
  2. http://jeff-schwartz.blogspot.com/2011/03/java-ee6-wicket-article-1-requirements.html
  3. http://jeff-schwartz.blogspot.com/2011/03/java-ee6-wicket-article-2-creating.html
  4. http://jeff-schwartz.blogspot.com/2011/03/java-ee6-wicket-article-3-generating.html
  5. http://jeff-schwartz.blogspot.com/2011/03/java-ee6-wicket-article-4-adding-jpa.html

In this, the 6th and final article in this series, we will examine the code, focusing on the integration we provided for consuming the EJB in our HomePage. I will also discuss some best practices that I’ve used here, especially pertaining to Wicket components and project structure.

Consuming EJB In Wicket

In article #3 we added the JavaEE Inject jars to the GuestBook project. JavaEE Inject is a Wicket module that provides integration through Java EE 6 resource injection and supports three annotations:

  1. @EJB
  2. @PersistenceUnit
  3. @Resource

With JavaEE Inject any Wicket application running in a Java EE6 container can consume the above 3 types of JavaEE resources. Before an application can use them, however, its WebApplication class method init must be overridden to add the JavaEEComponentInjector into Wicket’s request cycle. The code is simple as shown below:

@Override
    protected void init() {
        super.init();
        addComponentInstantiationListener(new JavaEEComponentInjector(this));
    }

What we are doing in the above code is calling Wicket’s Application class method addComponentInstantiationListener with a reference to a JavaEEComponentInjecter object which the JavaEE Inject module provides. addComponentInstantiationListener adds the JavaEEComponentInjector object to its lists of component instantiation listeners that it maintains.

With the JavaEEComponentInjector now maintained whenever a Wicket component is instantiated, JavaEEComponentInjector will scan the component for one of the above three annotations and injects their associated resources into the component.

There is one limitation, though, imposed by JavaEE Inject and that is it can only inject resources into a component that is a subclass of WicketPage. This  has implications on the modularity of our code in that we can’t directly reference EJB methods, for instance, directly in classes that aren’t derived from WebPage. Or can we? Well, we can and I will show you how we did it in GuestBook.

Techniques For Using JavaEE Inject Resources Outside Of WebPages

GuestBook uses an instance of a DataView to display the list of names of its visitors. A DataView requires a pseudo model of type IDataProvider<T> dataProvider to provide the data that it displays and GuestDataProvider serves that purpose. GuestDataProvider uses methods from our session bean to access the data in the guest table but it, like HomePage, is declared in its own file so how can it access the session bean and call into its methods?

The solution was to create the following interface that GuestDataProvider implements:

public interface IEjbDataProvider<T> extends IDataProvider<T> {
    AbstractFacade<T>  getFacade();
}

GuestDataProvider is declared as abstract because it doesn’t actually implement getFacade which is defined as returning an instance to AbstractFacade. Instead, GuestFacade relies on its subclasses to provide the method’s implementation.

In the HomePage constructor we create an instance of a GuestDataProvider and override its getFacade method by returning the reference to our session bean.

public class HomePage extends BasePage {

    @EJB(name = "GuestFacade")
    private GuestFacade guestFacade;

    public HomePage() {
        super();

        add(new FeedbackPanel("feedback"));

        /*
         * A loadable and detachable model whose sole purpose is to always
         * return a new Guest object when its load method is called.
         */
        LoadableDetachableModel<Guest> newGuestLoadableDetachableModel = new LoadableDetachableModel<Guest>() {

            @Override
            protected Guest load() {
                return new Guest();
            }
        };

        /*
         * The guestForm uses a nested model. The outer model is a compound property
         * model and the inner model is a light weight loadable and detachable model.
         */
        Form<Guest> guestForm = new Form<Guest>("guestform", new CompoundPropertyModel<Guest>(newGuestLoadableDetachableModel)) {

            @Override
            protected void onSubmit() {
                Guest guest = getModelObject();
                guestFacade.create(guest);
                setModelObject(new Guest());
                guest = new Guest();
            }
        };

        /*
         * Add the custom NameTextField compent for
         * first and last name to the form, nesting each
         * in a FormComponentFeedbackBorder for displaying
         * data entry errors.
         */
        guestForm
                .add(new FormComponentFeedbackBorder("firstNameBorder")
                .add(new NameTextField("firstName")));
        guestForm
                .add(new FormComponentFeedbackBorder("lastNameBorder")
                .add(new NameTextField("lastName")));
        add(guestForm);

        GuestDataProvider gdp = new GuestDataProvider() {

            @Override
            public AbstractFacade<Guest> getFacade() {
                return guestFacade;
            }
        };

        /*
         * When dealing with potentially large lists of data it is
         * better to use a DataView whose constructor takes a data
         * provider as its second parameter.
         */
        DataView<Guest> guestListView = new DataView<Guest>("namelist", gdp) {

            @Override
            protected void populateItem(Item<Guest> item) {
                Guest guest = item.getModelObject();
                item.add(new Label("name", guest.toString()));
            }

        };

        add(guestListView);

    }
}

This demonstrates that though JavaEE Inject places restrictions on where we can inject resources there are simple patterns that we can employ to overcome them.

Wicket Models Put To Best Use

HomePage also uses loadable/detachable models for both the form and the list of visitor names it displays. When Wicket completes its request cycle the components containing them will not contribute any model data to the serialization of the page.

Though I won’t go into detail here, understanding and mastering Wicket models is a critical component of your Wicket education. If you don’t know the difference between static and dynamic models or you aren’t familiar with loadable/detachable models you can read my article here to get up to speed on this subject.

Components

Now, lets talk a bit about Wicket components and some ideas that I’d like to share with you.

Whenever I develop a Web application I like to encapsulate business rules so that they can be reused. Presentation components are a prime target for encapsulating business rules, especially if they are used more than once on a page or on more than one page. Business rules encompass issues like requiring the user to enter a value for an input field, min and max values, etc.

Some frameworks make extending their presentation components more difficult than one would imagine. On the other hand, this is one area where Wicket shines – Wicket’s components are strikingly easy to extend and to imbed business rules in.

In article #1 we expressed the business rules for GuestBook as follows:

  • Limit the number of characters our visitors can key in for their first and last names to a maximum of 45 characters each.
  • Both first and last name are required.

GuestBook uses NameTextField, a subclass of TextField, to encapsulate and enforce its business rules.

public class NameTextField extends TextField<String>{

    /**
     *
     * @param id
     * @param model
     */
    public NameTextField(String id, IModel<String> model) {
        super(id,model);
        setRequired(true);
    }

    /**
     *
     * @param id
     */
    public NameTextField(String id) {
        this(id,null);
    }

    @Override
    protected void onComponentTag(ComponentTag tag) {
        tag.put("maxlength", "45");
        tag.put("size", "55");
        super.onComponentTag(tag);
    }


}

Since both first name and last name must enforce the same requirements on the user it makes sense to encapsulate the logic in one place. In the above code we set the component’s required flag to true and we override the onComponentTag method to output the ‘maxlength’ and ‘size’ attributes for the input tag.

This is a prime example of applying the ‘Don’t Repeat Yourself’ (DRY) principle in action and Wicket, to its credit, makes this an incredibly easy process.

Now a word or two on structuring your code.

Project Structure

One of the things I really hate is dealing with a project which just dumps everything into a single package whose name provides absolutely no insight as to the purpose of the code contained in it. Bad! Bad! Bad!

In GuestBook, package names provide useful insight as to the purpose of the code they contain. The packages also serve to layer the project’s structure by areas of concern, namely entities, session beans and Wicket which it further sub groups into components and data providers.

Some Final Thoughts

Well that just about wraps things up for this series but before we say goodbye I’d like to extend my thanks to all the developers and contributors who have made Java, Wicket and its infrastructure of contributed libraries and NetBeans the productive resources they are for us.

I hope you have enjoyed reading this series and following along with me as much as I have enjoyed sharing my time with you.

Please feel free to leave your comments for any of the articles in this series and I will try to find the time to respond to them all.

May all your days coding be happy and productive ones.

3 comments:

  1. Java programming
    This is very helpful to learn java programming I am very thanks to you that you provide me great knowledge about java.
    I am also try to write java program, some java coding is available on my blog please visit my blog and give me your important advise. This is very helpful to me.
    http://javaprogramming-farhan.blogspot.com

    ReplyDelete
  2. Thanks a lot Jeff. It was a very cool series. You could have shown how to work with DataView, like sorting by first name.

    ReplyDelete
  3. If we have X no of entities, does it mean we have to create X no of data providers?

    ReplyDelete

Note: Only a member of this blog may post a comment.

About Me

My photo
New York, NY, United States
Software Developer