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.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Article source code | SoccerOrg.zip | 14KB | HTTP |
Information about download methods
Learn
-
Introduction to Spring 2 and JPA (Sing Li, developerWorks, August
2006): Learn more about JPA.
-
Implementing composite keys with JPA and Hibernate (Stephen
Morris, developerWorks, August 2009): Dive deeper into JPA and Hibernate.
-
Create
RESTful Web services with Java technology (Dustin Amrhein and Nick
Gallardo, developerWorks, February 2010): Get a thorough introduction to
JAX-RS.
-
Create Ajax applications for the mobile web (Michael Galpin,
developerWorks, March 2010): See how JAX-RS is great for mobile web
applications as well.
-
Writing a custom Dojo application (Wendi Nusbickel and Melissa
Betancourt, developerWorks, December 2008): Find out much more about Dojo.
-
Develop HTML widgets with Dojo (Igor Kusakov, developerWorks,
October 2006): Explore Dojo's extensibility.
- "Comment
lines: Using Ant and ShrinkSafe to improve performance of Web
applications that use Dojo" (Kevin Haverlock, developerWorks, March 2010): See a real-world example
of how you can use REST calls to lazily load JSON data for populating a Dojo Dijit tree widget.
- "Comment
lines: Lazily loading your Dojo Dijit tree widget can improve
performance" (Scott Johnson, developerWorks, May 2008): Learn how to use
the Ant build utility together with Dojo's ShrinkSafe to automate the profile
build, resulting in improved performance of your Dojo-based web pages.
- The developerWorks Web development zone
specializes in articles covering various web-based solutions.
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
- Create your My developerWorks profile today and set up a watchlist on Dojo.
Get connected and stay connected with My developerWorks.
- Find other developerWorks members interested in web development.
- Web developers, share your experience and knowledge in the Web development group.
- Share what you know: Join one of our developerWorks groups focused on web topics.
- Roland Barcia talks about Web 2.0 and middleware in his blog.
- Follow developerWorks' members' shared bookmarks on web topics.
- Get answers quickly: Visit the Web 2.0 Apps forum.

Michael 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.




