JAX-WS client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part 2: Creating a proxy client

This series introduces you to JAX-WS 2.0, the new programming model supported in the Web Services Feature Pack for WebSphere Application Server V6.1. Part 2 guides you through creating a dynamic proxy client.

Nikhil Thaker (nikhil.v.thaker@us.ibm.com), Staff Software Engineer, IBM

Nikhil Thaker photoNikhil Thaker is a Staff Software Engineer with IBM Software Group, and a member of the WebSphere product team that developed Web services feature pack. He has more than nine years of experience in Enterprise Application Integration, and has focused on Web services for the past two years. He has worked with various IBM customers as an IT Specialist in Enterprise Application Integration in IBM Global Services. His industry exposure includes Automotive, Health Care, telecommunication and utilities. You can reach Nikhil at nikhil.v.thaker@us.ibm.com.



Dan Sedov (sedov@us.ibm.com), Staff Software Engineer, IBM

Dan Sedov photoDan Sedov is a Staff Software Engineer performing function testing in IBM Software Group for the Web services component of WebSphere Application Server. For the past two years, Dan has been a member of the WebSphere product team that developed and tested the first Web Services Feature Pack. He has worked on creating and automating JAX-WS Web services engine tests. You can reach Dan at sedov@us.ibm.com.



19 September 2007

Also available in Chinese Russian

Introduction

Develop skills on this topic

This content is part of a progressive knowledge path for advancing your skills. See Building and deploying JAX-WS web services

Part 1 of this article series gave you an overview of Java API for XML Web Services (hereafter called JAX-WS) client APIs and explained how to use the JAX-WS programming model to create a Dispatch client. In this article, you'll learn how to create a dynamic proxy client. You'll learn how to:

  1. Generate JAX-WS artifacts using available tools
  2. Create a dynamic proxy client
  3. Configure the request context
  4. Invoke SEI operations to get create an account, withdraw money, and check the account balance

JAX-WS 2.0 dynamic proxy clients

Dynamic proxies provide access to service endpoint interfaces (SEIs) at run time without requiring static generation of a stub class. A JAX-WS implementation handles proxies using the Java™ 2 Platform, Standard Edition (J2SE) 5.0 dynamic proxy feature. A dynamic proxy is a class that implements a list of interfaces and is generated at runtime by the Java Runtime Environment (JRE). Thus, JAX-WS clients do not have stubs -- a major advantage over JAX-RPC. JAX-WS dynamic proxies always implement javax.xml.ws.BindingProvider, therefore a proxy, like a Dispatch client, is also referred to as a BindingProvider.

An example

For this example, we'll use a simple bank application to demonstrate different ways to use dynamic proxy clients. We'll create a new account, withdraw funds, and view account information:

The following WSDL defines a BankingService with a single AccountsPort port. The portType BankingSEI defines a three operations createAccount, withdraw, and getAccountInfo.

Listing 1. WSDL definition
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BankingService"
   xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://www.example.com/services/Banking"
   xmlns:types="http://www.example.com/schemas/Banking"
   targetNamespace="http://www.example.com/services/Banking">

   <types>
      <schema
		xmlns="http://www.w3.org/2001/XMLSchema"
	 	targetNamespace=”http://www.example.com/schemas/Banking”>

		<!--
		definition of createAccount operation request and response beans
		-->
		<element name=”createAccount”>
			<complexType>
				<sequence>
					<element name=”owner” type=”string” />
				<element name=”initialBalance” type=”double” />
				</sequence>
			</complexType>
		</element>

		<element name=”createAccountResponse”>
			<complexType>
				<sequence>
					<element name=”accountNumber” type=”long” />
				</sequence>
			</complexType>
		</element>


		<!--
		definition of withdraw operation request, response and fault beans 
		-->
		<element name=”withdraw”>
			<complexType>
				<sequence>
					<element name=”accountNumber” type=”long” />
					<element name=”amount” type=”double” />
				</sequence>
			</complexType>
		</element>

		<element name=”withdrawResponse”>
			<complexType>
				<sequence>
					<element name=”amount” type=”double” />
				</sequence>
			</complexType>
		</element>

		<element name=”InsufficientFunds”>
			<complexType>
				<sequence>
					<element name=”errorMessage” type=”string” />
					<element name=”errorCode” type=”int” />
				</sequence>
			</complexType>
		</element>

		<!--
		definition of getAccountInfo operation request and response beans 
		-->
		<element name=”getAccountInfo”>
			<complexType>
				<sequence>
					<element name=”accountNumber” type=”long” />
				</sequence>
			</complexType>
		</element>

		<element name=”getAccountInfoResponse”>
			<complexType>
				<sequence>
					<element name=”balance” type=”double” />
					<element name=”owner” type=”string” />
				</sequence>
			</complexType>
		</element>
	</schema>
   </types>

   <message name="createAccountRequest">
      <part name="request" element="types:createAccount"/>
   </message>

   <message name="createAccountResponse">
      <part name="response" element=”types:createAccountResponse"/>
   </message>

   <message name="withdrawRequest">
      <part name="request" element=”types:withdraw "/>
   </message>

   <message name="withdrawResponse">
      <part name="response" element=”types:withdrawResponse"/>
   </message>

   <message name="InsufficientFunds">
      <part name="error" element=”types:InsufficientFunds"/>
   </message>

   <message name="getAccountInfoRequest">
      <part name="request" element=”types:getAccountInfo"/>
   </message>

   <message name="getAccountInfoResponse">
      <part name="response" element=”types:getAccountInfoResponse"/>
   </message>

   <portType name="BankingSEI">
      <operation name="createAccount">
         <input message="tns:createAccountRequest"/>
         <output message="tns:createAccountResponse"/>
      </operation>

      <operation name="withdraw">
         <input message="tns:withdrawRequest"/>
         <output message="tns:withdrawResponse"/>
	   <fault message="tns:InsufficientFunds"/>
      </operation>

      <operation name="getAccountInfo">
         <input message="tns:getAccountInfoRequest"/>
         <output message="tns:getAccountInfoResponse"/>
      </operation>
   </portType>
   
   <binding name="BankingSoap11Binding" type="tns:BankingSEI">
      <soap:binding
		style="document" 
		transport="http://schemas.xmlsoap.org/soap/http"/>

      <operation name="createAccount">
         <soap:operation soapAction="createAccount"/>
         <input>	
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
      </operation>

      <operation name=" withdraw ">
         <soap:operation soapAction="withdraw"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault>
            <soap:fault name=”InsufficientFunds” use="literal"/>
         </fault>
      </operation>

      <operation name="getAccountInfo">
         <soap:operation soapAction="getAccountInfo"/>
         <input>	
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
      </operation>

   </binding>

   <service name="BankingService">
      <port binding="tns: BankingSoap11Binding" name="AccountsPort">
         <soap:address
             location="http://localhost:8080/banking/services/BankingService"/>
      </port>
   </service>
</definitions>

Let's take a look at a simple dynamic proxy client that creates an account, withdraws money, and then checks the account balance:

Listing 2. WJAX-WS client – dynamic proxy example
// Create a Dynamic Proxy client
BankingService service = new BankingService();
BankingSEI port = service.getAccountsPort();

// Use Proxy Instance as BindingProvider
BindingProvider bp = (BindingProvider) port;

// (Optional) Configure RequestContext with endpoint's URL
Map<String, Object> rc = bp.getRequestContext();
rc.put (BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:9080/hello/services/BankingService”);

// Create an account with $10 in it
int accountNumber = port.createAccount("Joe Customer”, 10.00);

// Withdraw money
try{
port.withdraw(accountNumber, 1000.00);
} catch (InsufficientFundsException ife){
	InsufficientFunds if = ife.getFaultInfo();
	System.out.println("Error message: ” + if.getMessage());
}

// get account info
Holder<String> owner = new Holder<String>();
Holder<Double> balance = new Holder<Double>();

port.getAccountInfo(accountNumber, owner, balance);
System.out.println("Account number " + accountNumber);
System.out.println("Account belongs to" + owner.getValue());
System.out.println("Account balance is " + balance.getValue());

The example above uses the following steps to invoke an operation on an endpoint using the dynamic proxy API:

  1. Create a dynamic proxy client.
  2. Configure the request context.
  3. Create a new account.
  4. Withdraw some money.
  5. Get the account balance/

Step 1: Generate JAX-WS artifacts using tools

As mentioned in Part 1 , dynamic proxy clients hide the complexity of working with XML APIs by exposing the XML via JAXB 2.0 data binding technology. This means that before you can create a client, you must pass a WSDL document through JAX-WS tools to generate the necessary artifacts. With the Web Services Feature Pack for WebSphere Application Server V6.1, you can use either the wsImport command located at WAS_HOME/bin/wsimport, the wsImport Ant task, or the Application Server Toolkit. wsImport generates the following artifacts based on the WSDL in Listing 1.

Here’s a simple example of how you can use wsImport with the WSDL file:

<cmd>wsimport bankingservice.wsdl –d c:\app\classfiles –keep –s c:\app\javafiles

The following tables show the artifacts generated by the tools.

JAX-B schema artifacts Purpose
CreateAccount
ResponseWithdraw
WithdrawResponse
InsufficientFunds
GetAccountInfo
GetAccountInfo
Response
XML beans, each representing an XML element
ObjectFactory

Factory for XML elements and complexTypes defined by this schema. When a WSDL references multiple schemas, each generated package has its own ObjectFactory.

JAX-WS WSDL artifacts Purpose
InsufficientFundsException A throwable exception that contains an InsufficientFunds bean
BankingSEI An Java interface representing the portType
BankingService A Java equivalent for a WSDL service. This class contains getPort methods for each port mapped to this WSDL service.

Step 2: Create a dynamic proxy client

As mentioned in the previous section, JAX-WS tools generate a concrete Service implementation class, also referred to as Static Service. The generated class has two public constructors, one with no arguments and one with two arguments, representing the WSDL location (java.net.URL) and the service name (javax.xml.namespace.QName), respectively.

Service is an abstraction that represents a WSDL service. A Service instance can be a dynamic service, as described in Part 1, or a static service. In this article, we'll focus on using a static service.

A J2SE client can use BankingService to create a Service instance via its two constructors:

  • BankingService(): This is the default way to create a service and assumes that the WSDL is in the same location as when the artifacts were generated (either as a local file or a URL).
  • BankingService(javax.xml.namespace.QName, java.net.URL): This method is used to point to a new WSDL location. The WSDL must be parsed to read binding and endpoint address information before an endpoint can be invoked.
Listing 3. Create a static service instance
// Create a static Service instance
URL wsdlLocation =
    new 
URL("http://localhost:9080/banking/services/BankingService?wsdl");
QName serviceName =
  new Qname("http://www.example.com/services/BankingService",
"BankingService");

BankingService service = new BankingService(serviceName, wsdlLocation);

A stub is obtained at run time by the getAccountsPort() method of the BankingService. As mentioned earlier, the generated Service class will have a getPort method for each port defined in the WSDL Service.

Listing 4. Use the static service to create a proxy
// Create a Dynamic Proxy client
BankingSEI port = service.getAccountsPort();

Step 3: Configure the request context

JAX-WS client APIs use the BindingProvider interface to maintain separate contexts for the request and response phases of a message exchange with the endpoint. The request and response contexts are of type Map<String, Object> and are obtained using the getRequestContext and getResponseContext methods of BindingProvider.

Following are the standard properties that you can set on the request context after the service instance is created:

PropertyPurpose
javax.xml.ws.service.endpoint.address The address of the service endpoint as a protocol specific URI. The URI scheme must match the protocol binding in use.
javax.xml.ws.security.auth.usernamejavax.xml.ws.security.auth.password User name and password for HTTP basic authentication.
javax.xml.ws.session.maintain Indicates that the client wishes to participate in an HTTP session.
javax.xml.ws.soap.http.soapaction.usejavax.xml.ws.soap.http.soapaction.uri Controls whether the SOAPAction HTTP header is used in SOAP-over-HTTP requests.

Following are WebSphere-specific properties:

PropertyPurpose
com.ibm.wsspi.websvcs.Constants.READ_TIMEOUT_PROPERTY
com.ibm.wsspi.websvcs.Constants.WRITE_TIMEOUT_PROPERTY
Controls the timeouts for sending and receiving data in milliseconds. Modify these properties only when connecting to a server over a slow connection or sending large amounts of data.

The following example overrides the endpoint address specified in the WSDL with our own address:

Listing 5. Set the request context
// (Optional) Configure RequestContext with endpoint's URL
Map<String, Object> rc = bp.getRequestContext();
rc.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, 
"http://www.example.com:9080/banking/services/BankingService");

Performance tip: You may be wondering wondering why we would want to change the address, since Web services usually reside in well-known public locations. There are several problems with referencing the WSDL via “?wsdl” locations:

  1. When a service instance is created, the WSDL document must be parsed.
  2. Network delays add to the performance hit, especially if the WSDL and its schemas are large.
  3. When working with a public WSDL (such as eBay or Amazon), the WSDL may be updated with new operations that will cause a mismatch between the generated SEI and the WSDL, forcing you to regenerate the artifacts.

For these reasons, we recommend you store the WSDL locally and always use the above example to point to the actual endpoint.


Step 4: Invoke SEI operations

In the following sections, we'll describe how to create a new account, withdraw money, and check the account balance.

Create a new account

The first example is very simple and represents a typical operation. We'll create a new account with $10.00 initial balance. The service returns the account number for the newly created account:

Listing 6. Invoke an SEI operation using a proxy
// Create an account with $10 in it
int accountNumber = port.createAccount("Joe Customer", 10.00);

You may be wondering why we use the owner and initialBalance parameters rather than the createAccount element. The reason is that our WSDL follows a document/literal-wrapped pattern. The characteristics of the document/literal wrapped pattern are:

  • The input message has a single part, which must be an element.
  • The operation has the same name as the element.
  • The element's complex type has no attributes.

Had we violated any of the above conditions, we would have been using just plain document/literal and the operation signature would have looked like this:

Listing 7. Example of document/literal (non-wrapped) operation
CreateAccountResponse createAccount(CreateAccount request);

Withdraw money

In this example, we'll introduce Web services faults and how you can catch them as Java exceptions when invoking a WSDL operation using a proxy. To explain Web service faults, let's withdraw more money from the account than is available. Most banks catch this as a user error and prevent any money from being withdrawn.

Listing 1 introduces the WSDL fault InsufficientFunds. This fault is another possible output from the withdraw operation. Based on the WSDL to Java mapping rules defined by the JAX-WS specification, the fault name specified in the binding section of WSDL document is appended with Exception. Thus, the InsufficientFunds fault becomes InsufficientFundsException, which becomes Java Exception.

Listing 8. WSDL snippets showing withdraw operation
<schema>
	<element name=”InsufficientFunds”>
		<complexType>
			<sequence>
				<element name=”errorMessage” type=”string” />
				<element name=”errorCode” type=”int” />
			</sequence>
		</complexType>
	</element>
</schema>

<message name="InsufficientFunds">
      <part name="error" element=”types:InsufficientFunds"/>
</message>

<portType name="BankingSEI">
	<operation name="withdraw">
         <input message="tns:withdrawRequest"/>
         <output message="tns:withdrawResponse"/>
	    <fault message="tns:InsufficientFunds"/>
      </operation>
</portType>

<binding name="BankingSoap11Binding" type="tns:BankingSEI">

      <operation name=" withdraw ">
         <soap:operation soapAction="withdraw"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault>
            <soap:fault name=”InsufficientFunds” use="literal"/>
         </fault>
      </operation>
</binding>

As you can see in Listing 9, invoking the withdraw operation on an account with less than $1000 throws an InsufficientFundsException. This exception can be caught and the InsufficientFunds fault bean can be extracted by the getFaultInfo() method.

Listing 9. Withdraw operation throws an exception
// Withdraw money
try{
port.withdraw(accountNumber, 1000.00);
} catch (InsufficientFundsException ife){

//getFaultInfo returning InsufficientFunds bean.
	InsufficientFunds if = ife.getFaultInfo();
	System.out.println("Error message: " + if.getMessage());
}

Note that even though the withdraw operation does not explicitly declare that it will throw a javax.xml.ws.WebServiceException, it still throws it if there are any errors in dispatching the request to the server.

Get account balance

Now let's check an account balance, and see whether we have incurred any bank fees. We expect the operation to return multiple values, namely the account balance and owner's name. Since Java does not support multi-valued functions, we'll introduce holders in this operation.

Let's look at the schema elements used for the getAccountInfo operation. These elements are very similar to those used in previous operations, but the output element contains two parts rather than one:

Listing 10. Element definitions for getAccountInfo and getAccountInforResponse
<element name="getAccountInfo">
	<complexType>
		<sequence>
			<element name="accountNumber" type="long" />
		</sequence>
	</complexType>
</element>

<element name="getAccountInfoResponse">
	<complexType>
		<sequence>
			<element name="balance" type="double" />
			<element name="owner" type="string" />
		</sequence>
	</complexType>
</element>

Let's look at the different parameter types in this operation:

Parameter Type getAccountInfo example
wsdl:inputINaccountNumber is only present in getAccountInfo
wsdl:outputOUTowner and balanceare only present in getAccountInfoResponse
wsdl:input and wsdl:outputINOUT

Holder classes are used to support the OUT and INOUT parameters in mapped method signatures. They provide a mutable wrapper for otherwise immutable object references. JAX-WS defines a generic holder class javax.xml.ws.Holder<T> that can be used for any Java class. Following is the corresponding definition of the getAccountInfo operation. Since we're using holders, the method is declared as void.

Listing 11. getAccountInfo operation
void getAccountInfo(int accountNumber, Holder<String> owner,
                                             Holder<Double> balance);

Here's the code for how we create the holders, invoke this operation, and then read the values from holders:

Listing 12. Retrieve information from holders
// Create the Holder parameters
Holder<String> owner = new Holder<String>();
Holder<Double> balance = new Holder<Double>();

// Get account information
port.getAccountInfo(accountNumber, owner, balance);

System.out.println("Account number " + accountNumber);
System.out.println("Account belongs to" + owner.getValue());
System.out.println("Account balance is " + balance.getValue());

Notice that when we invoke the operation, we don't bother to initialize either the owner or balance parameters. This is because they are both OUT parameters and are only used to provide the return values. The endpoint will provide these values when it responds to our request.


Conclusion

Dynamic proxies provide a simple mechanism to invoke a Web service without requiring much knowledge about the underlying XML. The use of proxies eliminates the need for the developer to understand the data binding between Java and XML because the underlying JAX-WS implementation handles all the conversions. Thus, application programmers don't need to understand the underlying XML schema or XML composition APIs. However, application programmers using proxies should understand different types of WSDL, and whether they are document/literal, RPC/Literal, and whether the messages are wrapped or bare, The understanding of WSDL types eliminates the guesswork of what SOAP messages flow between the source and the destination, and aids in understanding how SOAP messages are used by the server to resolve an endpoint operation.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere, SOA and web services
ArticleID=255981
ArticleTitle=JAX-WS client APIs in the Web Services Feature Pack for WebSphere Application Server V6.1, Part 2: Creating a proxy client
publish-date=09192007