Anda di halaman 1dari 63

Wicket Application Development

started 6-June-2011

Wicket Application Development

Copyright 2010 H. Turgut Uyar <uyar@itu.edu.tr>

This tutorial is released under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. You are free: to Share to copy, distribute and transmit the work to Remix to adapt the work Under the following conditions: Attribution You must attribute the work in the manner specied by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). Noncommercial You may not use this work for commercial purposes. Share Alike If you alter, transform, or build upon this work, you may distribute the resulting work only under the same or similar license to this one. You can nd more information on the page: http://creativecommons.org/licenses/by-nc-sa/3.0/.

Wicket Application Development


iii

Contents

Basics 1.1 1.2 1.3 1.4 Creating the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dynamic Content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Adding Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Shorter Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1 1 5 7 10 13 13 15 19 19 20 23 27 28 31 37 38 39 45 45 46 49

Application Structure 2.1 2.2 Adding Stylesheets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Shared Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Data Model 3.1 3.2 3.3 Movies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Displaying Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Movie Collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Forms 4.1 4.2 Text Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Check Boxes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Data Persistence 5.1 5.2 A Collection Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

A Development Environment A.1 Installing NetBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2 Installing the Wicket Plugin B Solutions to Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Wicket Application Development


v

List of Figures

1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9

Screenshot: Home page containing a title and a link to the movie list page . . . . . . . . . . . . . . . Screenshot: NetBeans new project category form . . . . . . . . . . . . . . . . . . . . . . . . . . . . Screenshot: NetBeans new project name form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Screenshot: NetBeans new project server form . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Screenshot: NetBeans new project framework form . . . . . . . . . . . . . . . . . . . . . . . . . . . Screenshot: NetBeans new Wicket project default les . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Initial home page template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Initial home page class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Home page template showing the current date and time . . . . . . . . . . . . . . . . . . . .

1 2 2 2 3 4 5 5 6 6 8 8 9 10 10 11 13 14 14 15 15 16 16 17 17 18 18

1.10 Listing: Home page class providing the current date and time . . . . . . . . . . . . . . . . . . . . . . 1.11 Listing: Initial movie list page template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.12 Listing: Initial movie list page class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.13 Listing: Home page template with link to movie list page . . . . . . . . . . . . . . . . . . . . . . . . 1.14 Listing: Movie list page link class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.15 Listing: Home page class constructor with link component to movie list page . . . . . . . . . . . . . 1.16 Listing: Home page class constructor using shorter code . . . . . . . . . . . . . . . . . . . . . . . . 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Screenshot: Home page containing style sheet and navigation panel . . . . . . . . . . . . . . . . . . Listing: Application style sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Home page template with style sheet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Home page class constructor with style sheet component . . . . . . . . . . . . . . . . . . . . Listing: Base page class with style sheet component . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Home page class extending the base page class . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Navigation panel template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Navigation panel class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Home page template with navigation panel . . . . . . . . . . . . . . . . . . . . . . . . . . .

2.10 Listing: Base page class constructor with navigation panel . . . . . . . . . . . . . . . . . . . . . . . 2.11 Listing: Home page class with navigation panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9

Screenshot: Movie list page populated with test data . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie list template using a list view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie list page class with sample movie data . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Property list view component for movie list . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie collection class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Application class containing the collection object . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie list page class constructor getting movie list from application . . . . . . . . . . . . . . Screenshot: Movie display page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

19 20 21 22 23 23 24 25 25 26 27 28 29 30 30 31 31 32 34 35 38 39 39 40 41 42 43 43 44 44 46 46 47

3.10 Screenshot: Movie list page with links to movie display pages . . . . . . . . . . . . . . . . . . . . . 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 Screenshot: Movie edit page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Screenshot: Movie list page with delete option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Page template for editing a movie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie edit form class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie edit page class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Header panel template with link to movie adding page . . . . . . . . . . . . . . . . . . . . . Listing: Header panel class constructor with link component to movie adding page . . . . . . . . . . Listing: Movie list template with check boxes for entries . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie list form class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4.10 Listing: Movie list page class containing movie list form component . . . . . . . . . . . . . . . . . . 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 Screenshot: Creating a folder for external libraries in Netbeans . . . . . . . . . . . . . . . . . . . . . Listing: Movie collection interface. Listing: Movie collection interface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Screenshot: NetBeans project properties form for external libraries . . . . . . . . . . . . . . . . . . . Listing: Movie class with id attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Movie collection class using a database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Getting movies from a collection using JDBC . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Adding a movie to a collection using JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . . Listing: Deleting a movie from a collection using JDBC . . . . . . . . . . . . . . . . . . . . . . . .

5.10 Listing: Updating a movie in a collection using JDBC . . . . . . . . . . . . . . . . . . . . . . . . . . A.1 Screenshot: NetBeans install welcome page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A.2 Screenshot: NetBeans install option customization form . . . . . . . . . . . . . . . . . . . . . . . . . A.3 Screenshot: NetBeans Wicket plugin installation . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Wicket Application Development


vii

Preface
This tutorial aims to demonstrate how to build an application using the Apache Wicket web framework. You can nd more information about the Wicket project on its web site: http://wicket.apache.org/ Although the application code does not depend on any particular development environment, the narration makes use of NetBeans features and it can be easier to use NetBeans when repeating the steps explained in the tutorial. You can nd some installation notes about NetBeans in Appendix A.
Warning Please keep in mind that this is not a "best practices" tutorial; it just tries to make it easier to understand the Wicket framework for a beginner. Although I have prepared this tutorial to the best of my understanding on the subject, it may still contain errors, omissions, and improper uses of Java or Wicket features. If you have any corrections or suggestions, please e-mail them to me. Thanks.

Wicket Application Development


1 / 55

Chapter 1

Basics
We will rst start by creating a simple home page containing only a page title. Then we will add the current date and time to this page to show how dynamic elements are handled in Wicket. Next, we will create a second page also containing only a page title. This second page will be populated with the list of movies in later chapters. Finally, we will provide a link from the home page to the movie list page. In the end, the home page will look like in Figure 1.1.

Figure 1.1: Screenshot: Home page containing a title and a link to the movie list page

1.1

Creating the Project

Creating a Wicket project requires several conguration steps. Fortunately, the Wicket plugin for NetBeans can generate and manage these conguration les so that we wont have to deal with them. To start the project, choose "File New Project" from the main menu and ll in the requested information as follows: First, select "Java Web Web Application" as the project category (Figure 1.2). Next, specify the name of the project as MovieDB. You can accept the default project location suggested by NetBeans (Figure 1.3). In the third step, select "Apache Tomcat" as the server (Figure 1.4). Finally, select "Wicket" as your framework and type itucs.blg361.moviedb as the main package (Figure 1.5).

Figure 1.2: Screenshot: NetBeans new project category form

Figure 1.3: Screenshot: NetBeans new project name form

Figure 1.4: Screenshot: NetBeans new project server form

Wicket Application Development


3 / 55

Figure 1.5: Screenshot: NetBeans new project framework form

The project will be created with some default les for a typical Wicket application (Figure 1.6). The "Projects" pane separates the project les into groups such as source packages and conguration les. We will only work with the "Source Packages" group which, as seen in the screenshot, contains a package with the name we have specied at project creation. All of the les we will edit will go into this package.

In order to simplify the discussion, remove the following les from the project: BasePage.java, HeaderPanel. html, HeaderPanel.java, and style.css. Of the remaining les, leave Application.java as it is. Modify the contents of the les HomePage.html and HomePage.java as given in Figure 1.7 and Figure 1.8, respectively. When you run the project after applying these changes, you should see the text "MovieDB Homepage" in your browser.

07-June-2011

Figure 1.6: Screenshot: NetBeans new Wicket project default les

Wicket Application Development


5 / 55

1 2 3 4 5 6 7 8 9 10 11 12

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> </head> <body> <h1>MovieDB Homepage</h1> </body> </html>

Figure 1.7: Listing: Initial home page template

1 2 3 4 5 6 7 8 9

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.WebPage; public class HomePage extends WebPage { public HomePage() { } }

Figure 1.8: Listing: Initial home page class

This initial version of the application consists of only one web page and there is no dynamic content in that page. In Wicket, we have to write two les for every page in the application: one is an HTML le that contains the template of the page, and the other is a Java source le containing the components that control the dynamic elements in the template. The name of the class (and therefore the base name of the Java source le) has to be the same as the base name of the HTML template le, as in HomePage.java and HomePage.html. Since, in our rst example, there is no dynamic element in the template, there is no need for the class to supply any information to the template.

1.2

Dynamic Content

Let us add the current date and time to the home page. We change the template as in Figure 1.9. The lines 12-14 contain the markup that will display the date and time. The wicket:id attribute indicates that the contents of this div element are dynamic. Therefore, the date and time on line 13 is just a placeholder to be replaced by the data provided by a Wicket component at runtime.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> </head> <body> <h1>MovieDB Homepage</h1> <div style="text-align: right; font-size: 70%" wicket:id="datetime"> Tue May 25 14:47:56 EEST 2010 </div> </body> </html>

Figure 1.9: Listing: Home page template showing the current date and time For every dynamic element in the template, there has to be a corresponding component in the Java controller class. The template element and the component are matched using their Wicket id attributes. In our example, that means that the HomePage class must contain a Wicket component with the Wicket id datetime. We have to choose a suitable Wicket component that can supply the requested data. In this case, since the data is a simple string, we can use the Wicket Label component (Figure 1.10). The line 11 shows how to instantite a label component: the rst parameter is the Wicket id, and the second parameter is the string content of the label. When this component is added to the page (line 12), the connection between the template element and the label component will be established.
1 2 3 4 5 6 7 8 9 10 11 12 13 14

package itucs.blg361.moviedb; import java.util.Date; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.basic.Label; public class HomePage extends WebPage { public HomePage() { Date now = new Date(); Label labelDateTime = new Label("datetime", now.toString()); this.add(labelDateTime); } }

Figure 1.10: Listing: Home page class providing the current date and time
Tip Note that you have to import the necessary Wicket component class (line 5). You can use the NetBeans autocomplete feature to simplify this. When writing the label construction statement (line 11), type the rst few characters of the component (for example, Lab) and hit Ctrl-Space. NetBeans will suggest possible imports starting with these letters. If you select the Apache Wicket Label component, NetBeans will autocomplete the text as "Label" and add the import statement to the beginning of the le.

Wicket Application Development


7 / 55

The application, as implemented so far, will run as follows:

An Application object will be instantiated. This object will set the HomePage class as the entry page of the application. A HomePage object will be instantiated. This, in turn, will instantiate a Label object with the Wicket id datetime and set the current date and time as its contents. This label will be added to the page. The HomePage.html template will be used to display the page. All elements which do not have a Wicket id will be displayed as they are. The contents of the div element with the Wicket id datetime will be replaced by the content of the label component with the same id in the HomePage object.

Adding a component to another component creates a hierarchy between the two. For example, when we added the label component to the page component, it made the label a sub-component of the page. The HTML template also has a hierarchy due to the nesting of the HTML tags. Wicket matches the hierarchy of the HTML template to the hierarchy of the Java components. For example, consider the template below where the span element is nested inside the div element:
<body> <div wicket:id="x"> some text <span wicket:id="y">some other text</span> </div> </body>

The corresponding Java page component could contain code like given below. Note that, the componentX (Wicket id x) is added to this (the page component), whereas componentY (Wicket id y) is added to componentX.
SomeComponent componentX = new SomeComponent("x"); this.add(componentX); SomeOtherComponent componentY = new SomeOtherComponent("y"); componentX.add(componentY);

The parent component of any component can be retrieved using its getParent method. Continuing with the example above, the following code will assign componentX to parent:
SomeComponent parent = (SomeComponent) componentY.getParent();

1.3

Adding Links

Now we want to create a second page which will be responsible for listing the movies in the collection. But at rst, the page will only contain some static text, it will be populated in later chapters. To create this page, right click on the itucs.blg361.moviedb package in the "Projects" pane, select "New Wicket Page" from the context menu and enter MovieListPage as the name. This will create two les as explained before: MovieListPage.html and MovieListPage.java. Modify the template as in Figure 1.11. Again, since there is no dynamic element in this template, the corresponding class is mostly empty (Figure 1.12).

1 2 3 4 5 6 7 8 9 10 11 12

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> </head> <body> <h2>Movie List</h2> </body> </html>

Figure 1.11: Listing: Initial movie list page template

1 2 3 4 5 6 7 8 9

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.WebPage; public final class MovieListPage extends WebPage { public MovieListPage() { } }

Figure 1.12: Listing: Initial movie list page class

Our next step will be to provide a link from the home page to the movie list page. In order to achieve this, we modify the template as in Figure 1.13. The code for adding the link is on line 13. This line states that the necessary information for the a element will be supplied by a Wicket component with the id list_movies in the HomePage class. As before, the href attribute of the a element is just a placeholder and will be replaced by the value supplied by a Wicket link component.

Wicket Application Development


9 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> </head> <body> <h1>MovieDB Homepage</h1> <ul> <li><a href="#" wicket:id="list_movies">List movies</a></li> </ul> <div style="text-align: right; font-size: 70%" wicket:id="datetime"> Tue May 25 14:47:56 EEST 2010 </div> </body> </html>

Figure 1.13: Listing: Home page template with link to movie list page

The Wicket framework provides an abstract Link class which can be extended to create a new link class. For the new class, we have to implement a constructor and override the onClick method that species what will happen when the link is clicked. To create this class, right click on the package name, select "New Java Class" and give it the name MovieListPageLink. Note that since this is not a Wicket page, it does not need a template associated with it.

Tip When you add the superclass declaration to the class to make the line read

public class MovieListPageLink extends Link {


you will see a light bulb in the line number column next to this line indicating that there are some problems: First, it will complain that it cannot nd the symbol for the class Link. To x this, click on the light bulb and choose the suggestion to add the import for the Wicket Link class. Next, it will say that you have to override the onClick method. To x this, again click on the light bulb and choose the suggestion to implement all abstract methods. And nally, it will require you to write a constructor for this class.

The resulting le is given in Figure 1.14. Constructor methods of Wicket component classes take an id value as their rst parameter (their Wicket id). Since there is nothing special to do, the constructor will just invoke the constructor of its superclass with the same parameter (line 8). The onClick method creates a new instance of a movie list page (line 13) and calls the setResponsePage method to direct the user to this newly created page (line 14).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.link.Link; public class MovieListPageLink extends Link { public MovieListPageLink(String id) { super(id); } @Override public void onClick() { MovieListPage target = new MovieListPage(); this.setResponsePage(target); } }

Figure 1.14: Listing: Movie list page link class

The nal step is to add an instance of this link to the constructor of the home page (Figure 1.15).
1 2 3 4 5 6 7 8

public HomePage() { MovieListPageLink movieListLink = new MovieListPageLink("list_movies"); this.add(movieListLink); Date now = new Date(); Label labelToday = new Label("datetime", now.toString()); this.add(labelToday); }

Figure 1.15: Listing: Home page class constructor with link component to movie list page

1.4

Shorter Code

There are a few things we can do to make the code shorter. First of all, we do not have to assign the result of every intermediate step to a variable. For example, when adding the date and time to the home page, we can skip the assignment of the label to a variable and send the label directly as parameter to the add method. That means, we can change the lines 5-7 of Figure 1.15 as follows:
Date now = new Date(); this.add(new Label("datetime", now.toString()));

When the project gets larger, it becomes impractical to create Java les for every custom Wicket component we need. For example, we have to extend the Link class to create a link component for every link with a different target. Instead, we can use Java anonymous classes to reduce the number of les in our project and make it easier to manage. Note that in Figure 1.16, we extend the Wicket Link class where we instantiate a link to the movie list page and therefore we do not need a MovieListPageLink class anymore.

Wicket Application Development


11 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13

public HomePage() { Link movieListLink = new Link("list_movies") { @Override public void onClick() { this.setResponsePage(new MovieListPage()); } }; this.add(movieListLink); Date now = new Date(); this.add(new Label("datetime", now.toString())); }

Figure 1.16: Listing: Home page class constructor using shorter code Exercise. Add a link from the movie list page to the home page. (p. 49)

Wicket Application Development


13 / 55

Chapter 2

Application Structure

The pages in a web application share some components such as style sheets and global navigation panels. In this chapter, we will see how to implement such components without repeating code. First, we will add a style sheet to the application. Then we will create a base page that will contain the components that all pages in the application will acquire. See Figure 2.1 for the screenshot of the resulting home page.

Figure 2.1: Screenshot: Home page containing style sheet and navigation panel

2.1

Adding Stylesheets

We want all our pages to have the same style, so we will add a style sheet to our package. Right click on the package name in the "Projects" pane and select "New Other" from the context menu. From that list, select "Web Cascading Style Sheet" and give it the name style. This will create a le named style.css. Modify this le as given in Figure 2.2.

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

body { background-color: white; color: #6F6F6F; font-family: Verdana, Helvetica, Sans-serif, sans; font-size: 10pt; padding: 10px 10px 10px 10px; margin: 10px 10px 10px 10px; } h1, h2, h3, h4 { color: #E9601A; } h2 { font-size: 1.25em; } a { color: #6F6F6F; text-decoration: underline; } th { background: #C3C3C3; color: white; font-weight: bold; text-align: left; }

Figure 2.2: Listing: Application style sheet

To add this style sheet to the home page, change the head part of its template as in Figure 2.3. The HomePage class now has to contain a component for this style sheet, so we add a Wicket StyleSheetReference component in the constructor (Figure 2.4).

1 2 3 4 5

<head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head>

Figure 2.3: Listing: Home page template with style sheet

Wicket Application Development


15 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public HomePage() { this.add(new StyleSheetReference("stylesheet", HomePage.class, "style.css")); Link movieListLink = new Link("list_movies") { @Override public void onClick() { this.setResponsePage(new MovieListPage()); } }; this.add(movieListLink); Date now = new Date(); this.add(new Label("datetime", now.toString())); }

Figure 2.4: Listing: Home page class constructor with style sheet component

2.2

Shared Components

Adding shared components to multiple pages in an application is a tedious and error-prone approach. Therefore, we would like to be able to specify these at one point and let pages get them from that single source. For instance, in the previous section, we had to add the style sheet component to every page class. Instead, using inheritance., we can add it to a base page and extend all application pages from this base page. Add a new Java class named BasePage to your package (careful: a Java class, not a Wicket page; there will be no template for this class). The code of the class is given in Figure 2.5.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.model.IModel; import org.apache.wicket.markup.html.resources.StyleSheetReference; public class BasePage extends WebPage { public BasePage() { this(null); } public BasePage(IModel model) { super(model); this.add(new StyleSheetReference("stylesheet", BasePage.class, "style.css")); } }

Figure 2.5: Listing: Base page class with style sheet component Now the HomePage class will extend this BasePage instead of the generic WebPage (Figure 2.6).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class HomePage extends BasePage { public HomePage() { Link movieListLink = new Link("list_movies") { @Override public void onClick() { this.setResponsePage(new MovieListPage()); } }; this.add(movieListLink); Date now = new Date(); this.add(new Label("datetime", now.toString())); } }

Figure 2.6: Listing: Home page class extending the base page class

Another improvement concerns the navigation. We might need links to the home page or movie list page from many pages in the application. So, having a global navigation mechanism where all such links will be available in all pages might be a good idea. Wicket provides panels which let us handle components as a group. Add a Wicket Panel named HeaderPanel to your package and you will again get an HTML template and a Java source le. The template is given in Figure 2.7. We are only interested in the lines 9-14, the part where we describe the content of the panel. The corresponding Java class (Figure 2.8) places the link components into this panel component.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <title>MovieDB</title> </head> <body> <wicket:panel> <ul> <li><a href="#" wicket:id="home">Home</a></li> <li><a href="#" wicket:id="list_movies">List movies</a></li> </ul> </wicket:panel> </body> </html>

Figure 2.7: Listing: Navigation panel template

Wicket Application Development


17 / 55

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

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.link.Link; import org.apache.wicket.markup.html.panel.Panel; public class HeaderPanel extends Panel { public HeaderPanel(String id) { super(id); Link homeLink = new Link("home") { @Override public void onClick() { this.setResponsePage(new HomePage()); } }; this.add(homeLink); Link movieListLink = new Link("list_movies") { @Override public void onClick() { this.setResponsePage(new MovieListPage()); } }; this.add(movieListLink); } }

Figure 2.8: Listing: Navigation panel class

Now our actual pages (the home page and the movie list page) will only add this panel component instead of the link components. The home page template has to be modied as in Figure 2.9. To make this panel available in all pages, we add it to the base page (Figure 2.10). The only component left in the home page is the date and time label (Figure 2.11).

1 2 3 4 5 6 7 8 9

<body> <div wicket:id="mainNavigation">links to common pages</div> <h1>MovieDB Homepage</h1> <div style="text-align: right; font-size: 70%" wicket:id="datetime"> Tue May 25 14:47:56 EEST 2010 </div> </body>

Figure 2.9: Listing: Home page template with navigation panel

1 2 3 4 5 6

public BasePage(IModel model) { super(model); this.add(new StyleSheetReference("stylesheet", BasePage.class, "style.css")); this.add(new HeaderPanel("mainNavigation")); }

Figure 2.10: Listing: Base page class constructor with navigation panel

1 2 3 4 5 6 7

public class HomePage extends BasePage { public HomePage() { Date now = new Date(); this.add(new Label("datetime", now.toString())); } }

Figure 2.11: Listing: Home page class with navigation panel Exercise. Arrange the MovieListPage class so that it will use the style sheet and the navigation panel. (p. 50)

Wicket Application Development


19 / 55

Chapter 3

Data Model

In this chapter, we will create the Java classes in our data model, that is, the classes for movies and movie collections. First we will implement the movie class and ll the movie list page with some in-place generated test data. Then we will implement the movie collection class which will hold some application-wide test data. The resulting movie list page will look like in Figure 3.1.

Figure 3.1: Screenshot: Movie list page populated with test data

3.1

Movies

To represent movies in the application, we implement a plain Java class as in Figure 3.2. At rst, it only contains title and year attributes, along with their getters and setters. Apart from the default constructor which takes no parameters, there is one more constructor which takes the movie title as parameter.

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

package itucs.blg361.moviedb; public class Movie { private String title = null; private Integer year = null; public Movie() { } public Movie(String aTitle) { this.setTitle(aTitle); } public void setTitle(String aTitle) { this.title = aTitle; } public String getTitle() { return this.title; } public void setYear(Integer aYear) { this.year = aYear; } public Integer getYear() { return this.year; } }

Figure 3.2: Listing: Movie class

3.2

Displaying Lists

To display a list of movies, we have to connect an HTML template element to a Wicket ListView component. These components are associated with Java lists and every element in the Java list is associated with a ListItem component in the list view. If the Wicket id in the template matches a list view component, the underlying markup will be generated for each element in the list. We change the code for the movie list template as in Figure 3.3. This code will generate a tr element for each movie in the list. Again, note that, "The Matrix" and "1999" are just placeholders, they will be replaced when the page is actually rendered.

Wicket Application Development


21 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14

<body> <div wicket:id="mainNavigation">links to common pages</div> <h2>Movie List</h2> <table> <tr wicket:id="movie_list"> <td> <span wicket:id="title">The Matrix</span> (<span wicket:id="year">1999</span>) </td> </tr> </table> </body>

Figure 3.3: Listing: Movie list template using a list view

ListView is an abstract class that has to be extended in order to be used (Figure 3.4). By passing the movies list as the second parameter to the constructor (line 20), we associate it with this list view component and every item (movie) in the list will be associated with the corresponding list item component in this list view. Since we dont have a data source to supply us with the movie information at the moment, we generate a sample list (lines 12-18).

When extending the ListView class, we have to implement the populateItem method which will be executed for every element in the list. The current element will be passed as the item parameter, and we can get the Java object associated with this item using its getModelObject method. That means, the item.getModelObject() call on line 24 will give us the reference of the associated movie object. Then we use Wicket Label components to display the title and year of the movie on the web page (lines 25-26).

Note the hierarchies in the HTML template and the Java components:

The Wicket id of the tr element matches the Wicket id of the ListView component object.

The Wicket id of either of the span elements matches the Wicket id of one of the Label components.

Also note that the text for a label must be a string, so we convert the year of the movie to a string (line 26).

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

package itucs.blg361.moviedb; import import import import import java.util.LinkedList; java.util.List; org.apache.wicket.markup.html.basic.Label; org.apache.wicket.markup.html.list.ListItem; org.apache.wicket.markup.html.list.ListView;

public final class MovieListPage extends BasePage { public MovieListPage() { List<Movie> movies = new LinkedList<Movie>(); Movie movie1 = new Movie("The Shining"); movie1.setYear(1980); movies.add(movie1); Movie movie2 = new Movie("Barton Fink"); movie2.setYear(1991); movies.add(movie2); ListView movieListView = new ListView("movie_list", movies) { @Override protected void populateItem(ListItem item) { Movie movie = (Movie) item.getModelObject(); item.add(new Label("title", movie.getTitle())); item.add(new Label("year", movie.getYear().toString())); } }; this.add(movieListView); } }

Figure 3.4: Listing: Movie list page class with sample movie data Wicket also supports a simpler way of creating custom list views. If your code conforms to a few basic Java conventions, Wicket can automatically map the model object (in our case, the movie) to the component (the list view item). The rule is that the model object has to dene getters and setters for each of its attributes using a standard interface: The name of the getter method for an attribute has to start with "get", followed by the name of the attribute with its rst letter capitalized. For instance, if the name of the attribute is "title", the name of the getter has to be "getTitle". This method should take no parameters and return the value of the attribute in a proper format. The name of the setter method for an attribute has to start with "set", followed by the name of the attribute with its rst letter capitalized. Again, if the name of the attribute is "title", the name of the setter has to be "setTitle". This method should take the value to be assigned to the attribute as its only parameter and return nothing. Getters for boolean attributes have to be named starting with "is" instead of "get". For instance, if the attribute is a boolean named "successful", the name of the getter has to be "isSuccessful". Since our Movie class already conforms to these rules, we just have to extend the PropertyListView class instead of the ListView class to use this feature (Figure 3.5). Note that, in this code, we do not supply the contents for the title and year labels (lines 6-7). For the Wicket id title, Wicket will look for a method named getTitle and for the id year, the method named getYear. Also, the conversion from integer to string for years is handled automatically.

Wicket Application Development


23 / 55

1 2 3 4 5 6 7 8 9

PropertyListView movieListView = new PropertyListView("movie_list", movies) { @Override protected void populateItem(ListItem item) { item.add(new Label("title")); item.add(new Label("year")); } };

Figure 3.5: Listing: Property list view component for movie list

3.3

Movie Collections

Next, we will implement a class that will represent a movie collection. This class will contain a list of movie objects and some methods to interact with that list such as adding or deleting movies and getting a list of all movies (Figure 3.6).
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

package itucs.blg361.moviedb; import java.util.LinkedList; import java.util.List; public class MovieCollection { private List<Movie> movies; public MovieCollection() { this.movies = new LinkedList<Movie>(); } public List<Movie> getMovies() { return this.movies; } public void addMovie(Movie aMovie) { this.movies.add(aMovie); } public void deleteMovie(Movie aMovie) { this.movies.remove(aMovie); } }

Figure 3.6: Listing: Movie collection class In our earlier example, the list of movies was generated by the page that lists the movies. This is obviously not the proper place to generate the collection because there should be one movie collection object which is accessible to all components in the application. Therefore, the better place to generate it would be in the Application class (Figure 3.7). So, we add a collection object member to this class (line 7), instantiate it in the constructor (line 10) and

add a getCollection method which will return this collection (lines 24-26). Again, we provide some sample data (lines 12-17) in order to test our application.

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

package itucs.blg361.moviedb; import org.apache.wicket.protocol.http.WebApplication; public class Application extends WebApplication { private MovieCollection collection; public Application() { this.collection = new MovieCollection(); Movie movie1 = new Movie("The Shining"); movie1.setYear(1980); this.collection.addMovie(movie1); Movie movie2 = new Movie("Barton Fink"); movie2.setYear(1991); this.collection.addMovie(movie2); } public Class getHomePage() { return HomePage.class; } public MovieCollection getCollection() { return this.collection; } }

Figure 3.7: Listing: Application class containing the collection object

Now that the MovieListPage class does not generate the list of movies, it has to retrieve them from the application. So the lines 12-18 in Figure 3.4 will be replaced by the lines 2-4 in Figure 3.8. We can use the getApplication method of web page classes to get a handle of the application object that contains this web page (note that this is a Wicket application object that we have to cast to our Application class as in line 2).

Wicket Application Development


25 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public MovieListPage() { Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); List<Movie> movies = collection.getMovies(); PropertyListView movieListView = new PropertyListView("movie_list", movies) { @Override protected void populateItem(ListItem item) { item.add(new Label("title")); item.add(new Label("year")); } }; this.add(movieListView); }

Figure 3.8: Listing: Movie list page class constructor getting movie list from application

The "model" is a very important concept in Wicket development but we have discussed it here very very briey. You are encouraged to read the section titled "Working with Wicket Models" in the Wicket reference guide: https://cwiki.apache.org/WICKET/working-with-wicket-models.html

Exercise. Add a page that will display a movie (Figure 3.9). Then, organize the movie list page so that the entries are links to pages that will display the selected movie (Figure 3.10). (p. 50)

Figure 3.9: Screenshot: Movie display page

Figure 3.10: Screenshot: Movie list page with links to movie display pages

Wicket Application Development


27 / 55

Chapter 4

Forms

In this chapter, we will implement the parts that will enable us to modify the collection. This includes the operations to add a new movie and to delete an existing movie. Both operations require the use of forms containing components such as text boxes and check boxes. The resulting pages are given in Figure 4.1 and Figure 4.2.

Figure 4.1: Screenshot: Movie edit page

Figure 4.2: Screenshot: Movie list page with delete option

4.1

Text Boxes

First, we will add a new page to our application for adding movies. In order to do this, we need a page to edit the data of a movie. Add a Wicket page named MovieEditPage to the package and modify its contents as in Figure 4.3. From this template we can see that the corresponding Wicket page class has to contain a component with the Wicket id movie_edit which has to be able to handle forms (line 13). And this component has to contain two other components with the Wicket ids title and year, both of which will be responsible for handling text boxes (lines 17 and 21).

Wicket Application Development


29 / 55

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

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head> <body> <div wicket:id="mainNavigation">links to common pages</div> <form action="#" wicket:id="movie_edit"> <table> <tr> <th>Title:</th> <td><input wicket:id="title" type="text"/></td> </tr> <tr> <th>Year:</th> <td><input wicket:id="year" type="text" size="4"/></td> </tr> </table> <input value="Save" name="save" type="submit"/> </form> </body> </html>

Figure 4.3: Listing: Page template for editing a movie

Let us start by creating the form element. Add a new Java class with the name MovieEditForm to the package and specify the Wicket Form component as its superclass (Figure 4.4). This form component will take the movie object it will edit as a parameter to its constructor (line 9). The movie object will serve as the model for the component, i.e. it will provide the data for any of the components contained in the page. To achieve this, we create a Wicket CompoundPropertyModel object from the movie and set it as the model of this form (lines 12-13).

To control text boxes in the application, we use TextField components. We add two such components to the form (lines 15-16) using the Wicket ids specied in the template. Since we have set a movie object as the model of this form, the associations between the movie and the components will be handled automatically. For example, whenever the contents of the text box for the title needs to be determined, the getTitle method will be called for the movie object. And when the form is submitted, the setTitle method will be called using the contents of the text box as parameter.

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

package itucs.blg361.moviedb; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.CompoundPropertyModel; public class MovieEditForm extends Form { public MovieEditForm(String id, Movie aMovie) { super(id); CompoundPropertyModel model = new CompoundPropertyModel(aMovie); this.setModel(model); this.add(new TextField("title")); this.add(new TextField("year")); } @Override public void onSubmit() { Movie movie = (Movie) this.getModelObject(); Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); collection.addMovie(movie); this.setResponsePage(new MovieDisplayPage(movie)); } }

Figure 4.4: Listing: Movie edit form class When implementing a form, another major task is to specify what will happen when the form is submitted. We do this by overriding the onSubmit method of the Wicket Form class. For this particular form, we need to add the movie object to the collection; therefore, we get the movie which is the model object of this form (line 21), we get a handle to the application object (line 22), we get the collection object from the application (line 23), add the movie to the collection (line 24), and redirect the user to the page that will display this movie (line 25). Now we have to add this form component to the MovieEditPage class to complete the connection between the template and all the Wicket components (Figure 4.5). This page also takes the movie to be edited as a parameter to its constructor (line 5) and passes it to the form component (line 6).
1 2 3 4 5 6 7 8

package itucs.blg361.moviedb; public final class MovieEditPage extends BasePage { public MovieEditPage(Movie aMovie) { this.add(new MovieEditForm("movie_edit", aMovie)); } }

Figure 4.5: Listing: Movie edit page class To provide a link to this page from other pages we add a list item to the markup of the header panel template (Fig-

Wicket Application Development


31 / 55

ure 4.6) and a link component to the corresponding Java class (Figure 4.7). When this link is clicked, we instantiate a new movie object and send it to the edit page as parameter (lines 18-19). Note that, since all Movie class constructors require a title, an empty string is provided as title. Also note that, now that we can add movies to the collection, we dont need the sample movie data anymore and we can delete the relevant lines from the constructor of the Application class (Figure 3.7 lines 12-17).
1 2 3 4 5 6 7 8 9

<body> <wicket:panel> <ul> <li><a href="#" wicket:id="home">Home</a></li> <li><a href="#" wicket:id="list_movies">List movies</a></li> <li><a href="#" wicket:id="add_movie">Add movie</a></li> </ul> </wicket:panel> </body>

Figure 4.6: Listing: Header panel template with link to movie adding page

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

public HeaderPanel(String id) { super(id); Link homeLink = new Link("home") { ... }; this.add(homeLink); Link movieListLink = new Link("list_movies") { ... }; this.add(movieListLink); Link movieAddLink = new Link("add_movie") { @Override public void onClick() { Movie movie = new Movie(""); this.setResponsePage(new MovieEditPage(movie)); } }; this.add(movieAddLink); }

Figure 4.7: Listing: Header panel class constructor with link component to movie adding page

4.2

Check Boxes

Our next step is to delete movies from the collection. We will change the movie list page so that there will be a check box next to every movie and a delete button at the bottom of the page. When the delete button is pressed all the checked movies will be deleted.

First, we change the template for the movie list page as in Figure 4.8. The changes from the earlier version (Figure 3.3) are as follows: There is a check box in front of every movie link (line 10). These check boxes will be controlled by Wicket Check components in the Java code. We will group check boxes so that we can handle them easier using a CheckGroup component in Wicket. We use the div element on line 7 to establish this connection. Since check boxes need to be part of a form, we put all the elements under a form element (line 6). There is now a button for submitting this form (line 20).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

<body> <div wicket:id="mainNavigation">links to common pages</div> <h2>Movie List</h2> <form action="#" wicket:id="movie_list_form"> <div wicket:id="selected_movies"> <table> <tr wicket:id="movie_list"> <td><input type="checkbox" wicket:id="selected"/></td> <td> <a href="#" wicket:id="movie_link"> <span wicket:id="title">The Matrix</span> (<span wicket:id="year">1999</span>) </a> </td> </tr> </table> </div> <input type="submit" value="Delete" name="delete"/> </form> </body>

Figure 4.8: Listing: Movie list template with check boxes for entries The movie list view we have used so far is no longer sufcient for handling this template. We need a movie list form that will include the movie list view. The implementation is given in Figure 4.9. This component will keep the list of selected movies in the selectedMovies attribute (line 14), which is initialized as an empty linked list in the constructor (line 18). The constructor creates and adds a check box group component (line 20-22) which it associates with this list. This way, all movies associated with the check boxes under this check group will be elements of the list. Until now, the movie list view used to be directly under the movie list page but now it has to be placed under this check group according to the hierarchy in the HTML template. Therefore we move the code generating the movie list view from the MovieListPage class to this class (lines 24-41) and add the list view under the check group (line 42). This code contains a new statement for adding a check box to the list item (line 38). Sending the model of the item as a parameter to the constructor of the Check component (via the getModel method) creates the connection between the check box and the check group it is in. The MovieListPage class now only needs the form component, so its constructor will be as in Figure 4.10.

Wicket Application Development


33 / 55

As before, we override the onSubmit method to determine the action to take when the submit button is clicked. Since the selectedMovies attribute keeps the list of movies selected for deletion, we iterate over its elements and delete them from the collection (lines 49-51). Then we redirect the user to the movie list page (line 52).

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

package itucs.blg361.moviedb; import import import import import import import import java.util.LinkedList; java.util.List; org.apache.wicket.markup.html.basic.Label; org.apache.wicket.markup.html.form.Check; org.apache.wicket.markup.html.form.CheckGroup; org.apache.wicket.markup.html.form.Form; org.apache.wicket.markup.html.list.ListItem; org.apache.wicket.markup.html.list.PropertyListView;

public class MovieListForm extends Form { private List<Movie> selectedMovies; public MovieListForm(String id) { super(id); this.selectedMovies = new LinkedList<Movie>(); CheckGroup movieCheckGroup = new CheckGroup("selected_movies", this.selectedMovies); this.add(movieCheckGroup); Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); List<Movie> movies = collection.getMovies(); PropertyListView movieListView = new PropertyListView("movie_list", movies) { @Override protected void populateItem(ListItem item) { Movie movie = (Movie) item.getModelObject(); MovieDisplayPageLink movieLink = new MovieDisplayPageLink("movie_link", movie); movieLink.add(new Label("title")); movieLink.add(new Label("year")); item.add(new Check("selected", item.getModel())); item.add(movieLink); } }; movieCheckGroup.add(movieListView); } @Override public void onSubmit() { Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); for (Movie movie : this.selectedMovies) { collection.deleteMovie(movie); } this.setResponsePage(new MovieListPage()); } }

Figure 4.9: Listing: Movie list form class

Wicket Application Development


35 / 55

1 2 3 4

public MovieListPage() { MovieListForm movieListForm = new MovieListForm("movie_list_form"); this.add(movieListForm); }

Figure 4.10: Listing: Movie list page class containing movie list form component Exercise. Add a link to the movie display page which, when clicked, will take the user to the movie edit page. After saving the movie should be to updated in the collection (not added a second time). (p. 52)

Wicket Application Development


37 / 55

8-June-2011, Wednesday

Chapter 5

Data Persistence

A major problem with the application as implemented so far is that the data the user enters do not persist. Every time, the application starts with an empty collection and added movies are lost when the application is shut down. In this chapter we will see how to store the data in a database. This chapter has nothing to do with Wicket; it just uses various data persistence methods and assumes that the reader knows about these.

Let us create a folder to hold the JAR les of the external packages we will need in our project. Go to the "Files" tab next to the "Projects" tab in the left pane, right click on the toplevel project entry ("MovieDB") and select "New Other". In the le type selection form, select "Other" from the left list and "Folder" from the right list (Figure 5.1). In the next form, type "lib" as the folder name.

Figure 5.1: Screenshot: Creating a folder for external libraries in Netbeans

5.1

A Collection Interface

In order to make it easier to switch between different persistence mechanisms, we will rst create a simple interface that all these mechanisms will implement. In the "Projects" pane, right click on the "itucs.blg361.moviedb" source package and create a new Java interface with the name "IMovieCollection". The interface has to contain the methods as shown in the Figure 5.2.

Wicket Application Development


39 / 55

1 2 3 4 5 6 7 8 9 10

public interface IMovieCollection { public List<Movie> getMovies(); public void addMovie(Movie aMovie); public void deleteMovie(Movie aMovie); public void updateMovie(Movie aMovie); }

Figure 5.2: Listing: Movie collection interface. The MovieCollection class based on lists already implements this interface; we just have to acknowledge this by changing the class denition:
public MovieCollection implements IMovieCollection { ... }

We also have to change the code pieces that reference the MovieCollection class so that they will use the IMovieCollection interface. That means changing the Application class as in Figure 5.3 so that the only place where the actual collection implementation is used will be the constructor of the application object. Also the references to the MovieCollection class in the MovieEditForm and MovieListForm classes have to be changed to the IMovieCollection interface.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class Application extends WebApplication { private IMovieCollection collection; public Application() { this.collection = new MovieCollection(); } public Class getHomePage() { return HomePage.class; } public IMovieCollection getCollection() { return this.collection; } }

Figure 5.3: Listing: Movie collection interface.

5.2

JDBC

To keep things simple and to avoid database installation or conguration issues, we will use SQLite as our database. Download the JDBC driver JAR le for SQLite from the following page and put it in the "lib" directory you have created:

http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC Right click on your project in the NetBeans "Projects" pane, select "Properties" from the context menu and add the downloaded JAR le to your project using the "Add JAR/Folder" button in the "Libraries" menu (Figure 5.4).

Figure 5.4: Screenshot: NetBeans project properties form for external libraries

Next, create a database with the name "movies.db" in your home directory and create a table in it using the following SQL statement:
CREATE TABLE MOVIE (ID INTEGER PRIMARY KEY, TITLE VARCHAR(80), YEAR INTEGER)

Tip You can use the SQLite Manager add-on for Firefox to manage SQLite databases:

https://addons.mozilla.org/en-US/firefox/addon/5817/

As indicated by the SQL table creation statement above, movie objects now have id attributes. We modify our Movie class (Figure 5.5) to add this attribute (line 3) and its getter and setter methods (lines 9-15).

Wicket Application Development


41 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

public class Movie { private Integer id = null; private String title; private Integer year; ... public void setId(Integer anId) { this.id = anId; } public Integer getId() { return this.id; } ... }

Figure 5.5: Listing: Movie class with id attribute

Now we have to implement a MovieCollectionJDBC class (Figure 5.6) which will implement our common collection interface but this time using a database to store the data (line 1-3). The database is assumed to be a le with the name "movies.sqlite" in the users home directory (13-16).

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

public class MovieCollectionJDBC implements IMovieCollection { private Connection db; public MovieCollectionJDBC() { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException ex) { throw new UnsupportedOperationException(ex.getMessage()); } try { String homeDir = System.getProperty("user.home"); String dbFileName = homeDir + File.separator + "movies.sqlite"; String jdbcURL = "jdbc:sqlite:" + dbFileName; this.db = DriverManager.getConnection(jdbcURL); } catch (SQLException ex) { throw new UnsupportedOperationException(ex.getMessage()); } } public List<Movie> getMovies() { ... } public void addMovie(Movie aMovie) { ... } public void deleteMovie(Movie aMovie) { ... } public void updateMovie(Movie aMovie) { ... } }

Figure 5.6: Listing: Movie collection class using a database

The methods for getting the movie list, adding a movie, and removing a movie are simple JDBC operations. They are not in the scope of this tutorial. Only note that, in order to simplify the code, the example throws an UnsupportedOperationException whereever it needs to throw an exception. A real-life application should throw more appropriate exceptions.

Wicket Application Development


43 / 55

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

public List<Movie> getMovies() { List<Movie> movies = new LinkedList<Movie>(); try { String query = "SELECT ID, TITLE, YEAR FROM MOVIE"; Statement statement = this.db.createStatement(); ResultSet result = statement.executeQuery(query); while (result.next()) { Integer id = result.getInt("ID"); String title = result.getString("TITLE"); Integer year = result.getInt("YEAR"); Movie movie = new Movie(title); movie.setId(id); movie.setYear(year); movies.add(movie); } result.close(); statement.close(); } catch (SQLException ex) { throw new UnsupportedOperationException(ex.getMessage()); } return movies; }

Figure 5.7: Listing: Getting movies from a collection using JDBC

1 2 3 4 5 6 7 8 9 10 11

public void addMovie(Movie aMovie) { try { String query = "INSERT INTO MOVIE(TITLE, YEAR) VALUES(?, ?)"; PreparedStatement statement = this.db.prepareStatement(query); statement.setString(1, aMovie.getTitle()); statement.setInt(2, aMovie.getYear()); statement.executeUpdate(); } catch (SQLException ex) { throw new UnsupportedOperationException(ex.getMessage()); } }

Figure 5.8: Listing: Adding a movie to a collection using JDBC

1 2 3 4 5 6 7 8 9 10 11

public void deleteMovie(Movie aMovie) { try { String query = "DELETE FROM MOVIE WHERE (ID = ?)"; PreparedStatement statement = this.db.prepareStatement(query); statement.setInt(1, aMovie.getId()); statement.executeUpdate(); statement.close(); } catch (SQLException ex) { throw new UnsupportedOperationException(ex.getMessage()); } }

Figure 5.9: Listing: Deleting a movie from a collection using JDBC

1 2 3 4 5 6 7 8 9 10 11 12 13

public void updateMovie(Movie aMovie) { try { String query = "UPDATE MOVIE SET TITLE = ?, YEAR = ? WHERE (ID = ?)"; PreparedStatement statement = this.db.prepareStatement(query); statement.setString(1, aMovie.getTitle()); statement.setInt(2, aMovie.getYear()); statement.setInt(3, aMovie.getId()); statement.executeUpdate(); statement.close(); } catch (SQLException ex) { throw new UnsupportedOperationException(ex.getMessage()); } }

Figure 5.10: Listing: Updating a movie in a collection using JDBC After that, we just have to change the collection implementation chosen in the Application constructor:
public Application() { this.collection = new MovieCollectionJDBC(); }

Wicket Application Development


45 / 55

8-June-2011, Wednesday

Appendix A

Development Environment

I assume that you already have the Java Development Kit (JDK) installed on your computer. This tutorial has been prepared using the Sun JDK version 1.6.0; you can nd it on the Java web site: http://java.sun.com/.

A.1

Installing NetBeans

You can get the NetBeans installer for your operating system from the NetBeans web site:

http://netbeans.org/

This tutorial is based on version 6.8; later versions might or might not support Wicket plugins -or the plugin used in this tutorial. In case you have problems, you can nd this exact version on the page:

http://netbeans.org/downloads/6.8/

On the download page, choose a bundle that includes the "Java Web and EE" component. At the time of this writing, these are "Java" and "All". The "Java" bundle is sufcient for the purposes of this tutorial.

After starting the installer, the welcome page displays the default installation options (Figure A.1). Though you can accept these defaults, it is recommended that you customize the options and select only the following components (Figure A.2): Base IDE, Java SE, Java Web and EE, Features on Demand, Apache Tomcat.

Figure A.1: Screenshot: NetBeans install welcome page

Figure A.2: Screenshot: NetBeans install option customization form

A.2

Installing the Wicket Plugin

To develop Wicket applications in NetBeans, we will install the Wicket plugin. This plugin contains everything required to develop and run Wicket applications, so no separate Wicket installation is necessary.

Wicket Application Development


47 / 55

Download the NetBeans Wicket plugin les at the following site: http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=3586 Select "Tools Plugins" from the NetBeans menu and open the "Downloaded" tab. Click the "Add Plugins" button and select the downloaded les (Figure A.3). Hit the "Install" button and restart NetBeans.

Figure A.3: Screenshot: NetBeans Wicket plugin installation

Wicket Application Development


49 / 55

Appendix B

Solutions to Exercises
Chapter: Basics Add a link from the movie list page to the home page. MovieListPage.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> </head> <body> <h2>Movie List</h2> <ul> <li><a href="#" wicket:id="home">Home</a></li> </ul> </body> </html>

MovieListPage.java
package itucs.blg361.moviedb; import org.apache.wicket.markup.html.WebPage; import org.apache.wicket.markup.html.link.Link; public final class MovieListPage extends WebPage { public MovieListPage() { Link homeLink = new Link("home") { @Override public void onClick() { this.setResponsePage(new HomePage()); } }; this.add(homeLink); }

Chapter: Application Structure Arrange the MovieListPage class so that it will use the style sheet and the navigation panel. MovieListPage.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head> <body> <div wicket:id="mainNavigation">links to common pages</div> <h2>Movie List</h2> </body> </html>

MovieListPage.java
package itucs.blg361.moviedb; public final class MovieListPage extends BasePage { public MovieListPage() { } }

Chapter: Data Model Add a page that will display a movie. Then, organize the movie list page so that the entries are links to pages that will display the selected movie. MovieDisplayPage.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head> <body> <div wicket:id="mainNavigation">links to common pages</div> <h2 wicket:id="title">The Matrix</h2> <table> <tr> <th>Year:</th> <td wicket:id="year">1999</td>

Wicket Application Development


51 / 55

</tr> </table> </body> </html>

MovieDisplayPage.java
package itucs.blg361.moviedb; import org.apache.wicket.markup.html.basic.Label; public final class MovieDisplayPage extends BasePage { public MovieDisplayPage(Movie aMovie) { this.add(new Label("title", aMovie.getTitle())); this.add(new Label("year", aMovie.getYear().toString())); } }

MovieDisplayPageLink.java
package itucs.blg361.moviedb; import org.apache.wicket.markup.html.link.Link; public class MovieDisplayPageLink extends Link { private Movie movie; public MovieDisplayPageLink(String id, Movie aMovie) { super(id); this.movie = aMovie; } @Override public void onClick() { this.setResponsePage(new MovieDisplayPage(this.movie)); } }

MovieListPage.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head> <body> <div wicket:id="mainNavigation">links to common pages</div> <h2>Movie List</h2> <table>

<tr wicket:id="movie_list"> <td> <a href="#" wicket:id="movie_link"> <span wicket:id="title">The Matrix</span> (<span wicket:id="year">1999</span>) </a> </td> </tr> </table> </body> </html>

MovieListPage.java
package itucs.blg361.moviedb; import import import import java.util.List; org.apache.wicket.markup.html.basic.Label; org.apache.wicket.markup.html.list.ListItem; org.apache.wicket.markup.html.list.PropertyListView;

public final class MovieListPage extends BasePage { public MovieListPage() { Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); List<Movie> movies = collection.getMovies(); PropertyListView movieListView = new PropertyListView("movie_list", movies) { @Override protected void populateItem(ListItem item) { Movie movie = (Movie) item.getModelObject(); MovieDisplayPageLink movieLink = new MovieDisplayPageLink("movie_link", movie); movieLink.add(new Label("title")); movieLink.add(new Label("year")); item.add(movieLink); } }; this.add(movieListView); } }

Chapter: Forms Add a link to the movie display page which, when clicked, will take the user to the movie edit page. After saving the movie should be to updated in the collection (not added a second time). MovieCollection.java
package itucs.blg361.moviedb; import java.util.LinkedList; import java.util.List; public class MovieCollection {

Wicket Application Development


53 / 55

private List<Movie> movies; public MovieCollection() { this.movies = new LinkedList<Movie>(); } public List<Movie> getMovies() { return this.movies; } public void addMovie(Movie aMovie) { this.movies.add(aMovie); } public void deleteMovie(Movie aMovie) { this.movies.remove(aMovie); } public void updateMovie(Movie aMovie) { } }

MovieEditForm.java
package itucs.blg361.moviedb; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.CompoundPropertyModel; public class MovieEditForm extends Form { private boolean newMovie; public MovieEditForm(String id, Movie aMovie, boolean newMovieFlag) { super(id); CompoundPropertyModel model = new CompoundPropertyModel(aMovie); this.setModel(model); this.add(new TextField("title")); this.add(new TextField("year")); this.newMovie = newMovieFlag; } @Override public void onSubmit() { Movie movie = (Movie) this.getModelObject(); Application app = (Application) this.getApplication(); MovieCollection collection = app.getCollection(); if (this.newMovie) { collection.addMovie(movie); } else { collection.updateMovie(movie); }

this.setResponsePage(new MovieDisplayPage(movie)); } }

MovieEditPage.java
package itucs.blg361.moviedb; public final class MovieEditPage extends BasePage { public MovieEditPage(Movie aMovie) { this.add(new MovieEditForm("movie_edit", aMovie, true)); } public MovieEditPage(Movie aMovie, boolean newMovieFlag) { this.add(new MovieEditForm("movie_edit", aMovie, newMovieFlag)); } }

MovieDisplayPage.html
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns:wicket="http://wicket.apache.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>MovieDB</title> <link wicket:id="stylesheet" rel="stylesheet" type="text/css" href="#"/> </head> <body> <div wicket:id="mainNavigation">links to common pages</div> <h2 wicket:id="title">The Matrix</h2> <table> <tr> <th>Year:</th> <td wicket:id="year">1999</td> </tr> </table> <p><a href="#" wicket:id="edit_link">Edit</a></p> </body> </html>

MovieDisplayPage.java
package itucs.blg361.moviedb; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.Link; public final class MovieDisplayPage extends BasePage { private Movie movie; public MovieDisplayPage(Movie aMovie) {

Wicket Application Development


55 / 55

this.movie = aMovie; this.add(new Label("title", this.movie.getTitle())); this.add(new Label("year", Integer.toString(this.movie.getYear()))); Link editLink = new Link("edit_link") { @Override public void onClick() { MovieDisplayPage parent = (MovieDisplayPage) this.getParent(); this.setResponsePage(new MovieEditPage(parent.movie, false)); } }; this.add(editLink); } }