Skip to main content

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

Nikhil Thaker (nikhil.v.thaker@us.ibm.com), Staff Software Engineer, IBM
Nikhil Thaker photo
Nikhil 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, Software Group
Dan Sedov photo
Dan 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.

Summary:  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.

View more content in this series

Date:  19 Sep 2007
Level:  Intermediate
Activity:  1417 views
Comments:  

Introduction

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:input IN accountNumber is only present in getAccountInfo
wsdl:output OUT owner and balanceare only present in getAccountInfoResponse
wsdl:input and wsdl:output INOUT

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

About the authors

Nikhil Thaker photo

Nikhil 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 photo

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

Comments



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

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
author1-email=nikhil.v.thaker@us.ibm.com
author1-email-cc=crothemi@us.ibm.com
author2-email=sedov@us.ibm.com
author2-email-cc=crothemi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers