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.
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
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/
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 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.
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.
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.
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.
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 String
s. 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.
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.
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.
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.
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.
WelcomeSOAP
Web Service in NetBeansFigure 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
import
DeclarationsLines 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
AnnotationLine 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
MethodThe 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.
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.
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.
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).
3. Click the Add Operation... button to display the Add Operation... dialog (Fig. 28.3).
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
.
WelcomeSOAP
Web Service from NetBeansNow 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.
WelcomeSOAP
Web Service with GlassFish Application Server’s Tester
Web PageNext, 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
.
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
.
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.
WelcomeSOAP
Web Service from Another ComputerIf 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
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
WelcomeSOAP
Web Service’s WSDL from Another ComputerEventually, 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.
WelcomeSOAP
Web ServiceNow 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.
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.
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.
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.
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.
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
.
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.
WelcomeSOAP
Web ServiceFor 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
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.
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
.
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.
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
Lines 6–9 contain the import
s 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.
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.
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.
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
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.
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.
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
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.
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
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.
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.
Blackjack
Web ServiceOur 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, 1
–13
, representing the card’s face (ace through king, respectively), followed by a space and a digit, 0
–3
, 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.1–28.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
@HttpSessionScope
AnnotationIn 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.
Blackjack
Web ServiceA 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 String
s 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.
Blackjack
Web ServiceThe 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 JLabel
s—the maximum number of cards that can be dealt without automatically exceeding 21 (i.e., four aces, four twos and three threes). These JLabel
s are placed in an ArrayList
of JLabel
s (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
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.
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
.
dealJButtonActionPerformed
When the player clicks the Deal JButton
, method dealJButtonActionPerformed
(lines 543–602) clears all of the JLabel
s 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).
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).
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).
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
.
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.
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.
Reservation
DatabaseOur 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.
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
.
Software Engineering Observation 28.1
Using PreparedStatement
s 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
Our database contains four columns—the seat number (i.e., 1
–10
), 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.
Reservation
ServiceThis 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 26–27. 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:selectOneMenu
s 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:selectOneMenu
s. 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>
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
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.
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
Lines 19–40 define a constructor that takes two int
s 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.
EquationGeneratorXML
Web ServiceFigure 28.24 presents the EquationGeneratorXML
web service’s class for creating randomly generated Equation
s. 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
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.]
EquationGeneratorXML
Web ServiceThe 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
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).
EquationGeneratorJSON
Web ServiceAs 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
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.
EquationGeneratorJSON
Web ServiceThe 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
• 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.
• 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).
• 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.
• 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.
• 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 String
s.
• 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
.
• 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).
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.
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.
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.
• 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.
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.
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.
• 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.
• 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.
• 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.
• 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.
• 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).
• 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.
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.
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.
• 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.
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 ________.
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.
a. SOAP message or SOAP envelope.
b. POJO (plain old Java object)
c. HTTP.
d. operationName
.
e. serialization.
f. "application/json"
.
g. "application/xml"
.
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 String
s as arguments to the web service. The getEntries
web method should return an array of String
s 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 ArrayList
s 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 ArrayList
s 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
.
3.145.173.112