站内搜索: 请输入搜索关键词

当前页面: 开发资料首页Netbeans 专题Asynchronous JAX-WS Web Service Client End-to-End Scenario

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

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.

  1. Choose File > New Project (Ctrl-Shift-N). Select Java Application from the General category. Click Next.
  2. Name the project AsyncWSClient. Unselect the Create Main Class checkbox. Click Finish.
  3. 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.
  4. Select WSDL URL and type or paste in the following WSDL URL:

    http://www.esynaps.com/WebServices/SearchWS.asmx?WSDL

  5. Type org.me.wsc in Package. Click Finish.

    The Projects window displays the new web service client, within the Web Service References node:

  6. Right click on SearchWS and select Edit Web Service Attributes. The Edit Web Service Attributes editor appears.
  7. In the editor, expand Search. Select the Enable Asynchronous Client checkbox, as shown below:

  8. Click OK.

    Now this dialog box appears:

  9. 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.

  1. 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.
  2. 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.

  3. 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.

  1. 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.

  2. Secondly, add the asynchronous client code. Below the method you added in the previous step, add this one:
    public void callAsyncCallback(String word){
    
    }
  3. In the Projects window, expand Web Service References > SearchWS > WebSearchWS > WebSearchWSSoap, as shown below:

  4. 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.

  5. 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);
    
    }
  6. 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.

  1. 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.

  2. 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.

  1. 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.

  2. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  1. 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.

  2. 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.

  3. 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.
  4. In the Bundle.Properties file, change the CTL_AsyncWSClientTopComponent key as follows:

    CTL_AsyncWSClientTopComponent=Window
  5. 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.

  6. 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.
  7. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  1. 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.
  2. 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"
  3. 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=
  4. 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.

  1. Create the Google toolbar.
  2. 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.
  3. Next, expand platform6 and select Auto Update. Click OK to exit the Project Properties dialog box.
  4. In the layer.xml file, delete the tag that hides the Tools menu:

    <file name="Tools_hidden"/>
  5. Right-click the project and choose Clean and Build All. Right-click and choose Run Project.
  6. 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:





↑返回目录
前一篇: Adding Java Management Extensions (JMX) Instrumentation to a Java Application
后一篇: Blackberry Development Using NetBeans Mobility