http://www.developer.com/

Back to article

Real World REST Using Jersey, AJAX and JSON


October 9, 2009

Introduction

REST, Representational State Transfer, is a powerful, lightweight architecture that allows you to work with data in a comprehensive manner via HTTP. Yet, as powerful as REST is, getting it to work with your code can be a bit of a chore without some help. When it comes to coding in Java, Jersey provides the help that you need. Jersey is an open source project that simplifies the work required to adapt your Java code to REST.

In this article I'll give you a lightning fast overview of REST, and explain the basic operational concepts behind Jersey. Then I'll show you how to work with Jersey to make some real world Java code RESTful. Finally I'll show you how to access your RESTful code using browser based JavaScript, AJAX and JSON. As an added topic, I'll show you to inject a Base64 encoded image file onto a web page at run time.

All the code that I use in this article is available for download so that you follow along as you like.

Understanding the Article's Project Code

I am going to teach you the ins and outs of creating a basic Jersey application using a demonstration project. This demo project is named, CardDealer. CardDealer is a web application that renders a random playing card from a server side shoe of playing cards. You display a random playing card by clicking a "Get Card" button on the project's main web page, as shown in Figure 1 below.

CardDealer Web App
Figure 1: The CardDealer Web App uses AJAX to call a REST Service
(click to view)

Although this interaction may seem simple, there is a lot of technology going on underneath the hood. Jersey and AJAX play a key role in the interaction.

Stuff You Need To Know

The intention of this article is to get you up and running on REST using Jersey as quickly as possible. I've use the Maven Framework to organize and build the various Java modules that make up the project. Also, I've used Spring to do runtime injection of property values on a Java helper class that is part of the CardDealer project. And, as mentioned above, I've used AJAX to facilitate communication between the client side browser and the server side CardDealer REST code.

Thus, in order to get full benefit from reading this article, it’s helpful to have a basic understanding of the following technologies:

  • Maven, to define a project's dependencies, build the sample project and run unit tests
  • Spring, to do runtime property injection
  • AJAX, to make a XmlHttpRequest back to a web server
  • JSON, to encapsulate information into a client-side JavaScript object

Yet, if you find yourself needing to brush up on the technologies listed above, don’t fret! This article has a lot of information that you’ll find useful at a conceptual level.

What is REST?

REST is an acronym for Representational State Transfer. REST offers an elegant way to manipulate the HTTP protocol in order to exchange data between computers. The key is the HTTP Request. In the very old days of the Web one thought of using HTTP Requests mostly to get a file from a Web Server.

For example, I'd make an HTML web page that had my profile information on it, store that page as a file, myProfile.html in a directory, BobInfo. Then, others interested in that file would call the URL, http://www.myExampleWebSite.com/BobInfo/myProfile.html. The web server at the other end of the URL would fetch the file and send the file bytes back to the calling computer, either to a browser or some sort of computer based consuming intelligence that can interpret the HTML. Of course, over time things got a lot more complex. Data driven web pages required that web pages be made up on the fly. So, in that more modern world, myProfile.html might not contain HTML that represented my profile information as a hard coded web page. Instead the web server might be configured to interpret HTML files, or more typically, JSP, ASPX or PHP files, as code that contains HTML interspersed with server-side data-driven programming logic to dynamically construct a web page on the fly. Then, the notion of dynamically constructing a page of HTML text gets abandoned altogether. Why not have the web server intelligence create some XML and return that? Why not accept XML as part of an HTTP Request? Why not return a more complex XML structure such as a SOAP message. In the World Wide Web all things are possible. Thus, over time HTTP Requests and HTTP Responses became more complex and unwieldy. And, so it goes... Until REST.

In a REST world things get simpler and a lot more interesting. First and foremost,in REST a URL (or as some purist RESTafarians prefer, a URI) identifies a "thing". You can think of a "thing" as a noun.

For example, a URL, www.CoolCars.com/salesman/reselman/bob can refer to a salesman named Bob Reselman, while another URL, www.CoolCars.com/lenders/contact/reselman/bob can refer to a lender contact named, Bob Reselman. The important thing to understand is that the URL defines a "thing" and that the structure of that definition is defined by the domain being called. (Later on in this article I’ll show you how to use WADL, Web Application Description Language, to determine how to call things in a given REST domain.)

Okay, so REST tells us that URL defines a thing. The question that arises is what can we do with that thing? Well, we can get the thing from the domain; we can add the thing to the domain or we can delete the thing from the domain. How? This is where the use of the HTTP METHOD comes into play.

As mentioned above, REST utilizes the HTTP protocol to do very powerful things in a very simple way. One of the features of the HTTP protocol that REST utilizes extensively is the METHOD field. The most commonly known HTTP methods are GET and POST. In the old days, web developers used the GET and POST value as way for passing some query string and form information to a web server for processing. No biggie.

REST however, takes a much more elegant approach to the use of METHOD fields. REST says, "hey, let's use these methods as they were intended to be used, as directives about the intent of a HTTP Request". Thus, in REST, GET actually means to get some data, PUT means to add data (as does POST), and DELETE means to delete data. There are more HTTP Methods you can use in REST. But, for now let's focus on the basic ones.

So, now you have the basics of REST. REST allows you to define a thing via a URL, and then by setting the METHOD field in the HTTP Request, you can get the thing, add the thing or delete the thing. Of course this means that we are dealing with the HTTP protocol at a lower level than has been usual for most bread and butter web developers. But, once you get the hang of working with essential HTTP, life becomes a lot easier and a lot more powerful.

How does Jersey make REST easier?

So where does Jersey fit in to all of this? Jersey takes care of the lower level HTTP processing referenced above. Jersey provides a way to map a RESTful HTTP Request directly to intelligence in your Java code.

For example, let's say that we have a RESTful URL, http://localhost:9090/card-dealer-rest/rest/card-dealer/card that is sent to a Web server using the HTTP METHOD, GET. Before Jersey came along, in order to process the REST statement we'd do some low level work intercepting the HTTP Request coming into the computer, dissect the URL to determine the thing it represents and then do a whole bunch of programming based on the HTTP METHOD field to create an HTTP Response relevant to the RESTful HTTP Request. This is a lot of work! However, when we use Jersey things get a lot easier.

Please take a look the Jersey enhanced code in Listing 1 below.

Listing 1: Using Jersey annotations to bind a URL to a Java class

01: @Path("/card-dealer")
02: public class CardDealerRest {
03: //Some Java code follows......
04: .
05: .
06:     @GET
07:     @Produces(MediaType.APPLICATION_JSON)
08:     @Path("/card")
09:     public Response getCard(@QueryParam("shoeId") String shoeId) throws IOException, InvalidCardDeckException
10:        {
11:         //Some Java code  for the method follows......
12:         .
13:         .
14:         .
15:         return generateResponse(gson.toJson(rc),
16:                 MediaType.APPLICATION_JSON_TYPE);
17:     }
18:     //Some more Java code follows......
19:     .
20      .
21:     //end of class
22: }

Notice the use of the Java annotation, @Path, in line 01 above the declaration for the class, CardDealerRest. @Path is a Jersey annotation that maps a URL segment to a class and then subsequently to methods within the class. In this case of the use of @Path in line 01 above, /card-dealer, is mapped to the class, CardDealerRest. Thus, any HTTP Request that has a URL base of /card-dealer, will, with the help of Jersey, be routed by the web app to an instance Java class, CardDealerRest.

Also notice that line 08 uses @Path to map the subsequent part of the URL, /card, to the method, getCard(). Thus, using Jersey we've taken a RESTful HTTP Request and bound it to a method in a Java class in order to facilitate server-side behavior.

This is pretty much how Jersey works in terms of mapping a URL to code. In your web.xml you define a URL base as an entry point to which the Web server will pass the HTTP Request to the Jersey servlet. Then you use Jersey @Path annotations to define how the remainder of the URL is to be mapped, first to a class, which in Jersey parlance is called a resource class, and then to a method within the class. Figure 2 below illustrates this concept.

CardDealer Web App
Figure 2: The Jersey @Path annotation maps segments of a URL to a Java class and methods for execution
(click to view)

There are additional Jersey annotations that you use to do things such as declare the mime type that your the methods of the resource will accept, bind an HTTP Request METHOD to a particular method in the resource class and define a return mime type that will be set in HTTP Response.

Table 1 below is an excerpt from the Sun documentation that describes the basic Jersey annotations.

Table 1: The Jersey Annotations

Annotation Description
@Path The @Path annotation's value is a relative URI path indicating where the Java class will be hosted, for example, /helloworld. You can also embed variables in the URIs to make a URI path template. For example, you could ask for the name of a user, and pass it to the application as a variable in the URI, like this, /helloworld/{username}.
@GET The @GET annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP GET requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@POST The @POST annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP POST requests. The behavior of a resource is determined by the HTTP method to which the resource is responding
@PUT The @PUT annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP PUT requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@DELETE The @DELETE annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP DELETE requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@HEAD The @HEAD annotation is a request method designator and corresponds to the similarly named HTTP method. The Java method annotated with this request method designator will process HTTP HEAD requests. The behavior of a resource is determined by the HTTP method to which the resource is responding.
@PathParam The @PathParam annotation is a type of parameter that you can extract for use in your resource class. URI path parameters are extracted from the request URI, and the parameter names correspond to the URI path template variable names specified in the @Path class-level annotation.
@QueryParam The @QueryParam annotation is a type of parameter that you can extract for use in your resource class. Query parameters are extracted from the request URI query parameters.
@Consumes The @Consumes annotation is used to specify the MIME media types of representations a resource can consume that were sent by the client.
@Produces The @Produces annotation is used to specify the MIME media types of representations a resource can produce and send back to the client, for example, "text/plain".
@Provider The @Provider annotation is used for anything that is of interest to the JAX-RS runtime, such as MessageBodyReader and MessageBodyWriter. For HTTP requests, the MessageBodyReader is used to map an HTTP request entity body to method parameters. On the response side, a return value is mapped to an HTTP response entity body using a MessageBodyWriter. If the application needs to supply additional metadata, such as HTTP headers or a different status code, a method can return a Response that wraps the entity, and which can be built using Response.ResponseBuilder.

You can find a full JavaDoc description of Jersey annoations here.

Understanding the CardDealer Application

Now that we've covered the very basics of REST and using Jersey, let's do some real world programming. As mentioned above we're going to make a Web Application, CardDealer, that allows us to randomly call and view a graphical display of a playing card in a browser.

The CardDealer Application is made up of three components, a web based REST layer, which returns a JSON version of an object that describes a playing card, a Service layer that provides the logical card dealer components: ShoeFactory, Shoe, Card, CardDealerHelper and thirdly a file system based collection of files that provide graphical representation of each playing card.

The REST layer exposes a number of end points that are associated with RESTful URLs. These end points map to methods in a Java class that serves as a REST resource class. The REST resource class is, CardDealerRest. CardDealerRest's methods use card dealing functionality that reside in the Service layer classes, ShoeFactory, Shoe, Card, and CardDealerHelper, as described above.

The Shoe object is a logical representation of a card shoe that you find in a casino. As such, the Shoe object contains one or more decks of cards. The Shoe object exposes methods similar to a physical card shoe, for example Shoe.getCard(), Shoe.shuffle(), Shoe.getDeckCount(), Shoe.setDeckCount(), and Shoe.GetCardCodes().

You access a Shoe object from the ShoeFactory object. ShoeFactory is a singleton. The ShoeFactory manages at least one or many Shoe objects. You ask the ShoeFactory for a new Shoe by calling ShoeFactory.getShoe(). You call a particular Shoe by calling ShoeFactory,getShoe(String shoeId), where shoeId is the Shoe object's identifier.

A deck of cards is represented in a Shoe as an ArrayList of String objects, where each String is a unique card code. Card coding follows a particular naming convention special to the CardDealer application. The convention is, [suit_code][rank_number]. The suit_code is a character that describes a particular card suit as shown below:

s - spade
c - club
h - heart
d - diamond

The rank-number system is such that an Ace is assigned the lowest possible numeric value, 1 and a King is assigned the highest possible numeric value, 13. Thus, the card code for a 5 of Diamonds is d5. An Ace of Spades is s1 and a Jack of Clubs is c11.

The application requires that a directory exist that contains 52 images of playing cards, in which each playing card's filename corresponds to a card code and is named accordingly. The image can be of any type usually supported by a browser, jpg, gif, or png. For example, an image associate with the 6 of Hearts will be named, c6.jpg or c6.gif or c6.png. A 2 of Hearts will correspond to a file with a filename h2.jpg, h2.gif or h2.png. Again, the image filename must conform to a card code. File extension type is irrelevant. However, the Card object will inspect the image file to determine the file extension. The filename extension will be represented as a field on the Card object. The name of this field is, fileFormat.

The location of the directory that contains the card images is defined in the field, imageDirectoryLocationFileSpec of the object, CardDealerHelper. CardDealerHelper is part of the Service layer library. The application uses Spring runtime injection to set the value of CardDealerHelper.imgDirectoryLocationFileSpec.

Upon construction, the Shoe validates to make sure that the value of imgDirectoryLocationFileSpec is defined and that there are 52 card image files in the imgDirectoryLocationFileSpec directory, with filenames that correspond to each of the 52 card codes.

The Service layer publishes two exceptions, InvalidCardDeckException and EmptyShoeException. InvalidCardDeckException is an Exception that reports cards missing from the directory defined by imgDirectoryLocationFileSpec. EmptyShoeException is thrown when a card is called from the Shoe and there are no more cards in the Shoe.

The Shoe publishes a method, Shoe.getEmptyShoeCard(). Shoe.getEmptyShoeCard()returns a Card object that has a graphic, indicating an empty card shoe. You can make the empty card graphic to be whatever you like. The Service layer expects that the file associated with the empty card graphic is stored in the directory described by imgDirectoryLocationFileSpec. The filename of the empty card image is described by the field, CardDealerHelper.emptyShoeImageFileSpec. The value of CardDealerHelper.emptyShoeImageFileSpec is set at runtime using Spring injection.

In order to maintain complete random exposure of a card, the Card object publishes a field, imageBytes, which is the physical content of a given image file. The Card object's constructor has a parameter, cardCode, which indicates the card code to be supported. (Please see Listing 2.) The constructor's behavior retrieves an image file from the file system based on the cardCode parameter. Intelligence within the Card object's constructor attaches the card image to the Card object's imageBytes field as a byte array. In addition, the file type/extension of the image file is assigned to the Card object's fileFormat field. The card code is assigned to the Card object's code field. Thus, consumers of a Card object can render a card's image and know the suit and rank of a card, while never knowing the filename used.

Listing 2: A Card object is constructed based on card code information

01:    /**
02:     * Instantiates a new card.
03:     * 
04:     * @param cardCode the card code
05:     * @param shoeId the shoe id
06:     * 
07:     * @throws IOException Signals that an I/O exception has occurred.
08:     */
09:     public Card(String cardCode, String shoeId) throws IOException {
10:         code = cardCode;
11:         imagesDirectoryLocation = CardDealerHelper.getImageDirectoryLocation();
12:         String[] files = CardDealerHelper.getImagesDirectoryFileNames();
13:         //Go through all the files in the image directory and find
14:         //the one that matches the card code. Once found, process accordingly
15:         for (int i = 0; i < files.length; i++) {
16:             if (files[i].toLowerCase().indexOf(cardCode.toLowerCase()) != -1) {
17:                 this.shoeId = shoeId;
18:                 this.imageUrl = imagesDirectoryLocation + "/" + files[i];
19:                 this.imagesBytes = CardDealerHelper
20:                         .getImageBytesFromFileSpec(this.imageUrl);
21:                 this.fileFormat = files[i].substring(
22:                         files[i].lastIndexOf('.') + 1).toLowerCase();
23:             }
24:         }
25: 
26:         if (this.imagesBytes == null) {
27:             throw new IllegalArgumentException("Card image bytes is empty");
28:         }
29:     }

Also, should a user want to use a different set of card graphics, he or she can put differently styled card images in a alternate directory and then use the CardDealerHelper object's setImageDirectoryFileSpec() method to make the application look for the new card graphics in the alternate directory on the fly.

The architecture for CardDealer is illustrated below in Figure 3.

CardDealer Web App
Figure 3: CardDealer is a Web App that provides access to a service API via REST.
(click to view)

Figure 4 below is an informal sequence diagram that describes the control flow between the CardDealer components.

CardDealer Web App
Figure 4: CardDealer uses REST to make get a random playing card object from a Service API and return the object as JSON
(click to view)

Configuring the Application to Use Jersey

In order to get Jersey to work with the CardDealer application we need to configure the Jersey servlet to a URL root. Also, we need to tell Jersey the location of the Java code to use as the Java resource class. This information is part of the web application's web.xml file. Listing 3 below shows a snippet of web.xml that takes care of the Jersey configuration.

Listing 3: A snippet from the REST Web App's web.xml

<servlet> 
  <servlet-name>REST</servlet-name>
  <servlet-class>
    com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
  <!-- Make classes in com.edmunds.fun.cards Jersey RESTful-->
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.edmunds.fun.cards</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
 <!-- Map /rest/* to Jersey -->
<servlet-mapping>
  <servlet-name>REST</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping> 

Setting the REST Entry Point

Notice please that in Listing 3 above we've set the entry point for REST URLs to be /rest/*. The line of code below shows an example of a REST entry point that conforms to the entry point configured in Listing 2.

http://localhost:9090/card-dealer-rest/rest/....

Where:
http://localhost:9090 is the domain
/card-dealer-rest/ is the web app
/rest/ is the REST entry point

We are directing REST traffic to /rest/* because we want to leave the root open so that the web server can process URLs for standard web traffic such as:

  http://localhost:9090/card-dealer-rest/hellow-world.html

  http://localhost:9090/card-dealer-rest/index.html

The benefit here is that we can setup our web app to be both a REST server and a web site that feeds usual web pages that use the REST Server. The reason for having both REST Server and the web pages share the same domain is that we'll be able to write AJAX call backs to the REST server and avoid the cross site security issue. AJAX does not allow you to make XmlHttpRequest calls into any site other than the originating site. Thus, having everything in the same site makes our REST services available to our web pages.

Defining the Card Images Directory

As mentioned above, CardDealer uses Spring injection to set the location of the card image directory in CardDealerHelper. Also, Spring injection is used to supply the filename of the empty card graphic. This information is part of the Spring context configuration file, as shown below in List 4.

Listing 4: A snippet from the web app file, spring-context.xml, for Spring configuration settings

<context:component-scan base-package="com.edmunds.fun.cards" />
<!--
  Inject cardDealerHelper with the information for
  imageDirectoryLocationFileSpec and emptyShoeImageFileSpec
-->
<bean id="cardDealerHelper" class="com.edmunds.fun.cards.CardDealerHelper">
  <property name="imageDirectoryLocationFileSpec" value="C:/Projects/CardGames/card-deck" />
  <property name="emptyShoeImageFileSpec" value="empty-shoe.jpg" />
</bean>

Getting the REST Server up and running using Jetty

The stage is now set to start the REST server. The Maven POM file for the project has been configured to use the Jetty Web Server. Jetty has set in the project to run as a Maven goal. So starting it up is a simple affair.

First we call the Maven install goal to compile, build and install artifacts in the local machine's repository as shown in Figure 5.

Maven Install
Figure 5: Use the Maven install goal to build the project
(click to view)

Then we change the directory down to the project web app and start up the Jetty server using man jetty:run as shown in Figure 6.

Maven Jetty Run
Figure 6: We'll use the Jetty server to get web pages and access REST services
(click to view)

If all is in order, you'll see the Jetty 'up and running" message in your terminal's window as shown below in Figure 7.

Up and Running
Figure 7:The project's web app runs on port 9090

Now were ready to work on the client-side.

Getting REST Application Information Using WADL

One of nicer features of Jersey is that you can configure it to display a WADL file that describes the REST services available on the server. WADL is an acronym for Web Application Description Language. WADL is an XML file that tells you, among other things, how the Java resource class binds to REST URLs. The CardDealerRest project POM file has configured the necessary artifacts which enables the automatic display. So, to view the WADL file for the project we enter the URL:

http://localhost:9090/card-dealer-rest/rest/application.wadl

The trick to getting the WADL file to display automatically is to provide a URL that has "application.wadl" directly after the entry point to the REST service. Figure 8 below shows the result of calling the WADL for the CardDealer REST Service.

Maven Jetty Run
Figure 8: Web Application Description Language (WADL) is an XML structure that is self describing
(click to view)

Calling a Rest Service Using Ajax

Now we're ready to get some cards via a web page. We call, http://localhost:9090/card-dealer-rest/index.html which is the page that has the client side card retrieval code. We click the Get Card button and a card appears. How? By way of AJAX.

The button's click event is bound to a JavaScript getCard() function. getCard() makes an AJAX call back to the REST Server. (Please see Listing 5.) The REST server sends back a string that describes a card as a JSON version of the RestCard object. RestCard is a facade object that presents the Card.imageBytes property as a Base64 encoded string.

Listing 5: The JavaScript function getCard() is called when the Get Card button is clicked.

function getCard()
  {
    if (window.XMLHttpRequest)
       {
        // code for IE7+, Firefox, Chrome, Opera, Safari
        xmlhttp=new XMLHttpRequest();
       }
     else if (window.ActiveXObject)
       {
         // code for IE6, IE5
         xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
     else
        {
          alert("Your browser does not support XMLHTTP!");
        }
     //get the card's shoeId, if one is in play
     var currentShoeId = document.getElementById("shoeId").value;
     //Prepare the REST URL 
     var URL = "rest/card-dealer/card?shoeId=" + currentShoeId;
     xmlhttp.open("GET",url,true);
     xmlhttp.onreadystatechange=callback;
     xmlhttp.send(null);
  }

The JSON-ized RestCard object publishes three properties, code, image and shoeId. The code property reports the suit and rank of the card using the card code convention described above. shoeId reports the particular shoe from which the card was dealt and the property. Image is a Base64 encoded string that contains a graphic of the playing card. Listing 6 below shows the an example of a JSON string that describes a playing card retrieved from the CardDealer REST service.

Listing 6: The JSON Card object publishes the properties, code, image, and shoeId where the property image is a Base64 encoding of a card graphic

{"code":"c12",
"fileFormat:"gif",
"image":"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\\r\\nxjICAAA7\\r\\n",
"shoeId":"0ad84189-0ba3-46c3-a082-63f9e561d87d"}

Rendering JSON Based Images in a Browser

The AJAX callback JavaScript function that provides the intelligence to apply information in the returned RestCard JSON object to the browser DOM is shown below in Listing 7. Poetically enough the callback function is named, callback().

Listing 7: The JavaScript code called upon a successful AJAX HttpRequest

function callback(){
  if(xmlhttp.readyState==4){
    //Create the card JSON object
    var card = eval( "(" + xmlhttp.responseText + ")" );
    //Get the <img> element
    var img = document.getElementById("cardViewer");
    //Apply the Base64 string, telling the image element about the nature of
    //the data
    img.src="data:image/" + card.fileFormat + ";base64," + card.image;
    //Put the card code in the image alt text
    img.alt = card.code;
    //Get the hidden element and apply the shoeId value
    var shoeId = document.getElementById("shoeId");
    shoeId.value = card.shoeId;
    //Also, display the shoeId value on the page          
    var shoeIdDiv = document.getElementById("shoeIdDiv");
    shoeIdDiv.innerHTML = card.shoeId;
    //Display the card code on the page          
    var cardCodeDiv = document.getElementById("cardCodeDiv");
    cardCodeDiv.innerHTML = card.code;
  }
}

Basically the function, callback(), runs the Javascript eval() method on the xmlhttp.responseText (formally XMLHttpRequest.ResponseText), which is the string returned by the REST server. Running eval() converts the string into the JSON RestCard object. Once the string has been objectified, the JavaScript finds the relevant DOM objects on the web page and applies the associated values from the JSON RestCard object onto the DOM. Notice please in this line of the code below, taken from the JavaScript above:

img.src="data:image/" + card.fileFormat + ";base64," + card.image;

This is the piece of code that allows the JavaScript in the callback to render the Base64 string as an image on a web page. The code is short, but it does a lot. Blasting bytes into a web page as an image at runtime is no small undertaking. This is all possible because of the use of the data URL scheme.

data:[<mediatype>][;base64],<data> is a URL scheme that defines a URL as pure data. Thus, we can attach the Base64 encoded string to the image element on the fly!

Getting the code

Before we bring things to a close you might like to take a look at the Java and JavaScript source code that used in this project. Table 2 below provides a list of links that will take you to a particular piece of code. Also, a description is provided with each link.

Table 2: The CardDealer project source code

Code Filename Description Layer
index.html Web page that provides random card Display Web Site
CardDealerRest.java The Jersey Java resource class that exposes REST services REST
RestCard.java A facade object that exposes the Card image a Base64 string. REST
Card.java A Plain Old Java Object (POJO) that describes a playing card Service
CardDealerHelper.java A helper class the provides access to image files on the host computer Service
EmptyShoeException.java Thrown when a Shoe is empty of cards Service
InvalidCardDeckException.java Thrown when the image directory does not have all of the necessary image files, one for each playing card, named according to the CardDealer card-code convention. Service
Shoe.java A POJO that is an abstraction of the card shoe, as you find in a casino. You access a Shoe via the ShoeFactory Service
Shoeable.java The interface that describes the base services provided by a Shoe Service
ShoeFactory.java A singleton that creates, provides and manages Shoe objects Service

Conclusion

Whew! This has been a long trip to show a playing card on a web page. But getting a card on a page is only the facade that masks the significance and use of REST and Jersey, which is the purpose of this article. REST is not going away and Jersey continues to grow in use. You've learned that by mastering the basics of Jersey annotations and URL paths you can bring the power of REST to your Java code very quickly. So hopefully you've found this trip has been worthwhile undertaking.

Installing the CardDealer sample project

The sample project for this article, CardDealer is a fully configured Maven project. Thus, you need to have Maven installed on the machine that you are using to run this code.(Maven is supported by Windows, Linux and Mac OSX.) To get the project up and running take the following steps.

  1. Download the project source code zip file here, and the project's card image directory zip file here.
  2. Unzip the project code zip file, card-dealer-1.0.zip onto your machine
  3. Unzip the project's image directory zip file, card-deck.zip to onto your machine
  4. Set the location of the image directory in the node, <property name="imageDirectoryLocationFileSpec" value="LOCATION_OF_IMAGE_DIR_ON_YOUR_MACHINE" /> in the file, /YOUR_INSTALLATION_DIRECTORY/card-dealer-1.0/card-dealer-rest/src/main/webapp/WEB-INF/spring-context.xml.
  5. Install the code in your local Maven repository, as shown above in Figure 5.
  6. Start the Jetty web server, under Maven as shown in Figure 6.
  7. In your browser, enter the URL, http://localhost:9090/card-dealer-rest/index.html

Related Resources

About the Author

Bob Reselman is a Senior Technical Writer and Technical Editor for Edmunds Inc. Edmunds Inc. is a leading publisher of high volume, high availability, state of the art, Java based Web sites dedicated to empowering the automotive consumer. Experience Edmunds technology by visiting, www.Edmunds.com and www.InsideLine.com.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date