28. Web Services in Java

A client is to me a mere unit, a factor in a problem.

—Sir Arthur Conan Doyle

They also serve who only stand and wait.

—John Milton

...if the simplest things of nature have a message that you understand, rejoice, for your soul is alive.

—Eleonora Duse

Protocol is everything.

—Francoise Giuliani

Objectives

In this chapter you will learn:

• What a web service is.

• How to publish and consume web services in NetBeans.

• How XML, JSON, XML-Based Simple Object Access Protocol (SOAP) and Representational State Transfer (REST) Architecture enable Java web services.

• How to create client desktop and web applications that consume web services.

• How to use session tracking in web services to maintain client state information.

• How to connect to databases from web services.

• How to pass objects of user-defined types to and return them from a web service.

Outline

28.1 Introduction

28.2 Web Service Basics

28.3 Simple Object Access Protocol (SOAP)

28.4 Representational State Transfer (REST)

28.5 JavaScript Object Notation (JSON)

28.6 Publishing and Consuming SOAP-Based Web Services

28.6.1 Creating a Web Application Project and Adding a Web Service Class in NetBeans

28.6.2 Defining the WelcomeSOAP Web Service in NetBeans

28.6.3 Publishing the WelcomeSOAP Web Service from NetBeans

28.6.4 Testing the WelcomeSOAP Web Service with GlassFish Application Server’s Tester Web Page

28.6.5 Describing a Web Service with the Web Service Description Language (WSDL)

28.6.6 Creating a Client to Consume the WelcomeSOAP Web Service

28.6.7 Consuming the WelcomeSOAP Web Service

28.7 Publishing and Consuming REST-Based XML Web Services

28.7.1 Creating a REST-Based XML Web Service

28.7.2 Consuming a REST-Based XML Web Service

28.8 Publishing and Consuming REST-Based JSON Web Services

28.8.1 Creating a REST-Based JSON Web Service

28.8.2 Consuming a REST-Based JSON Web Service

28.9 Session Tracking in a SOAP Web Service

28.9.1 Creating a Blackjack Web Service

28.9.2 Consuming the Blackjack Web Service

28.10 Consuming a Database-Driven SOAP Web Service

28.10.1 Creating the Reservation Database

28.10.2 Creating a Web Application to Interact with the Reservation Service

28.11 Equation Generator: Returning User-Defined Types

28.11.1 Creating the EquationGeneratorXML Web Service

28.11.2 Consuming the EquationGeneratorXML Web Service

28.11.3 Creating the EquationGeneratorJSON Web Service

28.11.4 Consuming the EquationGeneratorJSON Web Service

Summary | Self-Review Exercises | Answers to Self-Review Exercises | Exercises

28.1. Introduction

This chapter introduces web services, which promote software portability and reusability in applications that operate over the Internet. A web service is a software component stored on one computer that can be accessed by an application (or other software component) on another computer over a network. Web services communicate using such technologies as XML, JSON and HTTP. In this chapter, we use two Java APIs that facilitate web services. The first, JAX-WS, is based on the Simple Object Access Protocol (SOAP)—an XML-based protocol that allows web services and clients to communicate, even if the client and the web service are written in different languages. The second, JAX-RS, uses Representational State Transfer (REST)—a network architecture that uses the web’s traditional request/response mechanisms such as GET and POST requests. For more information on SOAP-based and REST-based web services, visit our Web Services Resource Centers:

www.deitel.com/WebServices/
www.deitel.com/RESTWebServices/

These Resource Centers include information about designing and implementing web services in many languages and about web services offered by companies such as Google, Amazon and eBay. You’ll also find many additional tools for publishing and consuming web services. For more information about REST-based Java web services, check out the Jersey project:

jersey.java.net/

The XML used in this chapter is created and manipulated for you by the APIs, so you need not know the details of XML to use it here. To learn more about XML, see Chapter 15and visit our XML Resource Center:

www.deitel.com/XML/

Business-to-Business Transactions

Rather than relying on proprietary applications, businesses can conduct transactions via standardized, widely available web services. This has important implications for business-to-business (B2B) transactions. Web services are platform and language independent, enabling companies to collaborate without worrying about the compatibility of their hardware, software and communications technologies. Companies such as Amazon, Google, eBay, PayPal and many others are benefiting by making their server-side applications available to partners via web services.

By purchasing some web services and using other free ones that are relevant to their businesses, companies can spend less time developing applications and can create new ones that are more innovative. E-businesses for example, can provide their customers with enhanced shopping experiences. Consider an online music store. The store’s website links to information about various CDs, enabling users to purchase them, to learn about the artists, to find more titles by those artists, to find other artists’ music they may enjoy, and more. The store’s website may also link to the site of a company that sells concert tickets and provides a web service that displays upcoming concert dates for various artists, allowing users to buy tickets. By consuming the concert-ticket web service on its site, the online music store can provide an additional service to its customers, increase its site traffic and perhaps earn a commission on concert-ticket sales. The company that sells concert tickets also benefits from the business relationship by selling more tickets and possibly by receiving revenue from the online music store for the use of the web service.

Any Java programmer with a knowledge of web services can write applications that “consume” web services. The resulting applications would invoke web services running on servers that could be thousands of miles away.

NetBeans

NetBeans is one of many tools that enable you to publish and/or consume web services. We demonstrate how to use NetBeans to implement web services using the JAX-WS and JAX-RS APIs and how to invoke them from client applications. For each example, we provide the web service’s code, then present a client application that uses the web service. Our first examples build simple web services and client applications in NetBeans. Then we demonstrate web services that use more sophisticated features, such as manipulating databases with JDBC and manipulating class objects. For information on downloading and installing the NetBeans and the GlassFish server, see Section 26.1.

28.2. Web Service Basics

The machine on which a web service resides is referred to as a web service host. The client application sends a request over a network to the web service host, which processes the request and returns a response over the network to the application. This kind of distributed computing benefits systems in various ways. For example, an application without direct access to data on another system might be able to retrieve the data via a web service. Similarly, an application lacking the processing power to perform specific computations could use a web service to take advantage of another system’s superior resources.

In Java, a web service is implemented as a class that resides on a server—it’s not part of the client application. Making a web service available to receive client requests is known as publishing a web service; using a web service from a client application is known as consuming a web service.

28.3. Simple Object Access Protocol (SOAP)

The Simple Object Access Protocol (SOAP) is a platform-independent protocol that uses XML to interact with web services, typically over HTTP. You can view the SOAP specification at www.w3.org/TR/soap/. Each request and response is packaged in a SOAP message—XML markup containing the information that a web service requires to process the message. SOAP messages are written in XML so that they’re computer readable, human readable and platform independent. Most firewalls—security barriers that restrict communication among networks—allow HTTP traffic to pass through, so that clients can browse the web by sending requests to and receiving responses from web servers. Thus, SOAP-based services can send and receive SOAP messages over HTTP connections with few limitations.

SOAP supports an extensive set of types, including the primitive types (e.g., int), as well as DateTime, XmlNode and others. SOAP can also transmit arrays of these types. When a program invokes a method of a SOAP web service, the request and all relevant information are packaged in a SOAP message enclosed in a SOAP envelope and sent to the server on which the web service resides. When the web service receives this SOAP message, it parses the XML representing the message, then processes the message’s contents. The message specifies the method that the client wishes to execute and the arguments the client passed to that method. Next, the web service calls the method with the specified arguments (if any) and sends the response back to the client in another SOAP message. The client parses the response to retrieve the method’s result. In Section 28.6, you’ll build and consume a basic SOAP web service.

28.4. Representational State Transfer (REST)

Representational State Transfer (REST) refers to an architectural style for implementing web services. Such web services are often called RESTful web services. Though REST itself is not a standard, RESTful web services are implemented using web standards. Each method in a RESTful web service is identified by a unique URL. Thus, when the server receives a request, it immediately knows what operation to perform. Such web services can be used in a program or directly from a web browser. The results of a particular operation may be cached locally by the browser when the service is invoked with a GET request. This can make subsequent requests for the same operation faster by loading the result directly from the browser’s cache. Amazon’s web services (aws.amazon.com) are RESTful, as are many others.

RESTful web services are alternatives to those implemented with SOAP. Unlike SOAP-based web services, the request and response of REST services are not wrapped in envelopes. REST is also not limited to returning data in XML format. It can use a variety of formats, such as XML, JSON, HTML, plain text and media files. In Sections 28.7––28.8, you’ll build and consume basic RESTful web services.

28.5. JavaScript Object Notation (JSON)

JavaScript Object Notation (JSON) is an alternative to XML for representing data. JSON is a text-based data-interchange format used to represent objects in JavaScript as collections of name/value pairs represented as Strings. It’s commonly used in Ajax applications. JSON is a simple format that makes objects easy to read, create and parse and, because it’s much less verbose than XML, allows programs to transmit data efficiently across the Internet. Each JSON object is represented as a list of property names and values contained in curly braces, in the following format:

{ propertyName1 : value1, propertyName2 : value2 }

Arrays are represented in JSON with square brackets in the following format:

[ value1, value2, value3 ]

Each value in an array can be a string, a number, a JSON object, true, false or null. To appreciate the simplicity of JSON data, examine this representation of an array of address–book entries:

[ { first: 'Cheryl', last: 'Black' },
  { first: 'James', last: 'Blue' },
  { first: 'Mike', last: 'Brown' },
  { first: 'Meg', last: 'Gold' } ]

Many programming languages now support the JSON data format. An extensive list of JSON libraries sorted by language can be found at www.json.org.

28.6. Publishing and Consuming SOAP-Based Web Services

This section presents our first Java example of publishing (enabling for client access) and consuming (using) a web service. We begin with a SOAP-based web service.

28.6.1. Creating a Web Application Project and Adding a Web Service Class in NetBeans

When you create a web service in NetBeans, you focus on its logic and let the IDE and server handle its infrastructure. First you create a Web Application project. NetBeans uses this project type for web services that are invoked by other applications.

Creating a Web Application Project in NetBeans

To create a web application, perform the following steps:

1. Select File > New Project... to open the New Project dialog.

2. Select Java Web from the dialog’s Categories list, then select Web Application from the Projects list. Click Next >.

3. Specify the name of your project (WelcomeSOAP) in the Project Name field and specify where you’d like to store the project in the Project Location field. You can click the Browse button to select the location. Click Next >.

4. Select GlassFish Server 3 from the Server drop-down list and Java EE 6 Web from the Java EE Version drop-down list.

5. Click Finish to create the project.

This creates a web application that will run in a web browser, similar to the projects used in Chapters 26 and 27.

Adding a Web Service Class to a Web Application Project

Perform the following steps to add a web service class to the project:

1. In the Projects tab in NetBeans, right click the WelcomeSOAP project’s node and select New > Web Service... to open the New Web Service dialog.

2. Specify WelcomeSOAP in the Web Service Name field.

3. Specify com.deitel.welcomesoap in the Package field.

4. Click Finish to create the web service class.

The IDE generates a sample web service class with the name from Step 2 in the package from Step 3. You can find this class in your project’s Web Services node. In this class, you’ll define the methods that your web service makes available to client applications. When you eventually build your application, the IDE will generate other supporting files for your web service.

28.6.2. Defining the WelcomeSOAP Web Service in NetBeans

Figure 28.1 contains the completed WelcomeSOAPService code (reformatted to match the coding conventions we use in this book). First we discuss this code, then show how to use the NetBeans web service design view to add the welcome method to the class.


 1   // Fig. 28.1: WelcomeSOAP.java
 2   // Web service that returns a welcome message via SOAP.
 3   package com.deitel.welcomesoap;
 4
 5   import javax.jws.WebService; // program uses the annotation @WebService
 6   import javax.jws.WebMethod; // program uses the annotation @WebMethod  
 7   import javax.jws.WebParam; // program uses the annotation @WebParam    
 8
 9   @WebService() // annotates the class as a web service
10   public class WelcomeSOAP
11   {
12      // WebMethod that returns welcome message                      
13      @WebMethod( operationName = "welcome" )                        
14      public String welcome( @WebParam( name = "name" ) String name )
15      {
16         return "Welcome to JAX-WS web services with SOAP, " + name + "!";
17      } // end method welcome
18   } // end class WelcomeSOAP


Fig. 28.1. Web service that returns a welcome message via SOAP.

Annotation import Declarations

Lines 5–7 import the annotations used in this example. By default, each new web service class created with the JAX-WS APIs is a POJO (plain old Java object), so you do not need to extend a class or implement an interface to create a web service.

@WebService Annotation

Line 9 contains a @WebService annotation (imported at line 5) which indicates that class WelcomeSOAP implements a web service. The annotation is followed by parentheses that may contain optional annotation attributes. The optional name attribute specifies the name of the service endpoint interface class that will be generated for the client. A service endpoint interface (SEI) class (sometimes called a proxy class) is used to interact with the web service—a client application consumes the web service by invoking methods on the service endpoint interface object. The optional serviceName attribute specifies the service name, which is also the name of the class that the client uses to obtain a service endpoint interface object. If the serviceName attribute is not specified, the web service’s name is assumed to be the Java class name followed by the word Service. NetBeans places the @WebService annotation at the beginning of each new web service class you create. You can then add the name and serviceName properties in the parentheses following the annotation.

When you deploy a web application containing a class that uses the @WebService annotation, the server (GlassFish in our case) recognizes that the class implements a web service and creates all the server-side artifacts that support the web service—that is, the framework that allows the web service to wait for client requests and respond to those requests once it’s deployed on an application server. Some popular open-source application servers that support Java web services include GlassFish (glassfish.dev.java.net), Apache Tomcat (tomcat.apache.org) and JBoss Application Server (www.jboss.com/products/platforms/application).

WelcomeSOAP Service’s welcome Method

The WelcomeSOAP service has only one method, welcome (lines 13–17), which takes the user’s name as a String and returns a String containing a welcome message. This method is tagged with the @WebMethod annotation to indicate that it can be called remotely. Any methods that are not tagged with @WebMethod are not accessible to clients that consume the web service. Such methods are typically utility methods within the web service class. The @WebMethod annotation uses the operationName attribute to specify the method name that is exposed to the web service’s client. If the operationName is not specified, it’s set to the actual Java method’s name.


Image Common Programming Error 28.1

Failing to expose a method as a web method by declaring it with the @WebMethod annotation prevents clients of the web service from accessing the method. There’s one exception—if none of the class’s methods are declared with the @WebMethod annotation, then all the public methods of the class will be exposed as web methods.



Image Common Programming Error 28.2

Methods with the @WebMethod annotation cannot be static. An object of the web service class must exist for a client to access the service’s web methods.


The name parameter to welcome is annotated with the @WebParam annotation (line 14). The optional @WebParam attribute name indicates the parameter name that is exposed to the web service’s clients. If you don’t specify the name, the actual parameter name is used.

Completing the Web Service’s Code

NetBeans provides a web service design view in which you can define the method(s) and parameter(s) for your web services. To define the WelcomeSOAP class’s welcome method, perform the following steps:

1. In the project’s Web Services node, double click WelcomeSOAP to open the file WelcomeSOAPService.java in the code editor.

2. Click the Design button at the top of the code editor to show the web service design view (Fig. 28.2).

Image

Fig. 28.2. Web service design view.

3. Click the Add Operation... button to display the Add Operation... dialog (Fig. 28.3).

Image

Fig. 28.3. Adding an operation to a web service.

4. Specify the method name welcome in the Name field. The default Return Type (String) is correct for this example.

5. Add the method’s name parameter by clicking the Add button to the right of the Parameters tab then entering name in the Name field. The parameter’s default Type (String) is correct for this example.

6. Click OK to create the welcome method. The design view should now appear as shown in Fig. 28.3.

7. At the top of the design view, click the Source button to display the class’s source code and add the code line 18 of Fig. 28.1 to the body of method welcome.

Image

Fig. 28.4. Web service design view after new operation is added.

28.6.3. Publishing the WelcomeSOAP Web Service from NetBeans

Now that you’ve created the WelcomeSOAP web service class, you’ll use NetBeans to build and publish (that is, deploy) the web service so that clients can consume its services. NetBeans handles all the details of building and deploying a web service for you. This includes creating the framework required to support the web service. Right click the project name WelcomeSOAP in the Projects tab and select Deploy to build and deploy the web application to the GlassFish server.

28.6.4. Testing the WelcomeSOAP Web Service with GlassFish Application Server’s Tester Web Page

Next, you’ll test the WelcomeSOAP web service. We previously selected the GlassFish application server to execute this web application. This server can dynamically create a web page that allows you to test a web service’s methods from a web browser. To use this capability:

1. Expand the project’s Web Services in the NetBeans Projects tab.

2. Right click the web service class name (WelcomeSOAP) and select Test Web Service.

The GlassFish application server builds the Tester web page and loads it into your web browser. Figure 28.5 shows the Tester web page for the WelcomeSOAP web service. The web service’s name is automatically the class name followed by Service.

Image

Fig. 28.5. Tester web page created by GlassFish for the WelcomeSOAP web service.

Once you’ve deployed the web service, you can also type the URL

http://localhost:8080/WelcomeSOAP/WelcomeSOAPService?Tester

in your web browser to view the Tester web page. WelcomeSOAPService is the name (specified in line 11 of Fig. 28.1) that clients use to access the web service.

To test WelcomeSOAP’s welcome web method, type your name in the text field to the right of the welcome button and click the button to invoke the method. Figure 28.6 shows the results of invoking WelcomeSOAP’s welcome method with the value Paul.

Image

Fig. 28.6. Testing WelcomeSOAP’s welcome method.

Application Server Note

You can access the web service only when the application server is running. If NetBeans launches GlassFish for you, it will automatically shut it down when you close NetBeans. To keep it up and running, you can launch it independently of NetBeans before you deploy or run web applications. The GlassFish Quick Start Guide at glassfish.java.net/downloads/quickstart/index.html shows how to manually start and stop the server.

Testing the WelcomeSOAP Web Service from Another Computer

If your computer is connected to a network and allows HTTP requests, then you can test the web service from another computer on the network by typing the following URL (where host is the hostname or IP address of the computer on which the web service is deployed) into a browser on another computer:

http://host:8080/WelcomeSOAP/WelcomeSOAPService?Tester

28.6.5. Describing a Web Service with the Web Service Description Language (WSDL)

To consume a web service, a client must determine its functionality and how to use it. For this purpose, web services normally contain a service description. This is an XML document that conforms to the Web Service Description Language (WSDL)—an XML vocabulary that defines the methods a web service makes available and how clients interact with them. The WSDL document also specifies lower-level information that clients might need, such as the required formats for requests and responses.

WSDL documents help applications determine how to interact with the web services described in the documents. You do not need to understand WSDL to take advantage of it—the GlassFish application server generates a web service’s WSDL dynamically for you, and client tools can parse the WSDL to help create the client-side service endpoint interface class that a client uses to access the web service. Since GlassFish (and most other servers) generate the WSDL dynamically, clients always receive a deployed web service’s most up-to-date description. To access the WelcomeSOAP web service, the client code will need the following WSDL URL:

http://localhost:8080/WelcomeSOAP/WelcomeSOAPService?WSDL

Accessing the WelcomeSOAP Web Service’s WSDL from Another Computer

Eventually, you’ll want clients on other computers to use your web service. Such clients need the web service’s WSDL, which they would access with the following URL:

http://host:8080/WelcomeSOAP/WelcomeSOAPService?WSDL

where host is the hostname or IP address of the server that hosts the web service. As we discussed in Section 28.6.4, this works only if your computer allows HTTP connections from other computers—as is the case for publicly accessible web and application servers.

28.6.6. Creating a Client to Consume the WelcomeSOAP Web Service

Now you’ll consume the web service from a client application. A web service client can be any type of application or even another web service. You enable a client application to consume a web service by adding a web service reference to the application.

Service Endpoint Interface (SEI)

An application that consumes a web service consists of an object of a service endpoint interface (SEI) class (sometimes called a proxy class) that’s used to interact with the web service and a client application that consumes the web service by invoking methods on the service endpoint interface object. The client code invokes methods on the service endpoint interface object, which handles the details of passing method arguments to and receiving return values from the web service on the client’s behalf. This communication can occur over a local network, over the Internet or even with a web service on the same computer. The web service performs the corresponding task and returns the results to the service endpoint interface object, which then returns the results to the client code. Figure 28.7 depicts the interactions among the client code, the SEI object and the web service. As you’ll soon see, NetBeans creates these service endpoint interface classes for you.

Image

Fig. 28.7. Interaction between a web service client and a web service.

Requests to and responses from web services created with JAX-WS (one of many different web service frameworks) are typically transmitted via SOAP. Any client capable of generating and processing SOAP messages can interact with a web service, regardless of the language in which the web service is written.

We now use NetBeans to create a client Java desktop GUI application. Then you’ll add a web service reference to the project so the client can access the web service. When you add the reference, the IDE creates and compiles the client-side artifacts—the framework of Java code that supports the client-side service endpoint interface class. The client then calls methods on an object of the service endpoint interface class, which uses the rest of the artifacts to interact with the web service.

Creating a Desktop Application Project in NetBeans

Before performing the steps in this section, ensure that the WelcomeSOAP web service has been deployed and that the GlassFish application server is running (see Section 28.6.3). Perform the following steps to create a client Java desktop application in NetBeans:

1. Select File > New Project... to open the New Project dialog.

2. Select Java from the Categories list and Java Application from the Projects list, then click Next >.

3. Specify the name WelcomeSOAPClient in the Project Name field and uncheck the Create Main Class checkbox. Later, you’ll add a subclass of JFrame that contains a main method.

4. Click Finish to create the project.

Step 2: Adding a Web Service Reference to an Application

Next, you’ll add a web service reference to your application so that it can interact with the WelcomeSOAP web service. To add a web service reference, perform the following steps.

1. Right click the project name (WelcomeSOAPClient) in the NetBeans Projects tab and select New > Web Service Client... from the pop-up menu to display the New Web Service Client dialog.

2. In the WSDL URL field, specify the URL http://localhost:8080/WelcomeSOAP/WelcomeSOAPService?WSDL (Fig. 28.8). This URL tells the IDE where to find the web service’s WSDL description. [Note: If the GlassFish application server is located on a different computer, replace localhost with the hostname or IP address of that computer.] The IDE uses this WSDL description to generate the client-side artifacts that compose and support the service endpoint interface.

Image

Fig. 28.8. New Web Service Client dialog.

3. For the other options, leave the default settings, then click Finish to create the web service reference and dismiss the New Web Service Client dialog.

In the NetBeans Projects tab, the WelcomeSOAPClient project now contains a Web Service References folder with the WelcomeSOAP web service’s service endpoint interface (Fig. 28.9). The service endpoint interface’s name is listed as WelcomeSOAPService.

Image

Fig. 28.9. NetBeans Project tab after adding a web service reference to the project.

When you specify the web service you want to consume, NetBeans accesses and copies its WSDL information to a file in your project (named WelcomeSOAPService.wsdl in this example). You can view this file by double clicking the WelcomeSOAPService node in the project’s Web Service References folder. If the web service changes, the client-side artifacts and the client’s copy of the WSDL file can be regenerated by right clicking the WelcomeSOAPService node shown in Fig. 28.9 and selecting Refresh.... Figure 28.9 also shows the IDE-generated client-side artifacts, which appear in the Generated Sources (jax-ws) folder.

28.6.7. Consuming the WelcomeSOAP Web Service

For this example, we use a GUI application to interact with the WelcomeSOAP web service. To build the client application’s GUI, add a subclass of JFrame to the project by performing the following steps:

1. Right click the project name (WelcomeSOAPClient) in the NetBeans Project tab and select New > JFrame Form... to display the New JFrame Form dialog.

2. Specify WelcomeSOAPClientJFrame in the Class Name field.

3. Specify com.deitel.welcomesoapclient in the Package field.

4. Click Finish to close the New JFrame Form dialog.

Next, use the NetBeans GUI design tools to build the GUI shown in the sample screen captures at the end of Fig. 28.10. The GUI consists of a Label, a Text Field and a Button.

The application in Fig. 28.10 uses the WelcomeSOAP web service to display a welcome message to the user. To save space, we do not show the NetBeans autogenerated initComponents method, which contains the code that creates the GUI components, positions them and registers their event handlers. To view the complete source code, open the WelcomeSOAPClientJFrame.java file in this example’s folder under srcjavacomdeitel welcomesoapclient. NetBeans places the GUI component instance-variable declarations at the end of the class (lines 114–116). Java allows instance variables to be declared anywhere in a class’s body as long as they’re placed outside the class’s methods. We continue to declare our own instance variables at the top of the class.


 1   // Fig. 28.10: WelcomeSOAPClientJFrame.java
 2   // Client desktop application for the WelcomeSOAP web service.
 3   package com.deitel.welcomesoapclient;
 4
 5   import com.deitel.welcomesoap.WelcomeSOAP;       
 6   import com.deitel.welcomesoap.WelcomeSOAPService;
 7   import javax.swing.JOptionPane;
 8
 9   public class WelcomeSOAPClientJFrame extends javax.swing.JFrame
10   {
11      // references the service endpoint interface object (i.e., the proxy)
12      private WelcomeSOAP welcomeSOAPProxy;                                
13
14      // no-argument constructor
15      public WelcomeSOAPClientJFrame()
16      {
17         initComponents();
18
19         try
20         {
21            // create the objects for accessing the WelcomeSOAP web service
22            WelcomeSOAPService service = new WelcomeSOAPService();         
23            welcomeSOAPProxy = service.getWelcomeSOAPPort();               
24         } // end try
25         catch ( Exception exception )
26         {
27            exception.printStackTrace();
28            System.exit( 1 );
29         } // end catch
30      } // end WelcomeSOAPClientJFrame constructor
31
32      // The initComponents method is autogenerated by NetBeans and is called
33      // from the constructor to initialize the GUI. This method is not shown
34      // here to save space. Open WelcomeSOAPClientJFrame.java in this       
35      // example's folder to view the complete generated code.               
36
87      // call the web service with the supplied name and display the message
88      private void submitJButtonActionPerformed(
89         java.awt.event.ActionEvent evt )
90      {
91         String name = nameJTextField.getText(); // get name from JTextField
92
93         // retrieve the welcome string from the web service
94         String message = welcomeSOAPProxy.welcome( name ); 
95         JOptionPane.showMessageDialog( this, message,
96            "Welcome", JOptionPane.INFORMATION_MESSAGE );
97      } // end method submitJButtonActionPerformed
98
99      // main method begins execution
100     public static void main( String args[] )
101     {
102        java.awt.EventQueue.invokeLater(
103           new Runnable()
104           {
105              public void run()
106              {
107                 new WelcomeSOAPClientJFrame().setVisible( true );
108              } // end method run
109           } // end anonymous inner class
110        ); // end call to java.awt.EventQueue.invokeLater
111     } // end main
112
113     // Variables declaration - do not modify
114     private javax.swing.JLabel nameJLabel;
115     private javax.swing.JTextField nameJTextField;
116     private javax.swing.JButton submitJButton;
117     // End of variables declaration
118  } // end class WelcomeSOAPClientJFrame

Image

Fig. 28.10. Client desktop application for the WelcomeSOAP web service.

Lines 5–6 import the classes WelcomeSOAP and WelcomeSOAPService that enable the client application to interact with the web service. Notice that we do not have import declarations for most of the GUI components used in this example. When you create a GUI in NetBeans, it uses fully qualified class names (such as javax.swing.JFrame in line 9), so import declarations are unnecessary.

Line 12 declares a variable of type WelcomeSOAP that will refer to the service endpoint interface object. Line 22 in the constructor creates an object of type WelcomeSOAPService. Line 23 uses this object’s getWelcomeSOAPPort method to obtain the WelcomeSOAP service endpoint interface object that the application uses to invoke the web service’s methods.

The event handler for the Submit button (lines 88–97) first retrieves the name the user entered from nameJTextField. It then calls the welcome method on the service endpoint interface object (line 94) to retrieve the welcome message from the web service. This object communicates with the web service on the client’s behalf. Once the message has been retrieved, lines 95–96 display it in a message box by calling JOptionPane’s showMessageDialog method.

28.7. Publishing and Consuming REST-Based XML Web Services

The previous section used a service endpoint interface (proxy) object to pass data to and from a Java web service using the SOAP protocol. Now, we access a Java web service using the REST architecture. We recreate the WelcomeSOAP example to return data in plain XML format. You can create a Web Application project as you did in Section 28.6.1 to begin. Name the project WelcomeRESTXML.

28.7.1. Creating a REST-Based XML Web Service

NetBeans provides various templates for creating RESTful web services, including ones that can interact with databases on the client’s behalf. In this chapter, we focus on simple RESTful web services. To create a RESTful web service:

1. Right-click the WelcomeRESTXML node in the Projects tab, and select New > Other... to display the New File dialog.

2. Select Web Services under Categories, then select RESTful Web Services from Patterns and click Next >.

3. Under Select Pattern, ensure Simple Root Resource is selected, and click Next >.

4. Set the Resource Package to com.deitel.welcomerestxml, the Path to welcome and the Class Name to WelcomeRESTXMLResource. Leave the MIME Type and Representation Class set to application/xml and java.lang.String, respectively. The correct configuration is shown in Fig. 28.11.

5. Click Finish to create the web service.

Image

Fig. 28.11. Creating the WelcomeRESTXML RESTful web service.

NetBeans generates the class and sets up the proper annotations. The class is placed in the project’s RESTful Web Services folder. The code for the completed service is shown in Fig. 28.12. You’ll notice that the completed code does not include some of the code generated by NetBeans. We removed the pieces that were unnecessary for this simple web service. The autogenerated putXml method is not necessary, because this example does not modify state on the server. The UriInfo instance variable is not needed, because we do not use HTTP query parameters. We also removed the autogenerated constructor, because we have no code to place in it.


 1   // Fig. 28.12: WelcomeRESTXMLResource.java
 2   // REST web service that returns a welcome message as XML.
 3   package com.deitel.welcomerestxml;
 4
 5   import java.io.StringWriter;
 6   import javax.ws.rs.GET; // annotation to indicate method uses HTTP GET 
 7   import javax.ws.rs.Path; // annotation to specify path of resource     
 8   import javax.ws.rs.PathParam; // annotation to get parameters from URI 
 9   import javax.ws.rs.Produces; // annotation to specify type of data     
10   import javax.xml.bind.JAXB; // utility class for common JAXB operations
11
12   @Path( "welcome" ) // URI used to access the resource
13   public class WelcomeRESTXMLResource
14   {
15      // retrieve welcome message
16      @GET // handles HTTP GET requests                          
17      @Path( "{name}" ) // URI component containing parameter    
18      @Produces( "application/xml" ) // response formatted as XML 
19      public String getXml( @PathParam( "name" ) String name )   
20      {
21         String message = "Welcome to JAX-RS web services with REST and " +
22            "XML, " + name + "!"; // our welcome message
23         StringWriter writer = new StringWriter();
24         JAXB.marshal( message, writer ); // marshal String as XML
25         return writer.toString(); // return XML as String
26      } // end method getXml
27   } // end class WelcomeRESTXMLResource


Fig. 28.12. REST web service that returns a welcome message as XML.

Lines 6–9 contain the imports for the JAX-RS annotations that help define the RESTful web service. The @Path annotation on the WelcomeRESTXMLResource class (line 12) indicates the URI for accessing the web service. This URI is appended to the web application project’s URL to invoke the service. Methods of the class can also use the @Path annotation (line 17). Parts of the path specified in curly braces indicate parameters—they’re placeholders for values that are passed to the web service as part of the path. The base path for the service is the project’s resources directory. For example, to get a welcome message for someone named John, the complete URL is

http://localhost:8080/WelcomeRESTXML/resources/welcome/John

Arguments in a URL can be used as arguments to a web service method. To do so, you bind the parameters specified in the @Path specification to parameters of the web service method with the @PathParam annotation, as shown in line 19. When the request is received, the server passes the argument(s) in the URL to the appropriate parameter(s) in the web service method.

The @GET annotation denotes that this method is accessed via an HTTP GET request. The putXml method the IDE created for us had an @PUT annotation, which indicates that the method is accessed using the HTTP PUT method. Similar annotations exist for HTTP POST, DELETE and HEAD requests.

The @Produces annotation denotes the content type returned to the client. It’s possible to have multiple methods with the same HTTP method and path but different @Produces annotations, and JAX-RS will call the method matching the content type requested by the client. Standard Java method overloading rules apply, so such methods must have different names. The @Consumes annotation for the autogenerated putXml method (which we deleted) restricts the content type that the web service will accept from a PUT operation.

Line 10 imports the JAXB class from package javax.xml.bind. JAXB (Java Architecture for XML Binding) is a set of classes for converting POJOs to and from XML. There are many related classes in the same package that implement the serializations we perform, but the JAXB class contains easy-to-use wrappers for common operations. After creating the welcome message (lines 21–22), we create a StringWriter (line 23) to which JAXB will output the XML. Line 24 calls the JAXB class’s static method marshal to convert the String containing our message to XML format. Line 25 calls StringWriter’s toString method to retrieve the XML text to return to the client.

Testing RESTful Web Services

Section 28.6.4 demonstrated testing a SOAP service using GlassFish’s Tester page. GlassFish does not provide a testing facility for RESTful services, but NetBeans automatically generates a test page that can be accessed by right clicking the WelcomeRESTXML node in the Projects tab and selecting Test RESTful Web Services. This will compile and deploy the web service, if you have not yet done so, then open the test page. Your browser will probably require you to acknowledge a potential security issue before allowing the test page to perform its tasks. The test page is loaded from your computer’s local file system, not the GlassFish server. Browsers consider the local file system and GlassFish as two different servers, even though they’re both on the local computer. For security reasons, browsers do not allow so-called cross-site scripting in which a web page tries to interact with a server other than the one that served the page.

On the test page (Fig. 28.13), expand the welcome element in the left column and select {name}. The form on the right side of the page allows you to choose the MIME type of the data (application/xml by default) and lets you enter the name parameter’s value. Click the Test button to invoke the web service and display the returned XML.

Image

Fig. 28.13. Test page for the WelcomeRESTXML web service.


Image Error-Prevention Tip 28.1

At the time of this writing, the test page did not work in Google’s Chrome web browser. If this is your default web browser, copy the test page’s URL from Chrome’s address field and paste it into another web browser’s address field. Fig. 28.13 shows the test page in Mozilla Firefox.


The test page shows several tabs containing the results and various other information. The Raw View tab shows the actual XML response. The Headers tab shows the HTTP headers returned by the server. The Http Monitor tab shows a log of the HTTP transactions that took place to complete the request and response. The Sub-Resource tab shows the actual URL that was used to invoke the web service

http://localhost:8080/WelcomeRESTXML/resources/welcome/Paul

You can enter this URL in any browser on your computer to invoke the web service with the value Paul.

The test page provides its functionality by reading a WADL file from the server—you can see the URL of the WADL file in the upper-left corner of the test page. WADL (Web Application Description Language) has similar design goals to WSDL, but describes RESTful services instead of SOAP services.

28.7.2. Consuming a REST-Based XML Web Service

As we did with SOAP, we create a Java application that retrieves the welcome message from the web service and displays it to the user. First, create a Java application with the name WelcomeRESTXMLClient. RESTful web services do not require web service references, so you can begin building the GUI immediately by creating a JFrame form called WelcomeRESTXMLClientJFrame and placing it in the com.deitel.welcomerestxmlclient package. The GUI is identical to the one in Fig. 28.10, including the names of the GUI elements. To create the GUI quickly, you can simply copy and paste the GUI from the Design view of the WelcomeSOAPClientJFrame class and paste it into the Design view of the WelcomeRESTXMLClientJFrame class. Figure 28.14 contains the completed code.


 1   // Fig. 28.14: WelcomeRESTXMLClientJFrame.java
 2   // Client that consumes the WelcomeRESTXML service.
 3   package com.deitel.welcomerestxmlclient;
 4
 5   import javax.swing.JOptionPane;
 6   import javax.xml.bind.JAXB; // utility class for common JAXB operations
 7
 8   public class WelcomeRESTXMLClientJFrame extends javax.swing.JFrame
 9   {
10      // no-argument constructor
11      public WelcomeRESTXMLClientJFrame()
12      {
13         initComponents();
14      } // end constructor
15
16      // The initComponents method is autogenerated by NetBeans and is called
17      // from the constructor to initialize the GUI. This method is not shown
18      // here to save space. Open WelcomeRESTXMLClientJFrame.java in this    
19      // example's folder to view the complete generated code.               
20
71      // call the web service with the supplied name and display the message
72      private void submitJButtonActionPerformed(
73         java.awt.event.ActionEvent evt)
74      {
75         String name = nameJTextField.getText(); // get name from JTextField
76
77         // the URL for the REST service
78         String url =
79            "http://localhost:8080/WelcomeRESTXML/resources/welcome/" + name;
80
81         // read from URL and convert from XML to Java String 
82         String message = JAXB.unmarshal( url, String.class );
83
84         // display the message to the user
85         JOptionPane.showMessageDialog( this, message,
86               "Welcome", JOptionPane.INFORMATION_MESSAGE );
87      } // end method submitJButtonActionPerformed
88
89      // main method begins execution
90      public static void main( String args[] )
91      {
92         java.awt.EventQueue.invokeLater(
93            new Runnable()
94            {
95               public void run()
96               {
97                  new WelcomeRESTXMLClientJFrame().setVisible( true );
98               } // end method run
99            } // end anonymous inner class
100        ); // end call to java.awt.EventQueue.invokeLater
101     } // end main
102
103     // Variables declaration - do not modify
104     private javax.swing.JLabel nameJLabel;
105     private javax.swing.JTextField nameJTextField;
106     private javax.swing.JButton submitJButton;
107     // End of variables declaration
108  } // end class WelcomeRESTXMLClientJFrame

Image

Fig. 28.14. Client that consumes the WelcomeRESTXML service.

You can access a RESTful web service with classes from Java API. As in the RESTful XML web service, we use the JAXB library. The JAXB class (imported on line 6) has a static unmarshal method that takes as arguments a file name or URL as a String, and a Class<T> object indicating the Java class to which the XML will be converted (line 82). In this example, the XML contains a String object, so we use the Java compiler shortcut String.class to create the Class<String> object we need as the second argument. The String returned from the call to the unmarshal method is then displayed to the user via JOptionPane’s showMessageDialog method (lines 85–86), as it was with the SOAP service. The URL used in this example to extract data from the web service matches the URL used by the test page.

28.8. Publishing and Consuming REST-Based JSON Web Services

While XML was designed primarily as a document interchange format, JSON is designed as a data exchange format. Data structures in most programming languages do not map directly to XML constructs—for example, the distinction between elements and attributes is not present in programming-language data structures. JSON is a subset of the JavaScript programming language, and its components—objects, arrays, strings, numbers—can be easily mapped to constructs in Java and other programming languages.

The standard Java libraries do not currently provide capabilities for working with JSON, but there are many open-source JSON libraries for Java and other languages; you can find a list of them at json.org. We chose the Gson library from code.google.com/p/google-gson/, which provides a simple way to convert POJOs to and from JSON.

28.8.1. Creating a REST-Based JSON Web Service

To begin, create a WelcomeRESTJSON web application, then create the web service by following the steps in Section 28.7.1. In Step 4, change the Resource Package to com.deitel.welcomerestjson, the Class Name to WelcomeRESTJSONResource and the MIME Type to application/json. Additionally, you must download the Gson library’s JAR file, then add it to the project as a library. To do so, right click your project’s Libraries folder, select Add JAR/Folder... locate the downloaded Gson JAR file and click Open. The complete code for the service is shown in Fig. 28.15.


 1   // Fig. 28.15: WelcomeRESTJSONResource.java
 2   // REST web service that returns a welcome message as JSON.
 3   package com.deitel.welcomerestjson;
 4
 5   import com.google.gson.Gson; // converts POJO to JSON and back again  
 6   import javax.ws.rs.GET; // annotation to indicate method uses HTTP GET
 7   import javax.ws.rs.Path; // annotation to specify path of resource    
 8   import javax.ws.rs.PathParam; // annotation to get parameters from URI
 9   import javax.ws.rs.Produces; // annotation to specify type of data    
10
11   @Path( "welcome" ) // path used to access the resource
12   public class WelcomeRESTJSONResource
13   {
14      // retrieve welcome message
15      @GET // handles HTTP GET requests                            
16      @Path( "{name}" ) // takes name as a path parameter          
17      @Produces( "application/json" ) // response formatted as JSON
18      public String getJson( @PathParam( "name" ) String name )    
19      {
20         // add welcome message to field of TextMessage object
21         TextMessage message = new TextMessage(); // create wrapper object
22         message.setMessage( String.format( "%s, %s!",
23            "Welcome to JAX-RS web services with REST and JSON", name ) );
24
25         return new Gson().toJson( message ); // return JSON-wrapped message
26      } // end method getJson
27   } // end class WelcomeRESTJSONResource
28
29   // private class that contains the message we wish to send
30   class TextMessage
31   {
32      private String message; // message we're sending
33
34      // returns the message
35      public String getMessage()
36      {
37         return message;
38      } // end method getMessage
39
40      // sets the message
41      public void setMessage( String value )
42      {
43         message = value;
44      } // end method setMessage
45   } // end class TextMessage


Fig. 28.15. REST web service that returns a welcome message as JSON.

All the annotations and the basic structure of the WelcomeRESTJSONResource class are the same as REST XML example. The argument to the @Produces attribute (line 17) is "application/json". The TextMessage class (lines 30–45) addresses a difference between JSON and XML. JSON does not permit strings or numbers to stand on their own—they must be encapsulated in a composite data type. So, we created class TextMessage to encapsulate the String representing the message.

When a client invokes this web service, line 21 creates the TextMessage object, then lines 22–23 set its contained message. Next, line 25 creates a Gson object (from package com.google.gson.Gson) and calls its toJson method to convert the TextMessage into its JSON String representation. We return this String, which is then sent back to the client in the web service’s response. There are multiple overloads of the toJson method, such as one that sends its output to a Writer instead of returning a String.

RESTful services returning JSON can be tested in the same way as those returning XML. Follow the procedure outlined in Section 28.7.1, but be sure to change the MIME type to application/json in the test web page; otherwise, the web service will return an error stating that it cannot produce the desired response.

28.8.2. Consuming a REST-Based JSON Web Service

We now create a Java application that retrieves the welcome message from the web service and displays it to the user. First, create a Java application with the name WelcomeRESTJSONClient. Then, create a JFrame form called WelcomeRESTXMLClientJFrame and place it in the com.deitel.welcomerestjsonclient package. The GUI is identical to the one in Fig. 28.10. To create the GUI quickly, copy it from the Design view of the WelcomeSOAPClientJFrame class and paste it into the Design view of the WelcomeRESTJSONClientJFrame class. Figure 28.16 contains the completed code.


 1   // Fig. 28.16: WelcomeRESTJSONClientJFrame.java
 2   // Client that consumes the WelcomeRESTJSON service.
 3   package com.deitel.welcomerestjsonclient;
 4
 5   import com.google.gson.Gson; // converts POJO to JSON and back again
 6   import java.io.InputStreamReader;
 7   import java.net.URL;
 8   import javax.swing.JOptionPane;
 9
10   public class WelcomeRESTJSONClientJFrame extends javax.swing.JFrame
11   {
12      // no-argument constructor
13      public WelcomeRESTJSONClientJFrame()
14      {
15         initComponents();
16      } // end constructor
17
18      // The initComponents method is autogenerated by NetBeans and is called
19      // from the constructor to initialize the GUI. This method is not shown
20      // here to save space. Open WelcomeRESTJSONClientJFrame.java in this   
21      // example's folder to view the complete generated code.               
22
73      // call the web service with the supplied name and display the message
74      private void submitJButtonActionPerformed(
75         java.awt.event.ActionEvent evt )
76      {
77         String name = nameJTextField.getText(); // get name from JTextField
78
79         // retrieve the welcome string from the web service
80         try
81         {
82            // the URL of the web service
83            String url = "http://localhost:8080/WelcomeRESTJSON/" +
84               "resources/welcome/" + name;
85
86            // open URL, using a Reader to convert bytes to chars   
87            InputStreamReader reader =                              
88               new InputStreamReader( new URL( url ).openStream() );
89
90            // parse the JSON back into a TextMessage           
91            TextMessage message =                               
92               new Gson().fromJson( reader, TextMessage.class );
93
94            // display message to the user
95            JOptionPane.showMessageDialog( this, message.getMessage(),
96               "Welcome", JOptionPane.INFORMATION_MESSAGE );
97         } // end try
98         catch ( Exception exception )
99         {
100           exception.printStackTrace(); // show exception details
101        } // end catch
102     } // end method submitJButtonActionPerformed
103
104     // main method begin execution
105     public static void main( String args[] )
106     {
107        java.awt.EventQueue.invokeLater(
108           new Runnable()
109           {
110              public void run()
111              {
112                 new WelcomeRESTJSONClientJFrame().setVisible( true );
113              } // end method run
114           } // end anonymous inner class
115        ); // end call to java.awt.EventQueue.invokeLater
116     } // end main
117
118     // Variables declaration - do not modify
119     private javax.swing.JLabel nameJLabel;
120     private javax.swing.JTextField nameJTextField;
121     private javax.swing.JButton submitJButton;
122     // End of variables declaration
123  } // end class WelcomeRESTJSONClientJFrame
124
125  // private class that contains the message we are receiving
126  class TextMessage
127  {
128     private String message; // message we're receiving
129
130     // returns the message
131     public String getMessage()
132     {
133        return message;
134     } // end method getMessage
135
136     // sets the message
137     public void setMessage( String value )
138     {
139        message = value;
140     } // end method setMessage
141  } // end class TextMessage


Fig. 28.16. Client that consumes the WelcomeRESTJSON service.

Lines 83–84 create the URL String that is used to invoke the web service. Lines 87–88 create a URL object using this String, then call the URL’s openStream method to invoke the web service and obtain an InputStream from which the client can read the response. The InputStream is wrapped in an InputStreamReader so it can be passed as the first argument to the Gson class’s fromJson method. This method is overloaded. The version we use takes as arguments a Reader from which to read a JSON String and a Class<T> object indicating the Java class to which the JSON String will be converted (line 92). In this example, the JSON String contains a TextMessage object, so we use the Java compiler shortcut TextMessage.class to create the Class<TextMessage> object we need as the second argument. Lines 95–96 display the message in the TextMessage object.

The TextMessage classes in the web service and client are unrelated. Technically, the client can be written in any programming language, so the manner in which a response is processed can vary greatly. Since our client is written in Java, we duplicated the TextMessage class in the client so we could easily convert the JSON object back to Java.

28.9. Session Tracking in a SOAP Web Service

Section 26.8 described the advantages of using session tracking to maintain client-state information so you can personalize the users’ browsing experiences. Now we’ll incorporate session tracking into a web service. Suppose a client application needs to call several methods from the same web service, possibly several times each. In such a case, it can be beneficial for the web service to maintain state information for the client, thus eliminating the need for client information to be passed between the client and the web service multiple times. For example, a web service that provides local restaurant reviews could store the client user’s street address during the initial request, then use it to return personalized, localized results in subsequent requests. Storing session information also enables a web service to distinguish between clients.

28.9.1. Creating a Blackjack Web Service

Our next example is a web service that assists you in developing a blackjack card game. The Blackjack web service (Fig. 28.17) provides web methods to shuffle a deck of cards, deal a card from the deck and evaluate a hand of cards. After presenting the web service, we use it to serve as the dealer for a game of blackjack (Fig. 28.18). The Blackjack web service uses an HttpSession object to maintain a unique deck of cards for each client application. Several clients can use the service at the same time, but web method calls made by a specific client use only the deck of cards stored in that client’s session. Our example uses the following blackjack rules:

Two cards each are dealt to the dealer and the player. The player’s cards are dealt face up. Only the first of the dealer’s cards is dealt face up. Each card has a value. A card numbered 2 through 10 is worth its face value. Jacks, queens and kings each count as 10. Aces can count as 1 or 11—whichever value is more beneficial to the player (as we’ll soon see). If the sum of the player’s two initial cards is 21 (i.e., the player was dealt a card valued at 10 and an ace, which counts as 11 in this situation), the player has “blackjack” and immediately wins the game—if the dealer does not also have blackjack (which would result in a “push”—i.e., a tie). Otherwise, the player can begin taking additional cards one at a time. These cards are dealt face up, and the player decides when to stop taking cards. If the player “busts” (i.e., the sum of the player’s cards exceeds 21), the game is over, and the player loses. When the player is satisfied with the current set of cards, the player “stands” (i.e., stops taking cards), and the dealer’s hidden card is revealed. If the dealer’s total is 16 or less, the dealer must take another card; otherwise, the dealer must stand. The dealer must continue taking cards until the sum of the dealer’s cards is greater than or equal to 17. If the dealer exceeds 21, the player wins. Otherwise, the hand with the higher point total wins. If the dealer and the player have the same point total, the game is a “push,” and no one wins. The value of an ace for a dealer depends on the dealer’s other card(s) and the casino’s house rules. A dealer typically must hit for totals of 16 or less and must stand for totals of 17 or more. However, for a “soft 17”—a hand with a total of 17 with one ace counted as 11—some casinos require the dealer to hit and some require the dealer to stand (we require the dealer to stand). Such a hand is known as a “soft 17” because taking another card cannot bust the hand.

The web service (Fig. 28.17) stores each card as a String consisting of a number, 113, representing the card’s face (ace through king, respectively), followed by a space and a digit, 03, representing the card’s suit (hearts, diamonds, clubs or spades, respectively). For example, the jack of clubs is represented as "11 2" and the two of hearts as "2 0". To create and deploy this web service, follow the steps that we presented in Sections 28.6.128.6.3 for the WelcomeSOAP service.


 1   // Fig. 28.17: Blackjack.java
 2   // Blackjack web service that deals cards and evaluates hands
 3   package com.deitel.blackjack;
 4
 5   import com.sun.xml.ws.developer.servlet.HttpSessionScope;
 6   import java.util.ArrayList;
 7   import java.util.Random;
 8   import javax.jws.WebMethod;
 9   import javax.jws.WebParam;
10   import javax.jws.WebService;
11
12   @HttpSessionScope // enable web service to maintain session state
13   @WebService()
14   public class Blackjack
15   {
16      private ArrayList< String > deck; // deck of cards for one user session
17      private static final Random randomObject = new Random();
18
19      // deal one card
20      @WebMethod( operationName = "dealCard" )
21      public String dealCard()
22      {
23         String card = "";
24         card = deck.get( 0 ); // get top card of deck
25         deck.remove( 0 ); // remove top card of deck
26         return card;
27      } // end WebMethod dealCard
28
29      // shuffle the deck
30      @WebMethod( operationName = "shuffle" )
31      public void shuffle()
32      {
33         // create new deck when shuffle is called
34         deck = new ArrayList< String >();
35
36         // populate deck of cards
37         for ( int face = 1; face <= 13; face++ ) // loop through faces
38            for ( int suit = 0; suit <= 3; suit++ ) // loop through suits
39               deck.add( face + " " + suit ); // add each card to deck
40
41         String tempCard; // holds card temporarily during swapping
42         int index; // index of randomly selected card
43
44         for ( int i = 0; i < deck.size() ; i++ ) // shuffle
45         {
46            index = randomObject.nextInt( deck.size() - 1 );
47
48            // swap card at position i with randomly selected card
49            tempCard = deck.get( i );
50            deck.set( i, deck.get( index ) );
51            deck.set( index, tempCard );
52         } // end for
53      } // end WebMethod shuffle
54
55      // determine a hand's value
56      @WebMethod( operationName = "getHandValue" )
57      public int getHandValue( @WebParam( name = "hand" ) String hand )
58      {
59         // split hand into cards
60         String[] cards = hand.split( " " );
61         int total = 0; // total value of cards in hand
62         int face; // face of current card
63         int aceCount = 0; // number of aces in hand
64
65         for ( int i = 0; i < cards.length; i++ )
66         {
67            // parse string and get first int in String
68            face = Integer.parseInt(
69               cards[ i ].substring( 0, cards[ i ].indexOf( " " ) ) );
70
71            switch ( face )
72            {
73               case 1: // if ace, increment aceCount
74                  ++aceCount;
75                  break;
76               case 11: // jack
77               case 12: // queen
78               case 13: // king
79                  total += 10;
80                  break;
81               default: // otherwise, add face
82                  total += face;
83                  break;
84            } // end switch
85         } // end for
86
87         // calculate optimal use of aces
88         if ( aceCount > 0 )
89         {
90            // if possible, count one ace as 11
91            if ( total + 11 + aceCount - 1 <= 21 )
92               total += 11 + aceCount - 1;
93            else // otherwise, count all aces as 1
94               total += aceCount;
95          } // end if
96
97         return total;
98      } // end WebMethod getHandValue
99   } // end class Blackjack


Fig. 28.17. Blackjack web service that deals cards and evaluates hands.

Session Tracking in Web Services: @HttpSessionScope Annotation

In JAX-WS 2.2, it’s easy to enable session tracking in a web service. You simply precede your web service class with the @HttpSessionScope annotation. This annotation is located in package com.sun.xml.ws.developer.servlet. To use this package you must add the JAX-WS 2.2 library to your project. To do so, right click the Libraries node in your Blackjack web application project and select Add Library.... Then, in the dialog that appears, locate and select JAX-WS 2.2, then click Add Library. Once a web service is annotated with @HttpSessionScope, the server automatically maintains a separate instance of the class for each client session. Thus, the deck instance variable (line 16) will be maintained separately for each client.

Client Interactions with the Blackjack Web Service

A client first calls the Blackjack web service’s shuffle web method (lines 30–53) to create a new deck of cards (line 34), populate it (lines 37–39) and shuffle it (lines 41–52). Lines 37–39 generate Strings in the form "face suit" to represent each possible card in the deck.

Lines 20–27 define the dealCard web method. Method shuffle must be called before method dealCard is called the first time for a client—otherwise, deck could be null. The method gets the top card from the deck (line 24), removes it from the deck (line 25) and returns the card’s value as a String (line 26). Without using session tracking, the deck of cards would need to be passed back and forth with each method call. Session tracking makes the dealCard method easy to call (it requires no arguments) and eliminates the overhead of sending the deck over the network multiple times.

Method getHandValue (lines 56–98) determines the total value of the cards in a hand by trying to attain the highest score possible without going over 21. Recall that an ace can be counted as either 1 or 11, and all face cards count as 10. This method does not use the session object, because the deck of cards is not used in this method.

As you’ll soon see, the client application maintains a hand of cards as a String in which each card is separated by a tab character. Line 60 splits the hand of cards (represented by hand) into individual cards by calling String method split and passing to it a String containing the delimiter characters (in this case, just a tab). Method split uses the delimiter characters to separate tokens in the String. Lines 65–85 count the value of each card. Lines 68–69 retrieve the first integer—the face—and use that value in the switch statement (lines 71–84). If the card is an ace, the method increments variable aceCount. We discuss how this variable is used shortly. If the card is an 11, 12 or 13 (jack, queen or king), the method adds 10 to the total value of the hand (line 79). If the card is anything else, the method increases the total by that value (line 82).

Because an ace can have either of two values, additional logic is required to process aces. Lines 88–95 process the aces after all the other cards. If a hand contains several aces, only one ace can be counted as 11. The condition in line 91 determines whether counting one ace as 11 and the rest as 1 will result in a total that does not exceed 21. If this is possible, line 92 adjusts the total accordingly. Otherwise, line 94 adjusts the total, counting each ace as 1.

Method getHandValue maximizes the value of the current cards without exceeding 21. Imagine, for example, that the dealer has a 7 and receives an ace. The new total could be either 8 or 18. However, getHandValue always maximizes the value of the cards without going over 21, so the new total is 18.

28.9.2. Consuming the Blackjack Web Service

The blackjack application in Fig. 28.18 keeps track of the player’s and dealer’s cards, and the web service tracks the cards that have been dealt. The constructor (lines 34–83) sets up the GUI (line 36), changes the window’s background color (line 40) and creates the Blackjack web service’s service endpoint interface object (lines 46–47). In the GUI, each player has 11 JLabels—the maximum number of cards that can be dealt without automatically exceeding 21 (i.e., four aces, four twos and three threes). These JLabels are placed in an ArrayList of JLabels (lines 59–82), so we can index the ArrayList during the game to determine the JLabel that will display a particular card image.


 1   // Fig. 28.18: BlackjackGameJFrame.java
 2   // Blackjack game that uses the Blackjack Web Service.
 3   package com.deitel.blackjackclient;
 4
 5   import com.deitel.blackjack.Blackjack;       
 6   import com.deitel.blackjack.BlackjackService;
 7   import java.awt.Color;
 8   import java.util.ArrayList;
 9   import javax.swing.ImageIcon;
10   import javax.swing.JLabel;
11   import javax.swing.JOptionPane;
12   import javax.xml.ws.BindingProvider;
13
14   public class BlackjackGameJFrame extends javax.swing.JFrame
15   {
16      private String playerCards;
17      private String dealerCards;
18      private ArrayList<JLabel> cardboxes; // list of card image JLabels
19      private int currentPlayerCard; // player's current card number
20      private int currentDealerCard; // blackjackProxy's current card number
21      private BlackjackService blackjackService; // used to obtain proxy 
22      private Blackjack blackjackProxy; // used to access the web service
23
24      // enumeration of game states
25      private enum GameStatus
26      {
27         PUSH, // game ends in a tie
28         LOSE, // player loses
29         WIN, // player wins
30         BLACKJACK // player has blackjack
31      } // end enum GameStatus
32
33      // no-argument constructor
34      public BlackjackGameJFrame()
35      {
36         initComponents();
37
38         // due to a bug in NetBeans, we must change the JFrame's background
39         // color here rather than in the designer
40         getContentPane().setBackground( new Color( 0, 180, 0 ) );
41
42         // initialize the blackjack proxy
43         try
44         {
45            // create the objects for accessing the Blackjack web service
46            blackjackService = new BlackjackService();                   
47            blackjackProxy = blackjackService.getBlackjackPort();        
48
49            // enable session tracking                                   
50            ( (BindingProvider) blackjackProxy ).getRequestContext().put(
51               BindingProvider.SESSION_MAINTAIN_PROPERTY, true );        
52         } // end try
53         catch ( Exception e )
54         {
55            e.printStackTrace();
56         } // end catch
57
58         // add JLabels to cardBoxes ArrayList for programmatic manipulation
59         cardboxes = new ArrayList<JLabel>();
60
61         cardboxes.add( dealerCard1JLabel );
62         cardboxes.add( dealerCard2JLabel );
63         cardboxes.add( dealerCard3JLabel );
64         cardboxes.add( dealerCard4JLabel );
65         cardboxes.add( dealerCard5JLabel );
66         cardboxes.add( dealerCard6JLabel );
67         cardboxes.add( dealerCard7JLabel );
68         cardboxes.add( dealerCard8JLabel );
69         cardboxes.add( dealerCard9JLabel );
70         cardboxes.add( dealerCard10JLabel );
71         cardboxes.add( dealerCard11JLabel );
72         cardboxes.add( playerCard1JLabel );
73         cardboxes.add( playerCard2JLabel );
74         cardboxes.add( playerCard3JLabel );
75         cardboxes.add( playerCard4JLabel );
76         cardboxes.add( playerCard5JLabel );
77         cardboxes.add( playerCard6JLabel );
78         cardboxes.add( playerCard7JLabel );
79         cardboxes.add( playerCard8JLabel );
80         cardboxes.add( playerCard9JLabel );
81         cardboxes.add( playerCard10JLabel );
82         cardboxes.add( playerCard11JLabel );
83      } // end constructor
84
85      // play the dealer's hand
86      private void dealerPlay()
87      {
88         try
89         {
90            // while the value of the dealers's hand is below 17
91            // the dealer must continue to take cards
92            String[] cards = dealerCards.split( " " );
93
94            // display dealer's cards
95            for ( int i = 0; i < cards.length; i++ )
96            {
97               displayCard( i, cards[i] );
98            }
99
100           while ( blackjackProxy.getHandValue( dealerCards ) < 17 )
101           {
102              String newCard = blackjackProxy.dealCard(); // deal new card
103              dealerCards += " " + newCard; // deal new card
104              displayCard( currentDealerCard, newCard );
105              ++currentDealerCard;
106              JOptionPane.showMessageDialog( this, "Dealer takes a card",
107                 "Dealer's turn", JOptionPane.PLAIN_MESSAGE );
108           } // end while
109
110           int dealersTotal = blackjackProxy.getHandValue( dealerCards );
111           int playersTotal = blackjackProxy.getHandValue( playerCards );
112
113           // if dealer busted, player wins
114           if ( dealersTotal > 21 )
115           {
116              gameOver( GameStatus.WIN );
117              return;
118           } // end if
119
120           // if dealer and player are below 21
121           // higher score wins, equal scores is a push
122           if ( dealersTotal > playersTotal )
123           {
124              gameOver( GameStatus.LOSE );
125           }
126           else if ( dealersTotal < playersTotal )
127           {
128              gameOver( GameStatus.WIN );
129           }
130           else
131           {
132              gameOver( GameStatus.PUSH );
133           }
134        } // end try
135        catch ( Exception e )
136        {
137           e.printStackTrace();
138        } // end catch
139     } // end method dealerPlay
140
141     // displays the card represented by cardValue in specified JLabel
142     private void displayCard( int card, String cardValue )
143     {
144        try
145        {
146           // retrieve correct JLabel from cardBoxes
147           JLabel displayLabel = cardboxes.get( card );
148
149           // if string representing card is empty, display back of card
150           if ( cardValue.equals( "" ) )
151           {
152              displayLabel.setIcon( new ImageIcon( getClass().getResource(
153                 "/com/deitel/java/blackjackclient/" +
154                 "blackjack_images/cardback.png" ) ) );
155              return;
156           } // end if
157
158           // retrieve the face value of the card
159           String face = cardValue.substring( 0, cardValue.indexOf( " " ) );
160
161           // retrieve the suit of the card
162           String suit =
163              cardValue.substring( cardValue.indexOf( " " ) + 1 );
164
165           char suitLetter; // suit letter used to form image file
166
167           switch ( Integer.parseInt( suit ) )
168           {
169              case 0: // hearts
170                 suitLetter = 'h';
171                 break;
172              case 1: // diamonds
173                 suitLetter = 'd';
174                 break;
175              case 2: // clubs
176                 suitLetter = 'c';
177                 break;
178              default: // spades
179                 suitLetter = 's';
180                 break;
181           } // end switch
182
183           // set image for displayLabel
184           displayLabel.setIcon( new ImageIcon( getClass().getResource(
185              "/com/deitel/java/blackjackclient/blackjack_images/" +
186              face + suitLetter + ".png" ) ) );
187        } // end try
188        catch ( Exception e )
189        {
190           e.printStackTrace();
191        } // end catch
192     } // end method displayCard
193
194     // displays all player cards and shows appropriate message
195     private void gameOver( GameStatus winner )
196     {
197        String[] cards = dealerCards.split( " " );
198
199        // display blackjackProxy's cards
200        for ( int i = 0; i < cards.length; i++ )
201        {
202           displayCard( i, cards[i] );
203        }
204
205        // display appropriate status image
206        if ( winner == GameStatus.WIN )
207        {
208           statusJLabel.setText( "You win!" );
209        }
210        else if ( winner == GameStatus.LOSE )
211        {
212           statusJLabel.setText( "You lose." );
213        }
214        else if ( winner == GameStatus.PUSH )
215        {
216           statusJLabel.setText( "It's a push." );
217        }
218        else // blackjack
219        {
220           statusJLabel.setText( "Blackjack!" );
221        }
222
223        // display final scores
224        int dealersTotal = blackjackProxy.getHandValue( dealerCards );
225        int playersTotal = blackjackProxy.getHandValue( playerCards );
226        dealerTotalJLabel.setText( "Dealer: " + dealersTotal );
227        playerTotalJLabel.setText( "Player: " + playersTotal );
228
229        // reset for new game
230        standJButton.setEnabled( false );
231        hitJButton.setEnabled( false );
232        dealJButton.setEnabled( true );
233     } // end method gameOver
234
235     // The initComponents method is autogenerated by NetBeans and is called
236     // from the constructor to initialize the GUI. This method is not shown
237     // here to save space. Open BlackjackGameJFrame.java in this           
238     // example's folder to view the complete generated code                
239
542     // handles dealJButton click
543     private void dealJButtonActionPerformed(
544        java.awt.event.ActionEvent evt )
545     {
546        String card; // stores a card temporarily until it's added to a hand
547
548        // clear card images
549        for ( int i = 0; i < cardboxes.size(); i++ )
550        {
551           cardboxes.get( i ).setIcon( null );
552        }
553
554        statusJLabel.setText( "" );
555        dealerTotalJLabel.setText( "" );
556        playerTotalJLabel.setText( "" );
557
558        // create a new, shuffled deck on remote machine
559        blackjackProxy.shuffle();                       
560
561        // deal two cards to player
562        playerCards = blackjackProxy.dealCard(); // add first card to hand
563        displayCard( 11, playerCards ); // display first card
564        card = blackjackProxy.dealCard(); // deal second card
565        displayCard( 12, card ); // display second card
566        playerCards += " " + card; // add second card to hand
567
568        // deal two cards to blackjackProxy, but only show first
569        dealerCards = blackjackProxy.dealCard(); // add first card to hand
570        displayCard( 0, dealerCards ); // display first card
571        card = blackjackProxy.dealCard(); // deal second card
572        displayCard( 1, "" ); // display back of card
573        dealerCards += " " + card; // add second card to hand
574
575        standJButton.setEnabled( true );
576        hitJButton.setEnabled( true );
577        dealJButton.setEnabled( false );
578
579        // determine the value of the two hands                       
580        int dealersTotal = blackjackProxy.getHandValue( dealerCards );
581        int playersTotal = blackjackProxy.getHandValue( playerCards );
582
583        // if hands both equal 21, it is a push
584        if ( playersTotal == dealersTotal && playersTotal == 21 )
585        {
586           gameOver( GameStatus.PUSH );
587        }
588        else if ( dealersTotal == 21 ) // blackjackProxy has blackjack
589        {
590           gameOver( GameStatus.LOSE );
591        }
592        else if ( playersTotal == 21 ) // blackjack
593        {
594           gameOver( GameStatus.BLACKJACK );
595        }
596
597        // next card for blackjackProxy has index 2
598        currentDealerCard = 2;
599
600        // next card for player has index 13
601        currentPlayerCard = 13;
602     } // end method dealJButtonActionPerformed
603
604     // handles standJButton click
605     private void hitJButtonActionPerformed(
606        java.awt.event.ActionEvent evt )
607     {
608        // get player another card
609        String card = blackjackProxy.dealCard(); // deal new card
610        playerCards += " " + card; // add card to hand
611
612        // update GUI to display new card
613        displayCard( currentPlayerCard, card );
614        ++currentPlayerCard;
615
616        // determine new value of player's hand                
617        int total = blackjackProxy.getHandValue( playerCards );
618
619        if ( total > 21 ) // player busts
620        {
621           gameOver( GameStatus.LOSE );
622        }
623        else if ( total == 21 ) // player cannot take any more cards
624        {
625           hitJButton.setEnabled( false );
626           dealerPlay();
627        } // end if
628     } // end method hitJButtonActionPerformed
629
630     // handles standJButton click
631     private void standJButtonActionPerformed(
632        java.awt.event.ActionEvent evt )
633     {
634        standJButton.setEnabled( false );
635        hitJButton.setEnabled( false );
636        dealJButton.setEnabled( true );
637        dealerPlay();
638     } // end method standJButtonActionPerformed
639
640     // begins application execution
641     public static void main( String args[] )
642     {
643        java.awt.EventQueue.invokeLater(
644           new Runnable()
645           {
646              public void run()
647              {
648                 new BlackjackGameJFrame().setVisible( true );
649              }
650           }
651        ); // end call to java.awt.EventQueue.invokeLater
652     } // end main
653
654     // Variables declaration - do not modify
655     private javax.swing.JButton dealJButton;
656     private javax.swing.JLabel dealerCard10JLabel;
657     private javax.swing.JLabel dealerCard11JLabel;
658     private javax.swing.JLabel dealerCard1JLabel;
659     private javax.swing.JLabel dealerCard2JLabel;
660     private javax.swing.JLabel dealerCard3JLabel;
661     private javax.swing.JLabel dealerCard4JLabel;
662     private javax.swing.JLabel dealerCard5JLabel;
663     private javax.swing.JLabel dealerCard6JLabel;
664     private javax.swing.JLabel dealerCard7JLabel;
665     private javax.swing.JLabel dealerCard8JLabel;
666     private javax.swing.JLabel dealerCard9JLabel;
667     private javax.swing.JLabel dealerJLabel;
668     private javax.swing.JLabel dealerTotalJLabel;
669     private javax.swing.JButton hitJButton;
670     private javax.swing.JLabel playerCard10JLabel;
671     private javax.swing.JLabel playerCard11JLabel;
672     private javax.swing.JLabel playerCard1JLabel;
673     private javax.swing.JLabel playerCard2JLabel;
674     private javax.swing.JLabel playerCard3JLabel;
675     private javax.swing.JLabel playerCard4JLabel;
676     private javax.swing.JLabel playerCard5JLabel;
677     private javax.swing.JLabel playerCard6JLabel;
678     private javax.swing.JLabel playerCard7JLabel;
679     private javax.swing.JLabel playerCard8JLabel;
680     private javax.swing.JLabel playerCard9JLabel;
681     private javax.swing.JLabel playerJLabel;
682     private javax.swing.JLabel playerTotalJLabel;
683     private javax.swing.JButton standJButton;
684     private javax.swing.JLabel statusJLabel;
685     // End of variables declaration
686  } // end class BlackjackGameJFrame

Image
Image
Image
Image

Fig. 28.18. Blackjack game that uses the Blackjack webservice.

Configuring the Client for Session Tracking

When interacting with a JAX-WS web service that performs session tracking, the client application must indicate whether it wants to allow the web service to maintain session information. Lines 50–51 in the constructor perform this task. We first cast the service endpoint interface object to interface type BindingProvider. A BindingProvider enables the client to manipulate the request information that will be sent to the server. This information is stored in an object that implements interface RequestContext. The BindingProvider and RequestContext are part of the framework that is created by the IDE when you add a web service client to the application. Next, we invoke the BindingProvider’s getRequestContext method to obtain the RequestContext object. Then we call the RequestContext’s put method to set the property

BindingProvider.SESSION_MAINTAIN_PROPERTY

to true. This enables the client side of the session-tracking mechanism, so that the web service knows which client is invoking the service’s web methods.

Method gameOver

Method gameOver (lines 195–233) displays all the dealer’s cards, shows the appropriate message in statusJLabel and displays the final point totals of both the dealer and the player. Method gameOver receives as an argument a member of the GameStatus enumeration (defined in lines 25–31). The enumeration represents whether the player tied, lost or won the game; its four members are PUSH, LOSE, WIN and BLACKJACK.

Method dealJButtonActionPerformed

When the player clicks the Deal JButton, method dealJButtonActionPerformed (lines 543–602) clears all of the JLabels that display cards or game status information. Next, the deck is shuffled (line 559), and the player and dealer receive two cards each (lines 562–573). Lines 580–581 then total each hand. If the player and the dealer both obtain scores of 21, the program calls method gameOver, passing GameStatus.PUSH (line 586). If only the dealer has 21, the program passes GameStatus.LOSE to method gameOver (line 590). If only the player has 21 after the first two cards are dealt, the program passes GameStatus.BLACKJACK to method gameOver (line 594).

Method hitJButtonActionPerformed

If dealJButtonActionPerformed does not call gameOver, the player can take more cards by clicking the Hit JButton, which calls hitJButtonActionPerformed in lines 605–628. Each time a player clicks Hit, the program deals the player one more card (line 609) and displays it in the GUI (line 613). If the player exceeds 21, the game is over and the player loses (line 621). If the player has exactly 21, the player is not allowed to take any more cards (line 625), and method dealerPlay is called (line 626).

Method dealerPlay

Method dealerPlay (lines 86–139) displays the dealer’s cards, then deals cards to the dealer until the dealer’s hand has a value of 17 or more (lines 100–108). If the dealer exceeds 21, the player wins (line 116); otherwise, the values of the hands are compared, and gameOver is called with the appropriate argument (lines 122–133).

Method standJButtonActionPerformed

Clicking the Stand JButton indicates that a player does not want to be dealt another card. Method standJButtonActionPerformed (lines 631–638) disables the Hit and Stand buttons, enables the Deal button, then calls method dealerPlay.

Method displayCard

Method displayCard (lines 142–192) updates the GUI to display a newly dealt card. The method takes as arguments an integer index for the JLabel in the ArrayList that must have its image set and a String representing the card. An empty String indicates that we wish to display the card face down. If method displayCard receives a String that’s not empty, the program extracts the face and suit from the String and uses this information to display the correct image. The switch statement (lines 167–181) converts the number representing the suit to an integer and assigns the appropriate character to variable suitLetter (h for hearts, d for diamonds, c for clubs and s for spades). The character in suitLetter is used to complete the image’s file name (lines 184–186). You must add the folder blackjack_images to your project so that lines 152–154 and 184–186 can access the images properly. To do so, copy the folder blackjack_images from this chapter’s examples folder and paste it into the project’s srccomdeiteljavalackjackclient folder.

28.10. Consuming a Database-Driven SOAP Web Service

Our prior examples accessed web services from desktop applications created in NetBeans. However, we can just as easily use them in web applications created with NetBeans. In fact, because web-based businesses are becoming increasingly popular, it’s common for web applications to consume web services. In this section, we present an airline reservation web service that receives information regarding the type of seat a customer wishes to reserve and makes a reservation if such a seat is available. Later in the section, we present a web application that allows a customer to specify a reservation request, then uses the airline reservation web service to attempt to execute the request.

28.10.1. Creating the Reservation Database

Our web service uses a reservation database containing a single table named Seats to locate a seat matching a client’s request. Review the steps presented in Section 27.2.1 for configuring a data source and the addressbook database. Then perform those steps for the reservation database used in this example. Create a data source named jdbc/reservation. This chapter’s examples directory contains the Seats.sql SQL script to create the seats table and populate it with sample data. The sample data is shown in Fig. 28.19.

Image

Fig. 28.19. Data from the seats table.

Creating the Reservation Web Service

You can now create a web service that uses the Reservation database (Fig. 28.20). The airline reservation web service has a single web method—reserve (lines 23–79)—which searches the Seats table to locate a seat matching a user’s request. The method takes two arguments—a String representing the desired seat type (i.e., "Window", "Middle" or "Aisle") and a String representing the desired class type (i.e., "Economy" or "First"). If it finds an appropriate seat, method reserve updates the database to make the reservation and returns true; otherwise, no reservation is made, and the method returns false. The statements at lines 35–40 and lines 45–49 that query and update the database use objects of JDBC types ResultSet and PreparedStatement.


Image Software Engineering Observation 28.1

Using PreparedStatements to create SQL statements is highly recommended to secure against so-called SQL injection attacks in which executable code is inserted into SQL code. The site www.owasp.org/index.php/Preventing_SQL_Injection_in_Java provides a summary of SQL injection attacks and ways to mitigate against them.



 1   // Fig. 28.20: Reservation.java
 2   // Airline reservation web service.
 3   package com.deitel.reservation;
 4
 5   import java.sql.Connection;
 6   import java.sql.PreparedStatement;
 7   import java.sql.ResultSet;
 8   import java.sql.SQLException;
 9   import javax.annotation.Resource;
10   import javax.jws.WebMethod;
11   import javax.jws.WebParam;
12   import javax.jws.WebService;
13   import javax.sql.DataSource;
14
15   @WebService()
16   public class Reservation
17   {
18      // allow the server to inject the DataSource
19      @Resource( name="jdbc/reservation" )        
20      DataSource dataSource;                      
21
22      // a WebMethod that can reserve a seat
23      @WebMethod( operationName = "reserve" )
24      public boolean reserve( @WebParam( name = "seatType" ) String seatType,
25         @WebParam( name = "classType" ) String classType )
26      {
27         Connection connection = null;
28         PreparedStatement lookupSeat = null;
29         PreparedStatement reserveSeat = null;
30
31         try
32         {
33            connection = DriverManager.getConnection(                     
34               DATABASE_URL, USERNAME, PASSWORD );                        
35            lookupSeat = connection.prepareStatement(                     
36               "SELECT "number" FROM "seats" WHERE ("taken" = 0) " +
37               "AND ("location" = ?) AND ("class" = ?)" );            
38            lookupSeat.setString( 1, seatType );                          
39            lookupSeat.setString( 2, classType );                         
40            ResultSet resultSet = lookupSeat.executeQuery();              
41
42            // if requested seat is available, reserve it
43            if ( resultSet.next() )
44            {
45               int seat = resultSet.getInt( 1 );                          
46               reserveSeat = connection.prepareStatement(                 
47                  "UPDATE "seats" SET "taken"=1 WHERE "number"=?" );
48               reserveSeat.setInt( 1, seat );                             
49               reserveSeat.executeUpdate();                               
50               return true;
51            } // end if
52
53            return false;
54         } // end try
55         catch ( SQLException e )
56         {
57            e.printStackTrace();
58            return false;
59         } // end catch
60         catch ( Exception e )
61         {
62            e.printStackTrace();
63            return false;
64         } // end catch
65         finally
66         {
67            try
68            {
69               lookupSeat.close();
70               reserveSeat.close();
71               connection.close();
72            } // end try
73            catch ( Exception e )
74            {
75               e.printStackTrace();
76               return false;
77            } // end catch
78         } // end finally
79      } // end WebMethod reserve
80   } // end class Reservation


Fig. 28.20. Airline reservation web service.

Our database contains four columns—the seat number (i.e., 110), the seat type (i.e., Window, Middle or Aisle), the class type (i.e., Economy or First) and a column containing either 1 (true) or 0 (false) to indicate whether the seat is taken. Lines 35–40 retrieve the seat numbers of any available seats matching the requested seat and class type. This statement fills the resultSet with the results of the query

SELECT number
FROM seats
WHERE (taken = 0) AND (type = type) AND (class = class)

The parameters type and class in the query are replaced with values of method reserve’s seatType and classType parameters.

If resultSet is not empty (i.e., at least one seat is available that matches the selected criteria), the condition in line 43 is true and the web service reserves the first matching seat number. Recall that ResultSet method next returns true if a nonempty row exists, and positions the cursor on that row. We obtain the seat number (line 45) by accessing resultSet’s first column (i.e., resultSet.getInt(1)—the first column in the row). Then lines 45–49 configure a PreparedStatement and execute the SQL:

UPDATE seats
SET taken = 1
WHERE (number = number)

which marks the seat as taken in the database. The parameter number is replaced with the value of seat. Method reserve returns true (line 50) to indicate that the reservation was successful. If there are no matching seats, or if an exception occurred, method reserve returns false (lines 53, 58, 63 and 76) to indicate that no seats matched the user’s request.

28.10.2. Creating a Web Application to Interact with the Reservation Service

This section presents a ReservationClient JSF web application that consumes the Reservation web service. The application allows users to select "Aisle", "Middle" or "Window" seats in "Economy" or "First" class, then submit their requests to the web service. If the database request is not successful, the application instructs the user to modify the request and try again. The application presented here was built using the techniques presented in Chapters 2627. We assume that you’ve already read those chapters and thus know how to build a Facelets page and a corresponding JavaBean.

index.xhtml

index.xhtml (Fig. 28.21) defines two h:selectOneMenus and an h:commandButton. The h:selectOneMenu at lines 16–20) displays all the seat types from which users can select. The one at lines 21–24) provides choices for the class type. The values of these are stored in the seatType and classType properties of the reservationBean (Fig. 28.22). Users click the Reserve button (lines 25–26) to submit requests after making selections from the h:selectOneMenus. Clicking the button calls the reservationBean’s reserveSeat method. The page displays the result of each attempt to reserve a seat in line 28.


 1   <?xml version='1.0' encoding='UTF-8' ?>
 2
 3   <!-- Fig. 28.21: index.xhtml -->
 4   <!-- Facelets page that allows a user to select a seat -->
 5   <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 6      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 7   <html xmlns="http://www.w3.org/1999/xhtml"
 8      xmlns:h="http://java.sun.com/jsf/html"
 9      xmlns:f="http://java.sun.com/jsf/core">
10      <h:head>
11         <title>Airline Reservations</title>
12      </h:head>
13      <h:body>
14         <h:form>
15            <h3>Please select the seat type and class to reserve:</h3>
16            <h:selectOneMenu value="#{reservationBean.seatType}">
17               <f:selectItem itemValue="Aisle" itemLabel="Aisle" />
18               <f:selectItem itemValue="Middle" itemLabel="Middle" />
19               <f:selectItem itemValue="Window" itemLabel="Window" />
20            </h:selectOneMenu>
21            <h:selectOneMenu value="#{reservationBean.classType}">
22               <f:selectItem itemValue="Economy" itemLabel="Economy" />
23               <f:selectItem itemValue="First" itemLabel="First" />
24            </h:selectOneMenu>
25            <h:commandButton value="Reserve"
26               action="#{reservationBean.reserveSeat}"/>
27         </h:form>
28         <h3>#{reservationBean.result}</h3>
29      </h:body>
30   </html>

Image
Image
Image
Image

Fig. 28.21. Facelets page that allows a user to select a seat.

ReservationBean.java

Class ReservationBean (Fig. 28.22) defines the seatType, classType and result properties and the reserveSeat method that are used in the index.xhtml page. When the user clicks the Reserve button in index.xhtml, method reserveSeat (lines 57–74) executes. Lines 61–62 use the service endpoint interface object (created in lines 22–23) to invoke the web service’s reserve method, passing the selected seat type and class type as arguments. If reserve returns true, line 65 sets result to a message thanking the user for making a reservation; otherwise, lines 67–68 set result to a message notifying the user that the requested seat type is not available and instructing the user to try again.


 1   // Fig. 28.22: ReservationBean.java
 2   // Bean for seat reservation client.
 3   package reservationclient;
 4
 5   import com.deitel.reservation.Reservation;
 6   import com.deitel.reservation.ReservationService;
 7   import javax.faces.bean.ManagedBean;
 8
 9   @ManagedBean( name = "reservationBean" )
10   public class ReservationBean
11   {
12      // references the service endpoint interface object (i.e., the proxy)
13      private Reservation reservationServiceProxy; // reference to proxy
14      private String seatType; // type of seat to reserve
15      private String classType; // class of seat to reserve
16      private String result; // result of reservation attempt
17
18      // no-argument constructor
19      public ReservationBean()
20      {
21         // get service endpoint interface
22         ReservationService reservationService = new ReservationService();
23         reservationServiceProxy = reservationService.getReservationPort();
24      } // end constructor
25
26      // return classType
27      public String getClassType()
28      {
29         return classType;
30      } // end method getClassType
31
32      // set classType
33      public void setClassType( String classType )
34      {
35         this.classType = classType;
36      } // end method setClassType
37
38      // return seatType
39      public String getSeatType()
40      {
41         return seatType;
42      } // end method getSeatType
43
44      // set seatType
45      public void setSeatType( String seatType )
46      {
47         this.seatType = seatType;
48      } // end method setSeatType
49
50      // return result
51      public String getResult()
52      {
53         return result;
54      } // end method getResult
55
56      // invoke the web service when the user clicks Reserve button
57      public void reserveSeat()
58      {
59         try
60         {
61            boolean reserved = reservationServiceProxy.reserve(
62               getSeatType(), getClassType() );
63
64            if ( reserved )
65               result = "Your reservation has been made. Thank you!";
66            else
67               result = "This type of seat is not available. " +
68                  "Please modify your request and try again.";
69         } // end try
70         catch ( Exception e )
71         {
72            e.printStackTrace();
73         } // end catch
74      } // end method reserveSeat
75   } // end class ReservationBean


Fig. 28.22. Page bean for seat reservation client.

28.11. Equation Generator: Returning User-Defined Types

Most of the web services we’ve demonstrated received and returned primitive-type instances. It’s also possible to process instances of class types in a web service. These types can be passed to or returned from web service methods.

This section presents a RESTful EquationGenerator web service that generates random arithmetic equations of type Equation. The client is a math-tutoring application that accepts information about the mathematical question that the user wishes to attempt (addition, subtraction or multiplication) and the skill level of the user (1 specifies equations using numbers from 1 through 9, 2 specifies equations involving numbers from 10 through 99, and 3 specifies equations containing numbers from 100 through 999). The web service then generates an equation consisting of random numbers in the proper range. The client application receives the Equation and displays the sample question to the user.

Defining Class Equation

We define class Equation in Fig. 28.23. All the programs in this section have a copy of this class in their corresponding package. Except for the package name, the class is identical in each project, so we show it only once. Like the TextMessage class used earlier, the server-side and client-side copies of class Equation are unrelated to each other. The only requirement for serialization and deserialization to work with the JAXB and Gson classes is that class Equation must have the same public properties on both the server and the client. Such properties can be public instance variables or private instance variables that have corresponding set and get methods.


 1   // Fig. 28.23: Equation.java
 2   // Equation class that contains information about an equation.
 3   package com.deitel.equationgeneratorxml;
 4
 5   public class Equation
 6   {
 7      private int leftOperand;
 8      private int rightOperand;
 9      private int result;
10      private String operationType;
11
12      // required no-argument constructor
13      public Equation()
14      {
15         this( 0, 0, "add" );
16      } // end no-argument constructor
17
18      // constructor that receives the operands and operation type
19      public Equation( int leftValue, int rightValue, String type )
20      {
21         leftOperand = leftValue;
22         rightOperand = rightValue;
23
24         // determine result
25         if ( type.equals( "add" ) ) // addition
26         {
27            result = leftOperand + rightOperand;
28            operationType = "+";
29         } // end if
30         else if ( type.equals( "subtract" ) ) // subtraction
31         {
32            result = leftOperand - rightOperand;
33            operationType = "-";
34         } // end if
35         else // multiplication
36         {
37            result = leftOperand * rightOperand;
38            operationType = "*";
39         } // end else
40      } // end three argument constructor
41
42      // gets the leftOperand
43      public int getLeftOperand()
44      {
45         return leftOperand;
46      } // end method getLeftOperand
47
48      // required setter
49      public void setLeftOperand( int value )
50      {
51         leftOperand = value;
52      } // end method setLeftOperand
53
54      // gets the rightOperand
55      public int getRightOperand()
56      {
57         return rightOperand;
58      } // end method getRightOperand
59
60      // required setter
61      public void setRightOperand( int value )
62      {
63         rightOperand = value;
64      } // end method setRightOperand
65
66      // gets the resultValue
67      public int getResult()
68      {
69         return result;
70      } // end method getResult
71
72      // required setter
73      public void setResult( int value )
74      {
75         result = value;
76      } // end method setResult
77
78      // gets the operationType
79      public String getOperationType()
80      {
81         return operationType;
82      } // end method getOperationType
83
84      // required setter
85      public void setOperationType( String value )
86      {
87         operationType = value;
88      } // end method setOperationType
89
90      // returns the left hand side of the equation as a String
91      public String getLeftHandSide()
92      {
93         return leftOperand + " " + operationType + " " + rightOperand;
94      } // end method getLeftHandSide
95
96      // returns the right hand side of the equation as a String
97      public String getRightHandSide()
98      {
99         return "" + result;
100     } // end method getRightHandSide
101
102     // returns a String representation of an Equation
103     public String toString()
104     {
105        return getLeftHandSide() + " = " + getRightHandSide();
106     } // end method toString
107  } // end class Equation


Fig. 28.23. Equation class that contains information about an equation.

Lines 19–40 define a constructor that takes two ints representing the left and right operands, and a String representing the arithmetic operation. The constructor stores this information, then calculates the result. The parameterless constructor (lines 13–16) calls the three-argument constructor (lines 19–40) and passes default values.

Class Equation defines get and set methods for instance variables leftOperand (lines 43–52), rightOperand (lines 55–64), result (line 67–76) and operationType (lines 79–88). It also provides get methods for the left-hand and right-hand sides of the equation and a toString method that returns the entire equation as a String. An instance variable can be serialized only if it has both a get and a set method. Because the different sides of the equation and the result of toString can be generated from the other instance variables, there’s no need to send them across the wire. The client in this case study does not use the getRightHandSide method, but we included it in case future clients choose to use it.

28.11.1. Creating the EquationGeneratorXML Web Service

Figure 28.24 presents the EquationGeneratorXML web service’s class for creating randomly generated Equations. Method getXml (lines 19–38) takes two parameters—a String representing the mathematical operation ("add", "subtract" or "multiply") and an int representing the difficulty level. JAX-RS automatically converts the arguments to the correct type and will return a “not found” error to the client if the argument cannot be converted from a String to the destination type. Supported types for conversion include integer types, floating-point types, boolean and the corresponding type-wrapper classes.


 1   // Fig. 28.24: EquationGeneratorXMLResource.java
 2   // RESTful equation generator that returns XML.
 3   package com.deitel.equationgeneratorxml;
 4
 5   import java.io.StringWriter;
 6   import java.util.Random;
 7   import javax.ws.rs.PathParam;
 8   import javax.ws.rs.Path;
 9   import javax.ws.rs.GET;
10   import javax.ws.rs.Produces;
11   import javax.xml.bind.JAXB; // utility class for common JAXB operations
12
13   @Path( "equation" )
14   public class EquationGeneratorXMLResource
15   {
16      private static Random randomObject = new Random();
17
18      // retrieve an equation formatted as XML
19      @GET                                                             
20      @Path( "{operation}/{level}" )                                   
21      @Produces( "application/xml" )                                   
22      public String getXml( @PathParam( "operation" ) String operation,
23         @PathParam( "level" ) int level )                             
24      {
25         // compute minimum and maximum values for the numbers
26         int minimum = ( int ) Math.pow( 10, level - 1 );
27         int maximum = ( int ) Math.pow( 10, level );
28
29         // create the numbers on the left-hand side of the equation
30         int first = randomObject.nextInt( maximum - minimum ) + minimum;
31         int second = randomObject.nextInt( maximum - minimum ) + minimum;
32
33         // create Equation object and marshal it into XML
34         Equation equation = new Equation( first, second, operation );
35         StringWriter writer = new StringWriter(); // XML output here       
36         JAXB.marshal( equation, writer ); // write Equation to StringWriter
37         return writer.toString(); // return XML string                     
38      } // end method getXml
39   } // end class EquationGeneratorXMLResource


Fig. 28.24. RESTful equation generator that returns XML.

The getXml method first determines the minimum (inclusive) and maximum (exclusive) values for the numbers in the equation it will return (lines 26–27). It then uses a static member of the Random class (line 16) to generate two random numbers in that range (lines 30–31). Line 34 creates an Equation object, passing these two numbers and the requested operation to the constructor. The getXml method then uses JAXB to convert the Equation object to XML (line 36), which is output to the StringWriter created on line 35. Finally, it retrieves the data that was written to the StringWriter and returns it to the client. [Note: We’ll reimplement this web service with JSON in Section 28.11.3.]

28.11.2. Consuming the EquationGeneratorXML Web Service

The EquationGeneratorXMLClient application (Fig. 28.25) retrieves an XML-formatted Equation object from the EquationGeneratorXML web service. The application then displays the Equation’s left-hand side and waits for user to submit an answer.


 1   // Fig. 28.25: EquationGeneratorXMLClientJFrame.java
 2   // Math-tutoring program using REST and XML to generate equations.
 3   package com.deitel.equationgeneratorxmlclient;
 4
 5   import javax.swing.JOptionPane;
 6   import javax.xml.bind.JAXB; // utility class for common JAXB operations
 7
 8   public class EquationGeneratorXMLClientJFrame extends javax.swing.JFrame
 9   {
10      private String operation = "add"; // operation user is tested on
11      private int difficulty = 1; // 1, 2, or 3 digits in each number
12      private int answer; // correct answer to the question
13
14      // no-argument constructor
15      public EquationGeneratorXMLClientJFrame()
16      {
17         initComponents();
18      } // end no-argument constructor
19
20      // The initComponents method is autogenerated by NetBeans and is called
21      // from the constructor to initialize the GUI. This method is not shown
22      // here to save space. Open EquationGeneratorXMLClientJFrame.java in   
23      // this example's folder to view the complete generated code.          
24
143     // determine if the user answered correctly
144     private void checkAnswerJButtonActionPerformed(
145        java.awt.event.ActionEvent evt)
146     {
147        if ( answerJTextField.getText().equals( "" ) )
148        {
149           JOptionPane.showMessageDialog(
150              this, "Please enter your answer." );
151        } // end if
152
153        int userAnswer = Integer.parseInt( answerJTextField.getText() );
154
155        if ( userAnswer == answer )
156        {
157           equationJLabel.setText( "" ); // clear label
158           answerJTextField.setText( "" ); // clear text field
159           checkAnswerJButton.setEnabled( false );
160           JOptionPane.showMessageDialog( this, "Correct! Good Job!",
161              "Correct", JOptionPane.PLAIN_MESSAGE );
162        } // end if
163        else
164        {
165           JOptionPane.showMessageDialog( this, "Incorrect. Try again.",
166              "Incorrect", JOptionPane.PLAIN_MESSAGE );
167        } // end else
168     } // end method checkAnswerJButtonActionPerformed
169
170     // retrieve equation from web service and display left side to user
171     private void generateJButtonActionPerformed(
172        java.awt.event.ActionEvent evt)
173     {
174        try
175        {
176           String url = String.format( "http://localhost:8080/" +
177              "EquationGeneratorXML/resources/equation/%s/%d",
178              operation, difficulty );
179
180           // convert XML back to an Equation object
181           Equation equation = JAXB.unmarshal( url, Equation.class );
182
183           answer = equation.getResult();                              
184           equationJLabel.setText( equation.getLeftHandSide() + " =" );
185           checkAnswerJButton.setEnabled( true );
186        } // end try
187        catch ( Exception exception )
188        {
189           exception.printStackTrace();
190        } // end catch
191     } // end method generateJButtonActionPerformed
192
193     // obtains the mathematical operation selected by the user
194     private void operationJComboBoxItemStateChanged(
195        java.awt.event.ItemEvent evt)
196     {
197        String item = ( String ) operationJComboBox.getSelectedItem();
198
199        if ( item.equals( "Addition" ) )
200           operation = "add"; // user selected addition
201        else if ( item.equals( "Subtraction" ) )
202           operation = "subtract"; // user selected subtraction
203        else
204           operation = "multiply"; // user selected multiplication
205     } // end method operationJComboBoxItemStateChanged
206
207     // obtains the difficulty level selected by the user
208     private void levelJComboBoxItemStateChanged(
209        java.awt.event.ItemEvent evt)
210     {
211        // indices start at 0, so add 1 to get the difficulty level
212        difficulty = levelJComboBox.getSelectedIndex() + 1;
213     } // end method levelJComboBoxItemStateChanged
214
215     // main method begins execution
216     public static void main(String args[])
217     {
218        java.awt.EventQueue.invokeLater(
219           new Runnable()
220           {
221              public void run()
222              {
223                 new EquationGeneratorXMLClientJFrame().setVisible( true );
224              } // end method run
225           } // end anonymous inner class
226        ); // end call to java.awt.EventQueue.invokeLater
227     } // end main
228
229     // Variables declaration - do not modify
230     private javax.swing.JLabel answerJLabel;
231     private javax.swing.JTextField answerJTextField;
232     private javax.swing.JButton checkAnswerJButton;
233     private javax.swing.JLabel equationJLabel;
234     private javax.swing.JButton generateJButton;
235     private javax.swing.JComboBox levelJComboBox;
236     private javax.swing.JLabel levelJLabel;
237     private javax.swing.JComboBox operationJComboBox;
238     private javax.swing.JLabel operationJLabel;
239     private javax.swing.JLabel questionJLabel;
240     // End of variables declaration
241  } // end class EquationGeneratorXMLClientJFrame

Image

Fig. 28.25. Math-tutoring program using REST and XML to generate equations.

The default setting for the difficulty level is 1, but the user can change this by choosing a level from the Choose level JComboBox. Changing the selected value invokes the levelJComboBoxItemStateChanged event handler (lines 208–213), which sets the difficulty instance variable to the level selected by the user. Although the default setting for the question type is Addition, the user also can change this by choosing from the Choose operation JComboBox. This invokes the operationJComboBoxItemStateChanged event handler in lines 194–205, which assigns to instance variable operation the String corresponding to the user’s selection.

The event handler for generateJButton (lines 171–191) constructs the URL to invoke the web service, then passes this URL to the unmarshal method, along with an instance of Class<Equation>, so that JAXB can convert the XML into an Equation object (line 181). Once the XML has been converted back into an Equation, lines 183–184 retrieve the correct answer and display the left-hand side of the equation. The Check Answer button is then enabled (line 185), and the user must solve the problem and enter the answer.

When the user enters a value and clicks Check Answer, the checkAnswerJButtonActionPerformed event handler (lines 144–168) retrieves the user’s answer from the dialog box (line 153) and compares it to the correct answer that was stored earlier (line 155). If they match, lines 157–161 reset the GUI elements so the user can generate another equation and tell the user that the answer was correct. If they do not match, a message box asking the user to try again is displayed (lines 165–166).

28.11.3. Creating the EquationGeneratorJSON Web Service

As you saw in Section 28.8, RESTful web services can return data formatted as JSON as well. Figure 28.26 is a reimplementation of the EquationGeneratorXML service that returns an Equation in JSON format.


 1   // Fig. 28.26: EquationGeneratorJSONResource.java
 2   // RESTful equation generator that returns JSON.
 3   package com.deitel.equationgeneratorjson;
 4
 5   import com.google.gson.Gson; // converts POJO to JSON and back again
 6   import java.util.Random;
 7   import javax.ws.rs.GET;
 8   import javax.ws.rs.Path;
 9   import javax.ws.rs.PathParam;
10   import javax.ws.rs.Produces;
11
12   @Path( "equation" )
13   public class EquationGeneratorJSONResource
14   {
15      static Random randomObject = new Random(); // random number generator
16
17      // retrieve an equation formatted as JSON
18      @GET
19      @Path( "{operation}/{level}" )
20      @Produces( "application/json" )
21      public String getJson( @PathParam( "operation" ) String operation,
22         @PathParam( "level" ) int level )
23      {
24         // compute minimum and maximum values for the numbers
25         int minimum = ( int ) Math.pow( 10, level - 1 );
26         int maximum = ( int ) Math.pow( 10, level );
27
28         // create the numbers on the left-hand side of the equation
29         int first = randomObject.nextInt( maximum - minimum ) + minimum;
30         int second = randomObject.nextInt( maximum - minimum ) + minimum;
31
32         // create Equation object and return result
33         Equation equation = new Equation( first, second, operation );
34         return new Gson().toJson( equation ); // convert to JSON and return
35      } // end method getJson
36   } // end class EquationGeneratorJSONResource


Fig. 28.26. RESTful equation generator that returns JSON.

The logic implemented here is the same as the XML version except for the last line (line 34), which uses Gson to convert the Equation object into JSON instead of using JAXB to convert it into XML. The @Produces annotation (line 20) has also changed to reflect the JSON data format.

28.11.4. Consuming the EquationGeneratorJSON Web Service

The program in Fig. 28.27 consumes the EquationGeneratorJSON service and performs the same function as EquationGeneratorXMLClient—the only difference is in how the Equation object is retrieved from the web service. Lines 181–183 construct the URL that is used to invoke the EquationGeneratorJSON service. As in the WelcomeRESTJSONClient example, we use the URL class and an InputStreamReader to invoke the web service and read the response (lines 186–187). The retrieved JSON is deserialized using Gson (line 191) and converted back into an Equation object. As before, we use the getResult method (line 194) of the deserialized object to obtain the answer and the getLeftHandSide method (line 195) to display the left side of the equation.


 1   // Fig. 28.27: EquationGeneratorJSONClientJFrame.java
 2   // Math-tutoring program using REST and JSON to generate equations.
 3   package com.deitel.equationgeneratorjsonclient;
 4
 5   import com.google.gson.Gson; // converts POJO to JSON and back again
 6   import java.io.InputStreamReader;
 7   import java.net.URL;
 8   import javax.swing.JOptionPane;
 9
10   public class EquationGeneratorJSONClientJFrame extends javax.swing.JFrame
11   {
12      private String operation = "add"; // operation user is tested on
13      private int difficulty = 1; // 1, 2, or 3 digits in each number
14      private int answer; // correct answer to the question
15
16      // no-argument constructor
17      public EquationGeneratorJSONClientJFrame()
18      {
19         initComponents();
20      } // end no-argument constructor
21
22      // The initComponents method is autogenerated by NetBeans and is called
23      // from the constructor to initialize the GUI. This method is not shown
24      // here to save space. Open EquationGeneratorJSONClientJFrame.java in  
25      // this example's folder to view the complete generated code.          
26
147     // determine if the user answered correctly
148     private void checkAnswerJButtonActionPerformed(
149        java.awt.event.ActionEvent evt)
150     {
151        if ( answerJTextField.getText().equals( "" ) )
152        {
153           JOptionPane.showMessageDialog(
154              this, "Please enter your answer." );
155        } // end if
156
157        int userAnswer = Integer.parseInt( answerJTextField.getText() );
158
159        if ( userAnswer == answer )
160        {
161           equationJLabel.setText( "" ); // clear label
162           answerJTextField.setText( "" ); // clear text field
163           checkAnswerJButton.setEnabled( false );
164           JOptionPane.showMessageDialog( this, "Correct! Good Job!",
165              "Correct", JOptionPane.PLAIN_MESSAGE );
166        } // end if
167        else
168        {
169           JOptionPane.showMessageDialog( this, "Incorrect. Try again.",
170              "Incorrect", JOptionPane.PLAIN_MESSAGE );
171        } // end else
172     } // end method checkAnswerJButtonActionPerformed
173
174     // retrieve equation from web service and display left side to user
175     private void generateJButtonActionPerformed(
176        java.awt.event.ActionEvent evt)
177     {
178        try
179        {
180           // URL of the EquationGeneratorJSON service, with parameters
181           String url = String.format( "http://localhost:8080/" +
182              "EquationGeneratorJSON/resources/equation/%s/%d",
183              operation, difficulty );
184
185           // open URL and create a Reader to read the data
186           InputStreamReader reader =                              
187              new InputStreamReader( new URL( url ).openStream() );
188
189           // convert the JSON back into an Equation object
190           Equation equation =                              
191              new Gson().fromJson( reader, Equation.class );
192
193           // update the internal state and GUI to reflect the equation
194           answer = equation.getResult();
195           equationJLabel.setText( equation.getLeftHandSide() + " =" );
196           checkAnswerJButton.setEnabled( true );
197        } // end try
198        catch ( Exception exception )
199        {
200           exception.printStackTrace();
201        } // end catch
202     } // end method generateJButtonActionPerformed
203
204     // obtains the mathematical operation selected by the user
205     private void operationJComboBoxItemStateChanged(
206        java.awt.event.ItemEvent evt)
207     {
208        String item = ( String ) operationJComboBox.getSelectedItem();
209
210        if ( item.equals( "Addition" ) )
211           operation = "add"; // user selected addition
212        else if ( item.equals( "Subtraction" ) )
213           operation = "subtract"; // user selected subtraction
214        else
215           operation = "multiply"; // user selected multiplication
216     } // end method operationJComboBoxItemStateChanged
217
218     // obtains the difficulty level selected by the user
219     private void levelJComboBoxItemStateChanged(
220        java.awt.event.ItemEvent evt)
221     {
222        // indices start at 0, so add 1 to get the difficulty level
223        difficulty = levelJComboBox.getSelectedIndex() + 1;
224     } // end method levelJComboBoxItemStateChanged
225
226     // main method begins execution
227     public static void main( String args[] )
228     {
229        java.awt.EventQueue.invokeLater(
230           new Runnable()
231           {
232              public void run()
233              {
234                 new EquationGeneratorJSONClientJFrame().setVisible( true );
235              } // end method run
236           } // end anonymous inner class
237        ); // end call to java.awt.EventQueue.invokeLater
238     } // end main
239
240     // Variables declaration - do not modify
241     private javax.swing.JLabel answerJLabel;
242     private javax.swing.JTextField answerJTextField;
243     private javax.swing.JButton checkAnswerJButton;
244     private javax.swing.JLabel equationJLabel;
245     private javax.swing.JButton generateJButton;
246     private javax.swing.JComboBox levelJComboBox;
247     private javax.swing.JLabel levelJLabel;
248     private javax.swing.JComboBox operationJComboBox;
249     private javax.swing.JLabel operationJLabel;
250     private javax.swing.JLabel questionJLabel;
251     // End of variables declaration
252  } // end class EquationGeneratorJSONClientJFrame


Fig. 28.27. Math-tutoring program using REST and JSON to generate equations.

Summary

Section 28.1 Introduction

• A web service (p. 28-2) is a software component stored on one computer that can be accessed by an application (or other software component) on another computer over a network.

• Web services communicate using such technologies as XML, JSON and HTTP.

• JAX-WS (p. 28-2) is based on the Simple Object Access Protocol (SOAP; p. 28-2)—an XML-based protocol that allows web services and clients to communicate.

• JAX-RS (p. 28-2) uses Representational State Transfer (REST; p. 28-2)—a network architecture that uses the web’s traditional request/response mechanisms such as GET and POST requests.

• Web services enable businesses to conduct transactions via standardized, widely available web services rather than relying on proprietary applications.

• Web services are platform and language independent, so companies can collaborate via web services without hardware, software and communications compatibility issues.

• NetBeans is one of the many tools that enable you to publish and/or consume web services.

Section 28.2 Web Service Basics

• The machine on which a web service resides is referred to as a web service host.

• A client application that accesses the web service sends a method call over a network to the web service host, which processes the call and returns a response over the network to the application.

• In Java, a web service is implemented as a class. The class that represents the web service resides on a server—it’s not part of the client application.

• Making a web service available to receive client requests is known as publishing a web service (p. 28-4); using a web service from a client application is known as consuming a web service (p. 28-4).

Section 28.3 Simple Object Access Protocol (SOAP)

• SOAP is a platform-independent protocol that uses XML to make remote procedure calls, typically over HTTP. Each request and response is packaged in a SOAP message (p. 28-4)—an XML message containing the information that a web service requires to process the message.

• SOAP messages are written in XML so that they’re computer readable, human readable and platform independent.

• SOAP supports an extensive set of types—the primitive types, as well as DateTime, XmlNode and others. SOAP can also transmit arrays of these types.

• When a program invokes a method of a SOAP web service, the request and all relevant information are packaged in a SOAP message, enclosed in a SOAP envelope (p. 28-4) and sent to the server on which the web service resides.

• When a web service receives a SOAP message, it parses the XML representing the message, then processes the message’s contents. The message specifies the method that the client wishes to execute and the arguments the client passed to that method.

• After a web service parses a SOAP message, it calls the appropriate method with the specified arguments (if any) and sends the response back to the client in another SOAP message. The client parses the response to retrieve the method’s result.

Section 28.4 Representational State Transfer (REST)

• Representational State Transfer (REST) refers to an architectural style for implementing web services. Such web services are often called RESTful web services (p. 28-4). Though REST itself is not a standard, RESTful web services are implemented using web standards.

• Each operation in a RESTful web service is identified by a unique URL.

• REST can return data in many formats, including XML and JSON.

Section 28.5 JavaScript Object Notation (JSON)

• JavaScript Object Notation (JSON; p. 28-5) is an alternative to XML for representing data.

• JSON is a text-based data-interchange format used to represent objects in JavaScript as collections of name/value pairs represented as Strings.

• JSON is a simple format that makes objects easy to read, create and parse and allows programs to transmit data efficiently across the Internet, because it’s much less verbose than XML.

• Each value in a JSON array can be a string, a number, a JSON object, true, false or null.

Section 28.6.1 Creating a Web Application Project and Adding a Web Service Class in NetBeans

• When you create a web service in NetBeans, you focus on the web service’s logic and let the IDE handle the web service’s infrastructure.

• To create a web service in NetBeans, you first create a Web Application project (p. 28-5).

Section 28.6.2 Defining the WelcomeSOAP Web Service in NetBeans

• By default, each new web service class created with the JAX-WS APIs is a POJO (plain old Java object)—you do not need to extend a class or implement an interface to create a web service.

• When you deploy a web application containing a JAX-WS web service, the server creates the server-side artifacts that support the web service.

• The @WebService annotation (p. 28-7) indicates that a class represents a web service. The optional name attribute (p. 28-7) specifies the service endpoint interface (SEI; p. 28-7) class’s name. The optional serviceName attribute (p. 28-7) specifies the name of the class that the client uses to obtain an SEI object.

• Methods that are tagged with the @WebMethod annotation (p. 28-7) can be called remotely.

• The @WebMethod annotation’s optional operationName attribute (p. 28-7) specifies the method name that is exposed to the web service’s clients.

• Web method parameters are annotated with the @WebParam annotation (p. 28-8). The optional name attribute (p. 28-8) indicates the parameter name that is exposed to the web service’s clients.

Section 28.6.3 Publishing the WelcomeSOAP Web Service from NetBeans

• NetBeans handles all the details of building and deploying a web service for you. This includes creating the framework required to support the web service.

Section 28.6.4 Testing the WelcomeSOAP Web Service with GlassFish Application Server’s Tester Web Page

• GlassFish can dynamically create a web page for testing a web service’s methods from a web browser. To open the test page, expand the project’s Web Services node in the NetBeans Projects tab, then right click the web service class name and select Test Web Service.

• A client can access a web service only when the application server is running. If NetBeans launches the application server for you, the server will shut down when you close NetBeans. To keep the application server up and running, you can launch it independently of NetBeans.

Section 28.6.5 Describing a Web Service with the Web Service Description Language (WSDL)

• To consume a web service, a client must know where to find it and must be provided with the web service’s description.

• JAX-WS uses the Web Service Description Language (WSDL; p. 28-11)—a standard XML vocabulary for describing web services in a platform-independent manner.

• The server generates a web service’s WSDL dynamically for you, and client tools can parse the WSDL to help create the client-side proxy class that a client uses to access the web service.

Section 28.6.6 Creating a Client to Consume the WelcomeSOAP Web Service

• A web service reference (p. 28-12) defines the service endpoint interface class so that a client can access the service.

• An application that consumes a SOAP-based web service invokes methods on a service endpoint interface (SEI) object that interact with the web service on the client’s behalf.

• The service endpoint interface object handles the details of passing method arguments to and receiving return values from the web service. This communication can occur over a local network, over the Internet or even with a web service on the same computer.

• NetBeans creates these service endpoint interface classes for you.

• When you add the web service reference, the IDE creates and compiles the client-side artifacts—the framework of Java code that supports the client-side service endpoint interface class. The service endpoint interface class uses the rest of the artifacts to interact with the web service.

• A web service reference is added by giving NetBeans the URL of the web service’s WSDL file.

Section 28.6.7 Consuming the WelcomeSOAP Web Service

• To consume a JAX-WS web service, you must obtain an SEI object. You then invoke the web service’s methods through the SEI object.

Section 28.7.1 Creating a REST-Based XML Web Service

• The RESTful Web Services plug-in for NetBeans provides various templates for creating RESTful web services, including ones that can interact with databases on the client’s behalf.

• The @Path annotation (p. 28-18) on a JAX-RS web service class indicates the URI for accessing the web service. This is appended to the web application project’s URL to invoke the service. Methods of the class can also use the @Path annotation.

• Parts of the path specified in curly braces indicate parameters—they’re placeholders for arguments that are passed to the web service as part of the path. The base path for the service is the project’s resources directory.

• Arguments in a URL can be used as arguments to a web service method. To do so, you bind the parameters specified in the @Path specification to parameters of a web service method with the @PathParam annotation (p. 28-19). When the request is received, the server passes the argument(s) in the URL to the appropriate parameter(s) in the web service method.

• The @GET annotation (p. 28-19) denotes that a method is accessed via an HTTP GET request. Similar annotations exist for HTTP PUT, POST, DELETE and HEAD requests.

• The @Produces annotation (p. 28-19) denotes the content type returned to the client. It’s possible to have multiple methods with the same HTTP method and path but different @Produces annotations, and JAX-RS will call the method matching the content type requested by the client.

• The @Consumes annotation (p. 28-19) restricts the content type that a web service accepts from a PUT request.

• JAXB (Java Architecture for XML Binding; p. 28-19) is a set of classes for converting POJOs to and from XML. Class JAXB (package javax.xml.bind) contains static methods for common operations.

• Class JAXB’s static method marshal (p. 28-19) converts a Java object to XML format.

• GlassFish does not provide test pages for RESTful services, but NetBeans generates a test page that can be accessed by right clicking the project’s node in the Projects tab and selecting Test RESTful Web Services.

• On the test page, select a method element in the left column. The right side of the page displays a form that allows you to choose the MIME type of the data and lets you enter the method’s arguments. Click the Test button to invoke the web service and display the returned data.

• WADL (Web Application Description Language; p. 28-20) has similar design goals to WSDL, but describes RESTful services instead of SOAP services.

Section 28.7.2 Consuming a REST-Based XML Web Service

• Clients of RESTful web services do not require web service references.

• The JAXB class has a static unmarshal method that takes as arguments a file name or URL as a String, and a Class<T> object indicating the Java class to which the XML will be converted.

Section 28.8 Publishing and Consuming REST-Based JSON Web Services

• JSON components—objects, arrays, strings, numbers—can be easily mapped to constructs in Java and other programming languages.

• There are many open-source JSON libraries for Java and other languages. The Gson library from code.google.com/p/google-gson/ provides a simple way to convert POJOs to and from JSON.

Section 28.8.1 Creating a REST-Based JSON Web Service

• To add a JAR file as a library in NetBeans, right click your project’s Libraries folder, select Add JAR/Folder..., locate the JAR file and click Open.

• For a web service method that returns JSON text, the argument to the @Produces attribute must be "application/json".

• In JSON, all data must be encapsulated in a composite data type.

• Create a Gson object (from package com.google.gson) and call its toJson method to convert an object into its JSON String representation.

Section 28.8.2 Consuming a REST-Based JSON Web Service

• To read JSON data from a URL, create a URL object and call its openStream method (p. 28-26). This invokes the web service and returns an InputStream from which the client can read the response. Wrap the InputStream in an InputStreamReader so it can be passed as the first argument to the Gson class’s fromJson method (p. 28-26).

Section 28.9 Session Tracking in a SOAP Web Service

• It can be beneficial for a web service to maintain client state information, thus eliminating the need to pass client information between the client and the web service multiple times. Storing session information also enables a web service to distinguish between clients.

Section 28.9.1 Creating a Blackjack Web Service

• In JAX-WS 2.2, to enable session tracking in a web service, you simply precede your web service class with the @HttpSessionScope annotation (p. 28-29) from package com.sun.xml.ws.developer.servlet. To use this package you must add the JAX-WS 2.2 library to your project.

• Once a web service is annotated with @HttpSessionScope, the server automatically maintains a separate instance of the class for each client session.

Section 28.9.2 Consuming the Blackjack Web Service

• In the JAX-WS framework, the client must indicate whether it wants to allow the web service to maintain session information. To do this, first cast the proxy object to interface type BindingProvider. A BindingProvider enables the client to manipulate the request information that will be sent to the server. This information is stored in an object that implements interface RequestContext. The BindingProvider and RequestContext are part of the framework that is created by the IDE when you add a web service client to the application.

• Next, invoke the BindingProvider’s getRequestContext method to obtain the RequestContext object. Then call the RequestContext’s put method to set the property BindingProvider.SESSION_MAINTAIN_PROPERTY to true, which enables session tracking from the client side so that the web service knows which client is invoking the service’s web methods.

Section 28.11 Equation Generator: Returning User-Defined Types

• It’s also possible to process instances of class types in a web service. These types can be passed to or returned from web service methods.

• An instance variable can be serialized only if it’s public or has both a get and a set method.

• Properties that can be generated from the values of other properties should not be serialized to prevent redundancy.

• JAX-RS automatically converts arguments from an @Path annotation to the correct type, and it will return a “not found” error to the client if the argument cannot be converted from the String passed as part of the URL to the destination type. Supported types for conversion include integer types, floating-point types, boolean and the corresponding type-wrapper classes.

Self-Review Exercises

28.1 State whether each of the following is true or false. If false, explain why.

a. All methods of a web service class can be invoked by clients of that web service.

b. When consuming a web service in a client application created in NetBeans, you must create the proxy class that enables the client to communicate with the web service.

c. A proxy class communicating with a web service normally uses SOAP to send and receive messages.

d. Session tracking is automatically enabled in a client of a web service.

e. Web methods cannot be declared static.

f. A user-defined type used in a web service must define both get and set methods for any property that will be serialized.

g. Operations in a REST web service are defined by their own unique URLs.

h. A SOAP-based web service can return data in JSON format.

28.2 Fill in the blanks for each of the following statements:

a. A key difference between SOAP and REST is that SOAP messages have data wrapped in a(n) ________.

b. A web service in Java is a(n) ________—it does not need to implement any interfaces or extend any classes.

c. Web service requests are typically transported over the Internet via the ________ protocol.

d. To set the exposed name of a web method, use the ________ element of the @WebMethod annotation.

e. ________ transforms an object into a format that can be sent between a web service and a client.

f. To return data in JSON format from a method of a REST-based web service, the @Produces annotation is set to ________.

g. To return data in XML format from a method of a REST-based web service, the @Produces annotation is set to ________.

Answers to Self-Review Exercises

28.1

a. False. Only methods declared with the @WebMethod annotation can be invoked by a web service’s clients.

b. False. The proxy class is created by NetBeans when you add a web service client to the application.

c. True.

d. False. In the JAX-WS framework, the client must indicate whether it wants to allow the web service to maintain session information. First, you must cast the proxy object to interface type BindingProvider, then use the BindingProvider’s getRequestContext method to obtain the RequestContext object. Finally, you must use the RequestContext’s put method to set the property BindingProvider.SESSION_MAINTAIN_PROPERTY to true.

e. True.

f. True.

g. True.

h. False. A SOAP web service implicitly returns data in XML format.

28.2

a. SOAP message or SOAP envelope.

b. POJO (plain old Java object)

c. HTTP.

d. operationName.

e. serialization.

f. "application/json".

g. "application/xml".

Exercises

28.3 (Phone Book Web Service) Create a RESTful web service that stores phone book entries in the database PhoneBookDB and a web client application that consumes this service. The web service should output XML. Use the steps in Section 27.2.1 to create the PhoneBook database and a data source name for accessing it. The database contains one table—PhoneBook—with three columns—LastName, FirstName and PhoneNumber. The LastName and FirstName columns store up to 30 characters. The PhoneNumber column supports phone numbers of the form (800) 555-1212 that contain 14 characters. Use the PhoneBookDB.sql script provided in the examples folder to create the PhoneBook table.

Give the client user the capability to enter a new contact (web method addEntry) and to find contacts by last name (web method getEntries). Pass only Strings as arguments to the web service. The getEntries web method should return an array of Strings that contains the matching phone book entries. Each String in the array should consist of the last name, first name and phone number for one phone book entry. These values should be separated by commas.

The SELECT query that will find a PhoneBook entry by last name should be:

SELECT LastName, FirstName, PhoneNumber
FROM PhoneBook
WHERE (LastName = LastName)

The INSERT statement that inserts a new entry into the PhoneBook database should be:

INSERT INTO PhoneBook (LastName, FirstName, PhoneNumber)
VALUES (LastName, FirstName, PhoneNumber)

28.4 (Phone Book Web Service Modification) Modify Exercise 28.3 so that it uses a class named PhoneBookEntry to represent a row in the database. The web service should return objects of type PhoneBookEntry in XML format for the getEntries method, and the client application should use the JAXB method unmarshal to retrieve the PhoneBookEntry objects.

28.5 (Phone-Book Web Service with JSON) Modify Exercise 28.4 so that the PhoneBookEntry class is passed to and from the web service as a JSON object. Use serialization to convert the JSON object into an object of type PhoneBookEntry.

28.6 (Blackjack Web Service Modification) Modify the Blackjack web service example in Section 28.9 to include class Card. Modify web method dealCard so that it returns an object of type Card and modify web method getHandValue so that it receives an array of Card objects from the client. Also modify the client application to keep track of what cards have been dealt by using ArrayLists of Card objects. The proxy class created by NetBeans will treat a web method’s array parameter as a List, so you can pass these ArrayLists of Card objects directly to the getHandValue method. Your Card class should include set and get methods for the face and suit of the card.

28.7 (Project: Airline Reservation Web-Service Modification) Modify the airline reservation web service in Section 28.10 so that it contains two separate methods—one that allows users to view all available seats, and another that allows users to reserve a particular seat that is currently available. Use an object of type Ticket to pass information to and from the web service. The web service must be able to handle cases in which two users view available seats, one reserves a seat and the second user tries to reserve the same seat, not knowing that it’s now taken. The names of the methods that execute should be reserve and getAllAvailableSeats.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.173.112