blob: dd1de8820213987a68a457c76bdab12d16b685f2 [file] [log] [blame]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head><link rel="stylesheet" href="../../../print.css" type="text/css" media="print">
<!-- -*- xhtml -*- -->
<title>开发 JAX-WS Web 服务客户端 - NetBeans IDE 教程</title>
<link rel="stylesheet" type="text/css"
href="../../../netbeans.css">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="KEYWORDS" content="NETBEANS, TUTORIAL, GUIDE, USER, DOCUMENTATION, WEB SERVICE, WEB SERVICES, SOAP, CLIENT, JAX-WS">
<meta name="AUTHOR" content="Geertjan Wielenga, Jeff Rubinoff">
<meta name="description"
content="An overview, including a simple example, of web service consumption support in NetBeans IDE.">
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>开发 JAX-WS Web 服务客户端</h1>
<p>在本教程中,您将使用 NetBeans IDE 提供的 Web 服务工具来分析拼写检查器 Web 服务,然后构建一个与该服务交互的 Web 客户端。该客户端使用一个 Servlet 类和 Web 页。用户将信息从 Web 页传送到 Servlet。
<p><b>目录</b></p>
<img alt="此页上的内容适用于 NetBeans IDE 7.2、7.3、7.4 和 8.0" class="stamp" src="../../../images_www/articles/73/netbeans-stamp-80-74-73.png" title="此页上的内容适用于 NetBeans IDE 7.2、7.3、7.4 和 8.0">
<ul>
<li><a href="#exploringthefacilities">使用拼写检查器 Web 服务</a>
<ul><li><a href="#creatingtheclient">创建客户端</a></li>
<li><a href="#developingtheclient">开发客户端</a>
<ul>
<li><a href="#codingthejsppage">编码 Web 页</a></li>
<li><a href="#creatingandcodingtheservlet">创建拼写检查器 Servlet 并对其进行编码</a></li>
</ul></li>
<li><a href="#deployingtheclient">部署客户端</a></li></ul></li>
<li><a href="#asynch">异步 Web 服务客户端</a>
<ul><li><a href="#asynch-swing">创建 Swing 窗体</a></li>
<li><a href="#asynch-creatingtheclient">启用异步客户端</a></li>
<li><a href="#asynch-addcode">添加异步客户端代码</a></li>
</ul>
</li>
<li><a href="#applyingwhatyouhavelearned">应用所学知识</a></li>
</ul>
<p><b>要学习本教程,您需要具备以下软件和资源。</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">软件或资源</th>
<th class="tblheader" scope="col">要求的版本</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html" target="_blank">NetBeans IDE</a></td>
<td class="tbltd1">Java EE 下载包</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">Java 开发工具包 (JDK)</a></td>
<td class="tbltd1">版本 7 或版本 8</td>
</tr>
<tr>
<td class="tbltd1">符合 Java EE 规范的 Web 服务器或应用服务器</td>
<td class="tbltd1">Tomcat Web Server 7.0 <br> GlassFish Server Open Source Edition<br> Oracle WebLogic Server
</td>
</tr>
</tbody>
</table>
<p class="alert"><strong>注:</strong>如果使用的是 JDK 6,则需要 JDK 6 Update 7 或更高版本。</p>
<p class="notes">Tomcat 和 GlassFish Server 都可以随同 NetBeans IDE 的 Web 和 Java EE 分发一起安装。此外,也可以访问 <a href="https://glassfish.java.net/download.html">GlassFish Server 下载页</a><a href="http://tomcat.apache.org/download-60.cgi" target="_blank">Apache Tomcat 下载页</a></p>
<p class="alert"><strong>重要说明:</strong>Java EE 项目需要使用 Tomcat 7.x、GlassFish Server 或 Oracle WebLogic Server 12c。</p>
<p>下面显示了您将创建的客户端的外观,其中的所有数据都是从 Web 服务接收到的:</p>
<img alt="拼写检查器报告" border="1" class="margin-around" height="406" src="../../../images_www/articles/72/websvc/ws-client/jaxwsc-spellchecker-report.png" title="拼写检查器报告" width="600">
<p>当学完本教程时,您会发现对于此应用程序,您只需提供要检查的文本,调用 Web 服务上的操作以及呈现结果。IDE 会生成联系 Web 服务以及发送文本所需的全部代码。其余的操作则由拼写检查器 Web 服务来完成。它将标识拼错的单词并提供建议的替代项列表。
<p class="notes">本教程中使用的拼写检查器 Web 服务是由 <a href="http://www.cdyne.com/account/home.aspx" target="_blank">CDYNE 公司</a>提供的。CDYNE 旨在开发、销售并支持一整套数据增强、数据质量和数据分析 Web 服务以及商业智能集成。拼写检查器 Web 服务是 CDYNE 提供的一个 Web 服务。请注意,基于一个或多个 Web 服务的应用程序的功能大小取决于这些 Web 服务的可用性和可靠性。但是,CDYNE 在<a href="http://www.cdyne.com/company/faqs.aspx" target="_blank">常见问题解答</a>中指出,它有一个 100% 可用性目标,并且在发生自然灾害、恐怖行为或其他灾难性事件时,Web 服务流量会传输到其辅助数据中心。CDYNE 在编写本教程以及开发 NetBeans 方面给予了大力支持,NetBeans 要在此特别向其表示感谢。
<h2>使用拼写检查器 Web 服务<a name="exploringthefacilities"></a></h2>
<p>要通过网络使用名为 "consuming" 的 Web 服务,需要创建一个 Web 服务客户端。为了便于创建 Web 服务客户端,NetBeans IDE 提供了一个客户端创建工具,即“Web 服务客户端”向导,利用该向导可以生成用于查找 Web 服务的代码。此外,IDE 还提供了用于对所创建的 Web 服务客户端进行开发的工具,即 "Projects"(项目)窗口中包含节点的工作区域。这些工具包含在 NetBeans IDE 安装的 EE 包中。这些工具为即用型,无需任何插件。</p>
<h3 class="tutorial">创建客户端<a name="creatingtheclient"></a></h3>
<p>在此部分,您将利用向导生成基于 Web 服务 WSDL 文件的 Java 对象。</p>
<ol>
<li>选择 "File"(文件)> "New Project"(新建项目)(在 Windows 和 Linux 中为 Ctrl-Shift-N 组合键,在 MacOS 上为 ⌘-Shift-N 组合键)。在 "Categories"(类别)下,选择 "Java Web"。在 "Projects"(项目)下选择 "Web Application"(Web 应用程序)。单击 "Next"(下一步)。将项目命名为 <tt>SpellCheckService</tt>,然后确保将相应的服务器指定为目标服务器。(有关详细信息,请参考“入门指南”一节。)将其他所有选项保留为默认设置,然后单击 "Finish"(完成)。</li>
<li>在 "Projects"(项目)窗口中,右键单击 <tt>SpellCheckService</tt> 项目节点,选择 "New"(新建)> "Other"(其他),然后在 "New File"(新建文件)向导的 "Web Services"(Web 服务)类别中选择 "Web Service Client"(Web 服务客户端)。单击 "Next"(下一步)。 </li>
<li>选择 "WSDL URL" 并为 Web 服务指定以下 URL:
<p><a href="http://wsf.cdyne.com/SpellChecker/check.asmx?wsdl" target="_blank">http://wsf.cdyne.com/SpellChecker/check.asmx?wsdl</a></p>
<p>如果您处于防火墙的保护下,可能需要指定代理服务器,否则将无法下载 WSDL 文件。要指定代理服务器,请在向导中单击 "Set Proxy"(设置代理)。IDE 的 "Options"(选项)窗口将会打开,在此可为 IDE 设置普遍接受的代理。</p>
</li>
<li>将包名保留为空。默认情况下,将从 WSDL 中获取客户端类包名。在本例中为 <tt>com.cdyne.ws</tt>。单击 "Finish"(完成)。</li>
<li>在 "Projects"(项目)窗口的 "Web Service References"(Web 服务引用)节点中,您将看到以下内容: <br> <img alt="显示 Web 服务引用的 &quot;Projects&quot;(项目)窗口" class="margin-around b-all" height="331" src="../../../images_www/articles/72/websvc/ws-client/ws-refs.png" title="显示 Web 服务引用的 &quot;Projects&quot;(项目)窗口" width="329"></li>
</ol>
<p>"Projects"(项目)窗口显示了一个名为 "check" 的 Web 服务,可以对应用程序执行多次 "CheckTextBody" 和 "CheckTextBodyV2" 操作。这些操作用于检查字符串是否存在拼写错误,并返回要由客户端处理的数据。该服务的 V2 版本不需要身份验证。在本教程中,将使用 <tt>checkSoap.CheckTextBodyV2</tt> 操作。</p>
<p><tt>Generated Sources</tt>(生成的源文件)节点中,您将看到由 JAX-WS“Web 服务客户端”向导生成的客户端桩模块。</p>
<img alt="显示构建节点的包结构的文件视图" class="margin-around b-all" height="420" src="../../../images_www/articles/72/websvc/ws-client/gen-files.png" title="显示构建节点的包结构的文件视图" width="312">
<p>展开 "WEB-INF" 节点及其 "wsdl" 子节点。您将看到 WSDL 文件的本地副本,其名称为 <tt>check.asmx.wsdl</tt></p>
<img alt="显示本地 WSDL 副本和 WEB-INF 中的映射文件的 &quot;Projects&quot;(属性)窗口" border="1" class="margin-around b-all" height="359" src="../../../images_www/articles/72/websvc/ws-client/web-inf.png" width="260">
<p>用于创建客户端的 WSDL 的 URL 已在 <tt>jax-ws-catalog.xml</tt> 中映射到 WSDL 的本地副本。映射到本地副本有几个好处。首先,客户端无需 WSDL 的远程副本即可运行。其次,客户端运行速度将会加快,因为其不需要对远程 WSDL 文件进行解析。最后,更加易于移植。
</p>
<img alt="生成的 jax-ws-catalog 映射文件" border="1" class="margin-around b-all" height="346" src="../../../images_www/articles/72/websvc/ws-client/jax-ws-catalog.png" width="600">
<h3 class="tutorial"><a name="developingtheclient"></a>开发客户端</h3>
<p>您可以通过多种方法来实现 Web 服务客户端。Web 服务的 WSDL 文件限制了您可以向该 Web 服务发送的信息类型,以及您从该 Web 服务接收的信息类型。但是,WSDL 文件不限制您对其所需信息进行传递的<i>方式</i>,以及用户界面所包含的<i>内容</i>。您接下来要构建的客户端实现包含一个 Web 页和一个 Servlet,前者可使用户输入要检查的文本,后者可将这些文本传递至 Web 服务,然后构建一个包含结果的报告。</p>
<h4>编码 Web 页<a name="codingthejsppage"></a></h4>
<p>Web 页将包含一个文本区域和一个按钮,前者可供用户输入文本,后者用于将该文本发送至 Web 服务。根据您选择作为目标服务器的服务器版本不同,IDE 生成了 <tt>index.html</tt><tt>index.jsp</tt> 作为应用程序的索引页。</p>
<ol>
<li>在 "Projects"(项目)窗口中,展开 <tt>SpellCheckService</tt> 项目的 "Web Pages"(Web 页)节点,然后双击索引页(<tt>index.html</tt><tt>index.jsp</tt>)以在源代码编辑器中打开文件。</li>
<li>将以下代码复制并粘贴到索引页的 <tt>&lt;body></tt> 标记中:
<pre class="examplecode">&lt;body&gt;
&lt;form name="Test" method="post" action="SpellCheckServlet"&gt;
&lt;p&gt;Enter the text you want to check:&lt;/p&gt;
&lt;p&gt;
&lt;p&gt;&lt;textarea rows="7" name="TextArea1" cols="40" ID="Textarea1"&gt;&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;
&lt;input type="submit" value="Spell Check" name="spellcheckbutton"&gt;
&lt;/form&gt;
&lt;/body&gt;</pre>
<p>上面列出的代码指定当单击提交按钮时,<tt>textarea</tt> 的内容将被发送至名为 <tt>SpellCheckServlet</tt> 的 Servlet。
</li>
</ol>
<h4>创建 Servlet 并对其进行编码<a name="creatingandcodingtheservlet"></a></h4>
<p>在此部分,您将创建一个与 Web 服务交互的 Servlet。但是,执行交互的代码将由 IDE 提供。因此,您只需处理业务逻辑,即,准备要发送的文本以及对结果进行处理。</p>
<ol>
<li>在 "Projects"(项目)窗口中,右键单击 <tt>SpellCheckService</tt> 项目节点,选择 "New"(新建)> "Other"(其他),然后选择 "Web" > "Servlet"。单击 "Next"(下一步)以打开 "New Servlet"(新建 Servlet)向导。 </li>
<li>将 Servlet 命名为 <tt>SpellCheckServlet</tt>,然后在 "Package"(包)下拉列表中键入 <tt>clientservlet</tt>。单击 "Next"(下一步)。<br> <img alt="显示 servlet 名称和包的新建 Servlet 向导" class="margin-around b-all" height="345" src="../../../images_www/articles/72/websvc/ws-client/name-servlet.png" width="523"> </li>
<li>在 "Configure Servlet Deployment"(配置 Servlet 部署)面板中,注意此 Servlet 的 URL 映射是 <tt>/SpellCheckServlet</tt>。接受默认值,然后单击 "Finish"(完成)。该 Servlet 将在源代码编辑器中打开。 <br> <img alt="在浏览器中显示" border="1" class="margin-around b-all" height="422" src="../../../images_www/articles/72/websvc/ws-client/jaxwsc-servlet.png" width="591">
</li>
<li>将光标置于源代码编辑器中的 <tt>SpellCheckServlet.java</tt><tt>processRequest</tt> 方法主体内,并在该方法的顶部添加一些新行。 </li>
<li>右键单击上一步所创建的空白区域,然后选择 "Insert Code"(插入代码)> "Call Web Service Operation"(调用 Web 服务操作)。在 "Select Operation to Invoke"(选择要调用的操作)对话框中,单击 <tt>checkSoap.CheckTextBodyV2</tt> 操作,如下所示: <br> <img alt="显示 Web 服务引用的 &quot;Projects&quot;(项目)窗口" border="1" class="margin-around b-all" src="../../../images_www/articles/72/websvc/ws-client/insert-ws-ops.png" title="显示 Web 服务引用的 &quot;Projects&quot;(项目)窗口">
<p>单击 "OK"(确定)。</p>
<p><b>注:</b>您也可以将此操作节点直接从 "Projects"(项目)窗口拖放至编辑器中,而不调用以上所示的对话框。
<p><tt>SpellCheckServlet</tt> 类的末尾,将会看到一个用于调用 SpellCheckerV2 服务并返回 <tt>com.cdyne.ws.DocumentSummary</tt> 对象的私有方法。
<pre class="examplecode">private DocumentSummary checkTextBodyV2(java.lang.String bodyText) {<br> com.cdyne.ws.CheckSoap port = service.getCheckSoap();<br> return port.checkTextBodyV2(bodyText);<br>}</pre>
<p>只需使用此方法即可调用 Web 服务上的操作。此外,还需在类的顶部声明以下代码行(粗体显示):
<pre class="examplecode">public class SpellCheckServlet extends HttpServlet {
<strong>@WebServiceRef(wsdlLocation = "http://wsf.cdyne.com/SpellChecker/check.asmx?WSDL")
private Check service;</strong></pre>
<li><tt>processRequest()</tt> 方法的 <tt>try</tt> 块替换为以下代码。以下代码行中的注释说明了每行代码的用途。
<pre class="examplecode">try (PrintWriter out = response.getWriter()) {
<strong> //Get the TextArea from the web page</strong><br> String TextArea1 = request.getParameter(&quot;TextArea1&quot;);<br>
<strong>//Initialize WS operation arguments</strong>
java.lang.String bodyText = TextArea1;
<strong>//Process result</strong>
com.cdyne.ws.DocumentSummary doc = checkTextBodyV2(bodyText);
String allcontent = doc.getBody();
<b>//From the retrieved document summary,
//identify the number of wrongly spelled words:</b>
int no_of_mistakes = doc.getMisspelledWordCount();
<b>//From the retrieved document summary,
//identify the array of wrongly spelled words:</b>
List allwrongwords = doc.getMisspelledWord();
out.println("&lt;html&gt;");
out.println("&lt;head&gt;");
<b>//Display the report's name as a title in the browser's titlebar:</b>
out.println("&lt;title&gt;Spell Checker Report&lt;/title&gt;");
out.println("&lt;/head&gt;");
out.println("&lt;body&gt;");
<b>//Display the report's name as a header within the body of the report:</b>
out.println("&lt;h2&gt;&lt;font color='red'&gt;Spell Checker Report&lt;/font&gt;&lt;/h2&gt;");
<b>//Display all the content (correct as well as incorrectly spelled) between quotation marks:</b>
out.println("&lt;hr&gt;&lt;b&gt;Your text:&lt;/b&gt; \"" + allcontent + "\"" + "&lt;p&gt;");
<b>//For every array of wrong words (one array per wrong word),
//identify the wrong word, the number of suggestions, and
//the array of suggestions. Then display the wrong word and the number of suggestions and
//then, for the array of suggestions belonging to the current wrong word, display each
//suggestion:</b>
for (int i = 0; i &lt; allwrongwords.size(); i++) {
String onewrongword = ((Words) allwrongwords.get(i)).getWord();
int onewordsuggestioncount = ((Words) allwrongwords.get(i)).getSuggestionCount();
List allsuggestions = ((Words) allwrongwords.get(i)).getSuggestions();
out.println("&lt;hr&gt;&lt;p&gt;&lt;b&gt;Wrong word:&lt;/b&gt;&lt;font color='red'&gt; " + onewrongword + "&lt;/font&gt;");
out.println("&lt;p&gt;&lt;b&gt;" + onewordsuggestioncount + " suggestions:&lt;/b&gt;&lt;br&gt;");
for (int k = 0; k &lt; allsuggestions.size(); k++) {
String onesuggestion = (String) allsuggestions.get(k);
out.println(onesuggestion);
}
}
<b>//Display a line after each array of wrong words:</b>
out.println("&lt;hr&gt;");
<b>//Summarize by providing the number of errors and display them:</b>
out.println("&lt;font color='red'&gt;&lt;b&gt;Summary:&lt;/b&gt; " + no_of_mistakes + " mistakes (");
for (int i = 0; i &lt; allwrongwords.size(); i++) {
String onewrongword = ((Words) allwrongwords.get(i)).getWord();
out.println(onewrongword);
}
out.println(").");
out.println("&lt;/font&gt;");
out.println("&lt;/body&gt;");
out.println("&lt;/html&gt;");
}
</pre>
</li>
<li> 您会看到许多错误线和警告图标,指示未找到的类。要在粘贴代码之后修复导入,请按 Ctrl-Shift-I 组合键(在 Mac 上按 ⌘-Shift-I 组合键),或在任意位置单击鼠标右键,然后在打开的上下文菜单中选择 "Fix Imports"(修复导入)。(您可以选择要导入的 List 类。此处将接受默认的 java.util.List。)以下是已导入类的完整列表:
<pre class="examplecode">import com.cdyne.ws.Check;
import com.cdyne.ws.Words;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceRef;</pre>
<p class="notes"><b>注:</b>如果看到“找不到 <tt>com.cdyne.*</tt> 类”的警告内容,请不要担心。当构建项目后,IDE 在解析 WSDL 文件并查找类时,此问题会得到解决。</p>
<p>请注意,尚未处理上面列出的代码中存在的错误。有关详细信息,请参见<a href="#applyingwhatyouhavelearned">应用所学知识</a></p></li>
</ol>
<h3 class="tutorial">部署客户端<a name="deployingtheclient"></a></h3>
<p>IDE 使用 Ant 构建脚本来构建和运行应用程序。此构建脚本是由 IDE 基于您在创建项目时所输入的选项来构建的。您可以在项目的 "Project Properties"(项目属性)对话框(在 "Projects"(项目)窗口中右键单击项目节点,然后选择 "Properties"(属性))中调整这些选项。
<ol>
<li>右键单击项目节点,然后选择 "Run"(运行)。稍后,应用程序将部署并显示上一部分所编码的 Web 页。
<li>输入一些文本,确保其中的某些文本存在拼写错误: <br><img alt="带有要检查文本的 JSP 页" class="margin-around b-all" height="310" src="../../../images_www/articles/72/websvc/ws-client/jaxwsc-spellchecker-form.png" title="带有要检查文本的 JSP 页" width="600">
<li>单击 "Spell Check",然后查看结果: <br><img alt="显示错误的拼写检查器报告" class="margin-around b-all" height="406" src="../../../images_www/articles/72/websvc/ws-client/jaxwsc-spellchecker-report.png" title="显示错误的拼写检查器报告" width="600">
<!--<li>Use the application as described in <a href="#gettingtoknowthesample">Getting to Know the Sample</a>. For
troubleshooting, see <a href="#troubleshooting">Troubleshooting</a>.-->
</ol>
<h2 id="asynch">异步 Web 服务客户端</h2>
<p>默认情况下,NetBeans IDE 创建的 JAX-WS 客户端是同步的。同步客户端会调用对服务的请求,然后在等待响应时挂起其处理。但是,在某些情况下,您希望客户端继续一些其他处理而不是等待响应。例如,在某些情况下,服务可能需要大量时间来处理请求。继续处理而不等待服务响应的 Web 服务客户端称为“异步”。</p>
<p>异步客户端会发出服务请求,然后继续其处理而不等待响应。服务会处理客户端请求,然后在一段时间后返回响应,而客户端则在这段时间内检索响应并继续其处理。</p>
<p>异步客户端会通过“轮询”或“回调”方法使用 Web 服务。在“轮询”方法中,将调用一个 Web 服务方法并反复请求结果。“轮询”是一种阻止操作,因为它会阻止调用线程,所以这就是不在 GUI 应用程序中使用它的原因。在“回调”方法中,您在 Web 服务方法调用期间传递回调处理程序。当结果有效时,将调用该处理程序的 <tt>handleResponse()</tt> 方法。这种方法适用于 GUI 应用程序,因为您不必等待响应。例如,从 GUI 事件处理程序发出调用并立即返回控制权,这样可以使用户界面随时保持响应。轮询方法的缺点是,即使在捕获响应后使用响应,也必须对其进行轮询来查明已将其捕获。 </p>
<p>在 NetBeans IDE 中,通过勾选 Web 服务引用的编辑 Web 服务属性 GUI 中的框,将异步客户端的支持添加到 Web 服务客户端应用程序中。除了具有轮询 Web 服务或传递回调处理程序并等待结果的方法外,开发该客户端的所有其他方面都与同步客户端相同。</p>
<p>此部分的其余内容详述了如何创建 Swing 图形界面并将异步 JAX-WS 客户端嵌入其中。</p>
<h3 id="asynch-swing">创建 Swing 窗体</h3>
<p>在此部分,您将设计 Swing 应用程序。如果不愿意自己设计 Swing GUI,可以<a href="https://netbeans.org/projects/www/downloads/download/webservices%252FAsynchSpellCheckForm.zip" target="_blank">下载预先设计的 JFrame</a>,然后转至<a href="#asynch-creatingtheclient">创建异步客户端</a>中的此部分内容。 </p>
<p>Swing 客户端会获取您键入的文本,将其发送至服务,然后返回错误数和所有错误词语的列表。该客户端还会向您显示每个错误词语和替换该词语的建议,一次只显示一个错误词语。</p>
<img alt="预先设计的 Swing 客户端" class="margin-around b-all" height="437" src="../../../images_www/articles/72/websvc/ws-client/asynch-swing-client.png" width="465">
<p><strong>创建 Swing 客户端:</strong></p>
<ol>
<li>创建新的 Java 应用程序项目。将其命名为 <tt>AsynchSpellCheckClient</tt>。不要为该项目创建 <tt>Main</tt> 类。</li>
<li>在 "Projects"(项目)视图中,右键单击 <tt>AsynchSpellCheckClient</tt> 项目节点并选择 "New"(新建)> "JFrame Form..."(JFrame 窗体...)。</li>
<li>将该窗体命名为 <tt>MainForm</tt>,然后将其放置在包 <tt>org.me.forms</tt> 中。</li>
<li>创建 JFrame 后,打开项目属性。在 "Run"(运行)类别中,将 <tt>MainForm</tt> 设置为主类。 <br><img alt="显示选择 MainForm 作为主类的项目属性" class="margin-around b-all" height="355" src="../../../images_www/articles/72/websvc/ws-client/asynch-main-class.png" width="578"></li>
<li>在编辑器中,打开 <tt>MainForm.java</tt> 的 "Design"(设计)视图。在组件面板中,将三个滚动窗格拖放至 <tt>MainForm</tt> 中。定位滚动窗格并调整其大小。这些窗格将包含要进行检查所键入的文本、所有错误词语以及对某个错误词语提出的建议的文本字段。</li>
<li>将五个文本字段拖放至 <tt>MainForm</tt> 中。将其中的三个拖放至三个滚动窗格中。按如下方式对其进行修改:
<table class="margin-around"><tbody><tr><th colspan="3" align="center" class="tblheader">文本字段</th></tr><tr><th class="tblheader" scope="col">变量名称</th><th class="tblheader" scope="col">在滚动窗格中?</th><th class="tblheader" scope="col">可编辑?</th></tr>
<tr><td class="tbltd1">tfYourText</td><td class="tbltd1" align="center">Y</td><td class="tbltd1" align="center">Y</td></tr>
<tr><td class="tbltd1">tfNumberMistakes</td><td class="tbltd1" align="center">N</td><td class="tbltd1" align="center">N</td></tr>
<tr><td class="tbltd1">tfWrongWords</td><td class="tbltd1" align="center">Y</td><td class="tbltd1" align="center">N</td></tr>
<tr><td class="tbltd1">tfWrongWord1</td><td class="tbltd1" align="center">N</td><td class="tbltd1" align="center">N</td></tr>
<tr><td class="tbltd1">tfSuggestions1</td><td class="tbltd1" align="center">Y</td><td class="tbltd1" align="center">N</td></tr></tbody>
</table></li>
<li>将进度栏拖放至 <tt>MainForm</tt> 中。将该变量命名为 <tt>pbProgress</tt></li>
<li>将两个按钮拖放至 <tt>MainForm</tt> 中。将第一个按钮命名为 <tt>btCheck</tt>,并将其文本更改为 "Check Text" 或 "Check Spelling"。将第二个按钮命名为 <tt>btNextWrongWord</tt>,将其文本更改为 "Next Wrong Word",然后禁用该按钮。</li>
<li>将一些标签拖放至 <tt>MainForm</tt> 中,为应用程序提供一个标题并描述文本字段。</li>
</ol>
<p>将 JFrame 的外观按您喜欢的方式进行排列,然后进行保存。接下来将添加 Web 服务客户端功能。</p>
<h3 id="asynch-creatingtheclient">启用异步客户端</h3>
<p><a href="#creatingtheclient">创建客户端</a>中所述,添加 Web 服务引用。然后编辑 Web 服务属性以启用异步客户端。</p>
<ol>
<li>在 "Projects"(项目)窗口中,右键单击 <tt>AsynchSpellCheckClient</tt> 项目节点,然后选择 "New"(新建)> "Other"(其他)。在新建文件向导中,选择 "Web Services"(Web 服务)> "Web Service Client"(Web 服务客户端)。在“Web 服务客户端”向导中,指定 Web 服务的 URL:
<p><a href="http://wsf.cdyne.com/SpellChecker/check.asmx?wsdl" target="_blank">http://wsf.cdyne.com/SpellChecker/check.asmx?wsdl</a>。接受所有默认值,然后单击 "Finish"(完成)。这与<a href="#creatingtheclient">创建客户端</a>中所述步骤 2 以后的过程相同。</p>
</li>
<li>展开 "Web Service References"(Web 服务引用)节点,然后右键单击 <tt>check</tt> 服务。上下文菜单打开。<br> <img alt="Web 服务引用中检查服务节点的上下文菜单,光标悬停在 &quot;Edit Web Service Attributes&quot;(编辑 Web 服务属性)上" class="margin-around b-all" height="377" src="../../../images_www/articles/72/websvc/ws-client/asynch-edit-ws-attrib.png" width="318"></li>
<li>从上下文菜单中选择 "Edit Web Service Attributes"(编辑 Web 服务属性)。“Web 服务属性”对话框打开。</li>
<li>选择 "WSDL Customization"(WSDL 定制”)签。 </li>
<li>展开 "Port Type Operations"(端口类型操作)节点。展开<strong>第一个</strong> <tt>CheckTextBodyV2</tt> 节点并选择 "Enable Asynchronous Client"(启用异步客户端)。 <br> <img alt="&quot;Web Service Attribute&quot;(Web 服务属性)对话框,其中显示用于允许异步客户端执行端口类型操作的选项" class="margin-around b-all" height="577" src="../../../images_www/articles/72/websvc/ws-client/enable-async-client.png" width="563"></li>
<li>单击 "OK"(确定)。该对话框关闭,并出现一条警告,指出更改 Web 服务属性将会刷新客户端节点。<br> <img alt="警告,指示将刷新生成的类和 WSDL,这会影响使用所生成类的应用程序" class="margin-around b-all" height="175" src="../../../images_www/articles/72/websvc/ws-client/asynch-refresh-node-warning.png" width="468"></li>
<li>单击 "OK"(确定)。该警告信息关闭,并刷新客户端节点。如果展开 "Web Service References"(Web 服务引用)中的 <tt>check</tt> 节点,则会看到现在已具有 <tt>CheckTextBody</tt> 操作的 "Polling"(轮询)和 "Callback"(回调)版本。<br> <img alt="&quot;Projects&quot;(项目)窗口中的异步客户端操作" class="margin-around" height="366" src="../../../images_www/articles/72/websvc/ws-client/asynch-ws-refs.png" width="316"></li>
</ol>
<p>现已为您的应用程序启用了 SpellCheck 服务的异步 Web 服务客户端。 </p>
<h3 id="asynch-addcode">添加异步客户端代码 </h3>
<p>现在,您已经有了异步 Web 服务操作,可将其中一个异步操作添加到 <tt>MainForm.java</tt> 中。</p>
<p><strong>添加异步客户端代码:</strong></p>
<ol>
<li><tt>MainForm</tt> 中,切换到 "Source"(源)视图,然后将以下方法添加到最后一个右花括号的前面。 <br><pre class="examplecode">
public void callAsyncCallback(String text){
}</pre></li>
<li>在 "Projects"(项目)窗口中,展开 <tt>AsynchSpellCheckClient</tt> 的 "Web Service References"(Web 服务引用)节点,并找到 <tt>checkSoap.CheckTextBodyV2 [Asynch Callback]</tt> 操作。<br>
</li>
<li><tt>CheckTextBodyV2 [Asynch Callback]</tt> 操作拖至空的 <tt>callAsyncCallback</tt> 方法主体中。IDE 会生成以下 <tt>try</tt> 块。将此生成的代码与为同步客户端生成的代码进行比较。
<pre class="examplecode">try { // Call Web Service Operation(async. callback)
com.cdyne.ws.Check service = new com.cdyne.ws.Check();
com.cdyne.ws.CheckSoap port = service.getCheckSoap();
// TODO initialize WS operation arguments here
java.lang.String bodyText = "";
javax.xml.ws.AsyncHandler&lt;com.cdyne.ws.CheckTextBodyV2Response&gt; asyncHandler =
new javax.xml.ws.AsyncHandler&lt;com.cdyne.ws.CheckTextBodyV2Response&gt;() {
public void handleResponse(javax.xml.ws.Response&lt;com.cdyne.ws.CheckTextBodyV2Response&gt; response) {
try {
// TODO process asynchronous response here
System.out.println("Result = "+ response.get());
} catch(Exception ex) {
// TODO handle exception
}
}
};
java.util.concurrent.Future&lt;? extends java.lang.Object&gt; result = port.checkTextBodyV2Async(bodyText, asyncHandler);
while(!result.isDone()) {
// do something
Thread.sleep(100);
}
} catch (Exception ex) {
// TODO handle custom exceptions here
}</pre>
<p>在此代码与 Web 服务调用中,您会看到来自 SpellCheck 服务的响应是通过 <tt>AsynchHandler</tt> 对象进行处理的。同时,<tt>Future</tt> 对象会查看是否已返回了结果,然后使线程休眠直至完全返回了结果。</p>
</li>
<li>切换回 "Design"(设计)视图。双击 "Check Spelling" 按钮。IDE 会自动将 "ActionListener" 添加到按钮中,然后切换到 "Source"(源)视图中,光标同时出现在空的 <tt>btCheckActionPerformed</tt> 方法中。</li>
<li>将以下代码添加至 <tt>btCheckActionPerformed</tt> 方法主体。此代码会获取您在 <tt>tfYourText</tt> 字段中键入的文本,使进度栏显示 "waiting for server" 消息,并禁用 <tt>btCheck</tt> 按钮,以及调用异步回调方法。
<pre class="examplecode">private void btCheckActionPerformed(java.awt.event.ActionEvent evt) {
<b>String text = tfYourText.getText();
pbProgress.setIndeterminate(true);
pbProgress.setString("waiting for server");
btCheck.setEnabled(false);
callAsyncCallback(text);</b>
}</pre></li>
<li><tt>MainForm</tt> 类的开头,实例化名为 <tt>nextWord</tt> 的私有 <tt>ActionListener</tt> 字段。此 <tt>ActionListener</tt> 字段是为 Next Wrong Word 按钮提供的,使用该按钮可以前进到错误词语列表中的下一个错误词语,并显示该词语以及更正它的建议。您可以在此处创建私有字段,这样就可以在定义了 <tt>ActionListener</tt> 的情况下取消对其的注册。否则,每次检查新文本时,都将添加一个额外的监听程序并最终出现多个监听程序多次调用 <tt>actionPerformed()</tt> 的情况。这样,该应用程序将无法正常运行。
<pre class="examplecode">public class MainForm extends javax.swing.JFrame {
private ActionListener nextWord;
...</pre></li>
<li>将整个 <tt>callAsyncCallback</tt> 方法替换为以下代码。请注意,最外层的 <tt>try</tt> 块已删除。此代码块是多余的,因为已在方法内添加了更多特定的 <tt>try</tt> 块。并且在代码注释中介绍了对代码所做的其他更改。 <br>
<pre class="examplecode">public void callAsyncCallback(String text) {
com.cdyne.ws.Check service = new com.cdyne.ws.Check();
com.cdyne.ws.CheckSoap port = service.getCheckSoap();
// initialize WS operation arguments here
java.lang.String bodyText = text;
javax.xml.ws.AsyncHandler&lt;com.cdyne.ws.CheckTextBodyV2Response&gt; asyncHandler = new javax.xml.ws.AsyncHandler&lt;com.cdyne.ws.CheckTextBodyV2Response&gt;() {
public void handleResponse(final javax.xml.ws.Response&lt;com.cdyne.ws.CheckTextBodyV2Response&gt; response) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Create a DocumentSummary object containing the response.
// Note that getDocumentSummary() is called from the Response object
// unlike the synchronous client, where it is called directly from
// com.cdyne.ws.CheckTextBody<br>
com.cdyne.ws.DocumentSummary doc = response.get().getDocumentSummary();
<br>
//From the retrieved DocumentSummary,
//identify and display the number of wrongly spelled words:
<br> final int no_of_mistakes = doc.getMisspelledWordCount();
String number_of_mistakes = Integer.toString(no_of_mistakes);
tfNumberMistakes.setText(number_of_mistakes);
<br>
// Check to see if there are any mistakes
if (no_of_mistakes &gt; 0) {
<br> //From the retrieved document summary,
//identify the array of wrongly spelled words, if any:
<br> final List&lt;com.cdyne.ws.Words&gt; allwrongwords = doc.getMisspelledWord();
<br>
//Get the first wrong word
String firstwrongword = allwrongwords.get(0).getWord();
<br> //Build a string of all wrong words separated by commas, then display this in tfWrongWords
<br> StringBuilder wrongwordsbuilder = new StringBuilder(firstwrongword);
for (int i = 1; i &lt; allwrongwords.size(); i++) {
String onewrongword = allwrongwords.get(i).getWord();
wrongwordsbuilder.append(", ");
wrongwordsbuilder.append(onewrongword);
}
String wrongwords = wrongwordsbuilder.toString();
tfWrongWords.setText(wrongwords);
<br> //Display the first wrong word
tfWrongWord1.setText(firstwrongword);
<br>
//See how many suggestions there are for the wrong word
int onewordsuggestioncount = allwrongwords.get(0).getSuggestionCount();
<br>
//Check to see if there are any suggestions.
if (onewordsuggestioncount &gt; 0) {
<br> //Make a list of all suggestions for correcting the first wrong word, and build them into a String.
//Display the string of concactenated suggestions in the tfSuggestions1 text field
<br>
List&lt;String&gt; allsuggestions = ((com.cdyne.ws.Words) allwrongwords.get(0)).getSuggestions();
String firstsuggestion = allsuggestions.get(0);
StringBuilder suggestionbuilder = new StringBuilder(firstsuggestion);
for (int i = 1; i &lt; onewordsuggestioncount; i++) {
String onesuggestion = allsuggestions.get(i);
suggestionbuilder.append(", ");
suggestionbuilder.append(onesuggestion);
}
String onewordsuggestions = suggestionbuilder.toString();
tfSuggestions1.setText(onewordsuggestions);
} else {
// No suggestions for this mistake
tfSuggestions1.setText("No suggestions");
}
btNextWrongWord.setEnabled(true);
<br> // See if the ActionListener for getting the next wrong word and suggestions
// has already been defined. Unregister it if it has, so only one action listener
// will be registered at one time.
<br> if (nextWord != null) {
btNextWrongWord.removeActionListener(nextWord);
}
<br> // Define the ActionListener (already instantiated as a private field)
nextWord = new ActionListener() {
<br> //Initialize a variable to track the index of the allwrongwords list
int wordnumber = 1;
public void actionPerformed(ActionEvent e) {
if (wordnumber &lt; no_of_mistakes) {
<br> // get wrong word in index position wordnumber in allwrongwords
String onewrongword = allwrongwords.get(wordnumber).getWord();
<br> //next part is same as code for first wrong word
<br> tfWrongWord1.setText(onewrongword);
int onewordsuggestioncount = allwrongwords.get(wordnumber).getSuggestionCount();
if (onewordsuggestioncount &gt; 0) {
List&lt;String&gt; allsuggestions = allwrongwords.get(wordnumber).getSuggestions();
String firstsuggestion = allsuggestions.get(0);
StringBuilder suggestionbuilder = new StringBuilder(firstsuggestion);
for (int j = 1; j &lt; onewordsuggestioncount; j++) {
String onesuggestion = allsuggestions.get(j);
suggestionbuilder.append(", ");
suggestionbuilder.append(onesuggestion);
}
String onewordsuggestions = suggestionbuilder.toString();
tfSuggestions1.setText(onewordsuggestions);
} else {
tfSuggestions1.setText("No suggestions");
}
<br> // increase i by 1
wordnumber++;
<br> } else {
// No more wrong words! Disable next word button
// Enable Check button
btNextWrongWord.setEnabled(false);
btCheck.setEnabled(true);
}
}
};
<br> // Register the ActionListener
btNextWrongWord.addActionListener(nextWord);
<br> } else {
// The text has no mistakes
// Enable Check button
tfWrongWords.setText("No wrong words");
tfSuggestions1.setText("No suggestions");
tfWrongWord1.setText("--");
btCheck.setEnabled(true);
}
} catch (Exception ex) {
ex.printStackTrace();
}
<br> // Clear the progress bar
pbProgress.setIndeterminate(false);
pbProgress.setString("");
}
});
}
};
java.util.concurrent.Future result = port.checkTextBodyV2Async(bodyText, asyncHandler);
while (!result.isDone()) {
try {
<br>
//Display a message that the application is waiting for a response from the server
tfWrongWords.setText("Waiting...");
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
}
}
}</pre></li>
<li>按 Ctrl-Shift-I 组合键(在 Mac 上按 ⌘-Shift-I 组合键)并修复导入。这样会添加以下 import 语句:
<pre class="examplecode">import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;</pre></li>
</ol>
<p>现在,您可以构建并运行应用程序了!遗憾的是,您不可能在获取服务器响应的很长延迟时间里看到所发生的变化,因为服务的处理速度相当快。 </p>
<h2><a name="applyingwhatyouhavelearned"></a>应用所学知识</h2>
<p>现在,您已在 IDE 中创建了第一个 Web 服务客户端,接下来该您大显身手,对应用程序进行全方位扩展了。下面建议了两项可着手执行的任务。</p>
<ul>
<li>在 Servlet 中添加错误处理代码。</li>
<li>重写客户端,以使用户可与从 Web 服务返回的数据进行交互。
</li>
</ul>
<br>
<div class="feedback-box" ><a href="/about/contact_form.html?to=3&amp;subject=Feedback:%20JAX-WS%20Clients%20in%20NetBeans%20IDE">发送有关此教程的反馈意见</a></div>
<br style="clear:both;" >
<!-- ======================================================================================= -->
<h2><a name="seealso"></a>另请参见</h2>
<p>有关使用 NetBeans IDE 开发 Java EE 应用程序的更多信息,请参见以下资源:
</p>
<ul>
<li><a href="jax-ws.html">JAX-WS Web 服务入门指南</a></li>
<li><a href="rest.html">REST 风格的 Web 服务入门指南</a></li>
<li><a href="wsit.html">Advanced Web Service Interoperability</a>(高级 Web 服务互操作性)</li>
<li><a href="../../trails/web.html">Web 服务学习资源</a></li>
</ul>
<p>要发送意见和建议、获得支持以及随时了解 NetBeans IDE Java EE 开发功能的最新开发情况,请<a href="../../../community/lists/top.html">加入 nbj2ee@netbeans.org 邮件列表</a></p>
</body>