访问 Web 服务的 Java Swing 客户端:设计详细信息

此解决方案目录项的样例应用程序是一个 Swing 客户端,它提供了一个 GUI,用于将订单文档发送到字符串订单 Web 服务。以下便是该应用程序的屏幕快照:

Swing 应用程序的屏幕快照

此应用程序是用 Netbeans[TM] IDE 4.1 编写的。应用程序中的主类是 MainWindow,它是作为 Swing 框架实现的。它使用委托类 POServiceBD 来调用 Web 服务。

此应用程序可以独立运行,也可以与 Java Web Start 一起运行。此应用程序的 Java Web Start 启用版本在 webstart-client.war 中包含的 Web 应用程序中。

调用 Web 服务

应用程序客户端提供了使用桩模块、动态代理或 DII 调用字符串订单 Web 服务的选项。Web 服务是通过委托类 POServiceBD 调用的。

代码示例 1 说明了如何使用桩模块:

// import stub classes
import
com.sun.j2ee.blueprints.stringposervice.StringPurchaseOrderService_Impl;
import com.sun.j2ee.blueprints.stringposervice.StringPurchaseOrderServiceSEI;

// ... other contents of the class file

  // Using stubs
  StringPurchaseOrderService_Impl svcimpl = new StringPurchaseOrderService_Impl();
  StringPurchaseOrderServiceSEI poservice = svcimpl.getStringPurchaseOrderServiceSEIPort();
  ((Stub)poservice)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY, serviceUrl);
  String result = poservice.submitPO(xmlDocStr);

代码示例 1:使用桩模块调用 Web 服务

代码示例 2 说明了如何使用动态代理:

// import classes for service endpoint interface
import com.sun.j2ee.blueprints.stringposervice_wrapped.StringPurchaseOrderServiceSEI;
import com.sun.j2ee.blueprints.stringposervice_wrapped.SubmitPO;
import com.sun.j2ee.blueprints.stringposervice_wrapped.SubmitPOResponse;

// ... other contents of the class file

  // Using dynamic proxy
  ServiceFactory sf = ServiceFactory.newInstance();
  URL wsdlURL = new URL(serviceUrl + "?WSDL");
  QName serviceQname = new QName(NS_BODY, "StringPurchaseOrderService");
  Service s = sf.createService(wsdlURL, serviceQname);
  QName portQname = new QName(NS_BODY, "StringPurchaseOrderServiceSEIPort");
  StringPurchaseOrderServiceSEI port = (StringPurchaseOrderServiceSEI) s.getPort(portQname, StringPurchaseOrderServiceSEI.class);
  SubmitPO param = new SubmitPO(xmlDocStr);
  SubmitPOResponse response = port.submitPO(param);
  String result = response.getResult();

代码示例 2:使用动态代理调用 Web 服务

SubmitPO 类是从服务 WSDL 生成的,但是需要重新编写该类才能解决 JAX-RPC 编译器中的错误,在编译器中,该类以不同的方式命名其中一个私有字段 (String_1),其名称为 string_1 而不是 String_1。此错误导致 SubmiPO 类无法与动态代理或 DII 一起使用。

代码示例 3 说明了如何使用 DII:

// import classes for service endpoint interface
import com.sun.j2ee.blueprints.stringposervice_wrapped.StringPurchaseOrderServiceSEI;
import com.sun.j2ee.blueprints.stringposervice_wrapped.SubmitPO;
import com.sun.j2ee.blueprints.stringposervice_wrapped.SubmitPOResponse;

// ... other contents of the class file

  // Using DII
  ServiceFactory sf = ServiceFactory.newInstance();
  URL wsdlURL = new URL(serviceUrl + "?WSDL");
  QName serviceQname = new QName(NS_BODY, "StringPurchaseOrderService");
  Service s = sf.createService(wsdlURL, serviceQname);
  QName portQname = new QName(NS_BODY, "StringPurchaseOrderServiceSEIPort");
           
  Call call = s.createCall(portQname);
  call.setTargetEndpointAddress(serviceUrl);
  call.setProperty(Call.SOAPACTION_USE_PROPERTY, new Boolean(true));
  call.setProperty(Call.SOAPACTION_URI_PROPERTY,"");
           
  // For WS-I compliant document-literal, need to set the encoding style
  // to literal by specifying "" as the encoding style,
  // and by setting the operation style to document
  String ENCODING_STYLE_PROPERTY = "javax.xml.rpc.encodingstyle.namespace.uri";
  String URI_ENCODING = "";
  call.setProperty(ENCODING_STYLE_PROPERTY, URI_ENCODING);
  call.setProperty(Call.OPERATION_STYLE_PROPERTY, "document");
           
  // Note that the operation name need not be set by calling
  // call.setOperationName(new QName(NS_BODY, "submitPO"));
  // This is because the SOAP binding used by the Web service is document, not rpc.
           
  // The types for the request parameter and return value are defined in the
  // WSDL file itself, so their qnames are defined with the namespace of the body
  QName requestQname = new QName(NS_BODY, "submitPO");
  QName responseQname = new QName(NS_BODY, "submitPOResponse");
           
  // Define the type of the return value for the DII call.
  // SubmitPOResponse must match the wrapped type sent by the Web service.
  call.setReturnType(responseQname, SubmitPOResponse.class);
           
  // Define the type of the method parameter for the DII call.
  // In the WSDL file, the name of the message part for submitPO is "parameters"
  // Hence the request parameter is defined in this way.
  call.addParameter("parameters", requestQname, SubmitPO.class, ParameterMode.IN);
  SubmitPO param = new SubmitPO(xmlDocStr);
  Object[] params = {param};
           
  // Invoke the DII call
  SubmitPOResponse response = (SubmitPOResponse) call.invoke(params);
           
  String result = response.getResult();

代码示例 3:使用 DII 调用 Web 服务

请注意,由于 Web 服务使用的 SOAP 绑定属于文档样式,因此在调用时不需要设置操作名称。

Jar 签名

Swing 应用程序的 Java Web Start 版本捆绑 JAXRPC 运行环境 jar。由于这些 Jar 文件中包含的某些类要求对系统具有无限制访问权限,因此需要对 Jar 进行签名。这会在客户端上出现安全警告,但是在用户授予权限后,客户端就可以发出 Web 服务调用。

所有应用程序类都捆绑在单个 jar 文件 swing-client.jar 中。其他 Jar 文件用于 JAXRPC 运行环境(这些文件来自 J2EE SDK 安装的 lib/ 目录)。所有这些 Jar 文件都需要通过同一证书进行签名。使用 s1as 证书对 Jar 文件进行签名,该证书存在于 J2EE SDK 缺省域 (domain1) 的密钥库中。代码示例 4 提供了如何对 Jar 文件进行签名的 ant build.xml 片段:

    <!-- use the keystore present in the default domain of the J2EE SDK -->
    <property name="keystore.location" value="${j2ee.home}/domains/domain1/config/keystore.jks"/>
    <!-- we are using the default password used for the keystore. This value should be changed if the keystore password has been changed for the J2EE SDK -->
    <property name="keystore.password" value="changeit"/>
    <property name="key.alias" value="s1as"/>
 
    <!-- sign application jar -->
    <signjar jar="${war.build.dir}/swing-client.jar" signedjar="${war.build.dir}/swing-client-signed.jar" alias="${key.alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
     
    <property name="jaxrpc-webstart-support.dir" value="${war.build.dir}/jws-support"/>
    <!-- sign JAXRPC runtime jars -->
    <signjar jar="${j2ee.home}/lib/j2ee.jar" signedjar="${jaxrpc-webstart-support.dir}/j2ee.jar" alias="${key.alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
    <signjar jar="${j2ee.home}/lib/jaxrpc-api.jar" signedjar="${jaxrpc-webstart-support.dir}/jaxrpc-api.jar" alias="${key.alias}" keystore="${keystore.location}" storepass="${keystore.password}"/>
    <!-- ..... sign other jar files in the same way -->

代码示例 4:对 Jar 文件进行签名的 Ant 生成片段

生成 JNLP 描述符

JNLP 描述符用于调用安装在用户系统上的 Java Web Start 运行环境。JNLP 描述符的内容需要是动态的。例如,jnlp 元素需要代码库属性,该属性是应用程序 jar 文件所在的 URL。我们希望动态发现代码库,以便与部署应用程序 war 模块的 URL 匹配。同样,Swing 客户端需要动态确定字符串订单 Web 服务运行所在的 URL。通过在应用程序 war 模块中捆绑的 JSP 文件生成 JNLP 描述符,可以完成上述操作。
JSP 文件需要输出一个特定内容类型为 application/x-java-jnlp-file 的 XML 文件,以便正确调用 Java Web Start。这可以通过使用一个 JSP 页指令来实现,如代码示例 5 所示:

<%@ page contentType="application/x-java-jnlp-file" info="Swing Client JNLP" %>

代码示例 5:将 contentType 页指令用于 JNLP 描述符

此外,还需要构造代码库 URL。该 URL 由协议类型(httphttps)、服务器名称、服务器端口和应用程序 jar 文件所在的上下文路径组成。如果服务器端口为 80(对于 http)和 443(对于 https),则不需要指定它。代码示例 6 提供了计算代码库的 JSP 代码片段:

<%
  StringBuffer serverurl = new StringBuffer();
  serverurl.append(!request.isSecure() ? "http://" : "https://");
  serverurl.append(request.getServerName());
  if (request.getServerPort() != (!request.isSecure() ? 80 : 443))
  {
    serverurl.append(':');
    serverurl.append(request.getServerPort());
  }
  serverurl.append('/');
  String codebase = serverurl + request.getContextPath() + '/';
%>
<?xml version="1.0" encoding="UTF-8"?>
<%-- JNLP File for launching Swing Client with JavaWebStart --%>
<jnlp spec="1.0+" codebase="<%=codebase%>" href="swing-client.jnlp">

代码示例 6:动态生成代码库

计算字符串订单 Web 服务的 URL 的方式与代码示例 7 所示的方式类似:

<%
  String serviceurl = serverurl + "webservice/StringPurchaseOrderService";
%>
  <!-- ... other JNLP descriptor contents -->
  <resources>
    <!-- ... other resource delcarations -->
    <property name="stringwebservice.url" value="<%=serviceurl%>"/>
  </resources>

代码示例 7:将系统属性传递到 Java Web Start 应用程序

Swing 客户端使用 stringwebservice.url 属性设置 Web 服务 URL,如代码示例 8 所示。

  String serviceurl = System.getProperty("stringwebservice.url");
  if (serviceurl != null) {
    serviceUrlTextField.setText(serviceurl);
  }

代码示例 8:在 Java Web Start 应用程序中检索系统属性

Web 应用程序的欢迎页面 (index.html) 提供了通过 Java Web Start 启动 Swing 应用程序的链接。此链接不直接指向生成 JNLP 描述符的 JSP 文件。而是链接到已映射到 JSP 文件的虚拟 URL swing-client.jnlp。这样做可确保浏览器获取 JNLP 文件的 .jnlp 扩展名,以处理浏览器使用文件扩展名调用 Java Web Start 的情况。通过使用 servlet-mapping 元素(如代码示例 9 所示),可以做到这一点:

  <!-- map the JNLP JSP as a servlet since only servlets can have servlet-mapings -->
  <servlet>
    <servlet-name>jnlp-gen</servlet-name>
    <jsp-file>swing-client-jnlp.jsp</jsp-file>
  </servlet>
  <!-- map the virtual URL /swing-client.jnlp to the JSP file -->
  <servlet-mapping>
    <servlet-name>jnlp-gen</servlet-name>
    <url-pattern>/swing-client.jnlp</url-pattern>
  </servlet-mapping>

代码示例 9:将生成 JNLP 描述符的 JSP 文件映射到 .jnlp 扩展名

© Sun Microsystems 2005。Java BluePrints Solutions Catalog 中的所有内容受版权保护,未经 Sun Microsystems 的明确书面许可,不得在其他产品中发布。