Build better Web applications with Google Sitebricks

Create a sample Java Web application using Maven, Sitebricks, and Guice

Do you want to quickly build a Web application that can be maintained, or worked on, by other people? Google Sitebricks lets you rapidly develop Web applications that are built to last. Sitebricks uses dependency injection to do away with boilerplate code. It leverages type safety and inference to check the correctness of your application, so you catch problems at compile time instead of run time. In this article, learn how to build Web applications powered by Google Sitebricks.

Michael Galpin, Software architect, eBay

Michael Galpin's photoMichael Galpin is an architect at eBay. He is a frequent contributor to developerWorks. He has spoken at various technical conferences, including JavaOne, EclipseCon, and AjaxWorld. To get a preview of what he is working on next, follow @michaelg on Twitter.



16 February 2010

Also available in Chinese Russian Japanese

Introduction

Sitebricks, which is still in beta, is a new Java™ Web application framework. You might wonder, "Why do I need yet another Web framework?" With Google Sitebricks you can rapidly build a Web application that can be maintained, or worked on, by others. Sitebricks is built on top of Guice. It expands and extends many of the principles of Guice to the Web. Like Guice, it makes aggressive use of annotations to keep configuration as part of the code. You will not have to create or edit a lot of XML files to use Guice. Instead, Sitebricks lets you create Web applications while writing a lot less code. The code you write will be straightforward. You can look at Sitebricks code and quickly understand what's going on. Sitebricks does not compromise type safety or performance.

In this article, learn about the features of Sitebricks by creating a sample Web application. The example application, called Reviewr, will let users read and write reviews of restaurants. You will use a database to persist the data used in the application (but won't spend too much time on that). The example focuses on the parts of the application that exploit the features of Sitebricks.

Download the Reviewr application sample code from the table at the bottom of this article.

Prerequisites

See Resources to download the prerequisite programs.

  • Sitebricks is still in beta; version 0.8 is used in this article.
  • Currently, the easiest way to use Sitebricks is with Maven. Maven 2.0.9 was used for this article. Of course, Maven will pull in all of the other dependencies needed by Sitebricks, such as Google Guice.
  • Sitebricks relies on some advanced features in Java, so you will need JDK 1.6. In this article, JDK 1.6.0_17 was used.
  • You can use a Java Web application server to deploy the sample application in this article, but it is not required.
    Alternatively, the sample application includes an embedded Jetty server that you can use for testing.

Guice is used by Sitebricks and in the sample application. Familiarity with Guice, or with another dependency injection framework such as Spring, would be useful.


Using Maven to create a Sitebricks application

Like many modern Java frameworks, Sitebricks leverages Maven. Many frameworks use Maven's archetype system to set up a new project. This is also the plan for Sitebricks, but at the time of this writing, it was not ready yet. In the example in this article you'll take a bit more of a manual approach. You will follow Maven best practices and first create a project for business logic, as shown in Listing 1.

Listing 1. Creating a business logic project using Maven
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -DartifactId=reviewrMain
[INFO] Scanning for projects...

The code creates a simple project structure, typical of a Java application that will eventually be packaged into a JAR file. The next step is to create a second project that will be the Web application, as shown below.

Listing 2. Create a Java Web application using Maven
$ mvn archetype:create -DgroupId=org.developerworks.reviewr -
DartifactId=reviewrWeb -DarchetypeArtifactId=maven-archetype-webapp
[INFO] Scanning for projects...

This is similar to Listing 1, but this time you provided an archetype ID of maven-archetype-webapp. It's a core archetype in Maven that creates a basic Java Web application project. Now that you've created the projects, open up their pom files to add Sitebricks to them. First do this for the main business logic project, as shown in Listing 3.

Listing 3. Main project's pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/
   XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/
   maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.developerworks.reviewr</groupId>
    <artifactId>reviewrMain</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>reviewrMain</name>
    <url>http://maven.apache.org</url>
    <repositories>
        <repository>
            <id>sonatype-google-snapshots</id>
            <name>Sonatype Google Snapshots Repository</name>
            <url>http://oss.sonatype.org/content/repositories/
google-snapshots/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.sitebricks</groupId>
            <artifactId>sitebricks</artifactId>
            <version>0.8-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.2.2.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

This pom file has been modified in several ways from the original that was generated in Listing 1.

  • You've added a repository, http://oss.sonatype.org/content/repositories/google-snapshots/, which is where you can find Maven artifacts for Sitebricks and Guice.
  • In the dependencies section, you added two dependencies:
    • A dependency of Sitebricks, which will bring in all of the Sitebricks JARs needed by the project.
    • A dependency on Apache Derby, which is the database that you'll use in this project. Derby is an embedded database that is part of Java 6. The main thing you get from this dependency is the embedded JDBC driver needed for Derby.

The pom file for the Web application project is shown below.

Listing 4. The Web application's pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/
   XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/
   maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.developerworks.reviewr</groupId>
  <artifactId>reviewrWeb</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>reviewrWeb Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
        <groupId>org.developerworks.reviewr</groupId>
        <artifactId>reviewrMain</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>reviewrWeb</finalName>
    <plugins>
        <plugin>
             <groupId>org.mortbay.jetty</groupId>
             <artifactId>maven-jetty-plugin</artifactId>
        </plugin>
    </plugins>
  </build>
</project>

Listing 4 shows two small modifications. A dependency on the main project was added that will bring in the code you write in the main project, along with all of the dependencies from the main project (including the dependency on Sitebricks). You also added a Jetty plugin, which makes it possible to start an embedded Jetty Web server and run the application on it. Now that the projects are set up, you can start using Sitebricks.


Configuring Sitebricks using Guice

One of the benefits of using Sitebricks is that it requires a minimal amount of configuration. First, you need to modify the web.xml of the application, as shown in Listing 5.

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

<web-app>
  <display-name>Reviewr</display-name>
  <filter>
      <filter-name>webFilter</filter-name>
      <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
  </filter>

  <filter-mapping>
      <filter-name>webFilter</filter-name>
      <url-pattern>/*</url-pattern>
  </filter-mapping>

  <listener>
      <listener-class>org.developerworks.reviewr.AppInjector</listener-class>
  </listener>
</web-app>

The code above created a servlet filter that will intercept incoming HTTP requests. Instead of creating your own filter, you're using the GuiceFilter that comes with Guice. You are mapping the filter /* (sending all requests to that filter).

Listing 5 also set up a servlet context listener. This is a listener that will be invoked when the Web application is started. The rationale here is to configure Sitebricks when the application starts. So, you'll write a custom class called AppInjector, as shown below.

Listing 6. Sitebricks configuration with AppInjector
public class AppInjector extends GuiceServletContextListener {

    @Override
    public Injector getInjector() {
        Module module = new SitebricksModule() {
            @Override
            protected void configureSitebricks() {
                scan(ShowRestaurants.class.getPackage());
                bind(RestaurantDao.class).to(RestaurantEmbeddedJdbcDao.class);
                bind(ReviewDao.class).to(ReviewEmbeddedJdbcDao.class);
                bind(String.class)
                        .annotatedWith(Names.named("connStr"))
                        .toInstance("jdbc:derby:reviewrDb;create=true");
                bind(DbManager.class).to(EmbeddedDbManager.class);
            }
        };
        
        Injector injector = Guice.createInjector(module);
        return injector;
    }
}

If you're familiar with Guice, the code above should look familiar. This code creates a Guice module. Typically you implement the configure method in Guice, but in this case you use a Sitebricks-specific subclass of Module and implement its configureSitebricks method. That is where you do all of the typical Guice configuration: binding interfaces to their implementations and configuring application properties.

The example uses scan, an API that is unique to Sitebricks. It takes a Java package and adds its contents to the classes managed by Sitebricks. You'll see how this is used later.

You've done all of the configuration needed for the application. Most of this configuration is actually application specific, such as all of those bind calls made in AppInjector. They weren't necessary, but were done so you could use Guice to make it easier to write your business logic. If all you want is Sitebricks, you only need the web.xml and the scan call. Now that configuration is complete, you can write some application logic.


Showing your results

The first page in your application will show all of the restaurants in the system and will let users filter the list of restaurants based on what kind of food the restaurant serves. Sitebricks is a model-view-control (MVC) style of framework, but it concentrates on the controllers and views in your application. The controllers are simple Java classes. The controller for showing all of the restaurants is shown in Listing 7.

Listing 7. ShowRestaurants controller
@At("/")
public class ShowRestaurants {
    private List<Restaurant> restaurants;
    private String category;
    private LinkedHashSet<String> categories;
    private final RestaurantDao dao;

    @Inject
    public ShowRestaurants(RestaurantDao dao){
        this.dao = dao;
    }
    
    @Get
    public void get(){
        this.restaurants = dao.findAll();
        categories = new LinkedHashSet<String>(restaurants.size());
        for (Restaurant restaurant : restaurants){
            categories.add(restaurant.getCategory());
        }
        if (category != null && category.trim().length() > 0){
            List<Restaurant> filtered = new ArrayList<Restaurant>
(restaurants.size());
            for (Restaurant restaurant : restaurants){
                if (restaurant.getCategory().equalsIgnoreCase(category)){
                    filtered.add(restaurant);
                }
            }
            restaurants = filtered;
        }
    }
    // getters and setters omitted for brevity
}

Notice that this class is annotated with @At, which is a Sitebricks annotation. The "/" value tells Sitebricks to map any incoming requests for "/" to this class. The class will serve as the controller for the "home page" of your application.

Sitebricks follows HTTP and REST conventions closely, thus allowing different methods to be used for different HTTP methods, such as GET and POST. The @Get annotation indicates which methods to use for GET requests. You can name the method whatever you want; there is nothing special about "get." The annotation is what determines what method will be invoked.

The class above has several member variables. RestaurantDao is the object that is used for retrieving the data needed by the page. The @Inject annotation on the constructor to ShowRestaurants is telling Guice to inject a RestaurantDao. The other member variables, restaurants, categories, and category, form the data model of the page. The variables restaurants and categories are used in the view.

The category variable is actually a request parameter. Sitebricks will automatically bind a request variable of the same name to this. For example, if a request is made for http://<app>?category=Thai, then the category will be equal to Thai. Per the code, if there is a category given then it is used to filter the list of restaurants that are shown.

Listing 8 shows the view code that will use the data model that is built by the controller.

Listing 8. ShowRestaurants view
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html lang="en">
  <head>
    <title>Reviewr</title>
  </head>
  <body>
      <h1>All Restaurants</h1>
      <form method="get">
          <label for="category">Select category:</label>
          <select name="category" id="category">
              <option value="" label="Any"/>
              @Repeat(items=categories, var="category")
              <option>${category}</option>
          </select>
          <input type="submit" value="Filter"/>
      </form>
      <table border="1">
          <thead>
              <tr>
                  <td>Name</td>
                  <td>Category</td>
                  <td>Average Rating</td>
              </tr>
          </thead>
          <tbody>
              @Repeat(items=restaurants, var="restaurant")
              <tr><td><a 
href="/restaurant/${restaurant.name}">${restaurant.name}</a></td>
<td>${restaurant.category}</td><td>${restaurant.averageRating}
</td></tr>
          </tbody>
      </table>
      <div class="msg">Not in the list?
          <a href="/restaurants/new">Add a restaurant!</a>
      </div>
  </body>
</html>

The name ShowRestaurants.html, shown above, matches the controller. This convention is used by Sitebricks so you don't have to do it through configuration. The HTML is mostly pure HTML; there are no scriptlets (this is not a JSP). The Sitebricks widget @Repeat is used to iterate over a collection. For evaluating the widgets, it uses the MVEL expression language. MVEL is a superset of the typical JSP/JSF expression language. The code creates a table of restaurants, where the name of each restaurant is also a hyperlink to /restaurant/${restaurant.name}. For example, if there's a restaurant named TCBY then the link will be to /restaurant/TCBY. Dynamic URLs like this are a fundamental part of Sitebricks. Listing 9 shows how they work with the controller that handles the example.

Listing 9. RestaurantDetails controller
@At("/restaurant/:name")
public class RestaurantDetails {
    private final RestaurantDao dao;
    private final ReviewDao reviewDao;
    private final Logger logger;
    private Restaurant restaurant;
    private String text;
    private String author;
    private Integer rating;
    private Integer restaurantId;

    @Inject
    public RestaurantDetails(RestaurantDao dao, ReviewDao reviewDao, Logger logger){
        this.dao = dao;
        this.reviewDao = reviewDao;
        this.logger = logger;
    }

    @Get
    public void get(@Named("name") String name){
        try {
            name = URLDecoder.decode(name, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        this.restaurant = dao.findByName(name);
        reviewDao.getReviewsForRestaurant(restaurant);
    }

    @Post
    public String addReview(@Named("name") String name){
        try {
            name = URLDecoder.decode(name, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        restaurant = dao.findByName(name);
        reviewDao.create(restaurant, text, author, rating);
        return "/reviewrWeb/restaurant/"+restaurant.getName();
    }
    
    public boolean getNoReviews() {
        return this.restaurant.getReviews().size() == 0;
    } 
    // getters and setters omitted for brevity    
}

Again, notice the @At annotation. This time its value is "/restaurant/:name". :name is used to tell Sitebricks that this is a dynamic variable. It will match the URL to this pattern and put the last part of the URL in a variable called name. This is a normal named variable in Guice.

The get method has an input parameter that has been annotated with @Named("name"). This will cause Guice to inject the value it extracted from the URL into this variable when invoking the get method (which will be invoked for GET requests, because it has the @Get annotation on it).

The example does a URL decoding of the variable because it comes in as part of the URL, not as an HTTP request parameter where the decoding has already been done for you. Without it, Elephant Bar would come in as Elephant%20Bar. You might notice that this class also has the method addReview which has an @Post annotation.


Form submission

Handling forms can be one of the most tedious tasks in Web applications. As you might expect, Sitebricks shines in this area, too. Listing 9 shows the controller for the restaurant details page. The view code is shown in Listing 10 below.

Listing 10. Restaurant details view
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html lang="en">
  <head>
    <title></title>
  </head>
  <body>
      <h1>${restaurant.name}</h1>
      <h2>Category: ${restaurant.category}</h2>
      <h2>Average Rating: ${restaurant.averageRating}</h2>
      <h2>Reviews</h2>
      
      @ShowIf(noReviews)
      <p>This restaurant has no reviews yet.</p>

      @Repeat(items=restaurant.reviews, var="review")
      <div class="review">${review.author}:${review.rating}<br/>${review.text}</div>

      <form method="post">
          <label for="author">Your name:</label>
          <input type="text" id="author" name="author"/>
          <label for="rating">Your rating:</label>
          <select name="rating">
              <option value="0" label="0"/>
              <option value="1" label="1"/>
              <option value="2" label="2"/>
              <option value="3" label="3"/>
              <option value="4" label="4"/>
              <option value="5" label="5"/>
          </select><br/>
          <label for="text">Write your review:</label><br/>
          <textarea id="text" name="text" cols="60" rows="40"/><br/>
          <input type="hidden" name="restaurantId" value="${restaurant.id}"/>
          <input type="submit" value="Submit your review"/>
      </form>
  </body>
</html>

This file follows the naming convention, so it is RestaurantDetails.html. At the top of the file there is HTML code for displaying information about the restaurant and all of its reviews. @ShowIf, another core Sitebricks widget, is used. The parameter to this widget is a boolean variable, which in this case is called noReviews. Back in Listing 9, you will not see a variable by that name but there is a method called getNoReviews. This is per the JavaBeans convention, and thus that method will be invoked when the @ShowIf widget is evaluated.

The more interesting part of the HTML template is the form at the bottom. The form uses the post method, just as any form that is changing the server should (in this case, adding a review to the database). It submits back to itself, so the same controller will be used. In Listing 9 you can see that there's a different method, addReview, that's annotated with @Post. This is the method that will be invoked when the form is submitted. The same named value pattern is used as in the get.

Join the Web development group on My developerWorks

Discuss topics and share resources with other developers about Web development in the My developerWorks Web development group.

Not a member of My developerWorks? Join now!

Take a look at each of the form elements and their names. These elements match up to the member variables of the RestaurantDetails class. Sitebricks will automatically bind the form data to the member variables, just as it would do for request parameters. This makes your job as a developer much easier. The addReview method, which is the redirect method in Sitebricks, returns a string. The method simply returns a string that is the relative URL that should be redirected to. In this case, the URL is the same restaurant details URL, so the details page will be redisplayed. You could easily change this to the home page URL, or any other that you want.


Summary

In this article you learned how convenient it is to use Google Sitebricks to develop Java Web applications. You explored all of the essentials: creating a project that uses Sitebricks, configuring Sitebricks, using its widgets, and mapping URLs. The article also covered some of the other convenient features, such as dynamic URLs with named variables and form binding.

Sitebricks is a lightweight framework that provides versatile tools for rapidly creating Web applications. It extends the Guice philosophy to the Web. Best of all, Sitebricks is still a young framework. You'll want to keep an eye on upcoming features and on the ecosystem developing around Sitebricks.


Download

DescriptionNameSize
Source code for this articlereviewr.zip108KB

Resources

Learn

Get products and technologies

  • Download Sitebricks from Google code and get all of the latest information.
  • Get Guice from Google Code.
  • Get the Java SDK. (JDK 1.6.0_17 was used in this article.)
  • Download Maven. (Version 2.0.0 was used in this article.)
  • Innovate your next open source development project with IBM trial software, available for download or on DVD.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=468341
ArticleTitle=Build better Web applications with Google Sitebricks
publish-date=02162010