January 19, 2021
Hot Topics:

Real World REST Using Jersey, AJAX and JSON

  • By Bob Reselman
  • Send Email »
  • More Articles »

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:         }
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

  <!-- Make classes in com.edmunds.fun.cards Jersey RESTful-->
 <!-- Map /rest/* to Jersey -->

Page 2 of 4

This article was originally published on October 9, 2009

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

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