In this post I am going to show you how to implement the Builder design pattern in Java.
Solution offered by the pattern
This pattern separates the construction of a complex object from its representation, in a way that the same construction process allows to create different representations.
To do so we must use an object, the Director, that determines the product construction logic and sends those instruction to an object, the Builder, responsible for the product construction. Since the product to be carrued out are of different nature, there will be particular Builders for every kind of product, but one only Director. In the process of construction the Director calls invokes the chosen Builder's methods based on the type of the desired product (the Builders should implement a common interface to allow the Director to interact with all those).
Pattern Structure
Builder Design Pattern Class Diagram
Design pattern application - schema model
Builder Application Balls schema
Partecipants
Builder: Abstract class BallBuilder:
- Declares an interface for the operations that create the parts of the object Product;
- Implements the default operations for every operation.
ConcreteBuilder: the classes BasketBallBuilder and VolleyBallBuilder:
- They provide the concrete operations for the interface correspondant to bhe Builder;
- Construct and assemblate the parts of a Product;
- Provides a method that returns the created Product.
Director: class Director
- Construct the Product invoking the methods of the interface of the builder.
Product: classes Ball, BasketBall and VolleyBall:
- Represents the complex object in construction. The ConcreteBuilders return the internal representation of the products;
- Includes classes that defines the parts of a Product.
Code Description
I've declared the abstract class BallBuilder extended by the ConcreteBuilders for the construction of every kind ofball. The class BallBuilder contains all the methods that the ConcreteBuilder could override, and they do implement only those necessary to construct the Product; this is why the BallBuilder provides all the methods a default code, in this case null code; the only abstract method is getBall() that all the ConcreteBuilders must implement to return the constructed object.
package com.fa.builderdesignpattern.builder;
import com.fa.builderdesignpattern.product.Ball;
public abstract class BallBuilder {
public abstract Ball getBall();
public void createNewBallProduct() {};
public void buildWeight() {};
public void buildCircumference() {};
public void buildColor() {};
public void buildPrice() {};
}
This class presents a set of methods declared with a default implementation and the first method declared abstract that must be implement from the builders to return the different kind of objects.
The methods presented are those needed to construct the product and the abstract one that returns the constructed product of type Ball.
The following classes are the ConcreteBuilders and contain the code necessary to return the instantiated product:
package com.fa.builderdesignpattern.builder;
import com.fa.builderdesignpattern.product.Ball;
import com.fa.builderdesignpattern.product.BasketBall;
public class BasketBallBuilder extends BallBuilder {
protected Ball basketBall;
@Override
public void buildWeight() {
basketBall.setWeight("624 g");
}
@Override
public void buildCircumference() {
basketBall.setCircumference("75-75.88 cm");
}
@Override
public void buildColor() {
basketBall.setColor("Orange surface with black ribs and a possible logo is the traditional color scheme of basketballs.");
}
@Override
public void buildPrice() {
basketBall.setPrice("$95");
}
@Override
public Ball getBall() {
return basketBall;
}
@Override
public void createNewBallProduct() {
basketBall = new BasketBall();
}
}
package com.fa.builderdesignpattern.builder;
import com.fa.builderdesignpattern.product.Ball;
import com.fa.builderdesignpattern.product.VolleyBall;
public class VolleyBallBuilder extends BallBuilder {
Ball volleyBall;
@Override
public void buildWeight() {
volleyBall.setWeight("270 g");
}
@Override
public void buildCircumference() {
volleyBall.setCircumference("65-67 cm");
}
@Override
public void buildColor() {
volleyBall.setColor("Volleyballs may be solid white or a combination of two or three different easily distinguishable colors.");
}
@Override
public void buildPrice() {
volleyBall.setPrice("$60");
}
@Override
public Ball getBall() {
return volleyBall;
}
@Override
public void createNewBallProduct() {
volleyBall = new VolleyBall();
}
}
As you can see the concrete builders do override the default behaviour defined in the AbstractBuilder and the method getBall() that was declared abstract in the AbstractBuilder is now implemented to return the constructed Ball.
The Director class receives a reference to a ConcreteBuilder and will execute its methods to construct the Product. The Director in this example has a method createNewBallProduct() to instantiate a new Ball and a method constructBall() that is called to set the properties of the new created product. Finally the method getBall() returns the new instantiated Ball.
package com.fa.builderdesignpattern.director;
import com.fa.builderdesignpattern.builder.BallBuilder;
import com.fa.builderdesignpattern.product.Ball;
public class Director {
private BallBuilder ballBuilder;
public void setBallBuilder(BallBuilder bb) { ballBuilder = bb; }
public Ball getBall() { return ballBuilder.getBall(); }
public void constructBall() {
ballBuilder.createNewBallProduct();
ballBuilder.buildWeight();
ballBuilder.buildCircumference();
ballBuilder.buildColor();
ballBuilder.buildPrice();
}
}
Note that the Director is not aware of which type of ConcreteBuilder it is working with because it is managed through the common interface of the Builders (BallBuilder).
The class BuilderExample contains the main that demonstrates the pattern realizing an instance of both the Builders.
Note that the type of ConcreteBuilder is chosen only at this level.
Lets now see the Products: in this example I have defined an abstract superclass Ball that is extended by the concrete Products VolleyBall and BasketBall.
package com.fa.builderdesignpattern.product;
public class BasketBall extends Ball {
@Override
public String bounce() {
return "BasketBall BOUNCE";
}
}
package com.fa.builderdesignpattern.product;
public class VolleyBall extends Ball {
@Override
public String bounce() {
return "VolleyBall BOUNCE";
}
}
The abstract class Ball defines the properties and methods common to the concrete Products and defines the abstract method bounce() that its child override defining their own behaviours.
Example execution
BasketBall: weight: 624 g
circumference: 75-75.88 cm
color: Orange surface with black ribs and a possible logo is the traditional color scheme of basketballs.
price: $95
BasketBall BOUNCE
VolleyBall: weight: 270 g
circumference: 65-67 cm
color: Volleyballs may be solid white or a combination of two or three different easily distinguishable colors.
price: $60
VolleyBall BOUNCE
In this post I am going to show you implement the Abstract Factory pattern in Java.
Description
The Abstract Factory pattern presents an interface for creating a family of products in a way that the client that uses those doesn't know anything about the concrete classes.
Solution offered by the pattern
This pattern is based on the definition of interfaces for every kind of product. There will be concrete classes that implement those interfaces that allow clients to make use of the products. The product families will be created by an object known as factory.
Every family has its particular factory used by the client to create products instances. Because the aim is not to tie the client to a specific factory, these factory implement a common interface known by the client.
Pattern structure
Abstract Factory Design Pattern Structure
Design pattern application - schema model
Abstract Factory Application - Widgets schema
Partecipants:
AbstractFactory:interfaceWidgetFactory
- declares operations that create and return products.
- define the products created by the ConcreteFactory.
Client: class Client.
- uses the AbstractFactory to call the ConcreteFactory of a product family.
- uses products by their interface AbstractProduct.
Code Description
Create the interfaces that the classes of every product family will implement. Widget is a marker interface used to identify interface components, while the interfaces TextBox, ComboBox and ListBox define the methods the clients invoke to draw the components.
package com.fa.abstractfactory.product;
public interface Widget { }
package com.fa.abstractfactory.product;
public interface TextBox extends Widget {
public void draw();
}
package com.fa.abstractfactory.product;
public interface ListBox extends Widget {
public void draw();
}
package com.fa.abstractfactory.product;
public interface ComboBox extends Widget {
public void draw();
}
We do implement the concrete classes of the products.
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ComboBox;
public class LinuxComboBox implements ComboBox{
private List comboList;
public LinuxComboBox(List list) {
System.out.println("LinuxComboBox");
this.comboList = list;
}
public void draw() {
for(int i=0; i < comboList.size(); i++) {
System.out.println("- " + comboList.get(i) + " -");
}
}
}
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ComboBox;
public class MacComboBox implements ComboBox {
private List comboList;
public MacComboBox(List list) {
System.out.println("MacComboBox");
this.comboList = list;
}
public void draw() {
for(int i=0; i < comboList.size(); i++) {
System.out.println("| " + comboList.get(i) + " |");
}
}
}
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ComboBox;
public class WindowsComboBox implements ComboBox {
private List comboList;
public WindowsComboBox(List list) {
System.out.println("WindowsComboBox");
this.comboList = list;
}
public void draw() {
for(int i=0; i < comboList.size(); i++) {
System.out.println("< " + comboList.get(i) + " >");
}
}
}
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ListBox;
public class LinuxListBox implements ListBox{
private List list;
public LinuxListBox(List list) {
System.out.println("LinuxListBox");
this.list = list;
}
public void draw() {
for(int i=0; i < list.size(); i++) {
System.out.println("- " + list.get(i) + " -");
}
}
}
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ListBox;
public class MacListBox implements ListBox{
private List list;
public MacListBox(List list) {
System.out.println("MacListBox");
this.list = list;
}
public void draw() {
for(int i=0; i < list.size(); i++) {
System.out.println("| " + list.get(i) + " |");
}
}
}
package com.fa.abstractfactory.product.impl;
import java.util.List;
import com.fa.abstractfactory.product.ListBox;
public class WindowsListBox implements ListBox {
private List list;
public WindowsListBox(List list) {
System.out.println("WindowsListBox");
this.list = list;
}
public void draw() {
for(int i=0; i < list.size(); i++) {
System.out.println("< " + list.get(i) + " >");
}
}
}
package com.fa.abstractfactory.product.impl;
import com.fa.abstractfactory.product.TextBox;
public class LinuxTextBox implements TextBox {
private String text;
public LinuxTextBox(String value) {
System.out.println("LinuxTextBox");
this.text = value;
}
public void draw() {
System.out.println("- " + text + " -");
}
}
package com.fa.abstractfactory.product.impl;
import com.fa.abstractfactory.product.TextBox;
public class MacTextBox implements TextBox{
private String text;
public MacTextBox(String value) {
System.out.println("MacTextBox");
this.text = value;
}
public void draw() {
System.out.println("| " + text + " |");
}
}
package com.fa.abstractfactory.product.impl;
import com.fa.abstractfactory.product.TextBox;
public class WindowsTextBox implements TextBox {
private String text;
public WindowsTextBox(String text) {
System.out.println("WindowsTextBox");
this.text = text;
}
public void draw() {
System.out.println("< " + text + " >");
}
}
The AbstractFactory interface defines the methods that return the generic type of Products.
Than we define the ConcreteFactory classes that create and return the concrete product for each family
package com.fa.abstractfactory.productfactory;
import java.util.List;
import com.fa.abstractfactory.factory.WidgetFactory;
import com.fa.abstractfactory.product.ComboBox;
import com.fa.abstractfactory.product.ListBox;
import com.fa.abstractfactory.product.TextBox;
import com.fa.abstractfactory.product.impl.LinuxComboBox;
import com.fa.abstractfactory.product.impl.LinuxListBox;
import com.fa.abstractfactory.product.impl.LinuxTextBox;
public class LinuxWidgetFactory implements WidgetFactory{
@Override
public TextBox createTextBox(String value) {
return new LinuxTextBox(value);
}
@Override
public ListBox createListBox(List list) {
return new LinuxListBox(list);
}
@Override
public ComboBox createComboBox(List list) {
return new LinuxComboBox(list);
}
}
package com.fa.abstractfactory.productfactory;
import java.util.List;
import com.fa.abstractfactory.factory.WidgetFactory;
import com.fa.abstractfactory.product.ComboBox;
import com.fa.abstractfactory.product.ListBox;
import com.fa.abstractfactory.product.TextBox;
import com.fa.abstractfactory.product.impl.MacComboBox;
import com.fa.abstractfactory.product.impl.MacListBox;
import com.fa.abstractfactory.product.impl.MacTextBox;
public class MacWidgetFactory implements WidgetFactory{
@Override
public TextBox createTextBox(String text) {
return new MacTextBox(text);
}
@Override
public ListBox createListBox(List list) {
return new MacListBox(list);
}
@Override
public ComboBox createComboBox(List list) {
return new MacComboBox(list);
}
}
package com.fa.abstractfactory.productfactory;
import java.util.List;
import com.fa.abstractfactory.factory.WidgetFactory;
import com.fa.abstractfactory.product.ComboBox;
import com.fa.abstractfactory.product.ListBox;
import com.fa.abstractfactory.product.TextBox;
import com.fa.abstractfactory.product.impl.WindowsComboBox;
import com.fa.abstractfactory.product.impl.WindowsListBox;
import com.fa.abstractfactory.product.impl.WindowsTextBox;
public class WindowsWidgetFactory implements WidgetFactory{
@Override
public TextBox createTextBox(String text) {
return new WindowsTextBox(text);
}
@Override
public ListBox createListBox(List list) {
return new WindowsListBox(list);
}
@Override
public ComboBox createComboBox(List list) {
return new WindowsComboBox(list);
}
}
The Client class defines the objects that every family product uses. Its constructor receives an object correnspondent to a particular family. The test method defines an instance for every particular family product and applies the different methods defined by the interface that the products implement.
Note that the Client uses the Products without knowing which concrete object they belong to.
package com.fa.abstractfactory.client;
import java.util.Arrays;
import java.util.List;
import com.fa.abstractfactory.factory.*;
import com.fa.abstractfactory.product.impl.MacComboBox;
import com.fa.abstractfactory.product.impl.MacListBox;
import com.fa.abstractfactory.product.impl.MacTextBox;
import com.fa.abstractfactory.productfactory.MacWidgetFactory;
public class Client {
private WidgetFactory factory;
public Client(WidgetFactory factory) {
this.factory = factory;
}
public void test() {
factory.createTextBox("Text").draw();
List list = Arrays.asList("First", "Second", "Third");
factory.createListBox(list).draw();
factory.createComboBox(list).draw();
}
}
Next there is an example class that instantiates 3 Client objects passing to their constructor a family of product:
package com.fa.abstractfactory.test;
import com.fa.abstractfactory.client.Client;
import com.fa.abstractfactory.productfactory.LinuxWidgetFactory;
import com.fa.abstractfactory.productfactory.MacWidgetFactory;
import com.fa.abstractfactory.productfactory.WindowsWidgetFactory;
public class AbstractFactoryExample {
public static void main(String[] args) {
Client client1 = new Client(new MacWidgetFactory());
client1.test();
Client client2 = new Client(new LinuxWidgetFactory());
client2.test();
Client client3 = new Client(new WindowsWidgetFactory());
client3.test();
}
}
Next image shows you the console output, resulting from launching the previous starter class.
MacTextBox
| Text |
MacListBox
| First |
| Second |
| Third |
MacComboBox
| First |
| Second |
| Third |
LinuxTextBox
- Text -
LinuxListBox
- First -
- Second -
- Third -
LinuxComboBox
- First -
- Second -
- Third -
WindowsTextBox
< Text >
WindowsListBox
< First >
< Second >
< Third >
WindowsComboBox
< First >
< Second >
< Third >
Creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to de design. Creational design patterns solve this problem somehow controlling this object creation.
Creational design patterns are further categorized into Object-creational patterns and Class-creational patterns, where Object-creational patterns deal with Object creation and Class-creational patterns deal with Class-instantiation. In greater details, Object-creational patterns defer part of its object creation to another object, while Class-creational patterns defer its objection creation to subclasses.
Five well-known design patterns that are part of creational patterns are:
Abstract Factory
Builder
Factory
Prototype
Singleton
Structural Patterns
Structural design patterns are those that ease the design by identifying a simple way to realize relationship between entities. Structural class-creation patterns use inheritance to compose interfaces. Structural object-patterns define ways to compose objects to obtain new functionality.
Example of structural patterns include:
Adapter
Bridge
Composite
Decorator
Extensibility
Facade
Proxy
Behavioral Patterns
Behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. These patterns increase flexibility in carrying out this communication.
Examples of this type of design patterns include:
Chain of Responsibility
Command
Interpreter
Iterator
Mediator
Memento
Observer
State
Strategy
Template
Visitor
In my next posts I am going to show you how to implement some of the patterns listed above.
In this post I am going to show you how to configure and create a Web Service using Spring WS. Web Services developed with Spring are called contract-first Web Services because these starts with the XML Schema/WSDL contract first followed by the Java code. In the first part of this tutorial I show you how to create the contract of the web service with XML and XSD containing request and response objects. In the second part I show you how to imlement the Web Service with Spring WS.
The most important thing when doing contract-first Web Services is to think in terms of XML. It is the XML that is sent across the wire, and you should focus on that- The fact that Java is used to implement the Web Service is an implementation detail.
The Web Service I am going to create is used to call the Google Maps services that returns an image of a locality. The reaquest contains an address or coordinates of a point in the earth or the request contains addresses or coordinates of 2 points and the response image contains the route to get from the origin to destination.
Messages
In this section I will focus on the XML messages that are sent to and from the Web Service.
As I wrote above, two of the four requests sent to the Web Service contain a coordinate (latitude, longitude) of a point in the earth or a String address to be searched by the Google Maps services, this is the declaration of these kind of objects using XSD:
Below an example of these two XML object types:
The next two request types are just a little different from the two above. These will contain two coordinates or two addresses to calculate the route to get from origin to destination. The last type has also another attribute called waypoints that is a List of intermediate addresses which to pass through to get to destination:
Next an example of these two object requests:
The service contract based on the XSD types above is a WSDL file. It is not required that we write it by hand because Spring creates it for us when we deploy the Web Service in a Application Server.
Web Service configuration
We can now create a new project to implements the Web Service using Java.
I will use Maven 2 to create the initial project structure.
Once created the project structure, under the src/main/webapp folder we will find the standard web application deployment descriptor WEB-INF/web.xml which defines a Spring-WS MessageDispatcherServlet and maps all incoming requests to this servlet.
In addition to the above WEB-INF/web.xml file, we need to create the Spring configuration file WEB-INF/spring-ws-servlet.xml. This file contains all the Spring beans related to the WS such as the Web Service Implementation, the Endpoint and the contracts of the Web Service (XSD and WSDL). The name of this file is derived from the name of the attendant servlet (in this case spring-ws) with -servlet.xml appended to it. Below we can see the configuration file:
The first bean I have declared is a PropertyPlaceholder used to import some default values from a properties file declared in a folder on the root of the Application Server. The other bean declared are the Service Implementation that injects some default properties, by Inversion of Control, that get the values from the property placeholders from the file imported above. Follows the Endpoint declaration which injects the web service bean to a constructor argument. The last two beans represent the contract of the Web Service: XSD definition and WSDL definition that gets some properties to be injected. There's another configuration file that we need to create: it is the pom.xml that is the Maven configuration file used to list the dependencies of the application and information such as the build process. Follows my pom.xml:
The dependencies needed for this project are Spring-WS and the libraries needed to test the application. There is also the maven plugin for the build process. The Endpoint In Spring WS, we need to implement Endpoints to handle incoming XML requests. It is created by annotating the class with the @Endpoint annotation and it has one or more methods to handle the incoming requests each related to a Web Service method. Before creating the Endpoint class I have used JAX-B to create the Java classes related to the XSD types I have defined earlier for the requests and responses. The command to be used to generate the classes is: xjc SchemaFile.xsd. Follows my web service endpoint:
package com.faeddalberto.googlews.endpoint;
@Endpoint
public class GoogleServicesEndpoint {
private GoogleServices service;
@Autowired
public GoogleServicesEndpoint(GoogleServices service) {
this.service = service;
}
@PayloadRoot(localPart="ImageFromCoordinatesRequest",
namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
public GoogleServicesResponse getImageFromCoordinates(
ImageFromCoordinatesRequest aRequest){
return service.getImageFromCoordinates(aRequest);
}
@PayloadRoot(localPart="ImageFromAddressRequest",
namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
public GoogleServicesResponse getImageFromAddress(
ImageFromAddressRequest aRequest){
return service.getImageFromAddress(aRequest);
}
@PayloadRoot(localPart="ImageRouteFromCoordinatesRequest",
namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
public GoogleServicesResponse getImageRouteFromCoordinates(
ImageRouteFromCoordinatesRequest aRequest){
return service.getImageRouteFromCoordinates(aRequest);
}
@PayloadRoot(localPart="ImageRouteFromAddressesRequest",
namespace="http://www.faeddalberto.com/GoogleServicesContracts/types")
public GoogleServicesResponse getImageRouteFromAddresses(
ImageRouteFromAddressesRequest aRequest){
return service.getImageRouteFromAddresses(aRequest);
}
}
This Endpoint class, annotated with the @Endpoint annotation, has a constructor to which is injected the Web Service Implementation Bean as I showed you in the Spring configuration file.
The @PayloadRoot annotations tells Spring WS that the methods are suitable for handling XML messages.
This annotation uses some attributes: the localpart attribute that defines which is the request that the method handles and the namespace attribute that defines the namespace which the request is related to. The four methods declared in the Endpoint class call the four web service methods and all four get a different request as argument that are the classes defined by the JAX-B APIs from the XSD.
All the methods return the same response that is the response of the web service: an image related to the request or an error message if any error has occurred in the web service.
Implementing the Web Service
Let's now give a look at the Java code to implement the Web Service. As usual we have the Interface that declares the service operations and the Service Implementation Bean that implements these operations.
Follows the web service interface:
package com.faeddalberto.googlews.service;
public interface GoogleServices {
public GoogleServicesResponse getImageFromCoordinates(
ImageFromCoordinatesRequest request);
public GoogleServicesResponse getImageFromAddress(
ImageFromAddressRequest request);
public GoogleServicesResponse getImageRouteFromCoordinates(
ImageRouteFromCoordinatesRequest request);
public GoogleServicesResponse getImageRouteFromAddresses(
ImageRouteFromAddressesRequest request);
}
The Web Service Interface has four methods declared that are the methods the Web Service Implementation Bean implements:
package com.faeddalberto.googlews.service;
public class GoogleServicesImpl implements GoogleServices {
private static final String SENSOR = "sensor=true";
private static final String QUESTION_MARK = "?";
private static final String AMPERSAND = "&";
private static final String PIPE = "|";
private String directionUrl;
private String staticUrl;
private String mapType;
private String zoom;
private String size;
private String path;
private String markers;
private String center;
private String origin;
private String destination;
private String wayPoints;
public GoogleServicesResponse getImageFromCoordinates(
ImageFromCoordinatesRequest request) {
System.out.println("getImageFromCoordinates");
GoogleServicesResponse response = new GoogleServicesResponse();
center += request.getLatitude() + "," + request.getLongitude();
markers += request.getLatitude() + "," + request.getLongitude();
staticUrl += QUESTION_MARK + size + AMPERSAND + zoom +
AMPERSAND + center + AMPERSAND + markers + AMPERSAND +
mapType + AMPERSAND + SENSOR;
byte[] imageBytes = null;
try {
imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);
} catch (IOException e) {
System.err.println("IOException " + e.getMessage());
response.setErrorDescription("An error has
occurred while creating the image");
}
response.setImage(imageBytes);
return response;
}
public GoogleServicesResponse getImageFromAddress(
ImageFromAddressRequest request) {
System.out.println("getImageFromAddress");
GoogleServicesResponse response = new GoogleServicesResponse();
center += request.getAddress().replace(" ", "+");
staticUrl += QUESTION_MARK + size + AMPERSAND + zoom +
AMPERSAND + center + AMPERSAND +
mapType + AMPERSAND + SENSOR;
byte[] imageBytes = null;
try {
imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);
} catch (IOException e) {
System.err.println("IOException " + e.getMessage());
response.setErrorDescription("An error has occurred
while creating the image");
}
response.setImage(imageBytes);
return response;
}
public GoogleServicesResponse getImageRouteFromCoordinates(
ImageRouteFromCoordinatesRequest request) {
System.out.println("getImageRouteFromCoordinates");
GoogleServicesResponse response = new GoogleServicesResponse();
origin += request.getLatitudeOrg() +
"," + request.getLongitudeOrg();
destination += request.getLatitudeDst() +
"," + request.getLongitudeDst();
directionUrl += QUESTION_MARK + origin + AMPERSAND
+ destination + AMPERSAND + SENSOR;
try {
response.setImage(getImageRoute());
} catch (MalformedURLException e) {
System.err.println("MalformedURLException " + e.getMessage());
response.setErrorDescription("An error has occurred
while processing the request");
} catch (IOException e) {
System.err.println("IOException " + e.getMessage());
response.setErrorDescription("An error has occurred
while creating the image containing the route");
}
return response;
}
public GoogleServicesResponse getImageRouteFromAddresses(
ImageRouteFromAddressesRequest request) {
GoogleServicesResponse response = new GoogleServicesResponse();
System.out.println("getImageRouteFromAddresses");
origin += request.getAddressOrg();
destination += request.getAddressDst();
for(int i = 0; i < request.getWaypoints().size(); i++) {
if (i != 0) wayPoints += PIPE;
wayPoints += request.getWaypoints().get(i).replace(" ", "+");
}
directionUrl += QUESTION_MARK + origin + AMPERSAND +
destination + AMPERSAND + wayPoints + AMPERSAND + SENSOR;
try {
response.setImage(getImageRoute());
} catch (MalformedURLException e) {
System.err.println("MalformedURLException " + e.getMessage());
response.setErrorDescription("An error has occurred
while processing the request");
} catch (IOException e) {
System.err.println("IOException " + e.getMessage());
response.setErrorDescription("An error has occurred
while creating the image containing the route");
}
return response;
}
private byte[] getImageRoute()
throws MalformedURLException, IOException {
System.out.println(directionUrl);
String xmlString =
HttpUtils.loadStringXmlFromHttp(directionUrl);
Document xmlDocumentResult =
XmlUtils.createXmlDocumentFromString(xmlString);
String routeCoord =
XmlUtils.getRouteCoordinatesFromXml(xmlDocumentResult);
staticUrl += QUESTION_MARK + size + AMPERSAND
+ path + routeCoord + AMPERSAND + SENSOR;
byte[] imageBytes = null;
imageBytes = HttpUtils.loadByteArrayImageFromHttp(staticUrl);
return imageBytes;
}
/* GETTERS & SETTERS */
public String getDirectionUrl() {
return directionUrl;
}
public void setDirectionUrl(String directionUrl) {
this.directionUrl = directionUrl;
}
public String getStaticUrl() {
return staticUrl;
}
public void setStaticUrl(String staticUrl) {
this.staticUrl = staticUrl;
}
public String getMapType() {
return mapType;
}
public void setMapType(String mapType) {
this.mapType = mapType;
}
public String getZoom() {
return zoom;
}
public void setZoom(String zoom) {
this.zoom = zoom;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getMarkers() {
return markers;
}
public void setMarkers(String markers) {
this.markers = markers;
}
public String getCenter() {
return center;
}
public void setCenter(String center) {
this.center = center;
}
public String getOrigin() {
return origin;
}
public void setOrigin(String origin) {
this.origin = origin;
}
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
public String getWayPoints() {
return wayPoints;
}
public void setWayPoints(String wayPoints) {
this.wayPoints = wayPoints;
}
}
The Web Service Implementation bean has some constants and some instance variables with default values injected by the Spring context configuration file.
The implemented methods, after reading the request values make use of two classes, HttpUtils and XmlUtils, that I've implemented to call the Google Services using the HttpConnection, Stream, and XML related APIs to parse the response of the Google services.
These are the two utils classes that I am talking about:
The util class related to Http:
package com.faeddalberto.googlews.utils;
public class HttpUtils {
public static String loadStringXmlFromHttp(
String urlString) throws IOException, MalformedURLException {
URL url = null;
BufferedReader reader = null;
StringBuilder stringBuilder = null;
try {
url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// just want to do an HTTP GET here
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.connect();
// read the output from the server
reader = new BufferedReader(
new InputStreamReader(connection.getInputStream()));
stringBuilder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line + "\n");
System.out.println(line);
}
} finally {
// close the reader; this can throw an exception too, so
// wrap it in another try/catch block.
if (reader != null) {
try {
reader.close();
} catch (IOException ioe) {
System.err.println("IOException " + ioe.getMessage());
}
}
}
return stringBuilder.toString();
}
public static byte[] loadByteArrayImageFromHttp(String urlString) throws IOException {
URL url = new URL(urlString); //Get an input stream for reading
HttpURLConnection hc = null;
hc = (HttpURLConnection) url.openConnection();
hc.setRequestMethod("GET");
hc.setDoOutput(true);
hc.connect();
InputStream in = new BufferedInputStream(hc.getInputStream());
try {
ByteArrayOutputStream bout = new ByteArrayOutputStream(10000);
int b;
while ((b = in.read()) != -1) {
bout.write(b);
}
return bout.toByteArray();
} finally {
in.close();
}
}
}
Next picture shows the browser with the resulting WSDL of the service calling http://127.0.0.1:8080/googlews/googleservices.wsdl. In the picture below we can only see the first part of the WSDL, containing the types associated with the service:
The next thing I want to show you are the tests of the different web service operations and the images resulting from the calls. I have used jUnit to test the service methods:
In this post I
describe the basic techniquies for implementing SOA components using the REST
paradigm. REST stands for REpresentational State Transfer.
REST and SOAP are quite different. SOAP is a
messaging protocol, whereas REST is a style of software architecture for
distributed hypermedia systems; that is, systems in which text, graphics,
audio, and other media are stored across a network and interconnected
through hyperlinks. A resource in the RESTful
sense is anything that has an URI; that is, an identifier that satisfies
formatting requirements. The formatting requirements are what make URIs uniform. Recall, too, that URI
stands for Uniform Resource Identifier; hence, the notions of URI and resource are intertwined.In practice, a resource is an informational item
that has hyperlinks to it. In the Web, HTTP is both a transport protocol and a messaging system
because HTTP requests and responses are messages. The payloads of HTTP messages
can be typed using the MIME type system, and HTTP provides response status
codes to inform the requester about whether a request succeeded and, if not,
why.
In a RESTful request
targeted at a resource, the resource itself remains on the service machine. The
requester typically receives a representation of
the resource if the request succeeds. It is the representation that transfers
from the service machine to the requester machine.
RESTful web services
require not just resources to represent but also client-invoked operations on
such resources. RESTful services are stateless; each request
from client to server must contain all the information necessary to understand
the request, and cannot take
advantage
of any stored context on the server.
At the core of the
RESTful approach is the insight that HTTP, despite the occurrence of Transport in its name, is an API and not simply a transport
protocol. HTTP has its well-known verbs,
officially known as methods. In the REST model, the only verbs allowed are GET,
POST, PUT, and DELETE.
HTTP verb Meaning in CRUD
terms
POST Create a new resource from the request data
GET Read a resource
PUT Update a resource from the request data
DELETE Delete a resource
HTTP also has standard
response codes, such as 404 to signal that the requested resource
could not be found, and
200 to signal that the request was handled successfully.
In short, HTTP provides
request verbs and MIME types for client requests and status
codes (and MIME types)
for service responses.
A key guiding principle
of the RESTful style is to respect the original meanings of the HTTP verbs. In
particular, any GET request should be side effect-free because a GET is a read rather
than a create, update,
or delete operation.
The RESTful approach tries
to simplify matters by taking what HTTP, with its MIME type system, already offers:
built-in CRUD operations, uniformly identifiable resources, and typed
representations that can capture a resource’s state. REST as a design
philosophy tries to isolate application complexity at the endpoints, that is,
at the client and at the service.
A service may require
lots of logic and computation to maintain resources and to generate
adequate representation
of resources—for instance, large and subtly formatted XML documents—and a
client may require significant XML processing to extract the desired
information from the XML representations transferred from the service to the client.
Yet the RESTful approach keeps the complexity out of the transport level, as a resource
representation is transferred to the client as the body of an HTTP response message.
XML and @WebServiceProvider
The @WebServiceProvider signals that the exchanged messages will be XML documents of some type, a notion captured in the phrase raw XML. In a RESTful request/response service, the service response is raw XML but the incoming request might not be XML at all. A GET request does not have a body; hence,
arguments sent as part of the request occur as attributes in the query string, a collection of key/value pairs. Here is a sample:
The question mark (?) begins the query string, and the attributes are key/value pairs separated by ampersands (&). The order of attributes in the query string is arbitrary; for instance, the year attribute could occur first in the query string without changing the meaning of the request.
By contrast, a POST request does have a body, which can be an arbitrary XML document instead of a SOAP envelope.
The structure of the XML is critical for defining how applications interact with each other in an SOA-style Web Services infrastructure.
It is a fundamental principle of systems integration design that applications must interact across well-defined interfaces.In my example that comes next you can see how XML Schema can be used to define the structure of a Movie. In this manner, any system that needs to insert a new Movie can map whatever form
their data is in to the movies.xsd schema and send it as a message to the movies service. The following two concepts provide the foundation for SOA-style integration using Web Services:
1. XML documents are used to exchange messages between applications. 2. XML Schema documents define the application interfaces.
Point (2) tells you that, even when using RESTful services (without WSDL and SOAP), the schema of the XML documents being exchanged between SOA components can be used to define interfaces for the services. Unfortunately, this use of XML Schema to formalize interfaces for RESTful services is not universally accepted as part of the REST framework.
In my RESTful service I followed an XML Schema that defines the types allowed to be exchanged from client and service. When developing SOA systems based on XML, it is important to employ XML Schema to validate documents. Just as a relational database management system allows you to impose constraints on data values and format within the database schema, so XML Schema can be used to validate the integrity of XML documents. XML Schema is important for maintaining data quality and integrity when sharing information among multiple systems. Next picture shows the XML Schema to exchange data between client and service:
As you can see, the XML Schema defines three complexType elements: actor that I than use in a list to compose the cast of a movie, movie that defines the stored informations about a movie and movies that is the global type containing a list of movies.
Following this Schema elements I created the associated Java classes: the Movie and the Actor classes:
package com.faeddalberto.restfulmovies.movie.types;
import java.util.List;
public class Movie {
private String title;
private String director;
private String genre;
private List actors;
private Integer year;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDirector() {
return director;
}
public void setDirector(String director) {
this.director = director;
}
public String getGenre() {
return genre;
}
public void setGenre(String genre) {
this.genre = genre;
}
public List getActors() {
return actors;
}
public void setActors(List actors) {
this.actors = actors;
}
public Integer getYear() {
return year;
}
public void setYear(Integer year) {
this.year = year;
}
}
package com.faeddalberto.restfulmovies.movie.types;
public class Actor {
private String name;
private String surname;
private String nationality;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
}
Both are simple Java POJO classes containing instance variables and getter & setter methods. The Movie class has a List of Actors and some other simple variables related to a Movie and the Actor class has some simple instance variables related to Actor.
A service annotated with @WebServiceProvider implements the Provider interface, which requires that the invoke method:
public Source invoke(Source request)
be defined. This method expects a Source of bytes (for instance, the bytes in an XML document that represents the service request) and returns a Source of bytes (the bytes in the XML response). When a request arrives, the infrastructure dispatches the request to the invoke method, which handles the request in some service-appropriate way.
The next picture shows an example of xml containing movies informattions belonging to the elements defined in the XML Schema that I showed you previously:
Note the use of namespaces in this example. The Movies element is from the namespace http://faeddalberto.com/movie/schemas. When developing SOA systems with XML, it is important to
use namespaces, because documents originating from different systems may use the same tags (e.g., “movie”), and it is important to interpret the tag in the proper context.
A RESTful Movies Service
The RESTful service that I am going to show you honors GET and POST requests.
The service is a container of movies informations stored in the XML file that I showed you previously.
In the next pictures I'll show you the code divided in different pieces, from the class declaration and annotations, the invoke method, the methods that serve the client requests, methods used to create the responses to the clients, the XML parsing. Than I'll show you the client used to query the service and how to deploy the service in a Application Server.
Let's start with the web service class declaration:
@WebServiceProvider
@ServiceMode(value = javax.xml.ws.Service.Mode.MESSAGE)
@BindingType(value = HTTPBinding.HTTP_BINDING)
public class RESTfulMoviesService implements Provider {
@Resource
WebServiceContext wsCtx;
private static final String fileName = "movies.xml";
private Map moviesMap;
private List movies;
private byte[] moviesBytes;
public RESTfulMoviesService() {
readMoviesFromFile(); // read the raw file bytes from movies.xml
deserialize(); // deserialize to a List
}
The class below is a WebServiceProvider rather than the more usual SOAP-based WebService. The service implements the generic Provider interface rather than a customized SEI with designated @WebMethods. There are two ServiceModes: PAYLOAD, the default, signals that the service wants access only to the underlying message payload (e.g., the body of an HTTP POST request); MESSAGE signals that the service wants access to entire message (e.g., the HTTP headers and body).
The HTTP_BINDING for the Binding Type opposed to the SOAP_BINDING used for SOAP web services to annunce that the web service deals with raw XML instead of SOAP over HTTP.
We have also the Injection of the resource WebServiceContext that is useful to extract the message context of the user request.
The other instance variables are useful to load and store the Movies from the XML document during the deserialization process that starts during web service initialization. Next picture shows the methods involved in the deserialization and intialization process:
private void deserialize() {
URI nsURI = null;
try {
nsURI = new URI("fa:movies");
}
catch(URISyntaxException e) { System.err.println(e); }
Source source = new StreamSource(new ByteArrayInputStream(moviesBytes));
movies = new MoviesParser().parseMovies(source, nsURI.toString());
moviesMap = Collections.synchronizedMap(new HashMap());
for(Movie movie: movies)
moviesMap.put(movie.getTitle(), movie);
}
private void readMoviesFromFile() {
try {
String path = getFilePath();
int len = (int) new File(path).length();
moviesBytes = new byte[len];
new FileInputStream(path).read(moviesBytes);
} catch(IOException e) {
System.err.println("IOException " + e);
}
}
private String getFilePath() {
String cwd = System.getProperty("user.dir");
String sep = System.getProperty("file.separator");
return "resources" + sep + fileName;
}
The method readMoviesFromFile() loads the file and reads its input stream to be stored in the byte array moviesBytes. In the deserialize() method we create a Source object containing the previously loaded byte array of movies. Calling the MovieParser class I then parse the Source object and create the List and Map storing the movies loaded from file. I'll show you later the MovieParser class used to read the movies informations. Let's now start giving a view on how the invoke method looks like:
public Source invoke(Source request) {
if(wsCtx == null)
throw new RuntimeException("Error injecting web service context");
MessageContext msgCtx = wsCtx.getMessageContext();
String httpVerb = (String) msgCtx.get(MessageContext.HTTP_REQUEST_METHOD);
httpVerb = httpVerb.trim().toUpperCase();
if(httpVerb.equals("GET")) return doGet(msgCtx);
else if(httpVerb.equals("POST")) return doPost(request);
else throw new HTTPException(405); //method not allowed
}
This method handles incoming requests and generates the response. I get the MessageContext to find the request method (GET or POST) and then call the associate method to serve the client request.
Let's see how I handle the GET request:
private Source doGet(MessageContext msgCtx) {
String queryString = (String) msgCtx.get(MessageContext.QUERY_STRING);
// get all DVDs
if(queryString == null)
return new StreamSource(new ByteArrayInputStream(moviesBytes));
else {
String title = getValueFromQs("title", queryString);
if(!moviesMap.containsKey(title))
throw new HTTPException(404); // movie not found
Movie movie = moviesMap.get(title);
// generate XML and return
ByteArrayInputStream is = new ByteArrayInputStream(responseXMLMovies(movie));
return new StreamSource(is);
}
}
private byte[] responseXMLMovies(Movie movie) {
Document doc = new MoviesParser().createXMLMovies(movie);
return encodeToStream(doc);
}
private byte[] encodeToStream(Document doc) {
Source source = new DOMSource(doc);
ByteArrayOutputStream out = null;
try {
out = new ByteArrayOutputStream();
Result result = new StreamResult(out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
System.err.println("TransformerConfigurationException " + e);
} catch (TransformerException e) {
System.err.println("TransformerException " + e);
}
return out.toByteArray();
}
private String getValueFromQs(String attribute, String queryString) {
String[] parts = queryString.split("=");
if(!parts[0].equalsIgnoreCase(attribute))
throw new HTTPException(400); // bad request
return parts[1].trim();
}
The first method in the picture is the doGet which I pass the MessageContext object to get the query string. If no query string has been used to query the web service, I do return the entire XML file content that I previously stored in the byte array after creating a StreamSource from it.
If a query string exists I get the value of the "title" parameter, using the getValueFromQs() method, that represents the Movie title. Once had its value, I check if that title exist on the stored Map:
if it does not exist I do throw an HTTPException(404): movie not found.
if it exists, I get the stored Movie informations from the Map and call the responseXMLMovies() method which uses the MovieParser class to create an XML document and returns it as DOMSource, created by the encodeToStream() method, to be sent as response to the client.
Let's now see how the POST request from the client will be handled by my service:
private Source doPost(Source request) {
if(request == null) throw new HTTPException(400); // bad request
URI nsURI = null;
try {
nsURI = new URI("fa:movies");
}
catch(URISyntaxException e) { System.err.println(e); }
List newMovie = new ArrayList();
newMovie = new MoviesParser().parseMovies(request, nsURI.toString());
if(moviesMap.containsKey(newMovie.get(0).getTitle()))
throw new HTTPException(400); // bad request
// add the new movie to the in-memory map and save the list into the file
movies.add(newMovie.get(0));
moviesMap.put(newMovie.get(0).getTitle(), newMovie.get(0));
serialize();
ByteArrayInputStream is = new ByteArrayInputStream(
responseConfirmation("Movie - " + newMovie.get(0).getTitle() + " - created"));
return new StreamSource(is);
}
private byte[] responseConfirmation(String msg) {
Document doc = new MoviesParser().createXMLConfirmation(msg);
return encodeToStream(doc);
}
private byte[] encodeToStream(Document doc) {
Source source = new DOMSource(doc);
ByteArrayOutputStream out = null;
try {
out = new ByteArrayOutputStream();
Result result = new StreamResult(out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
System.err.println("TransformerConfigurationException " + e);
} catch (TransformerException e) {
System.err.println("TransformerException " + e);
}
return out.toByteArray();
}
private void serialize() {
Document doc = new MoviesParser().createXMLMovies(movies);
try {
Source dom = new DOMSource(doc);
String path = getFilePath();
File file = new File(path);
Result result = new StreamResult(file);
//write the DOM document to the file
Transformer xmlSaver = TransformerFactory.newInstance().newTransformer();
xmlSaver.transform(dom, result);
}catch (TransformerConfigurationException e) {
System.err.println("TransformerConfigurationException " + e);
} catch (TransformerException e) {
System.err.println("TransformerException " + e);
}
}
The first method in the picture above is the doPost which I pass the Source parameter because the HTTP POST request has a body containing the user request to be treated as a Create. Inside the body request I have a new Movie to be saved into the XML file therefore I need to parse the Source object to get it.
Before parsing the Source object I create a Namespace URI which the movies elements belong to.
Once parsed I create a List of Movies containing the Movie passed by the client and add it into the Map before serializing all its content into the movies.xml file. Once the serialization process is done I return a confirmation to the client as a confirmation creating a new DOMSource containing only a confirmation string tag with the inserted Movie title. To create this source I do use the same encodeToStream() method used for the GET request.
Parsing XML
Now that I showed you how to handle GET and POST requests, I want to
show you the class MovieParser used to parse the XML file and load Movie
informations and to create DOM Document to be used as responses to be sent to the
clients. I used JAX-P APIs to read and write XML documents.
The first code snapshot that I show you is used to parse and get Movie
informations from an XML document:
public List parseMovies(Source result, String uri) {
DOMResult domResult = new DOMResult();
List movies = new ArrayList();
String movieTitle = null, movieDirector = null, movieGenre = null,
actorName = null, actorSurname = null, actorNationality = null;
Double movieYear = null;
try {
Transformer trans = TransformerFactory.newInstance().newTransformer();
trans.transform(result, domResult);
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
xp.setNamespaceContext(new NSResolver("fa", uri));
NodeList resultList = (NodeList)xp.evaluate("movies/movie",
domResult.getNode(), XPathConstants.NODESET);
int len = resultList.getLength();
for(int i = 0; i < len; i++) {
Movie movie = new Movie();
List actors = new ArrayList();
Node movieNode = resultList.item(i);
if (movieNode.getNodeType() == Node.ELEMENT_NODE) {
Element movieElmnt = (Element) movieNode;
movieTitle = getNodeValue(movieElmnt, "title");
movieDirector = getNodeValue(movieElmnt, "director");
movieGenre = getNodeValue(movieElmnt, "genre");
NodeList actorsList = movieElmnt.getElementsByTagName("actor");
for(int j = 0; j < actorsList.getLength(); j++) {
Node actorsNode = actorsList.item(j);
Actor actor = new Actor();
if (movieNode.getNodeType() == Node.ELEMENT_NODE) {
Element actorElmnt = (Element) actorsNode;
actorName = getNodeValue(actorElmnt, "name");
actorSurname = getNodeValue(actorElmnt, "surname");
actorNationality = getNodeValue(actorElmnt, "nationality");
actor.setName(actorName);
actor.setSurname(actorSurname);
actor.setNationality(actorNationality);
actors.add(actor);
}
}
movieYear = Double.parseDouble(getNodeValue(movieElmnt, "year"));
}
}
movie.setTitle(movieTitle);
movie.setDirector(movieDirector);
movie.setGenre(movieGenre);
movie.setActors(actors);
movie.setYear(movieYear.intValue());
movies.add(movie);
}
}catch(TransformerConfigurationException e) {
System.err.println("TransformerConfigurationException " + e.getMessage());
}catch(TransformerException e) {
System.err.println("TransformerException " + e.getMessage());
}catch(XPathExpressionException e) {
System.err.println("XPathExpressionException " + e.getMessage());
}
return movies;
}
private String getNodeValue(Element elem, String nodeName) {
NodeList nodeElmntLst = elem.getElementsByTagName(nodeName);
Element nodeElmnt = (Element) nodeElmntLst.item(0);
NodeList nodeLs = nodeElmnt.getChildNodes();
String nodeValue = ((Node) nodeLs.item(0)).getNodeValue();
return nodeValue;
}
After transforming a Source object to a DOMResult object using the Transformation APIs from JAX-P I create an XPath object to read informations from the DOMResult just created.
Using the xpath evaluate() method I get a list of movies from the document treating those as a NodeList through which I iterate to get the informations of each movie found.
Inside the cycle I iterate over the NodeList and get the value for every node using the getNodeValue() method which I pass the name of the Element I want to get the value of and the Element itself.
Iterating over the NodeList I also get another NodelList which is the one having movie Actors informations. This method returns a List of movies containing all the movies informations read from the file included Actors informations for every movie read.
Next task that I am going to show you is how to create a DOM Document from a List of Movies.
This method is used to create a response to be sent to the clients that require informations of a Movie or all the movies stored in the XML file in a GET request:
public Document createXMLMovies(List movies) {
Element rootElement = null;
Document xmlDoc = null;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
DOMImplementation impl = builder.getDOMImplementation();
xmlDoc = impl.createDocument(null, "movies", null);
rootElement = xmlDoc.getDocumentElement();
for(int j = 0; j < movies.size(); j++) {
Element movieElem = xmlDoc.createElementNS(null, "movie");
Element title = createElementWithValue( xmlDoc,
"title", movies.get(j).getTitle());
Element director = createElementWithValue( xmlDoc,
"director", movies.get(j).getDirector());
Element genre = createElementWithValue( xmlDoc,
"genre", movies.get(j).getGenre());
Element year = createElementWithValue( xmlDoc,
"year", ""+movies.get(j).getYear());
Element movieActors = xmlDoc.createElementNS(null, "actors");
for (int i = 0; i < movies.get(j).getActors().size(); i++) {
Element actor = xmlDoc.createElementNS(null, "actor");
Element name = createElementWithValue( xmlDoc, "name",
movies.get(j).getActors().get(i).getName());
Element surname = createElementWithValue( xmlDoc, "surname",
movies.get(j).getActors().get(i).getSurname());
Element nationality = createElementWithValue( xmlDoc, "nationality";
movies.get(j).getActors().get(i).getNationality());
actor = createElement(actor, name, surname, nationality);
movieActors.appendChild(actor);
}
movieElem = createElement(movieElem, title,
director, genre, movieActors, year);
rootElement.appendChild(movieElem);
}
} catch (ParserConfigurationException e) {
System.err.println("ParserConfigurationException " + e.getMessage());
}
//printDomDoc(xmlDoc);
return xmlDoc;
}
private Node createNode(Document xmlDoc, String name, String value) {
Node node = xmlDoc.createTextNode(name);
node.setNodeValue(value);
return node;
}
private Element createElementWithValue(Document xmlDoc, String name, String value) {
Element elem = xmlDoc.createElementNS(null, name);
elem.appendChild(createNode(xmlDoc, name, value));
return elem;
}
private Element createElement(Element elem, Node... nodes) {
for (Node node: nodes) {
elem.appendChild(node);
}
return elem;
}
In the first method I create a new DOM document with a "movies" element as root and with a for interation over the movies List passed as argument I create the elements to add as root element children, the "movie" elements.
The other methods are all utility methods that I use to create the elements and set a value on those using the DOM APIs. After having created all the elements to put into the file I use the appendChild() method to append every movie to the root element of the created document.
The Dispatch Client
Now that I showed you how to parse the XML document and read informations from it, I show you the client that I use to query the web service. It uses the counterpart of the Provider interface: the Dispatcher that has at its own time a invoke method used to manage the request to be sent to the service:
public class DispatchMoviesClient {
private List movies = new ArrayList();
public static void main(String[] args) {
new DispatchMoviesClient().setupAndTest();
}
private void setupAndTest() {
// create identifying name for service and port
URI nsURI = null;
try {
nsURI = new URI("fa:movies");
}
catch(URISyntaxException e) { System.err.println(e); }
QName serviceName = new QName("dvdService", nsURI.toString());
QName portName = new QName("dvdPort", nsURI.toString());
String endpoint = "http://127.0.0.1:8080/movies?title=Cloverfield";
// Now create a service dispatcher
Service service = Service.create(serviceName);
service.addPort(portName, HTTPBinding.HTTP_BINDING, endpoint);
Dispatch dispatch = service.createDispatch(portName,
Source.class, Service.Mode.PAYLOAD);
// send some requests:
// GET request to view the dvd list
invoke(dispatch, "GET", nsURI.toString(), null);
// POST request to insert a new movie
invoke(dispatch, "POST", nsURI.toString(), createMovie());
}
private void invoke(Dispatch dispatch, String verb,
String uri, Movie data) {
Map requestContext = dispatch.getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_METHOD, verb);
Source source = null;
if(data != null) {
source = makeMovieStreamSource(data);
}
// invoke
Source result = dispatch.invoke(source);
if(data == null) {
movies = new MoviesParser().parseMovies(result, uri);
displayGETResult();
}
else {
System.out.println("\nNew movie request: ");
displayPOSTResult(result, uri);
}
}
private Source makeMovieStreamSource(Movie movie) {
Document doc = new MoviesParser().createXMLMovies(movie);
Source source = new DOMSource(doc);
return source;
}
private void displayGETResult() {
if(movies.size() != 0)
System.out.println("Movies: ");
for (int i = 0; i < movies.size(); i++) {
System.out.println("Movie " + i);
System.out.println("\tTitle: " + movies.get(i).getTitle() +
", Director: " + movies.get(i).getDirector() +
", Genre: " + movies.get(i).getGenre() +
", Year: " + movies.get(i).getYear());
System.out.println(" Actors: ");
for(int j = 0; j < movies.get(i).getActors().size(); j++) {
System.out.println("\tName: " + movies.get(i).getActors().get(j).getName()
+ ", Surname " + movies.get(i).getActors().get(j).getSurname()
+ ", Nationality: " + movies.get(i).getActors().get(j).getNationality());
}
}
}
private void displayPOSTResult(Source result, String uri) {
MoviesParser parser = new MoviesParser();
String confirm = parser.parseConfirmation(result, uri);
System.out.println(confirm);
}
private Movie createMovie() {
Movie movie = new Movie();
movie.setTitle("The Job");
movie.setDirector("Kenny Golde");
movie.setGenre("Thriller");
Actor actor1 = new Actor();
actor1.setName("Brad");
actor1.setSurname("Renfro");
actor1.setNationality("American");
Actor actor2 = new Actor();
actor2.setName("Dominique");
actor2.setSurname("Swain");
actor2.setNationality("American");
Actor actor3 = new Actor();
actor3.setName("Eric");
actor3.setSurname("Mabius");
actor3.setNationality("American");
List actors = new ArrayList();
actors.add(actor1);
actors.add(actor2);
actors.add(actor3);
movie.setActors(actors);
movie.setYear(2003);
return movie;
}
}
Skipping the main method that calls the setupAndTest() method, we have the first instructions setting up the informations about the web service to be called. It creates identifying QName instances for a service and a port, creates a service object and adds a port, and then creates a Dispatch proxy associated with the port. This client-side dispatch object can dispatch XML documents as requests to the service as XML Source instances. A document is sent to the service through an invocation of the invoke method.
After these settings we have two requests against the web service: the first is a GET without parameters that receive all the movies stored in the XML file, while the second is a POST request that sends a Movie to be stored. In the invoke method, before invoking the web service through the dispatcher object, I set up the request context inserting the request method (GET or POST), if it is a GET request I don't need to prepare a Source to be sent because the GET request has not a body while if it is a POST request I create the Source to be sent using the same MovieParser that I used for the web service.
Using the dispatch object I call the web service using the invoke method and passing the source object just created, creating a source object into which put the response of the web service.
The other methods of this class are just utility methods used to display the results and to parse the source object using the MovieParser class.
Publishing the WS
Now we need only to publish the web service to allow clients send requests against it. If we want to publish the web service into an application server, differently from the SOAP web service that does not require a web.xml, we need to create a web.xml and another file called sun-jaxws.xml under the WEB-INF folder to publish it.
This is the web.xml:
In the web.xml file I create a listener using a Web Service Listener that waits for calls to the web service and a servlet called RestfulProviderD that then I declare in the sun-jaxws.xml telling it to refer to my web service for HTTP_BINDING requests.
That's all for my RESTful Web Service post.