Java Swing Client for Accessing Web Services: Design Details

The sample application for this solutions catalog entry is a swing client that provides a GUI to send a purchase order document to the String Purchase Order Web service. Here is a screenshot of the application:

Screenshot of the Swing application

This application is written with the Netbeans[TM] IDE 4.1. The primary class in the application is MainWindow, which is implemented as a Swing frame. It uses a delegate class POServiceBD to invoke web services.

This application can be run stand-alone as well as with Java Web Start. The Java Web Start enabled version of this application is included in a Web application contained in webstart-client.war

Invoking the Web Service

The application client provides options to invoke the String Purchase Order Web Service using stubs, dynamic proxy, or DII. The Web service is invoked through a delegate class POServiceBD.

Code Example 1 illustrates how stubs are used:

// 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);

Code Example 1: Using Stubs to Invoke a Web Service

Code Example 2 illustrates how dyamic proxies are used:

// 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();

Code Example 2: Using Dynamic Proxy to Invoke a Web Service

The class SubmitPO is generated from the service WSDL, however it needed to be rewritten to workaround a bug in the JAX-RPC compiler where it names one of the private fields (String_1) differently (string_1 instead of String_1). This bug results in the SubmiPO class becoming unusable for use with dynamic proxies or DII.

Code Example 3 illustrates how DII is used:

// 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();

Code Example 3: Using DII to Invoke a Web Service

Note that since the Web service uses document style for SOAP bindings, the operation name need not be set on call.

Jar Signing

The Java Web Start version of the Swing application bundles JAXRPC runtime jars. Since some of the classes contained in these Jar files require unrestricted access to the system, the Jars need to be signed.  This results in a Security warning at the client, but once the user grants the permissions, the client can make Web service calls.

All the application classes are bundled in a single jar file, swing-client.jar. The other Jar files are for the JAXRPC runtime which are taken from the lib/ directory of the J2EE SDK installation. All of these Jar files need to be signed by the same certificate. We sign the Jar files using the s1as certificate present in the keystore of the default domain (domain1) of the J2EE SDK. Code Example 4 presents an ant build.xml fragment illustrating how the Jar files are signed:

    <!-- 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 -->

Code Example 4: Ant Build Fragment for Signing Jar Files

Generating the JNLP Descriptor

The JNLP descriptor is used to invoke the Java Web Start runtime installed on the user system. The contents of the JNLP descriptor need to be dynamic. For example, the jnlp element needs a codebase attribute which is a URL where the application jar files are available. We would like the codebase to be discovered dynamically  to match the URL where the application war module was deployed. Similarly, the Swing client needs to dynamically determine the URL where the String Purchase Order Web Service is running. This can be done by generating the JNLP descriptor through a JSP file bundled in the application war module.
The JSP file needs to output an XML file with the specific content type of application/x-java-jnlp-file so that the Java Web Start gets invoked correctly. This can be done by using a JSP page directive as illustrated in Code Example 5:

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

Code Example 5: Using contentType Page Directive for JNLP Descriptors

The codebase URL needs to constructed as well. The URL consists of the protocol type (http or https), the server name, the server port, and the context path where the application jar files are available. The server port need not be specified if it is 80 for http and 443 for https. Code Example 6 presents a JSP code fragment that calculates the codebase:

<%
  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">

Code Example 6: Dynamically Generating the Codebase

The URL for the String purchase order Web service is calculated in a similar manner as illustrated in Code Example 7:

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

Code Example 7: Passing a System Property to a Java Web Start Application

The Swing client uses the stringwebservice.url property to set the Web service URL as shown in Code Example 8.

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

Code Example 8: Retrieving a System Property in a Java Web Start Application

The welcome page (index.html) of the Web application provides a link to launch the Swing application through Java Web Start. This link does not directly refer to the JSP file that generates the JNLP descriptor. Instead, it links to a virtual URL swing-client.jnlp which is mapped to the JSP file.This is done to ensure that the browser gets the .jnlp extension for the JNLP file to handle the case where a browser uses file extension to invoke Java Web Start. This is done by using a servlet-mapping element as shown in Code Example 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>

Code Example 9: Mapping the JSP File Generating the JNLP Descriptor to .jnlp Extension

© Sun Microsystems 2005. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.