Our Blog

Let us find tech solutions together

Mar 13

Here we are, the last day of our Apache Wicket series.  While there was only one day that focused on Apache Wicket, we’ve laid the groundwork needed to get a J2EE project that uses a web framework off the ground.  And step-by-step, if you follow along with the team on all 4 days preceding, you will have a greater understanding of how everything is put together, and why.

And as you can see, the actual work that went into the front-end of MysticPaste, didn’t take very long at all.  And as good community citizens, we’ve made the finished product and source code available.

MysticPaste.com - our implemented pastebin based on the code examples and tutorials shown throughout this series

MysticPaste Source - the source code for the pastebin, open source.

On Day 1, we learned the many steps it takes to put together a project that can be worked on in a team environment.  We’ve enjoyed your comments on different methodologies, and some we will definitely take into account.  The biggest thing to take away from that day, is to understand the underpinnings of why things are where they are in a project, and how adherence to the fairly accepted Maven standard structure can make life much much easier.

On Day 2, we ran through some basics on Mocks, and with a bit of upfront design on interfaces, a testing harness is available for building out the backend.  We also learned how to take Unitils and extend our tests passed just a functional unit, and move through many layers of the built system, and ensure we go a bit beyond just code coverage.

On Day 3, with unit tests in place, we felt safe writing some implementations so we moved the codebase from failing tests due to no implementation, to working tests.  We learned a bit about designing your domain model based on requirements and design discussions, and molding the service and persistence layer to support your business goals.

On Day 4, we got to the most exciting part of our journey, Apache Wicket.  The article walked you through some of the basics of putting a page together using markup inheritance, and amazingly enough, how this simple act removes the need for technology so common in the MVC world to support this.  Best of all, because its all in Java, ultimately you can actually use your IDE and refactor or debug as needed.  Each of the most basic components with forms, and display, and the wicket-based tags that act as extensions to your HTML pages, were reviewed with links off to the Javadoc for further discovery.  One of the many reasons to love Wicket, is the clear separation of functional concerns, no code in your template pages, it’s just HTML.

If you’re like the members of the Mystic team, you will CRAVE more.  More information, more discovery into how we can fully integrate Apache Wicket as a tool in our arsenal.  Aside from downloading the distribution and following along with our tutorial, here are some options ahead:

  1. TSSJS / LV - Architecting Applications using Apache Wicket - A talk at the symposium by Andrew Lombardi about Apache Wicket.
  2. Apache Wicket Training - Mystic is available for corporate trainings, and is in process of putting together a schedule for different technologies to learn about in 2009.  If you'd like to have us come to your business, have your manager contact us at: trainings@mysticcoders.com

What's next?

With the concepts in these articles, we’ve laid the groundwork for many more short articles on interesting technology in the future.  There is definitely a lot of new and interesting items we can add to MysticPaste without overcomplicating the streamlined interface, such as:

  • Syntax Highlighting
  • Embedding AJAX where it makes sense
  • Remote Interface to the API for pasting from IDE's
  • IRC-based bot integration
  • Thoughts? What other things would you like to see us cover on the blog? Drop us a line at: talk@mysticcoders.com
Read more
Mar 12

5 Days of Wicket - The UI

By Steve Forsyth | Comments

So… you should now have a fairly good understanding of how to put Wicket together with Spring and Hibernate, creating your DAOs and services and putting that code through the test gauntlet. We can see that our foundation is rock solid… but we’re missing the eye-candy… so let’s hop over to the UI and show you where Wicket really shines.  

Base Class

Most if not all web applications use some sort of base template to remove duplication such as the header and footer. Wicket has a built-in way of handling this instead of having to use a separate library such as SiteMesh. Wicket uses inheritance to facilitate templates. They provide their own base class called WebPage that our application specific base class will extend from to get started. The WebPage class sets us up with a blank web page in seconds. For our application, we have a simple header/footer that we want all of our pages to use and a very simple menu that I threw into the base page that I named BasePage.

public class BasePage extends WebPage {... This along with an html page gives us a basic template that all of our pages will extend from.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>Mystic Paste</title>

<link rel="stylesheet" type="text/css" href="css/style.css"/>
    <!--[if IE]>

<link rel="stylesheet" type="text/css" href="css/ie.css" />
    <![endif]-->
  </head>
  <body>

<div id="leftSide">&nbsp;</div>

<div id="rightSide">&nbsp;</div>

<div id="center">
      <!-- header -->

<div id="header">
<div id="logo"><a class="logo" href="http://mysticpaste.com/new"> </a></div>
      </div>

<div id="nav">
<ul id="menus">
<li class="cat-item"><a class="home" title="Home" href="http://www.mysticcoders.com/">Home</a></li>
<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>
<li wicket:id="historyLinkContainer" class="cat-item"><a wicket:id="historyLink" href="#" title="View Paste History">History</a></li>
<li><a class="lastmenu" href="javascript: return false;"> </a></li>
        </ul>
      </div>

<div id="header_bottom"> </div>

      <!-- content -->

<div id="content">
        <wicket:child/>
      </div>

<div class="clear"> </div>

      <!-- footer -->

<div id="footer_left"> </div>

<div id="footer_right"> </div>

<div id="footer_center">
<div id="copyright">Copyright &copy; 2000-2009 Mystic Coders, LLC</div>
      </div>
    </div>

<div id="logo_footer"><img src="images/logo_bottom.png" width="74" height="57"/></div>
  </body>
</html>

This html file sits on the file system in the same package as your BasePage class and is named the same but with a .html extension… BasePage.html. We have decided to separate the java files from the html by putting the html within the same package structure underneath the resources folder. Note the wicket:id attributes and the tag… the wicket:id attributes are used within the java code to identify the components and the wicket:child tag is used as a placeholder signaling that any page that extends this page will be filling in the body of the tag. The 2 links with wicket:id attributes are used for menu item links and the surrounding li tags contain wicket:id attributes to facilitate the highlighting of the current page.  

PASTE IT!

Wicket starts to get fun when we get into forms. We need to create a form that will let the user choose the language type for formatting the pasted content, whether or not this is a private post (not easily guessed url and won’t show in history), and the content itself. We are going to want to make sure that the end result has a fairly simple url that is easy to copy and paste.   The first thing we usually do is come up with the page class and the html… so we are going to create a class that extends our BasePage:

public class PasteItemPage extends BasePage {... The matching html page again, resides in the same package as the Java class and is named the same. PasteItemPage.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<wicket:extend>

<form wicket:id="pasteForm">
<div id="paste_options">
<ul>
<li>private:</li>
<li><input wicket:id="private" type="checkbox" /></li>
      </ul>

<ul>
<li>language:</li>
<li>
<select wicket:id="type" class="language">
<option>Choose One</option>

<option>Java</option>

<option>Groovy</option>

<option>PHP</option>
          </select>
        </li>
      </ul>
    </div>

<div id="paste_content">
<div id="textLeft"> </div>

<div id="textRight"> </div>

<div id="textCenter"><textarea wicket:id="content" wrap="off"></textarea></div>
    </div>

<div id="paste_submit"><input type="submit" value="Paste" /></div>
    </form>
</wicket:extend>

Note the wicket:extend tags which tells Wicket that everything within those tags are the contents that we are interested in… for instance, you could have the whole html file with html/head/body tags if you wanted to and wicket would ignore everything except for the data between the wicket:extend tags.   The wicket:id attributes are placed in the form and the form components. These attributes will allow us to create a Wicket form and bind to the form components.   Wicket provides components for just about everything you want to do, so we extend the Wicket Form class and add that to our page, we then add our form fields (DropDownChoice, CheckBox and TextArea) to the form.   Components in Wicket are hierarchical, you MUST nest/add your components in your java code to match exactly the nesting of your html components. For example, the following snippet is taken from BasePage.html:

1
<li wicket:id="newLinkContainer" class="cat-item"><a wicket:id="newLink" href="#" title="New Paste">New</a></li>

the corresponding java code looks like this:

1
2
3
4
    WebMarkupContainer newLinkContainer = new WebMarkupContainer("newLinkContainer");
    ...
    newLinkContainer.add(new BookmarkablePageLink("newLink", PasteItemPage.class));
    add(newLinkContainer);

In the html markup, the href tag marked with wicket:id=”newLink” is nested inside of the li tag marked with wicket:id=”newLinkContainer”. We therefore need to match this hierarchy within our corresponding java code. In the Java code, I have created a WebMarkupContainer component with id=”newLinkContainer” to match to our li tag, I then add the nested BookmarkablePageLink with id=”newLink” to the newLinkContainer component. I then add the newLinkContainer component to the page as the newLinkContainer is not contained within any other wicket tags.   This nesting can get very deep depending on the web page layout. It is not difficult to keep track of the nesting but sometimes you may forget to fix the html or the Java code when making changes to the either file. However, the Wicket developers built in a clean error message that comes up when you run the application and there is a mismatch between your html and the Java code. For example, if I use our example above and add the newLink to the page rather than to the newLinkContainer, I get the following error message:

1
2
WicketMessage: Unable to find component with id 'newLink' in [MarkupContainer [Component id = newLinkContainer]]. This means that you declared wicket:id=newLink in your markup, but that you either did not add the component to your page at all, or that the hierarchy does not match.
[markup = file:/...paste/web/pages/paste/PasteItemPage.html

These error messages make it easy to find the problems with the hierarchy rather than guessing as to where the problem might be.   The following is the full Java source for our PasteItemPage:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class PasteItemPage extends BasePage {

    @SpringBean
    PasteService pasteService;

    public PasteItemPage() {
        super(PasteItemPage.class);
        add(new PasteForm("pasteForm", new CompoundPropertyModel(new PasteItem())));
    }

    public class PasteForm extends Form {
        public PasteForm(String id, IModel model) {
            super(id, model);

            add(new CheckBox("private"));
            add(new DropDownChoice("type", Arrays.asList(LanguageType.JAVA, LanguageType.CSS, LanguageType.HTML)));
            add(new TextArea("content"));
        }

        @Override
        protected void onSubmit() {
            PasteItem pasteItem = (PasteItem) PasteForm.this.getModelObject();
            pasteService.createItem("web", pasteItem);
            PageParameters params = new PageParameters();
            if (pasteItem.isPrivate()) {
                params.put("0", pasteItem.getPrivateToken());
                setResponsePage(ViewPrivatePage.class, params);
            } else {
                params.put("0", Long.toString(pasteItem.getId()));
                setResponsePage(ViewPublicPage.class, params);
            }
        }
    }
}

As you might have noticed… Wicket uses Models to back the components. In our case… we use a CompoundPropertyModel which makes it extremely easy to bind to components. It basically tells any component that uses this model to bind the property from the model object with a component using the components id. For instance, we have add(new CheckBox(“private”)); which says that we want to add a CheckBox component with the id of “private” and bind it to the property of our model object with the same name (the private field of PasteItem). I have added the CompoundPropertyModel to the form component which automagically backs all components added to the form but can easily be overridden by just passing in a new model to any components that need a different model. There are many other types of Models to choose from as you may not need or want the CompountPropertyModel due to mismatches in the names and such. The DropDownChoice and TextArea components are bound to the html SELECT and TEXTAREA tags in the same manor.   The last piece of the form submission is completed by overriding the onSubmit method of the form and saving our model object with a simple call to our service. That is it for capturing user input and saving it… not sure that it gets much easier than that!   As part of the save routine, one other noteworthy tidbit here is how we forward on to the next page:

1
2
3
4
5
6
7
8
        PageParameters params = new PageParameters();
        if (pasteItem.isPrivate()) {
            params.put("0", pasteItem.getPrivateToken());
            setResponsePage(ViewPrivatePage.class, params);
        } else {
            params.put("0", Long.toString(pasteItem.getId()));
            setResponsePage(ViewPublicPage.class, params);
        }

The setResponsePage method is exactly that… we give it the page that we want to forward to… in this case, if it is a private message, then we forward to our page we created for private pastes, otherwise, we forward to our regular public view page. Notice that we create a PageParameters object, Wicket abstracts away the dreadful request object from you and gives you a convenient object for adding and retrieving page parameters. Now… as I mentioned earlier, we want simple urls… so normally, you would put something like params.put(“id”, pasteItem.getId()); and this would pass the request param of id=5 or with Wickets bookmarkable pages, you would see something like http://your.domain.com/view/id/5. We decided we didn’t want the id to show as it provides no use within the url itself… so… Wicket gives us the ability to create our own URL encoding strategy and provides a few already implemented strategies. Within the Wicket Application class that was generated on Day 1, we can add the following:

mount(new IndexedParamUrlCodingStrategy("/view", ViewPublicPage.class)); This tells Wicket that anyone forwarding to my ViewPublicPage will use the IndexedParamUrlCodingStrategy… which works as follows: we add/pull params from the PageParameters using keys of 0, 1, 2… etc. As you can see in our code example, we use 0 as we only have one param. The end result of this is that our url will look something like this:

This doesn't seem like much, but it does have a slightly cleaner url and depending on your application can help greatly with SEO.
&nbsp;

<h1>Simple and Clean</h1>
I have shown you a very simple form and how easy it is to create a working form submission, but have you noticed that there isn't any java snippets of any kind in the html?
In my opinion, that is one of the best features of wicket, there ARE <em>wicket:ids</em> but those are attributes and tags that are ignored by most GUI designers such as Dreamweaver so the HTML can be ported back and forth if need be without the graphics designer hosing the developers hard work. Even if you have to take a fresh copy of the HTML... it is far simpler to just have to add the <em>wicket:ids</em> back in than to merge in all of the XML or JSP crud that most other Java frameworks force the developer to work with.
&nbsp;
Gone are the days of System.outs in your jsps to figure out what is going on in there. With Wicket... all of your code is in Java classes which can be debugged easily with your favorite IDE. You can walk through your loops to see what you are populating and why. You can even debug portions of Ajax calls as Wicket Ajax enabled components hide the complexity of Ajax submissions and data retrieval.
&nbsp;
Enough jabbering... let's see some list action and paging goodness!
&nbsp;

<h1>History</h1>
Form handling and components are wonderful but I think the history page shows off some of my favorite components within Wicket. There is a nice selection of different types of repeater components and a great paging component that we will use to display paste history.
&nbsp;
Let's start off by talking about the <a target="_blank" href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/repeater/data/DataView.html">DataView</a> component that we are going to use to display the pastes. The <a target="_blank" href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/repeater/data/DataView.html">DataView</a> component is a repeater that allows us to easily mark what we want to repeat within the html and fill in the data from our model object. This is done by adding the <a target="_blank" href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/repeater/data/DataView.html">DataView</a> to our page and then implementing the <a target="_blank" href="http://wicket.apache.org/docs/wicket-1.3.2/wicket/apidocs/org/apache/wicket/markup/repeater/data/DataView.html">DataView</a>s <em>populateItem</em> method as follows:

``` java
        add(historyDataView = new DataView("history", new HistoryDataProvider(pasteService), 10) {
            protected void populateItem(Item item) {
                PasteItem pasteItem = (PasteItem) item.getModelObject();

                PageParameters params = new PageParameters();
                params.put("0", Long.toString(pasteItem.getId()));
                item.add(new BookmarkablePageLink("viewLink", ViewPublicPage.class, params));

                final String[] contentLines = pasteItem.getContent().split("n");
                item.add(new Label("lineCount", "(" + contentLines.length + " Line" + (contentLines.length > 1 ? "s" : "") + ")"));

                item.add(new Label("posted", getElapsedTimeSincePost(pasteItem)));

                List lines = new ArrayList();
                int count = 0;
                for (String contentLine : contentLines) {
                    count++;
                    if (count > 5) {
                        break;
                    } else {
                        lines.add(contentLine);
                    }
                }
                item.add(new ListView("content", lines) {
                    protected void populateItem(ListItem item) {
                        String content = (String) item.getModelObject();
                        Label contentLine = new Label("contentLine", ((item.getIndex() + 1) + "     ").substring(0, 5) + content.replaceAll("r", "").replaceAll("n", ""));
                        item.add(contentLine);
                        if ((item.getIndex() + 1) % 2 == 0) {
                            item.add(new SimpleAttributeModifier("class", "highlight"));
                        }
                    }
                });

                item.add(new BookmarkablePageLink("viewLink2", ViewPublicPage.class, params) {
                    @Override
                    public boolean isVisible() {
                        return contentLines.length > 5;
                    }
                });
            }
        });

and the corresponding html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div wicket:id="history" class="historyItem">
<div class="view">
<div class="historyItemHeader">
<div class="historyItemView"><a wicket:id="viewLink" href="#">View Paste</a></div>
<div wicket:id="lineCount" class="historyItemLines">(27 lines)</div>
<div wicket:id="posted" class="historyItemTime">1 hour ago</div>
          </div>

<div class="historyItemHeaderBottom"> </div>

<div wicket:id="content"><re wicket:id="contentLine">asdfl;kajsdf; a;sldkfj a;lskdjf</re></div>

<div class="historyItemView"><a wicket:id="viewLink2">More...</a></div>
        </div>
    </div>

Digging in… you mark with a wicket:id what you want to repeat… in our case, it is the container div for a history item which we marked as wicket:id=”history”. For every object (PasteItem) within our models list, we are going to get a new div with contents. For each object within the list, we add a BookmarkablePageLink which links to the paste view, the line count and elapsed time which we add as Label components, a repeater to display the first 5 lines of the paste, and a More link which displays only if there are more than 5 lines in the paste.   A BookmarkablePageLink means we are going to have a “clean” URL and we have already covered the PageParameters. The Label has a convenience constructor to allow for Strings rather than having to wrap them in a model. As mentioned earlier, the line count and elapsed time are derived and therefore cannot be pulled from the model object but instead are set manually. Then we have another type of repeater to display the paste. I have chosen a ListView as I’m passing it a List and don’t need to worry about length or paging. The last component we add is the conditional link to the paste view where we override the isVisible method to tell Wicket whether or not this component is visible.   That covers the DataView… now, what about paging? Wicket has a PagingNavigator component that has a prebuilt paging mechanism that can be easily overridden to accommodate just about any type of paging look and feel that your little heart desires. The requirements for using the PagingNavigator are that you need to start with a reapeater that implements IPageable (DataView) and you will need to supply the DataView with a data provider that implements IDataProvider. I have chosen to extend DefaultDataProvider and implement as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HistoryDataProvider extends DefaultDataProvider {

    PasteService pasteService;

    public HistoryDataProvider(PasteService pasteService) {
        this.pasteService = pasteService;
    }

    public Iterator iterator(int first, int count) {
        return pasteService.getLatestItems("web", count, first, false).iterator();
    }

    public int size() {
        return new Long(pasteService.getLatestItemsCount("web")).intValue();
    }

    public IModel model(Object object) {
        return new Model((PasteItem) object);
    }

}

You can see that the data provider allows us to only pull what is displayed on the current page and gives the paging mechanism the overall count value via the size method. In return, the paging mechanism supplies the start and count for the pulling of what is to be displayed.   Last is the addition of the PagingNavigator components which I have chosen to show at the top and bottom of the list.   HistoryPage.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<wicket:extend>

<div class="navContainer">
<div wicket:id="pageNav" class="pageNav"><a href="#">Previous</a><a href="#">1</a><a href="#">2</a><a href="#">Next</a></div></div>

<div wicket:id="history" class="historyItem">
<div class="view">
<div class="historyItemHeader">
<div class="historyItemView"><a wicket:id="viewLink" href="#">View Paste</a></div>
<div wicket:id="lineCount" class="historyItemLines">(27 lines)</div>
<div wicket:id="posted" class="historyItemTime">1 hour ago</div>
          </div>

<div class="historyItemHeaderBottom"> </div>

<div wicket:id="content"><re wicket:id="contentLine">asdfl;kajsdf; a;sldkfj a;lskdjf</re></div>

<div class="historyItemView"><a wicket:id="viewLink2">More...</a></div>
        </div>
    </div>

<div class="navContainer">
<div wicket:id="pageNav2" class="pageNav"><a href="#">Previous</a><a href="#">1</a><a href="#">2</a><a href="#">Next</a></div></div>
</wicket:extend>

and HistoryPage.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class HistoryPage extends BasePage {

    @SpringBean
    PasteService pasteService;

    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");

    DataView historyDataView;

    public HistoryPage() {
        super(HistoryPage.class);

        add(historyDataView = new DataView("history", new HistoryDataProvider(pasteService), 10) {
            protected void populateItem(Item item) {
                PasteItem pasteItem = (PasteItem) item.getModelObject();

                PageParameters params = new PageParameters();
                params.put("0", Long.toString(pasteItem.getId()));
                item.add(new BookmarkablePageLink("viewLink", ViewPublicPage.class, params));

                final String[] contentLines = pasteItem.getContent().split("n");
                item.add(new Label("lineCount", "(" + contentLines.length + " Line" + (contentLines.length > 1 ? "s" : "") + ")"));

                item.add(new Label("posted", getElapsedTimeSincePost(pasteItem)));

                List lines = new ArrayList();
                int count = 0;
                for (String contentLine : contentLines) {
                    count++;
                    if (count > 5) {
                        break;
                    } else {
                        lines.add(contentLine);
                    }
                }
                item.add(new ListView("content", lines) {
                    protected void populateItem(ListItem item) {
                        String content = (String) item.getModelObject();
                        Label contentLine = new Label("contentLine", ((item.getIndex() + 1) + "     ").substring(0, 5) + content.replaceAll("r", "").replaceAll("n", ""));
                        item.add(contentLine);
                        if ((item.getIndex() + 1) % 2 == 0) {
                            item.add(new SimpleAttributeModifier("class", "highlight"));
                        }
                    }
                });

                item.add(new BookmarkablePageLink("viewLink2", ViewPublicPage.class, params) {
                    @Override
                    public boolean isVisible() {
                        return contentLines.length > 5;
                    }
                });
            }
        });

        add(new PagingNavigator("pageNav", historyDataView));
        add(new PagingNavigator("pageNav2", historyDataView));
    }
}

Note that we have just added the 2 PagingNavigators at the bottom of the code, passing in the DataView that we created above. That is it… you now have a fully functioning history page with paging navigation. Again, not sure it can get much easier than that.  

Testing

Testing you say? Whoa… we can’t test the front-end without going through a lot of trouble can we?   Well… the truth is that Wicket provides a way to do quite a bit of front-end testing and it is pretty much as easy as testing any other Java code!   What would we want to test? Well, I believe we would want to test that a successful paste would indeed go to the correct page and that the view of the post would contain what we pasted. We might also want to see if our links work… do they go to the correct page? We don’t have a complicated application, so we are going to show a small test, but the testing framework can check for just about anything that can happen on a page. For now, take a look at this simple test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class TestPastePage extends AbstractIntegrationTest {

    @SpringBeanByType
    private PasteService svc;

    @SpringBeanByType
    private PasteItemDao dao;

    protected WicketTester tester;

    @Before
    public void setup() {
        AnnotApplicationContextMock appctx = new
                AnnotApplicationContextMock();
        appctx.putBean("pasteDao", dao);
        appctx.putBean("pasteService", svc);

        tester = new WicketTester(MysticPasteApplication.class);
        WebApplication app = tester.getApplication();

        app.addComponentInstantiationListener(new SpringComponentInjector(app, appctx));
    }

    @Test
    public void testPaste() {
        tester.startPage(PasteItemPage.class);
        tester.assertRenderedPage(PasteItemPage.class);
        FormTester ft = tester.newFormTester("pasteForm");
        ft.select("type", 0);
        ft.setValue("content", "blahblahblah");
        ft.submit();
        tester.assertRenderedPage(ViewPublicPage.class);
        tester.assertContains("blahblahblah");
        tester.assertLabel("type", "JAVA");
    }

    @Test
    public void testHistoryMenuClick() {
        tester.startPage(PasteItemPage.class);
        tester.assertRenderedPage(PasteItemPage.class);
        tester.clickLink("historyLinkContainer:historyLink");
        tester.assertRenderedPage(HistoryPage.class);
    }
}

Well… this looks simple enough. First, we’ll test to see if a paste works by looking at what happens in testPaste:

  • start the page we want to look at.
  • validate that the page was rendered and that we are still on this page.
  • setup the form tester.
  • set the values for the language drop-down and the paste content.
  • submit the form.
  • assert that it went to the page we were expecting it to go to next.
  • see if it contains the paste, in this case "blahblahblah".
  • and finally, see if the label for Language is set to JAVA.

Very cool… the test passes… next we test the menu item history link. We open our starting page, kick off the link via clickLink which is set to our history link and then verify that it indeed went to our history page.   I bet you never thought testing front-end code could be so easy. The WicketTester does all the work so you can now have far greater test coverage than you would normally have with a web application.  

Conclusion

Wicket allows a developer to create applications as rapidly as any framework I have seen to date while keeping the html as pristine as possible. Occasionally, I am forced to go back to older applications and deal with jsps both old and new and I always come away with a headache and nosebleed due to the punches taken in dealing with jsps and jstl. I wish I had the time and space to go into more details about some of the helpful components that Wicket offers and I haven’t even touched on Wickets Ajax components in this version of the MysticPaste application. Pay close attention to our blog to see follow up posts and Wicket higher learning as we make improvements to the MysticPaste application. We will also continue to post Wicket tips and tricks as we come across them.

Read more
Mar 11

As mentioned in day 2 of our series, we have a main service interface that came from an initial discussion of what we think a pastebin should do. What can be easier than creating an entity model for a pastebin application, right? It’s just a big text area that holds a piece of text (content) that someone wants to share with the rest of the world, or at least with someone else who might be interested in looking at it. However, there are some specific things that we wanted to accomplish in our application that came from a series of ideas by the different team members. Of course, although not really a requirement, we wanted to build this application using Apache Wicket.

Requirements

Here is a list of the ideas (or requirements) we wanted for our pastebin, in no particular order:

  • A paste item must be identified by a unique id and/or timestamp, and it should contain text and optionally a language identifier (useful for syntax highlighting).
  • A Paste item may be a Reply of another Paste item (can have a parent item).
  • A Paste item may have one or more replies (children items).
  • A Paste item may be private. Private items will be identified by a special random string token of a defined length.
  • A Paste item may be associated with a specific user (author).
  • A given author that has been identified to the system will be able to see all the paste items that he/she created.

Models/Entities

From the set of requirements we can get a sense of how the entities are going to be created and how they’re going to relate to each other, as seen in the following diagram:

Class Diagram

This looks like a very simple entity model, but it fits with what we want to accomplish for this particular project. During the brainstorming session many features were discussed, like the ability to edit and/or delete an item that you own (identified by some session token saved in a cookie), the ability to upload images, have an API for external clients that want access to the pastebin, etc. This might be implemented in the future, but were omitted for the first iteration of the project. However, some of the basis for the functionalities is there, like having a client token to identify the different clients accessing the server and be able to show them in the future.

So now that we have a base, let’s get to do some actual coding. As mentioned before, we decided on using Hibernate as our ORM mapping. This is how we define the PasteItem class (the getters and setters have been omitted from the class to make it easier to read):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import static javax.persistence.EnumType.STRING;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Entity
@NamedQueries({@NamedQuery(name = "item.getById", query = "from PasteItem item where item.id = :id"),
        @NamedQuery(name = "item.find",
                query = "from PasteItem item where item.isPrivate != true order by item.timestamp desc"),
        @NamedQuery(name = "item.findThreaded",
                query = "from PasteItem item where item.isPrivate != true and item.parent is null order by item.timestamp desc"),
        @NamedQuery(name = "item.findByLanguage",
                query = "from PasteItem item where item.isPrivate != true and item.type = :type order by item.timestamp desc"),
        @NamedQuery(name = "item.findByLanguageThreaded",
                query = "from PasteItem item where item.isPrivate != true and item.parent is null and item.type = :type order by item.timestamp desc"),
        @NamedQuery(name = "item.findByToken", query = "from PasteItem item where item.privateToken = :token"),
        @NamedQuery(name = "item.findByUser",
                query = "from PasteItem item where item.isPrivate != true and item.userToken = :token"),
        @NamedQuery(name = "item.count", query = "select count(*) from PasteItem item where item.isPrivate != true")})
public class PasteItem implements Serializable {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    protected long id;

    @Lob
    protected String content;

    @Enumerated(STRING)
    protected LanguageType type;

    @Temporal(TemporalType.TIMESTAMP)
    protected Date timestamp;

    @Basic
    protected String userToken;

    @Basic
    protected String clientToken;

    @Basic
    protected boolean isPrivate;

    @Basic
    @Column(name = "PRIVATE_TOKEN", unique = true, updatable = false)
    protected String privateToken;

    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JoinColumn(name = "PARENT_ITEM_ID", nullable = true)
    protected PasteItem parent;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
    protected List<PasteItem> children;
    ...
}

We are using annotations to define our entity model. The interesting part here is that we didn’t use any Hibernate-specific annotations, and instead relied on the standard ones from the JPA API. Hibernate understands these annotations so it seems logical to use them because then we can make the application more portable, as we could replace Hibernate for any JPA-enabled library. Also, another great feature of JPA is that we can define a set of named queries that our Dao implementations can use. This allows you to define almost anything that’s related to the persistence layer under one class, like the entity, its properties and the different ways to access the entity via queries under one place, which then becomes your main reference class.

Service layer

We defined a primary service interface that the Wicket front-end will use to interact between the web pages and the rest of the application. The service layer classes are usually responsible for implementing the required business rules of the application, and they can be as complex or as simple as required, depending on what the actual requirements of the application are. Wikipedia defines a business rule as:

Business rule is a statement that defines or constrains some aspect of the business. It is intended to assert business structure or to control or influence the behavior of the business. Individual business rules that describe the same facet of an enterprise are usually arranged into business rulesets. Business rules describe the operations, definitions and constraints that apply to an organization in achieving its goals.

In layman’s terms, a business rule is anything that affects the way we act on our data. This is how our service implementation looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import com.mysticcoders.mysticpaste.model.LanguageType;
import com.mysticcoders.mysticpaste.model.PasteItem;
import com.mysticcoders.mysticpaste.persistence.PasteItemDao;
import com.mysticcoders.mysticpaste.utils.TokenGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class PasteServiceImpl implements PasteService {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final int DEFAULT_TOKEN_LENGTH = 10;

    private PasteItemDao itemDao;

    private int tokenLength;

    public PasteServiceImpl() {
        this.tokenLength = DEFAULT_TOKEN_LENGTH;
    }

    public PasteServiceImpl(PasteItemDao itemDao, int tokenLength) {
        this.itemDao = itemDao;
        this.tokenLength = tokenLength;
    }

    @Transactional(readOnly = true)
    public List<PasteItem> getLatestItems(String clientToken, int count, int startIndex, boolean threaded)
            throws InvalidClientException {
        logger.trace("Service: getLatestItems. clientToken = {}, count = {}, startIndex = {}, threaded = {}",
                new Object[]{clientToken, count, startIndex, threaded});
        List<PasteItem> results = null;
        if (threaded) {
            results = itemDao.findThreaded(count, startIndex);
        } else {
            results = itemDao.find(count, startIndex);
        }

        if (null == results) {
            logger.warn("Found no items in database.");
            results = new ArrayList<PasteItem>();
        }
        return results;
    }

    @Transactional(readOnly = true)
    public PasteItem getItem(String clientToken, long id) throws InvalidClientException {
        return itemDao.get(id);
    }

    @Transactional(readOnly = true)
    public PasteItem findPrivateItem(String clientToken, String privateToken) throws InvalidClientException {
        return itemDao.findByToken(privateToken);
    }

    @Transactional(readOnly = true)
    public List<PasteItem> findItemsByLanguage(String clientToken, LanguageType languageType, int count,
                                               int startIndex, boolean threaded)
            throws InvalidClientException {

        List<PasteItem> results = null;
        if (threaded) {
            results = itemDao.findByLanguageThreaded(languageType, count, startIndex);
        } else {
            results = itemDao.findByLanguage(languageType, count, startIndex);
        }
        if (null == results) {
            results = new ArrayList<PasteItem>();
        }
        return results;
    }

    @Transactional(rollbackFor = Exception.class)
    public long createItem(String clientToken, PasteItem item) throws InvalidClientException {
        if (null != item &amp;&amp; item.isPrivate()) {
            item.setPrivateToken(TokenGenerator.generateToken(getTokenLength()));
        }
        // set created Timestamp
        item.setTimestamp(new Date(System.currentTimeMillis()));
        return itemDao.create(item);
    }

    public long getLatestItemsCount(String clientToken) throws InvalidClientException {
        return itemDao.getPasteCount();
    }

    public PasteItemDao getItemDao() {
        return itemDao;
    }

    public void setItemDao(PasteItemDao itemDao) {
        this.itemDao = itemDao;
    }

    public int getTokenLength() {
        return tokenLength;
    }

    public void setTokenLength(int tokenLength) {
        this.tokenLength = tokenLength;
    }
}

The business rules for the pastebin are very light in nature. One of the business rule we have from the requirements is to generate a random string token to use as the private paste identifier, so that instead of having a sequential id (which can be guessed), it is identified by this string and a special url.

We’re using slf4j as our logging mechanism. This allows us to statically map our logging to one of log4j, jdk, etc., and it also allows us to have very simple logging messages that will help us ‘debug’ the application in a sense. Nowadays it is considered bad practice to use System.out.println() messages as we don’t have control over them (i.e. they will always appear). Having a logging mechanism with separate message levels allows us to control what we want to show.

It is also interesting to note that in order to support a transactional set of methods, we used Spring’s @Transactional annotation. This annotation allows us to mark a method (and its underlying method calls) as part of one transaction, and optionally to mark said transaction as read-only. Also, take care that marking a method as transactional is often not enough, as we have to specify under which conditions the transaction needs to rollback (in our case, every time an exception is thrown). This is because by default, spring only rolls back transactions when runtime exceptions are thrown.

Persistence layer

Since we are using Hibernate, our persistence layer becomes a really easy, thin layer. Here’s our Dao implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import com.mysticcoders.mysticpaste.model.LanguageType;
import com.mysticcoders.mysticpaste.model.PasteItem;
import com.mysticcoders.mysticpaste.persistence.PasteItemDao;

import java.util.List;

public class PasteItemDaoImpl extends AbstractDaoHibernate<PasteItem> implements PasteItemDao {

    protected PasteItemDaoImpl() {
        super(PasteItem.class);
    }

    public Long create(PasteItem item) {
        save(item);
        return item.getId();
    }

    public PasteItem get(long id) {
        PasteItem item = (PasteItem) getSession().getNamedQuery("item.getById")
                .setLong("id", id).setMaxResults(1)
                .uniqueResult();
        return item;
    }

    public List<PasteItem> findByLanguage(LanguageType languageType, int count, int startIndex) {
        return getSession()
                .getNamedQuery("item.findByLanguage")
                .setParameter("type", languageType)
                .setMaxResults(count).setFirstResult(startIndex).list();
    }

    public List<PasteItem> findByLanguageThreaded(LanguageType languageType, int count, int startIndex) {
        return getSession()
                .getNamedQuery("item.findByLanguageThreaded")
                .setParameter("type", languageType)
                .setMaxResults(count).setFirstResult(startIndex).list();
    }

    public List<PasteItem> find(int count, int startIndex) {
        return getSession().getNamedQuery("item.find")
                .setMaxResults(count).setFirstResult(startIndex).list();
    }

    public List<PasteItem> findThreaded(int count, int startIndex) {
        return getSession()
                .getNamedQuery("item.findThreaded")
                .setMaxResults(count).setFirstResult(startIndex).list();
    }

    public PasteItem findByToken(String privateToken) {
        return (PasteItem) getSession()
                .getNamedQuery("item.findByToken")
                .setParameter("token", privateToken)
                .uniqueResult();
    }

    public long getPasteCount() {
        Long count = (Long) getSession()
                .getNamedQuery("item.count")
                .setMaxResults(1).uniqueResult();
        return null == count ? 0 : count;
    }
}

We have taken advantage of JPA’s @NamedQuery annotation to greatly simplify the code in our Dao implementation. Since all the queries for accessing a PasteItem were already defined inside the entity (in our case, the PasteItem class), we only need to refer to those queries here, set the named parameters and get the results.

We also defined an abstract class to “generify” (term borrowed from IDEA’s inspection) Hibernate’s access:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;

public class AbstractDaoHibernate<T> extends HibernateDaoSupport {
    private Class entityClass;

    private SessionFactory sessionFactory;

    protected AbstractDaoHibernate(Class dataClass) {
        super();
        this.entityClass = dataClass;
    }

    @SuppressWarnings("unchecked")
    private T load(Long id) {
        return (T) getSession().get(entityClass, id);
    }

    @SuppressWarnings("unchecked")
    private T loadChecked(Long id) throws EntityNotFoundException {
        T persistedObject = load(id);

        if (persistedObject == null) {
            throw new EntityNotFoundException(entityClass, id);
        }
        return persistedObject;
    }

    public void merge(T detachedObject) {
        getSession().merge(detachedObject);
    }

    public void save(T persistedObject) {
        getSession().saveOrUpdate(persistedObject);
    }

    private void delete(T persistedObject) {
        getSession().delete(persistedObject);
    }

    public void delete(Long id) {
        delete(loadChecked(id));
    }
}

This class takes advantage of Java 5 generics in order to implement the common persistence methods that we have. It also extends Spring’s HibernateDaoSupport to make it easier to integrate with Spring.

Wiring everything together

Once we have the service and the persistence layer, we need a way to put everything together in order for our application to work. Since we are using Spring, we only need to define our beans in the applicationContext.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <!-- Services Beans -->
    <bean id="pasteService" class="com.mysticcoders.mysticpaste.services.PasteServiceImpl">

<property name="itemDao" ref="pasteItemDao"/>
<property name="tokenLength" value="${private.token.length}" />
    </bean>

    <!-- DAOs -->
    <bean id="pasteItemDao" class="com.mysticcoders.mysticpaste.persistence.hibernate.PasteItemDaoImpl">

<property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <!-- Wicket Application -->
    <bean id="wicketApplication" class="com.mysticcoders.mysticpaste.MysticPasteApplication"/>

    <!--  Database Beans -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- Hibernate session factory -->
    <bean id="sessionFactory"
          class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">

<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>

<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>

<prop key="use_outer_join">${hibernate.use_outer_join}</prop>

<prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop>

<prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>

<prop key="hibernate.cache.provider_class">${hibernate.cache.provider}</prop>

<prop key="hibernate.connection.pool_size">10</prop>

<prop key="hibernate.jdbc.batch_size">1000</prop>

<prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>

            </props>
        </property>

<property name="annotatedClasses">
<list>
                <value>com.mysticcoders.mysticpaste.model.PasteItem</value>
            </list>
        </property>

<property name="schemaUpdate" value="${hibernate.schemaUpdate}"/>
    </bean>

    <!-- Tell Spring it should use @Transactional annotations -->
    <tx:annotation-driven/>

    <bean id="transactionManager"
          class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory" ref="sessionFactory"/>
    </bean>
</beans>

The Service is configured as a bean, with a reference to the Dao implementation and to a variable that will be replaced by maven when building for the appropriate platform, as mentioned in day 1. The Dao is configured with a reference to Hibernate’s sessionFactory bean, and the rest is the configuration for Hibernate (the data source, the transaction manager, etc.). Since we’re using annotations, we need to set the property annotatedClasses with a list of the Hibernate (or JPA) configured entity classes, in this case the PasteItem class. In order for the @Transactional annotation to work we need to tell Spring that our transaction is driven by those annotations with the tag. I’ve been involved in previous projects where the other developers think they are doing transactions because they used the annotation, but forgot to add this piece to the configuration, thus resulting in database inconsistencies.

Conclusion

Designing the backend involves very little Wicket (or nothing at all as we saw here), but it’s very important to separate our different layers to make the application easier to maintain. Using Spring and Hibernate is win-win situation because we leave many configuration options to Spring, and we are able to provide an easy to use and easy to understand implementation of our service and persistence layer.

EDIT: Added the wicketApplication bean to the Spring configuration file.

Read more