当前页面: 开发资料首页 → Netbeans 专题 → Asynchronous JAX-WS Web Service Client End-to-End Scenario
摘要: 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
Before you begin, you need to install the following software on your computer:
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.
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.
The Projects window displays the new web service client, within the Web Service References node:
Now this dialog box appears:
Observe the Output window and note that the IDE is invoking Ant targets to generate the necessary JAX-WS client-side artifacts for you.
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.
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.
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.
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.
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.
public void callAsyncCallback(String word){ }
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.
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); }
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.
The editor switches to the Source view and opens in the btSearchActionPerformed method.
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.
Our simple Swing application is now complete and we can try it out.
The application is built and run.
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.
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.
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.
We will now create a module suite project to provide the framework of our new rich-client application.
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.
Select the AsyncWSClient.jar shown in the screenshot above. Do not worry about a license file at this point. Click Next.
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.
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.
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.
Click Next.
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.
CTL_AsyncWSClientTopComponent=Window
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.
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.
Change Application Title to "Web Service Locator", by adding spaces to "WebServiceLocator". Click OK.
<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.
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.
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:
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.
jdkhome="C:\Program Files\Java\jdk1.5.0_06"
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=
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.
<file name="Tools_hidden"/>