
Asynchronous JAX-WS Web Service Client End-to-End Scenario
Contributed and maintained
by Jaroslav Pospisil, Milan Kuchtiak, and Geertjan Wielenga
This document takes you through the basics of using the IDE to develop a JAX-WS
web service client that consumes a live web service asynchronously. The live web
service is the eSynaps Web Service Search service.
It returns URLs that point to web services containing a given search string. For example, if you send the string "weather" to the
web service, a list of URLs to web services providing weather information is returned. In this tutorial we use a
simple Java class in a Java SE application as the client.
Asynchronous web service clients consume web services either through the "polling" approach or the "callback" approach.
In the "polling" approach, you invoke a web service method and repeatedly ask for the result. Polling is a blocking
operation because it blocks the calling thread, which is why you do not want to use it in a GUI application.
In the "callback" approach you pass a callback handler during the web service method invocation. The handler's handleResponse()
method is called when the result is available. This approach is suitable to GUI applications because you do not have to wait for
the response. For example, you make a call from a GUI event handler and return control immediately, keeping the user interface responsive.
The drawback of the polling approach
is that, even though the response is consumed after it is caught, you have to poll
for it to find out that it has been caught. Therefore,
we will use the callback approach, which continues as normal
until the response comes back. The handler then takes the response and handles it.
We will use this approach in our scenario to handle the URLs found by the web service. First we'll parse the URLs to remove XML tags and
then we'll display them in a JTextArea.
Once we have a working application, we will migrate it
to the NetBeans Platform, allowing us to inherit the NetBeans Platform's
modular architecture, as well as the GUI features available for
rich-client application development in the IDE.
Expected duration: 45 minutes
Software Needed for the Scenario
Before you begin, you need to install the following software on your computer:
Table of Contents
Installing and Configuring the Working
Environment
Install NetBeans 5.5 and run the IDE. Since this scenario uses a web service client
in a Java SE project, there is no need to have the Sun Java System Application Server 9.0
installed.
Creating a Java SE Application and a Web Service Client
In order to create a web service client, we need either a Java SE application,
an EJB module, or a web module first.
Here we will use a Java SE application.
- Choose File > New Project (Ctrl-Shift-N). Select Java Application from
the General category. Click Next.
- Name the project AsyncWSClient. Unselect the Create Main Class checkbox. Click Finish.
- In the Projects window, right-click the AsyncWSClient project node and
choose New > File/Folder (Ctrl-N). In the New File wizard, choose Web Services from
the Categories list and Web Service Client from the File Types list. Click Next.
- Select WSDL URL and type or paste in the following WSDL URL:
http://www.esynaps.com/WebServices/SearchWS.asmx?WSDL
- Type org.me.wsc in
Package. Click Finish.
The Projects window displays the new web service client, within
the Web Service References node:
- Right click on SearchWS and select Edit Web Service Attributes.
The Edit Web Service Attributes editor appears.
- In the editor, expand Search. Select the
Enable Asynchronous Client checkbox, as shown below:
- Click OK.
Now this dialog box appears:
- Click OK.
Observe the Output window and note that the IDE is invoking Ant targets to generate
the necessary JAX-WS client-side artifacts for you.
Asynchronous Interaction with a Web Service
Now that we have used the IDE to generate the web service client for consuming the SearchWS web service, we can continue.
Officially, the SearchWS web service has only one operation, Search, which searches the web for URLs,
using the search word that we give it as a request parameter. However, the execution of this operation
could take some time. Therefore, if we consume the result
in a standard synchronous way and simply wait, our workflow could be interrupted. Therefore, we
will consume the result asynchronously, which is the
purpose of this tutorial.
We have now completed the first step—we have enabled the web service client to allow
asynchronous operation calling. If you expand the SearchWS node, you see
there are now three operations, as shown below:
As discussed in the introduction, we will use the Search [Async Callback] operation
shown in the screenshot above. You can find more detailed information about the differences
between the two asynchronous approaches at
https://jax-ws.dev.java.net/jax-ws-20-fcs/docs/asynch.html.
Designing the User Interface
To enable the user to specify a search string, send it to the web service, and see the
result, we need to create a user interface. The NetBeans GUI Builder, also known as Matisse,
makes this part of our application very easy to create.
- Right-click the AsyncWSClient project and choose New > File/Folder (Ctrl-N).
In the Java GUI Forms category, choose JFrame Form, and click Next.
Name the form MainForm and type org.me.forms in Package.
Click Finish.
- Add these controls from the Component Palette (Ctrl-Shift-8) and change
their properties as listed in the table below:
| Component |
Property |
Value |
| JLabel |
Text |
Enter seach text: |
| JTextField |
Text |
|
|
Variable Name |
tfWord |
| JButton |
Text |
Search |
|
Variable Name |
btSearch |
| JProgressBar |
stringPainted |
Enabled |
|
Variable Name |
pgProgress |
| JLabel |
Text |
URLs found: |
| JTextArea |
background |
[204,204,204] |
|
editable |
Not enabled |
|
lineWrap |
Enabled |
|
Variable Name |
taResults |
Note: Use the Properties window (Ctrl-Shift-7) to change the properties above.
All the properties above can be changed in the Properties tab of the Properties window,
except for the variable name, which is set in the Code tab. However, an easier way to modify the
variable name is to right-click the component and then use the Change Variable Name menu item.
Also note that even though you put a JTextArea straight onto the form,
the GUI builder puts that JTextArea into a JScrollPane.
- Reorder and resize the components until the user interface looks as follows:
Adding Business Logic
We will interact with the web service by using the asynchronous callback approach provided
by JAX-WS in Java EE 5, as discussed in the introduction to this tutorial.
We will use an asynchronous method in our scenario to handle the URLs found by the web service. First we'll
parse the URLs to remove XML tags and
then we'll display them in our JTextArea.
- To add code that parses the returned URLs, click the Source button on the upper left side of the Source Editor and then scroll down to
the end of file. Just above the final bracket that ends the class, add this method:
private String removeTags(String str){
String text = str;
text = text.replaceAll("<Results><url>","");
text = text.replaceAll("</url><url>","/\n");
text = text.replaceAll("</url></Results>","");
return text;
}
Usually you would use an XML parser to parse the result, but for purposes of this tutorial String.replace() is enough.
- Secondly, add the asynchronous client code. Below the method you added in the previous
step, add this one:
public void callAsyncCallback(String word){
}
- In the Projects window, expand Web Service References > SearchWS > WebSearchWS
> WebSearchWSSoap, as shown below:
- Drag and drop the Search [Async Callback] node from the Projects window into the
callAsyncCallback method that you created in step 2 above.
After you have dropped it, without any additional coding on your part, you should see this:
public void callAsyncCallback(String word) {
try { // Call Web Service Operation(async. callback)
org.me.wsc.WebSearchWS service = new org.me.wsc.WebSearchWS();
org.me.wsc.WebSearchWSSoap port = service.getWebSearchWSSoap();
// TODO initialize WS operation arguments here
java.lang.String keyWord = "";
javax.xml.ws.AsyncHandler<org.me.wsc.SearchResponse> asyncHandler = new javax.xml.ws.AsyncHandler<org.me.wsc.SearchResponse>() {
public void handleResponse(javax.xml.ws.Response<org.me.wsc.SearchResponse> response) {
try {
// TODO process asynchronous response here
System.out.println("Result = "+ response.get());
} catch(Exception ex) {
// TODO handle exception
}
}
};
java.util.concurrent.Future<? extends java.lang.Object> result = port.searchAsync(keyWord, asyncHandler);
while(!result.isDone()) {
// do something
Thread.sleep(100);
}
} catch (Exception ex) {
// TODO handle custom exceptions here
}
}
Note: There is an alternative way to let the IDE generate
the above code snippet for you. Within the callAsyncCallback method,
right-click in the Source Editor and then choose Web Service Client Resources > Call Web Service Operation.
In the dialog box that appears, select the Search [Async Callback] operation and click OK.
- Now modify the generated code snippet to pass the word that is entered
in the JTextField to the web service and put the result in the JTextArea. Also,
make sure to schedule your Swing calls inside the event dispatching thread (AWT). The try/catch
block that was generated for you is not needed in this scenario. The easiest way to make this change
is to copy/paste the code below:
public void callAsyncCallback(String word){
org.me.wsc.WebSearchWS service = new org.me.wsc.WebSearchWS();
org.me.wsc.WebSearchWSSoap port = service.getWebSearchWSSoap();
javax.xml.ws.AsyncHandler<org.me.wsc.SearchResponse> asyncHandler = new javax.xml.ws.AsyncHandler<org.me.wsc.SearchResponse>() {
public void handleResponse(final javax.xml.ws.Response<org.me.wsc.SearchResponse> response) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
String results = "";
try {
results = response.get().getSearchResult();
results = removeTags(results);
taResults.setText(results);
} catch(Exception ex) {
ex.printStackTrace();
}
pgProgress.setIndeterminate(false);
pgProgress.setString("");
btSearch.setEnabled(true);
}
});
}
};
port.searchAsync(word, asyncHandler);
}
- Finally, we need an import statement for the javax.swing.Utilities class. Let the
IDE generate this import statement for you by right-clicking in the Source Editor and choosing
Fix Imports (Alft-Shift-F).
Hooking the User Interface to the Business Logic
Next, we connect the logic we provided in the previous section to our user interface. The connection
to the web service is made when the user clicks the JButton. When the button is clicked, the progress bar is activated,
and the callback method is invoked.
- In the Design View, right click the JButton and then
select Events > Action > ActionPerformed, as shown below:
The editor switches to the
Source view and opens in the btSearchActionPerformed method.
- Insert the highlighted code shown below:
private void btSearchActionPerformed(java.awt.event.ActionEvent evt) {
String word = tfWord.getText();
pgProgress.setIndeterminate(true);
pgProgress.setString("waiting for server");
btSearch.setEnabled(false);
callAsyncCallback(word);
}
We first set the progress bar to indeterminate mode, to let it run continuously, and then we set its text to
"waiting for server". Then the asynchronous callback method is called and the word retrieved from the
JTextField is passed to it.
Running the Application
Our simple Swing application is now complete and
we can try it out.
- Right-click the project and choose Run Project. You are prompted to set org.me.forms.MainForm
as your main class. Click OK.
The application is built and run.
- Write a word, such as "weather" in the JTextField, and then click Search.
The progress bar becomes active, as shown below:
After a few seconds, the progress bar stops and the
results appear in the JTextArea, as shown below:
If the JTextArea stays empty after the progress bar
stops, click Search again to repeat the search.
The Internet server, where the web service that is used in this scenario is stored,
is sometimes congested by requests from clients.
Note: If you are behind a firewall, the application automatically knows the correct
proxy server and proxy port to use. When you created the web service client, the system
settings were written to the run.jvmargs property in the project.properties file.
If you right-click the project in the Projects window, choose Properties, and then click the Run category,
you can also see the generated values in the VM Options field. If needed, you can change them.
Extending and Distributing the Application
Now that we have a complete, functioning application, it is time to think about how to extend it
quickly and efficiently. For example, maybe we want to add a Google
toolbar to our application. A simple approach for allowing us to do that is
to move the application to the NetBeans Platform. Once it is on the NetBeans Platform,
the application will be able to have its own Update Center wizard, identical to the IDE's
Update Center wizard under the Tools menu, that will let us
add external modules, such as the Google toolbar module.
We also need to think about strategies for distributing our application. When we
build a Java project, the IDE creates a JAR file in the application's dist folder.
Maybe we want to provide an executable file instead, or in addition to, the JAR file.
Another way of launching the
application is via a browser, using Java Web Start Technology.
When we move the application to the NetBeans Platform, the IDE is able to support
our application in a number of ways, which includes creating a ZIP distribution and
a web startable application, via one click on a menu item.
Wrapping the Infrastructure
Our simple Swing application contains several artifacts that we can make
available to the NetBeans Platform via "wrapper" modules. A wrapper module is a
module that contains no code, but places libraries on an application's classpath.
We need to place several JAR files on our application's classpath. Firstly, we need
the JAX-WS client-side artifacts that the IDE generated for us. Next, we need the
Java EE 5 JAR files that we referenced in our JFrame Form. Once we have these JAR
files on the classpath, we can use them in our NetBeans Platform application.
- Right-click the project and choose Build Project. In the Files window (Ctrl-2), look in the dist
folder and notice a JAR file called AsyncWSClient.jar. Among other files, the JAR file contains the JAX-WS
client-side artifacts that we used for
interacting with the web service in the previous sections.
We will now create a module suite project to provide the framework of our new rich-client application.
- Choose File > New Project (Ctrl-Shift-N) and choose Module Suite Project from the
NetBeans Plug-in Modules category. Click Next. Create a Module Suite project called WebServiceLocator. Click Finish.
The Projects window shows the project structure for your new WebServiceLocator application. The project
structure represents the skeleton of an application built on top of the NetBeans Platform.
Now we need to add the JAR file containing the web service client artifacts to our application.
- In the Projects window, expand the WebServiceLocator project, right-click on Modules,
choose Add New Library and
create a Library Wrapper Module project. In the Library textfield, browse to the dist
folder that contains the JAR file
discussed in step 1 above. You should see something like the following:
Select the AsyncWSClient.jar
shown in the screenshot above. Do not worry about a license file at this point. Click Next.
- Keep the default name AsyncWSClient. Click Next and then click Finish.
You have now provided a JAR file wrapper and placed it on the application's
classpath so that the JAX-WS client-side artifacts are available for interacting with the web service.
- As described in the previous step, create another Library Wrapper Module project. This time wrap all the JAR files
from inside the lib folder shown in the screenshot above.
Note: Select all the JAR files that
you find in the lib folder, by using the Ctrl and Shift keys together with your mouse.
The JARs in the lib folder are needed because
they provide the JAX-WS infrastructure required by the business logic
you will provide in the next section.
Adding the User Interface and Business Logic
In this section, you recreate the user interface that you built in the first part of this tutorial.
However, instead of creating it in a JFrame Form, you will be creating it in a TopComponent.
A TopComponent
is a class belonging to the NetBeans APIs. It embeds a visual
component in NetBeans IDE, or in an application built on the NetBeans Platform, such as the one that you are now creating.
Creating the user interface on a TopComponent is just as easy as providing it on a JFrame Form, because
both are supported by the intuitive GUI Builder, also known as Matisse. Once you have the TopComponent, you need to provide the business logic
for connecting to the JAX-WS client-side artifacts that you wrapped at the start of this section. Finally, you need to hook the user interface
to the business logic, exactly as done before.
- Choose File > New Project and choose Module Project from the
Netbeans Plug-in Modules category. Click Next. Create a module project called AsyncWSClientUI.
Make sure to let the wizard add the new module to the module suite, and select Set as Main Project, as shown below:
Click Next.
- In the Basic Module Configuration panel, type any code name base you like, such as org.netbeans.modules.asyncwsclientui.
Click Finish.
Once you have clicked Finish at the end of the wizard, right-click the AsyncWSClientUI project node, choose Properties,
and in the Sources tab set the source level to 1.5,
because we will use generics in our code. When prompted to do so, you can choose to enable warnings, if you like,
although doing so is not important for this tutorial scenario.
- Right-click the AsyncWSClientUI project node and choose New > Window Component.
Create a Window component and, in the first page of the wizard, specify that
it should be located in the "editor" window position and that it should
open on application start. Specify AsyncWSClient for the Class Name Prefix.
It is not necessary to specify an icon. Click Finish.
- In the Bundle.Properties file, change the CTL_AsyncWSClientTopComponent key as follows:
CTL_AsyncWSClientTopComponent=Window
- Refer back to the following previous subsections to recreate the user interface
and the underlying business logic:
Note: You will see several lines underlined in red. These lines indicate
that there are code dependencies that have not been declared. In the next steps, you
will declare the required dependencies.
- Right-click the AsyncWSClientUI project node, choose Properties and, in the Project
Properties dialog box, choose Libraries. Click Add Dependency. Add the
following dependencies, if they are not already added: AsyncWSClient, JAX-WS-JARS,
Swing Layout Extensions integration, UI Utilities API, Utilities API, Window System API.
Repeat this step for the AsyncWSClient project, but this time only add JAX-WS-JARS.
- Right-click the WebServiceLocator project and choose Clean and Build All.
This will build the whole application and should result in a BUILD SUCCESSFUL message in the Output window.
Tweaking and Running the Application
We now have a complete application, consisting of three parts—the core of the application,
which is the NetBeans Platform; all the modules provided by NetBeans IDE; and, finally, our
own modules—the two wrapper modules and the module that provides the user interface
and business logic. However, we do not need the modules provided by NetBeans IDE and so,
before going further, we will exclude these modules from our application. Then,
because we want our application to resemble the original application as closely as possible,
we will also remove all the menus and toolbars provided by the NetBeans Platform
by default. Also, we will set a title for the application's titlebar. We will run our
application from inside NetBeans IDE and test it out and discover that the
application is a fully functioning web service client for the web service
that we worked with in the first part of this tutorial.
- Right-click WebServiceLocator project node, choose Properties, and click Application in the
Project Properties dialog box. Select Create Standalone Application. Click Exclude, as shown
below:
Change Application Title to "Web Service Locator", by adding spaces to "WebServiceLocator".
Click OK.
- Expand AsyncWSClientUI, expand Important Files, expand XML Layer, expand
<this layer in context>, expand Menu Bar. Select the contents of Menu Bar (Ctrl-Mouse
on the first one, then Shift-Mouse on the last one). Right-click, choose Delete, and click Yes when
prompted to confirm deletion. Wait a few moments while the IDE adds tags to the layer.xml file.
Repeat the same process in the Toolbars folder. In the XML view of the XML Layer file,
notice that the following tags have been added:
<folder name="Menu">
<file name="Edit_hidden"/>
<file name="File_hidden"/>
<file name="GoTo_hidden"/>
<file name="Help_hidden"/>
<file name="Tools_hidden"/>
<file name="View_hidden"/>
<file name="Window_hidden"/>
</folder>
<folder name="Toolbars">
<file name="Edit_hidden"/>
<file name="File_hidden"/>
<file name="Memory_hidden"/>
<file name="Standard.xml_hidden"/>
</folder>
Note: Instead of using the Delete menu item as described in this step, you could just
paste the above tags straight into the layer.xml file.
- Right-click the Web Service Locator application and choose Run. The application
starts up, shows the default NetBeans Platform splash screen and then displays your new application.
Note: The first time that you run the application, it will be very large. That is because
it uses the NetBeans Platform's default resolution. If you resize the application when it is running,
the settings are saved. However, if you clean the project, by using either the Clean or Build and Clean
project commands, your settings are lost and the application's resolution will return to the
NetBeans Platform's default settings.
Try out your application and notice that it works in the same way as your original application, except that now
it is running on the NetBeans Platform, as shown below:
If the window component doesn't open automatically or if you accidentally close it before taking
the steps that follow, just insert this in the
layer.xml file and then restart the application from inside the IDE and open the window component from
the newly created Client menu:
<folder name="Menu">
<folder name="Client">
<file name="org-netbeans-modules-asyncwsclientui-AsyncWSClientAction.shadow">
<attr name="originalFile" stringvalue="Actions/Window/org-netbeans-modules-asyncwsclientui-AsyncWSClientAction.instance"/>
</file>
</folder>
</folder>
Then, remove the above tags from the layer.xml file and run the application again. The menu
will not be there anymore, but the window component will be open, because its opened state was
saved when you opened it via
the menu item.
- In the Files window (Ctrl-2), expand Web Service Locator, expand branding, and then
expand modules. Expand org-netbeans-core-windows.jar and continue expanding until
you reach the Bundle.properties file. Open the file and notice these key-value pairs:
CTL_MainWindow_Title=Web Service Locator {0}
CTL_MainWindow_Title_No_Project=Web Service Locator {0}
Now remove the {0} from each line. By doing this, you remove the number that appears
in the titlebar in the screenshot in step 3 above. When you run the application again, and try it out,
you should see the following:
Creating a Distribution
Once the basic application is complete, you can provide a distribution for it.
The distribution can either be in the form of a ZIP file, as discussed below, or
as a web startable application.
- Right-click the Web Service
Locator application and choose Build ZIP Distribution. The IDE runs an Ant target. The Output window shows
where the ZIP file is created.
- In your filesystem, locate the ZIP file and unzip it. In the distribution's etc folder,
there is a CONF file. Open this file and set the JDK. For example, set it as follows:
jdkhome="C:\Program Files\Java\jdk1.5.0_06"
- Next, add -J-Dnb.tabs.suppressCloseButton=true to the default_options variable. This will hide
the "x" in the top right corner of the window component so that the user cannot close it.
Now the CONF file looks as follows, with the changes in bold:
# ${HOME} will be replaced by JVM user.home system property
default_userdir="${HOME}/.${APPNAME}/dev"
default_mac_userdir="${HOME}/Library/Application Support/${APPNAME}/dev"
# options used by the launcher by default, can be overridden by explicit
# command line switches
default_options="-J-Xms24m -J-Xmx64m -J-Dnetbeans.logger.console=true -J-ea -J-Dnb.tabs.suppressCloseButton=true"
# default location of JDK/JRE, can be overridden by using --jdkhome <dir> switch
jdkhome="C:\Program Files\Java\jdk1.5.0_06"
# clusters' paths separated by path.separator (semicolon on Windows, colon on Unices)
#extra_clusters=
- Run the application from the executable in the bin folder. Notice that there is now no "x" in the top
right corner of the tab:
Adding a Google Toolbar to the Application
One of the modules that the IDE provides adds the "Update Center" menu item to the Tools menu.
When the user selects this menu item, the Update Center wizard is opened. Using this wizard, modules
can be selected and installed in an application, letting the user extend and enrich an application
easily and efficiently.
- Create the Google toolbar.
- To be able to install it, or any other module, right-click the Web Service
Locator application and choose Properties. In the Project Properties dialog box, click Libraries.
Expand nb5.5 and select Update Centers.
- Next, expand platform6 and select Auto Update. Click OK to exit
the Project Properties dialog box.
- In the layer.xml file, delete the
tag that hides the Tools menu:
<file name="Tools_hidden"/>
- Right-click the project and choose
Clean and Build All. Right-click and choose Run Project.
- In the Tools menu, choose Update Center and use it to install modules. For example,
if you install the Google toolbar, and provide the external HTML browser with its
dependent modules to the application, your application could look like this:
|
|