Create rich data-centric web applications using JAX-RS, JPA, and Dojo

Develop an application for managing a youth soccer league

Developing a rich application for manipulating large amounts of data used to be the exclusive domain of desktop applications. Now it can be done in a web application, and you don't have to be a Java™Script guru to do it. Learn how to use the Dojo toolkit to create eye-popping, data-centric web applications and hook them up to a back end based on the JavaEE standards such as JAX-RS and JPA. These technologies allow you to leverage convention over configuration principles to easily wire together complex applications in no time at all.

Share:

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.



13 July 2010

Also available in Chinese Japanese

Getting started

In this article you will develop a data-centric web application using some of the latest server-side Java technologies and the Dojo toolkit for creating a rich user interface. These technologies significantly reduce the amount of code you have to write, on both the server and client sides. Familiarity with Java and JavaScript is recommended to get the most out of this article. You will need a Java 1.6 JDK to compile and run the code; JDK 1.6.0_20 was used in this article. You will also need a Java Web container; Apache Tomcat 6.0.14 was used in this article. For data persistence, any database with a JDCB 2.0 compliant driver can be used. To keep things simple, an embedded database, Apache Derby 10.6.1, was used. This article uses the Java API for RESTful Web Services (JAX-RS), with Jersey 1.3 for the JAX-RS implementation. You will also use the Java Persistence API (JPA) with Hibernate 3.5.3 for the implementation. Finally, the Dojo toolkit 1.4 was used in this article as well. See Resources for links to these tools.


Data on the fly with the Java Persistence API

Many web applications are data centric—they present persistent data and allow the user to create or update this data. It sounds simple enough, but even when it comes to something as basic as reading and writing data from a database, things can get ugly. However, the Java Persistence API (JPA) greatly reduces the amount of tedious boilerplate code that you must write. We will take a look at a simple example of using JPA.

In this article you will develop a simple application for managing a youth soccer league. You will start by developing a simple data model for keeping track of the teams in your league and the players on those teams. You will use JPA for all access to this data. You will start with the first of two data models, a Team. Listing 1 shows this class.

Listing 1. The Team data model class
@Entity
public class Team {
....
....@Id 
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String name;
....
....@OneToMany
....private Collection<Player> players;
....
     // getters and setters........
}

This is a typical JPA annotated class. You use the @Entity annotation to declare that this class will be mapped to a database. You could optionally specify the name of the table for the class, or implement the convention where you use the same name as the class. Next, you annotate the id field of the class. You want this to be the primary key for your table, so use the @Id annotation to declare this. The id is not important from a business logic perspective; you just need it for the database. Since you want the database to take care of coming up with its values, use the @GeneratedValue annotation.

In Listing 1, you also declare another field, the name field. This will be the name of the team. Notice there are no JPA annotations on this field. By default this will be mapped to a column of the same name, and that is good enough for the purposes of this article. Finally, each team will have multiple players associated to it. You use the @OneToMany annotation to let the JPA runtime know that this is a managed relation with one team having many players. In your Java class, this is just a java.util.Collection of Player object. Listing 2 shows the Player class being referenced.

Listing 2. The Player data model class
@Entity
public class Player {
....
....@Id 
....@GeneratedValue(strategy = GenerationType.IDENTITY)
....private long id;
....
....private String firstName;
....
....private String lastName;
....
....private int age;
....
....@ManyToOne (cascade=CascadeType.ALL)
....private Team team;
....
     // getters and setters
}

The Player class shown in Listing 2 is similar to the Team class in Listing 1. It has more fields, but again in most cases you won't need to worry about annotating these fields. JPA will do the right thing for you. The one difference between Listing 1 and Listing 2 is how you specify the Player class's relationship to the Team class. In this case you use a @ManyToOne annotation, as there are many Players on one Team. Notice that you also specified a cascade policy. Take a look at some of the JPA documentation to pick the right cascade policy for your application. In this case, with this policy you can create a new Team and a Player at the same time and JPA will save both, which is convenient for your application.

Now that you have declared your two classes, you just need to tell the JPA runtime how to connect to your database. You do this by creating a persistence.xml file. The JPA runtime needs to find this file and use the metadata in it. The easiest way to do this is to put it into a /META-INF directory that is a subdirectory of your source code (it just needs to be in the root of the directory where your compiled classes are output). Listing 3 shows the persistence.xml file.

Listing 3. The persistence.xml for the soccer app
<persistence version="1.0"
....xmlns="http://java.sun.com/xml/ns/persistence"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
....xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
....<persistence-unit name="soccer">
........<class>org.developerworks.soccer.model.Team</class>
........<class>org.developerworks.soccer.model.Player</class>
........<properties>
............<property name="hibernate.dialect" 
                      value="org.hibernate.dialect.DerbyDialect" />
............<property name="hibernate.connection.driver_class"....
                      value="org.apache.derby.jdbc.EmbeddedDriver" />
............<property name="hibernate.connection.url" 
                       value="jdbc:derby:soccerorgdb;create=true" />
............<property name="hibernate.hbm2ddl.auto" value="update" />
............<property name="hibernate.show_sql" value="true" />
............<property name="hibernate.connection.characterEncoding" 
                      value="UTF-8" />
............<property name="hibernate.connection.useUnicode" 
                       value="true" />
........</properties>
....</persistence-unit>
</persistence>

Looking back at Listing 1 and 2, all of the code is generic JPA code. Actually all you ever use are JPA annotations and some of its constants. There is nothing specific to your database or the JPA implementation you used. As you can see from Listing 3, the persistence.xml file is where those specific things are found. Several excellent JPA implementations are available, including OpenJPA and TopLink (see Resources). You have used the venerable Hibernate, so you have several Hibernate-specific properties that you have specified. These are mostly straightforward things like the JDBC driver and URL, and some useful things like telling Hibernate to log the SQL that it is executing (something you would definitely not want to do a in a production situation, but it is great for debugging during development).

You will also notice from Listing 3 that you are using the Apache Derby database. In fact, you are using an embedded version of the database. So, you do not have to separately start up your database or worry about configuring it. Further, you have specified in the connection URL that the database should be created automatically, and you have told Hibernate to automatically create the schema (this is the hibernate.hbm2ddl.auto property). So if you just run your application, the database and the tables can all be created for you. This is great for development, but of course you may want different settings for a production system. Now that you have all of your data model code created and you have enabled access through JPA, we'll take a look at exposing this data so that a web application can take advantage of it.


RESTful access to data with JAX-RS

If you were creating this application five years ago, you would now start creating some Java Server Pages (JSPs) or Java Server Faces (JSFs) or some other similar templating technology. Instead of creating the UI for this application on the server, you are going to use Dojo to create it on the client. All you need to do is provide a way for your client-side code to access this data using Ajax. You can still use a templating solution for something like this, but it is much simpler to use the Java API for RESTful Web Services (JAX-RS). Let's start by creating a class for reading all of the Teams in the database and for creating new Teams. Listing 4 shows such a class.

Listing 4. Data access class for Teams
@Path("/teams")
public class TeamDao {
....
....private EntityManager mgr = 
          DaoHelper.getInstance().getEntityManager();
....
....@GET
....@Produces("application/json")....
....public Collection<Team> getAll(){
........TypedQuery<Team> query = 
                mgr.createQuery("SELECT t FROM Team t", Team.class);
........return query.getResultList();
....}
....
....@POST
....@Consumes("application/x-www-form-urlencoded")
....@Produces("application/json")
....public Team createTeam(@FormParam("teamName") String teamName){
........Team team = new Team();
........team.setName(teamName);
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........mgr.persist(team);
........txn.commit();
........return team;
....}
}

Listing 4 shows a class data access object class, hence the name TeamDao. We will get to the annotations on this class shortly, but let me first explain the data access. The class has a reference to the JPA class EntityManager. This is a central class in JPA and provides access to the underlying database. For your first method that retrieves all of the teams in the league, use the EntityManager to create a query. The query uses JPA's query language, which is very similar to SQL. This query simply gets all of the Teams. For the second method, you simply create a new Team using the name of the team that is passed in, create a transaction, save the new team, and commit the transaction using the EntityManager. All of this code is vanilla JPA code, as all of these classes and interfaces are part of the base API.

Now that you understand the JPA part of Listing 4, let's talk about the JAX-RS aspects of it. The first thing you will notice is that you use the @Path annotation to expose this to HTTP-based clients. The /teams string specifies the relative path to this class. The full URL path is going to be <host>/SoccerOrg/resources/teams. The /SoccerOrg will specify the path to your web application (of course, you can configure this to be something different, or remove this completely). The /resources part will be used to specify a JAX-RS end point. The /teams corresponds to the @Path annotation and specifies which of the JAX-RS classes to use.

Next, the first method, getAll, has a @GET annotation on it. This specifies that this method should be invoked if an HTTP GET request is received. Next, the method has a @Produces annotation. This declares the MIME type of the response. In this case, you want to produce JSON, since that is the easiest thing to use with a JavaScript-based client.

This is all you have to do to use JAX-RS to expose this class to web clients. However, you might be asking yourself: If this method returns a java.util.Collection of Team objects, how will this be sent to web clients? The @Produces annotation declares that you want it to be sent as JSON, but how will the JAX-RS serialize this into JSON? It turns out that all you need to enable this is to add one more annotation to the Team class as shown in Listing 5.

Listing 5. Modified Team class
@XmlRootElement
@Entity
public class Team {
....
// unchanged from Listing 1
........
}

By adding the @XmlRootElement annotation, the JAX-RS can now turn this class into a JSON object. You might recognize this annotation. It is not part of JAX-RS; it is instead part of the Java Architecture for XML Binding (JAXB) API that is part of the core Java 1.6 platform. This annotation would seem to indicate that it is for XML, but it can in fact be used for various JAXB outputs including JSON. There are many other JAXB annotations, but this is the only one that you need to use in this case. It will simply use conventions for serializing all of the fields of the Team class to JSON.

Now go back to Listing 4 and take a look at the second method of the class, the createTeam method. This method uses the @POST annotation to specify that it should be invoked when an HTTP POST request is received. Next, it uses the @Consumes annotation to declare what kind of POST request it can consume. The value specified here corresponds to the content-type header of the HTTP request. In this case it is specified as x-www-form-urlencoded. This is the type you will receive when an HTML form is submitted. Thus, this method will be invoked when an HTML form is submitted with the /SoccerOrg/resources/teams end point. Finally, notice that the method takes a single input parameter, a string called teamName. Notice that this parameter is decorated with the @FormParam annotation. This tells the JAX-RS runtime to look for a form parameter in the body of the request whose name is teamName (the value of the annotation) and bind that to a variable passed into the invocation of this method. With this you can easily handle a simple form submission and wire it up to your code. This could get messy if you had a lot of data being submitted. In such a case, you might want to use a more structured approach. Listing 6 shows an example for creating a Player object.

Listing 6. Handling structured POST data using JAX-RS
@Path("/players")
public class PlayerDao {
....private EntityManager mgr = 
          DaoHelper.getInstance().getEntityManager();
....
....@POST
....@Consumes("application/json")
....@Produces("application/json")
....public Player addPlayer(JAXBElement<Player> player){
........Player p = player.getValue();
........EntityTransaction txn = mgr.getTransaction();
........txn.begin();
........Team t = p.getTeam();
........Team mt = mgr.merge(t);
........p.setTeam(mt);
........mgr.persist(p);
........txn.commit();
........return p;
....}
....
....@GET
....@Produces("application/json")
....public List<Player> getAllPlayers(){
........TypedQuery<Player> query = 
............mgr.createQuery("SELECT p FROM Player p", Player.class);
........return query.getResultList();
....}
}

The PlayerDao class in Listing 6 is very similar to the TeamDao class from Listing 5. The main difference that you want to examine is its addPlayer method. This handles HTTP POST requests, similar to the createTeam method in TeamDao. However, it consumes application/json—that is, it is expecting JSON data. This implies two things. First, the request needs to specify a content-type of application/json so that this method will be invoked. Next, the body of the post should be JSON data. Notice that the input parameter of this method is of type JAXBElement<Player>, that is, it is a JAXB wrapper around a Player object. That tells JAX-RS to automatically parse the posted data into a JAXBElement wrapper, so you do not have to bother writing any parsing code for this. Notice that in the body of the method, it only takes one line of code to get a full Player object that can then be used to save the new Player to the database using JPA.

The last thing you need to do to complete the JAX-RS story is show the configuration needed to wire all of it up. For this, you only need to modify the web.xml of your application. Listing 7 shows the application's web.xml.

Listing 7. Application's web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
....xmlns="http://java.sun.com/xml/ns/javaee" 
....xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
....xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
....id="Soccer_Org" version="2.5">
  <display-name>SoccerOrg</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>JAXRS-Servlet</servlet-name>
   
 <servlet-class>com.sun.jersey.spi.container.servlet.
ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
     
 <param-value>org.developerworks.soccer.model;org.developerworks.
soccer.web</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>JAXRS-Servlet</servlet-name>
    <url-pattern>/resources/*</url-pattern>
  </servlet-mapping>
</web-app>

As you can see in Listing 7, your application has a single servlet declared. This is a servlet that is provided by Jersey, the JAX-RS implementation that you are using. You pass in a single initialization parameter to the servlet—the packages containing any classes that you want JAX-RS to know about. In this case you have a package where your data models are kept and a package where your data access objects are kept. You need the models to be discovered so that JAX-RS can convert them to JSON. Of course, you need the DAOs to be discovered so that JAX-RS can route requests to them. Finally, notice the servlet-mapping. This is where the /resources part of your URL paths is specified. Now you are ready to use all of this back-end code on the client to create a UI using Dojo.


Leveraging REST on the client with Dojo

The Dojo toolkit provides almost any kind of library or utility you might need for building the client side of your web application. You will see how it can help you when working with Ajax, forms, JSON, and creating UI widgets. (However, it can do much more than that. This just happens to be all you need for this simple example.) It is such a large system, that you may want to download the full toolkit and do a custom build of it to get exactly what you need for your application. For this example application, you will instead use the Google Ajax APIs to access the various parts of the toolkit that you need. This is both convenient and has some nice performance advantages since Google's copies of Dojo are provided through Google's own highly efficient content delivery network (CDN).

Your application is data centric, so you need to start by adding some data to it. We will use Dojo to create a UI for adding Teams. Listing 8 shows all of the code you need for this.

Listing 8. Adding Teams using Dojo
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test Harness</title>
        <link rel="stylesheet" type="text/css" 
       
 ....href="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dijit
/themes/soria/soria.css"/>
        <script type="text/javascript"
 src="http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojo/dojo.xd.js"
 djConfig="parseOnLoad: true"></script>
<script type="text/javascript">
....function init(){
....    var btn = dijit.byId("addTeamBtn");
....    dojo.connect(btn, "onClick", function(event){
....    ....event.preventDefault();
            event.stopPropagation();
            dojo.xhrPost({
                  form : dojo.byId("addTeamForm"),
            ....handleAs: "json",
            ....load : function(data){
            ........addTeam(data);
            ........alert("Team added");
            ....},
            ....error : function(error){
                ....    alert("Error adding team: " + error);
            ....}
            });
....    });
....}
</script>
</head>
<body class="soria">
....Add a Team<br/>
....<form method="POST" action="/SoccerOrg/resources/teams" id="addTeamForm">
........<label for="teamName">Team Name:</label>
........<input name="teamName" type="text" id="teamName"
 dojoType="dijit.form.TextBox"/>
........<button type="submit" id="addTeamBtn" dojoType=
"dijit.form.Button">Add Team</button>
....</form>
....<script type="text/javascript">
........dojo.require("dijit.form.Button");
....    dojo.require("dijit.form.TextBox");
....    dojo.addOnLoad(init);
....</script>
</body>
</html>

Notice in Listing 8 that you reference the base Dojo library from Google's CDN. Once you have that, you can then request each of the additional parts of Dojo you want using the dojo.require function (see the script block at the bottom of Listing 8). Notice that you just create a normal HTML form, but you use some extra Dojo-specific attributes. This tells Dojo to add some extra styling to the visual elements and to add some extra capabilities to the corresponding DOM elements. You tell Dojo to execute the init function once everything else (all of the Dojo components) are loaded. In that function, you use the dijit.byId function to get a handle on the button in the form. Dijit is Dojo's widget library. You could use the dojo.byId to reference any DOM element using its ID, but the similar dijit.byId gives you a widget with extra capabilities (if the element is marked as a widget, which is the case for the button in Listing 8).

You then use Dojo to associate an event handler for when the button is clicked. The handler stops form submission and uses Ajax instead through the dojo.xhrPost function. This function makes it easy to POST HTML forms. It figures out the Ajax end point by inspecting the HTML form's action attribute. It also reads all of the form elements and passes them to the Ajax POST. When it gets a response back from the server, it will invoke the load function that is passed to xhrPost. Notice that you declared that JSON will be returned by the server by setting the handleAs property passed to the xhrPost function. You will see the addTeam function shortly, but you can pass in the data object directly because Dojo has already safely parsed the JSON data into a usable JavaScript object. This addTeam function is used in conjunction with another form, for adding Players. Listing 9 shows the HTML for that form.

Listing 9. Add Player form
Add a Player<br/>
<form id="addPlayerForm" action="/SoccerOrg/resources/players">
....<label for="firstName">First Name:</label>
....<input name="firstName" id="firstName" type="text"
 dojoType="dijit.form.TextBox"/>
....<label for="lastName">Last Name:</label>
....<input type="text" name="lastName" id="lastName"
 dojoType="dijit.form.TextBox"/><br/>
....<label for="age">Age:</label>
....<input type="text" name="age" id="age"
 dojoType="dijit.form.TextBox"/><br/>
....<label for="team">Team:</label>
....<select id="team" name="team" dojoType="dijit.form.
Select"></select>
....<button type="submit" id="addPlayerBtn" dojoType=
"dijit.form.Button">Add Player</button>
</form>
<script type="text/javascript">
     dojo.require("dijit.form.Select");
     dojo.addOnLoad(loadTeams);
</script>

This form, like the one in Listing 8, is a valid HTML form. However, it also has Dojo-specific attributes added to its elements. Notice that it has a SELECT element that will serve as a drop-down list of the Teams, so that the user can pick which Team to add the new Player to. This is dynamic data that needs to be loaded from the server. Notice that you added another function to be called at startup—the loadTeams function. This is what loads the teams from the server. Listing 10 shows this function, as well as the addTeam function that you saw referenced in Listing 9.

Listing 10. The loadTeams and addTeam functions
var teams = {};
function loadTeams(){
....var select = dijit.byId("team");
....dojo.xhrGet({
........url: "/SoccerOrg/resources/teams",
........handleAs:"json",
........load : function(data){
............var i = 0;
............for (i in data.team){
................addTeam(data.team[i]);
............}
........},
........error : function(error){
............alert("Error loading team data: " + error);
........}
....});
}
function addTeam(team){
....teams[team.id] = team;
....var select = dijit.byId("team");
....var opt = {"label":team.name, "value":team.id};
....select.addOption(opt);
}

Here you once again use Dojo's Ajax utilities to access data provided by the JAX-RS end point created earlier. This time you use the dojo.xhrGet, which makes an HTTP GET request to an Ajax end point. In this case you need to specify its URL, but otherwise it is very similar to the xhrPost you saw in Listing 9. Finally, you see the addTeam method. This once again uses the Dojo widget's extra capabilities to easily add new options to the drop-down list that shows the teams. Now that you have seen how the player form is created, take a look at the code that handles its submissions (see Listing 11).

Listing 11. Adding a new Player
var button = dijit.byId("addPlayerBtn");
dojo.connect(button, "onClick", function(event){
.... event.preventDefault();
       event.stopPropagation();
       var data = dojo.formToObject("addPlayerForm");
       var team = teams[data.team];
       data.team = team;
       data = dojo.toJson(data);
       var xhrArgs = {
           postData: data,
           handleAs: "json",
           load: function(data) {
               alert("Player added: " + data);
               dojo.byId("gridContainer").innerHTML = "";
               loadPlayers();
           },
           error: function(error) {
               alert("Error! " + error);
           },
           url: "/SoccerOrg/resources/players",
           headers: { "Content-Type": "application/json"}
       };
       var deferred = dojo.xhrPost(xhrArgs);
});

This code is going to submit data to the PlayerDao.addPlayer method you saw back in Listing 6. This code expects the Player object to be serialized into a JSON data structure. First, you once again use Dojo to wire up an event handler to a button click on the form. Next, you use Dojo's convenience function, dojo.formToObject, to turn all of the data from the form into a JavaScript object. You then modify that JavaScript object slightly to match the structure expected on the server. Then you use Dojo's dojo.toJson function to turn this into a JSON string. Now this gets passed to dojo.xhrPost, similarly to how the addTeam form was submitted. Notice that you add the HTTP header Content-Type to make sure that it gets routed to the PlayerDao.addPlayer method.

The xhrPost once again has a load function that will be invoked once the Ajax request comes back with a successful response from the server. In this case it is clearing an element on the page called gridContainer and calling a function called loadPlayers. This is another Dojo widget used to show all of the players. Listing 12 shows the HTML and JavaScript used for this.

Listing 12. Player grid HTML and JavaScript
<style type="text/css">
    @import
 "http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/Grid.css";
    @import
 "http://ajax.googleapis.com/ajax/libs/dojo/1.4/dojox/grid/resources/soriaGrid.css";
    .dojoxGrid table { margin: 0; } 
    html, body { width: 100%; height: 100%; margin: 0; }
</style>
<script type="text/javascript">
function loadPlayers(){
....var pStore = new dojox.data.JsonRestStore({
........target: "/SoccerOrg/resources/players"
....});
....pStore._processResults = function(data, deferred){
........return {totalCount:deferred.fullLength || data.player.length, 
                    items: data.player};
....};
       var pLayout = [{
           field: "firstName",
           name: "First Name",
           width: "200px"
       },
       {
           field: "lastName",
           name: "Last Name",
           width: "200px"
       },
       {
           field: "age",
           name: "Age",
           width: "100px"
       },
       {
           field : "teamName",
           name : "Team",
           width: "200px"
       }];

       var grid = new dojox.grid.DataGrid({
           store: pStore,
           clientSort: true,
           rowSelector: "20px",
           structure: pLayout
       }, document.createElement("div"));
       dojo.byId("gridContainer").appendChild(grid.domNode);
       grid.startup();
}
</script>
<div id="gridContainer" style="width: 100%; height: 100%;"></div>
<script type="text/javascript">
    dojo.require("dojox.grid.DataGrid");
    dojo.require("dojox.data.JsonRestStore");
    dojo.addOnLoad(loadPlayers);
</script>

Listing 12 shows Dojo's DataGrid widget. This is one of the more rich widgets in Dojo, and so it requires some extra CSS as well. To create a grid, you need to do two things. First, you need to create a data store for it. In this case it will be JSON data coming from your server, so create a new JsonRestStore object and point it to the URL on your server that will produce this data. Then you override its _processResults. You only have to do this because it is expecting a JSON array of data, and your JAX-RS end point will produce a slightly more complicated object (it will have a single property called player whose value will be the JSON array that the JsonRestStore expects). The next thing the grid needs is layout metadata that tells it what columns to show and what the corresponding property on the JavaScript object will be. Then you can create the grid and drop it into your DOM tree.

Now you have completed the sample soccer application, and you have a very rich way to show the soccer players in the league. You could easily expand this simple example from here to add the editing of players, the sorting of the grid, or you can even add more data like games and results.


Conclusion

This article has shown you a quick way to create a rich, data-centric web application. You used several key technologies to remove tedious boilerplate code both from the server side and the client side: JPA, JAX-RS, and Dojo. In many cases you made use of default conventions to further reduce the amount of code needed to create your web application. The result is a very modern web application created with minimal code. All of the technologies it uses are extensible and production-quality, so you can confidently expand the sample application (or your own application) for more robust use cases in a straightforward manner. Even better is that there is no lock-in. You used open standards on the server side. You could easily switch out database technologies, for example. You used REST and JSON on the front end, meaning you can use a different UI kit, or you can easily hook up a mobile client.


Download

DescriptionNameSize
Article source codeSoccerOrg.zip14KB

Resources

Learn

Get products and technologies

  • Download the Dojo toolkit.
  • Get the Java SDK. JDK 1.6.0_17 was used in this article.
  • Get Apache Tomcat. Apache Tomcat 6.0.14 was used in this article.
  • Get Apache Derby 10.6.1.0.
  • Jersey is the open source, production-quality, reference implementation of JAX-RS.
  • Hibernate is an implementation of the Java Persistence API (JPA). Version 3.5.3 was used in this article.
  • Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2, Lotus, Rational, Tivoli, and WebSphere.

Discuss

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=500150
ArticleTitle=Create rich data-centric web applications using JAX-RS, JPA, and Dojo
publish-date=07132010