End-to-End Web Service Tutorial: Flower Application
In this tutorial, you first create an EJB module containing
a session bean that makes images available.
Next, you create
a web application
that contains a web service, which delegates
to the EJB module to retrieve the images.
The web service exposes methods that can be used
by clients to access the images provided by the EJB module.
Then you deploy the web application and the EJB module together,
as a unit, by adding them to an enterprise application
and then deploying the enterprise application. At this stage, you are
introduced to GlassFish testing functionality that
is available from the IDE.
Finally, you create a Java application that connects
to the web service to display the images in an album created
from Swing components. In the concluding section,
you are also shown how to optimize
the transfer of images from web services to clients.
At the end of this tutorial, you will have a running Java Swing
application that consumes the EJB module's images via a web
service, with this result:
Creating the EJB Module
The goal of this section is to create an EJB
module that exposes two methods, the first for
providing a single image, the second for
providing all the images. Normally, the images
would come from a database. Since database retrieval is not
the focus of this tutorial, we simply place the images in
a resource folder within our EJB module.
Choose File > New Project (Ctrl-Shift-N). The
New Project wizard appears. Select
EJB Module from the Enterprise category, as below:
Click Next.
Type FlowerAlbum in Project Name, as shown below:
Click Finish.
You now have an EJB module project that looks like this
in the Projects window:
Right-click the FlowerAlbum project node and choose New > Session Bean. Alternatively,
right-click the project node and choose New > Other. In the New File wizard,
choose Session Bean under Enterprise, as shown here:
The New Session Bean wizard appears.
Name the session bean Flower, together
with flower.album as the package name. Make sure to select Stateless and Remote. You
should now see the following:
Click Finish.
The IDE adds a session bean to the Source Packages node, together with a remote interface,
as shown here. In the Enterprise Beans node, a new node is added
for your new FlowerBean:
Expand the Enterprise Beans node, right-click the FlowerBean node,
and choose Add > Business Method, as shown here:
Type the following values in the Business Method dialog:
Name:getFlower
Return Type:byte[]
Click Add. In Name, type name. Leave the other
values unchanged. You should now see the following:
Click the Exceptions tab. Click Add. Type IOException.
You should now see the following:
Click OK. You now have the basis of a method in your bean class,
and a method declaration in the remote interface class.
Call up the Business Method dialog again. This time, enter
the following values:
Name:allFlowers
Return Type:List<byte[]>
You should now see the following:
As before, add IOException to the Exceptions tab.
Click OK.
In the remote interface class, notice that the methods have
successfully been generated by the previous steps:
Look in the bean class and notice that stubs have been
created for the declared methods:
@Stateless
public class FlowerBean implements FlowerRemote {
public byte[] getFlower(String name) throws IOException {
return null;
}
public List<byte[]> allFlowers() throws IOException {
return null;
}
}
Alternatively, instead of using the Business Method dialog
you can manually add code to the bean class and to the
remote interface class. However, if you use the Business Method
dialog, the IDE adds code to both the bean class and the
remote interface class, simultaneously.
Fix imports in the bean and remote interface classes.
In each class, place the cursor anywhere in the code, right-click
to open the context menu, and select Fix Imports. A dialog box opens
showing all necessary imports. Press OK and NetBeans generates the
import statements. Alternatively, press Ctrl-Shift-I in each class to
open the import dialog box.
You have now declared your methods in the remote interface
and implemented stubs in the bean class.
The Projects window should now show two new nodes in the
Enterprise Beans node, for your new methods, as shown here:
Fill out the bean class with the following code, for exposing
a single image and for exposing all images:
@Stateless
public class FlowerBean implements FlowerRemote {
private static final String[] FLOWERS = {"aster", "honeysuckle", "rose", "sunflower"};
public byte[] getFlower(String name) throws IOException {
URL resource = this.getClass().getResource("/flower/album/resources/"+name+".jpg");
return getBytes(resource);
}
public List<byte[]> allFlowers() throws IOException {
List<byte[]> flowers = new ArrayList<byte[]>();
for (String flower:FLOWERS) {
URL resource = this.getClass().getResource("/flower/album/resources/"+flower+".jpg");
flowers.add(getBytes(resource));
}
return flowers;
}
private byte[] getBytes(URL resource) throws IOException {
InputStream in = resource.openStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for(int read; (read = in.read(buf)) != -1;) {
bos.write(buf, 0, read);
}
return bos.toByteArray();
}
}
Create a new subpackage called resources. Copy the
images found at the start of this tutorial into that package, as shown here:
In your code, notice
that the methods getFlower and allFlowers both
make use of the images in this package.
You EJB module is now complete! In the next section, we will create
a web service that will delegate to the EJB module, in order to
retrieve the images at the appropriate points in the code.
Creating the Web Service
The goal of this section is to create a web application
that contains a web service. The web service should delegate
to the EJB module for the retrieval of images. Therefore,
we will need to put the EJB module on the web application's
classpath.
Choose File > New Project (Ctrl-Shift-N). The
New Project wizard appears. Select
Web Application from the Web category. Click
Next. The New Web Application wizard appears.
Type FlowerService in Project Name, as shown below:
Click Finish. The IDE creates a new web application project.
To be able to access our EJB module from the web service that
we will create later,
we will need to put the former on the latter's classpath. First, right-click
the web application's Libraries node and choose Add Project, as
shown below:
Next, browse to the EJB module and select it. You should now see a new
node, for the EJB module, added to the web application's Libraries
node, as below:
Right-click the FlowerService node and choose New > Web Service.
Alternatively, choose New > Other and then select Web Service
under Web Services in the New File wizard, as below:
In the New Web Service wizard, type FlowerService in
Web Service Name and flower.album in Package Name.
Select Create Web Service from Existing Session Bean
and then browse to the EJB module and select it, as below:
Click OK in the Browse Enterprise Bean dialog.
Click Finish in the New Web Service wizard. The IDE adds the infrastructure of a web service
to your application, which includes stubs for the methods
obtained from the EJB module, as shown here:
Click the Design toggle button in the top left corner
of the editor. The Web Service Visual Designer is shown, as
below:
You can use the Web Service Visual Designer
to see the structure of your web service in one
glance. In addition, you can add functionality
to your web service by clicking buttons such
as Add Operation. By using the Quality of Service
section, you can very easily enable advanced features, such
as web service security.
Click the Source toggle button to switch back to
the Source view. Rewrite the class so that it looks as follows:
The web service is now complete, delegating to the EJB module,
and exposing its images.
Testing the Web Service
When you deploy a web service to an application container,
the IDE lets you test the web service to see if it functions
as you expect. The Tester application, provided
by GlassFish and the Sun Java System Application Server,
is available from the IDE for this purpose.
Below, we begin with the creation of an enterprise application.
When we add our EJB module and our web service to the enterprise
application, we will be able to deploy them together, as one unit,
by deploying the enterprise application.
Choose File > New Project (Ctrl-Shift-N). The
New Project wizard appears. Select
Enterprise Application from the Enterprise category, as below:
Click Next.
In Project Name, type FlowerApplication.
Make sure to deselect the Create EJB Module checkbox
and the Create Web Application Module checkbox, because you
do not want to create new modules. You should now see
the following in the wizard:
Click Finish. A new enterprise application is added to the IDE.
Now we need to add our two modules to the application.
Right-click the FlowerApplication Java EE Modules node
and choose Add Java EE Module, as shown below:
Choose both modules, as shown below:
Click OK. You should now see that the two modules are added to the
application, as shown below:
Right-click the FlowerApplication node, choose Properties and type
/FlowerService?Tester in the Relative URL field, as
shown here:
Click OK.
Right-click the FlowerApplication node and choose Run. If the server
is not running, the IDE will start it. Then it will deploy
the application, containing our two modules, to the server. Next,
because of the settings specified in the previous dialog, the
browser will open and display the Tester application, shown below:
Once you see the Tester application, open
the Services window in the IDE, expand the Servers node, and notice that
the IDE added new nodes for the deployed application and its
modules, as shown here:
You now know for sure that the application has been
successfully deployed.
Click WSDL File in the Tester application and notice that
the browser now shows the WSDL file:
Above, the browser shows, among other things, the location of the schema. Put the URL
to the schema in the browser and then you can see the schema file:
Type the name of one of the images, such as
"rose" in the Tester application:
Click the getFlower button and the IDE shows you information
about the invocation in the browser:
When you look at the "Method Returned" above, notice that it is garbled. What we want to have returned
is an image, not a series of symbols that do not make sense. However, since java.awt.Image is not
a valid schema type, we need to manually tweak the schema file to return the type that we want to have returned.
We will do this in the next section.
Tweaking the WSDL File and Schema File
In this section, we add the WSDL file and schema file to our application. Then we tweak them to
interpret arrays of bytes as Images. We also need to
adjust various parts of our application to correctly locate our schema and WSDL file. In the
process, you will be introduced to various tools in the IDE that can help you at this stage.
Expand the web application until you reach the wsdl node:
Note: Currently the node is empty. The WSDL file and schema
are generated at deployment, because we have been using the defaults;
therefore this node, which would normally house them, is empty. Now
that we want to interpret arrays of bytes as Images, we need to provide
our own WSDL file and schema. We will put them in this node.
Right-click the wsdl node, choose New > Other
and use the XML Schema template, shown below, to create
a file called FlowerService.xsd.
Repeat the process,
but this time use the WSDL Document template and create a file called
FlowerService.wsdl.
You should now see the following
in the wsdl node:
Copy the content of the WSDL file in the browser
to the template you created above. If you used the
defaults when creating and deploying the web service,
the WSDL file should be here:
Edit the WSDL file and insert the namespace
declarations. These decarations are necessary for the WSDL to be valid.
Replace the lines at the beginning of the file:
<!--
Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2-hudson-182-RC1.
-->
<!--
Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2-hudson-182-RC1.
-->
<definitions targetNamespace="http://album.flower/" name="FlowerService">
Edit the schema file and insert the namespace
declarations. These decarations are necessary for the schema file to be valid.
Replace the lines at the beginning of the file:
<!--
Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2-hudson-182-RC1.
-->
<xs:schema version="1.0" targetNamespace="http://album.flower/">
Next, we will change the WSDL file so that our
local schema file is referenced instead of the one
that is on-line. Open the WSDL file in the Design mode,
as shown below.
Expand the Types node. Right-click the
Referenced Schema node.Choose Add > Import.
The Add Import dialog opens.
As shown below, you can now browse to the schema file and select it:
Click OK.
Delete the other schema reference, which refers to the online
schema file, which we do not want to refer to anymore, and then
you will see that you can access nodes from the schema file,
indicating that you have now correctly referenced it:
We need to explicitly make the application server use our
own version of the WSDL file, otherwise the application server
will generate its own WSDL file, from our web service's annotations.
You should now see the following in the web service class:
Finally, the point of this whole section is to have a modified
schema file that specifies the expected content type of the
return element. To identify the return element in the schema file,
open the schema file and look at the line below, as well as the
code in line 39, both of which may be on different lines in your own
application:
Keep all the existing elements, but
add the following attributes to both:
You should now see the following in the same lines:
Now, when you redeploy the web service to the Tester
application, and invoke one of the operations, you will
see that an image is correctly returned:
Now that the Tester application has confirmed that images
are correctly being returned, we can create our Swing client to
retrieve and display them.
Note: You can find correctly modified versions of the WSDL and Schema files in the downloaded sample project. Both files are in the folder web/WEB-INF/wsdl.
Creating the Swing Client
The goal of this exercise is to create a client for the web service you previously created and deployed, and then add a GUI interface to that client. The interface displays the images that the web service passes as binary data.
Choose File > New Project (Ctrl-Shift-N). The
New Project wizard appears. Select
Java Application from the Java category. Click
Next. The New Java Application wizard appears.
Type FlowerClient in Project Name and click Finish.
The IDE creates a new Java application project.
Right-click the FlowerClient project node
and choose New > Web Service Client. In the New
Web Service Client wizard, click WSDL URL, paste in the
URL to the WSDL file, and enter the package, as shown below:
Click Finish. The IDE downloads the WSDL file, adds client
stubs for interacting with the web service,
and adds nodes to the Projects window in the Java application project, as shown below:
Add a JFrame to the project and name it
FlowerFrame.
public class Main {
private static int downloadedPictures;
public static void main(String[] args) {
final Map<String,Image> flowers = new HashMap<String,Image>(4);
final Map<String,Image> thumbs = new HashMap<String,Image>(4);
// Show the FlowerFrame:
final FlowerFrame frame = new FlowerFrame(flowers);
frame.setVisible(true);
// The client connects to the service with this code:
flowerclient.FlowerService_Service service = new flowerclient.FlowerService_Service();
final flowerclient.FlowerService port = service.getFlowerServicePort();
Runnable[] tasks = new Runnable[4];
// The web service getFlower operation
// is called 4 times, each in a separate thread.
// When the operation finishes the picture is shown in
// a specific button.
for (int i=0; i<4;i++) {
final int index = i;
tasks[i] = new Runnable() {
public void run() {
try {
// Call the getFlower operation
// on the web service:
Image img = port.getFlower(FlowerFrame.FLOWERS[index]);
System.out.println("picture downloaded: "+FlowerFrame.FLOWERS[index]);
// Add strings to the hashmap:
flowers.put(FlowerFrame.FLOWERS[index],img);
// Call the showFlower operation
// on the FlowerFrame:
frame.showFlower();
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
downloadedPictures++;
}
};
new Thread(tasks[i]).start();
}
// The web service getThumbnails operation is called
// in a separate thread, just after the previous four threads finish.
// When the images are downloaded, the thumbnails are shown at
// the bottom of the frame.
Runnable thumbsTask = new Runnable() {
public void run() {
try {
while (downloadedPictures < 4) {
try {Thread.sleep(100);} catch (InterruptedException ex) {}
}
// Call the getThumbnails operation
// on the web service:
List<Image> images = port.getThumbnails();
System.out.println("thumbs downloaded");
if (images != null && images.size() == 4) {
for (int i=0;i<4;i++) {
thumbs.put(FlowerFrame.FLOWERS[i],images.get(i));
}
frame.setThumbnails(thumbs);
}
} catch (IOException_Exception ex) {
ex.printStackTrace();
}
}
};
new Thread(thumbsTask).start();
}
}
Remove the existing main method in the FlowerFrame class.
The web service is now complete, with code that interacts with
the web service that delegates to the EJB module to exposes its images. Right-click
the client and chose Run. The Swing application should start up and, after a moment,
is filled with the images received from the web service.
Logging and Optimizing the Web Service
JAX-WS provides an easy and effective way to optimize
binary data transfer. It is known as "message optimization",
provided by the Message Transmission Optimization Mechanism (MTOM).
Message optimization is the process of transmitting web service
messages in the most efficient manner. It is achieved in web
service communication by encoding messages prior to
transmission and then de-encoding them when they reach their
final destination. MTOM uses XOP (XML-binary Optimized
Packaging) to transmit binary data to and from the web service.
Enabling MTOM is simply achieved via the
Web Service Designer, as shown in this section and
described here.
To be able to verify, later, that MTOM is working correctly, we
will begin by setting up a logging mechanism on the server. We can
monitor the request and response messages without changing the client
code. We will pass the system property
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true
to the server. This way, we will be able to monitor the
SOAP messages, as well as the HTTP headers, that the
web service is receiving and sending from and to the client.
Open the Admin Console, from the server's node
in the Services window, as shown here:
The Admin Console opens in the browser. Enter your
username and password and press Enter.
In the Admin Console, use the JVM Options section,
shown below, to set this property on the server:
While the server starts up,
look in the Output window and make sure you see the property
amongst the other server output,
as shown below:
Redeploy the enterprise application to the Tester application, invoke an operation
via the Tester application, and notice the Output window again,
showing HTTP requests and responses, because of the logging mechanism
that you enabled in the previous steps:
Now that we have HTTP logging working correctly,
we will optimize the message transfer of our images.
Open the web service in the Web Service Visual Designer
and select "Optimize Transfer of Binary Data (MTOM)":
In the Services window, restart the server.
While the server starts up, notice the following in the Output window:
As indicated in the highlighted lines in the Output window,
by using MTOM we ensure that binary data is
not contained within the SOAP body. Instead, it
is sent as a SOAP attachment, while the attachment is
included in the SOAP message.
To send comments and suggestions, get support, and keep informed on the latest
developments on the NetBeans IDE Java EE development features, join
the mailing list.