Component UI Development with Apache Wicket
May 5th, 2011
Apache Wicket is a component based framework for web development which uses convention over configuration.
At a recent client, our team had the frequent need to integrate with internal and external systems via web services. Because we were consistently on very tight time lines and were not typically provided with web service client libraries, it became crucial to be able to quickly generate accurate client side bindings for the web services. Doing this by hand can be quite tedious, time consuming, and potentially error-prone. The purpose of this article is to explain how to leverage Apache CXF and Maven to quickly generate client side web service bindings, and to detail a simple framework implemented on top of the generated classes to allow quick configuration of the client bindings at run time. In order illustrate thoroughly, a fictitious WSDL will be leveraged as a starting point.
Note that much of the information found in this article can be found in various Apache CXF documentation pages, including:
Three technologies will be leveraged to generate the web service bindings/domain classes:
Maven is optional, but for the purposes of this discussion it will be assumed that it is being used.
The Apache CXF wsdl2java plug-in is used to generate the client side web service bindings. Leveraging the plug in, the code generation can be executed via the Maven build. The plug-in allows for a variety of optional parameters to be specified in order to control how the web service binding get generated. The resulting classes are built on CXF and JAXB libraries.
The following example WSDL will be used facilitate the illustration of the use of wsdl2java and Maven:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<wsdl:definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:tns="http://test.test.com/test_api/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://test.test.com/test_api/">
<wsdl:documentation>Test WSDL for Illustration of wsdl2java usage with maven</wsdl:documentation>
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://test.test.com/test_api/">
<s:element name="TestElement1">
<s:complexType>
<s:sequence>
<s:element maxOccurs="1" minOccurs="0" name="type1" type="tns:Type1" />
<s:element maxOccurs="1" minOccurs="0" name="type2" type="tns:Type2Request" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="Type1">
<s:sequence>
<s:element maxOccurs="1" minOccurs="0" name="Type1Field1" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type1Field2" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type1Field3" type="s:string" />
</s:sequence>
</s:complexType>
<s:complexType name="Type2Request">
<s:sequence>
<s:element maxOccurs="1" minOccurs="0" name="Type2Field1" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type2Field2" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type2Field3" type="s:string" />
</s:sequence>
</s:complexType>
<s:element name="Type2Response">
<s:complexType>
<s:sequence>
<s:element maxOccurs="1" minOccurs="1" name="Type2ResponseField1" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type2ResponseField2" type="s:string" />
<s:element maxOccurs="1" minOccurs="0" name="Type2ResponseField3" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
<wsdl:message name="TestOperationSoapIn">
<wsdl:part element="tns:TestElement1" name="parameters" />
</wsdl:message>
<wsdl:message name="TestOperationSoapOut">
<wsdl:part element="tns:Type2Response" name="parameters" />
</wsdl:message>
<wsdl:portType name="Test_Operation_ManagerSoap">
<wsdl:operation name="TestOperation">
<wsdl:documentation>A random test operation</wsdl:documentation>
<wsdl:input message="tns:TestOperationSoapIn" />
<wsdl:output message="tns:TestOperationSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Test_Operation_ManagerSoap" type="tns:Test_Operation_ManagerSoap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="TestOperation">
<soap:operation soapAction="http://test.test.com/test_api/TestOperation" style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="Test_Operation_ManagerSoap12" type="tns:Test_Operation_ManagerSoap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="TestOperation">
<soap12:operation soapAction="http://test.test.com/test_api/TestOperation"
style="document" />
<wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Test_Operation_Manager">
<wsdl:documentation>Random test operation api</wsdl:documentation>
<wsdl:port binding="tns:Test_Operation_ManagerSoap" name="Test_Operation_ManagerSoap">
<soap:address location="https://test.test.com/test-api.asmx" />
</wsdl:port>
<wsdl:port binding="tns:Test_Operation_ManagerSoap12" name="Test_Operation_ManagerSoap12">
<soap12:address location="https://test.test.com/test-api.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In the web service client bindings, the goal is to generate classes for each of the types defined in the wsdl:types section of the WSDL, and to generate a service class for the TestOperationManager service in the WSDL. Additionally, based on the contents of the WSDL, we expect there to be soap 1.1 and 1.2 bindings.
Much of the work will be done via the maven configuration in the POM.xml file, specifically within the CXF codegen (wsdl2java) plug in. A sample POM.xml enabling generation of client binding code for the above WSDL follows:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<cxf.version>2.2.9</cxf.version>
<java.version>1.6</java.version>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<verbose>true</verbose>
<fork>true</fork>
<compilerVersion>${java.version}</compilerVersion>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>test-service</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/test.wsdl</wsdl>
<serviceName>Test_Operation_Manager</serviceName>
<bindingFiles>
<bindingFile>${basedir}/src/main/resources/test_binding.xml</bindingFile>
</bindingFiles>
<extraargs>
<extraarg>-xjc-Xts</extraarg>
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-xjc-ts</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-common-utilities</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-tools-common</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-simple</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
</project>
Note that the wsdl2java tool can be executed from the command line, so maven is not technically required for the generation of client code bindings.
The following elements within the configuration require some additional explanation:
<extraarg>-xjc-Xts</extraarg>
This will force toString methods to be generated for mapped data types and elements (they are not generated be default). A full listing of possible values for extra arguments can be found in the last section of http://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html.
<bindings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://java.sun.com/xml/ns/jaxws">
<bindings node="wsdl:definitions">
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
</bindings>
The output of this portion of the build will be domain objects and services corresponding to everything defined in the specified WSDL. The outputs are logically separated by package name, based on the contents of the WSDL. The generated code leverages JAXB for binding, and is almost entirely annotation driven. It should be noted that JAXB is not the only option available for binding (see http://cxf.apache.org/docs/databindings.html).
Here is an example of one of the generated domain classes:
package com.test.test.test_api;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import org.apache.cxf.jaxb.JAXBToStringBuilder;
import org.apache.cxf.jaxb.JAXBToStringStyle;
/**
* <p>
* Java class for Type2Request complex type.
*
* <p>
* The following schema fragment specifies the expected content contained within
* this class.
*
* <pre>
* <complexType name="Type2Request">
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="Type2Field1" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="Type2Field2" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="Type2Field3" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Type2Request", propOrder = { "type2Field1", "type2Field2",
"type2Field3" })
public class Type2Request {
@XmlElement(name = "Type2Field1")
protected String type2Field1;
@XmlElement(name = "Type2Field2")
protected String type2Field2;
@XmlElement(name = "Type2Field3")
protected String type2Field3;
/**
* Gets the value of the type2Field1 property.
*
* @return possible object is {@link String }
*
*/
public String getType2Field1() {
return type2Field1;
}
/**
* Sets the value of the type2Field1 property.
*
* @param value
* allowed object is {@link String }
*
*/
public void setType2Field1(String value) {
this.type2Field1 = value;
}
/**
* Gets the value of the type2Field2 property.
*
* @return possible object is {@link String }
*
*/
public String getType2Field2() {
return type2Field2;
}
/**
* Sets the value of the type2Field2 property.
*
* @param value
* allowed object is {@link String }
*
*/
public void setType2Field2(String value) {
this.type2Field2 = value;
}
/**
* Gets the value of the type2Field3 property.
*
* @return possible object is {@link String }
*
*/
public String getType2Field3() {
return type2Field3;
}
/**
* Sets the value of the type2Field3 property.
*
* @param value
* allowed object is {@link String }
*
*/
public void setType2Field3(String value) {
this.type2Field3 = value;
}
/**
* Generates a String representation of the contents of this type. This is an
* extension method, produced by the 'ts' xjc plugin
*
*/
@Override
public String toString() {
return JAXBToStringBuilder.valueOf(this, JAXBToStringStyle.DEFAULT_STYLE);
}
}
Next, lets look at an example of a service class that gets generated. There are actually two pertinent classes generated:
package com.test.test.test_api;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.Service;
/**
* This class was generated by Apache CXF 2.2.9 Wed Sep 15 10:57:23 CDT 2010
* Generated source version: 2.2.9
*
*/
@WebServiceClient(name = "Test_Operation_Manager", wsdlLocation = "http://www.objectpartners.com/test/test.wsdl", targetNamespace = "http://test.test.com/test_api/")
public class TestOperationManager extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName(
"http://test.test.com/test_api/", "Test_Operation_Manager");
public final static QName TestOperationManagerSoap = new QName(
"http://test.test.com/test_api/", "Test_Operation_ManagerSoap");
public final static QName TestOperationManagerSoap12 = new QName(
"http://test.test.com/test_api/", "Test_Operation_ManagerSoap12");
static {
URL url = null;
try {
url = new URL(
"http://www.objectpartners.com/test/test.wsdl");
} catch (MalformedURLException e) {
System.err
.println("Can not initialize the default wsdl from http://www.objectpartners.com/test/test.wsdl");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
public TestOperationManager(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public TestOperationManager(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public TestOperationManager() {
super(WSDL_LOCATION, SERVICE);
}
/**
*
* @return returns TestOperationManagerSoap
*/
@WebEndpoint(name = "Test_Operation_ManagerSoap")
public TestOperationManagerSoap getTestOperationManagerSoap() {
return super.getPort(TestOperationManagerSoap,
TestOperationManagerSoap.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on
* the proxy. Supported features not in the <code>features</code>
* parameter will have their default values.
* @return returns TestOperationManagerSoap
*/
@WebEndpoint(name = "Test_Operation_ManagerSoap")
public TestOperationManagerSoap getTestOperationManagerSoap(
WebServiceFeature... features) {
return super.getPort(TestOperationManagerSoap,
TestOperationManagerSoap.class, features);
}
/**
*
* @return returns TestOperationManagerSoap
*/
@WebEndpoint(name = "Test_Operation_ManagerSoap12")
public TestOperationManagerSoap getTestOperationManagerSoap12() {
return super.getPort(TestOperationManagerSoap12,
TestOperationManagerSoap.class);
}
/**
*
* @param features
* A list of {@link javax.xml.ws.WebServiceFeature} to configure on
* the proxy. Supported features not in the <code>features</code>
* parameter will have their default values.
* @return returns TestOperationManagerSoap
*/
@WebEndpoint(name = "Test_Operation_ManagerSoap12")
public TestOperationManagerSoap getTestOperationManagerSoap12(
WebServiceFeature... features) {
return super.getPort(TestOperationManagerSoap12,
TestOperationManagerSoap.class, features);
}
}
Only a single instance of this service class needs to be configured within an application.
package com.test.test.test_api;
import java.util.concurrent.Future;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
/**
* This class was generated by Apache CXF 2.2.9 Wed Sep 15 10:57:23 CDT 2010
* Generated source version: 2.2.9
*
*/
@WebService(targetNamespace = "http://test.test.com/test_api/", name = "Test_Operation_ManagerSoap")
@XmlSeeAlso( { ObjectFactory.class })
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface TestOperationManagerSoap {
@WebMethod(operationName = "TestOperation")
public Response<com.test.test.test_api.Type2Response> testOperationAsync(
@WebParam(partName = "parameters", name = "TestElement1", targetNamespace = "http://test.test.com/test_api/") TestElement1 parameters);
@WebMethod(operationName = "TestOperation")
public Future<?> testOperationAsync(
@WebParam(partName = "parameters", name = "TestElement1", targetNamespace = "http://test.test.com/test_api/") TestElement1 parameters,
@WebParam(name = "asyncHandler", targetNamespace = "") AsyncHandler<com.test.test.test_api.Type2Response> asyncHandler);
@WebResult(name = "Type2Response", targetNamespace = "http://test.test.com/test_api/", partName = "parameters")
@WebMethod(operationName = "TestOperation", action = "http://test.test.com/test_api/TestOperation")
public Type2Response testOperation(
@WebParam(partName = "parameters", name = "TestElement1", targetNamespace = "http://test.test.com/test_api/") TestElement1 parameters);
}
Taking a look at the methods generated above, you will see:
Here is an example of how to configure the port for the service. First, a service “configurator” will be defined. This class is responsible for tasks such as security and transport configuration, as well as configuration of any necessary interceptors for the framework. Note that you can append interceptors for a service within this implementation as needed (in the example below only logging interceptor are appended). Within the following class, it is assumed that the service will require basic username/password authentication, and that the credentials will be wired in via Spring or obtained via some other mechanism.
package cxf.test;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HttpBasicAuthSupplier;
import org.apache.cxf.message.Message;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import java.net.URL;
/**
* General purpose configurator for generated apache CXF web service client
* implementations that require basic username/password authentication
*/
public class ServiceConfigurator {
private String basicAuthUser;
private String basicAuthPassword;
public void configure(Object service) {
Client client = ClientProxy.getClient(service);
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
httpConduit.setAuthSupplier(new HttpBasicAuthSupplier() {
@Override
public UserPass getPreemptiveUserPass(String s, URL url, Message message) {
return createUserPass(getBasicAuthUser(), getBasicAuthPassword());
}
@Override
public UserPass getUserPassForRealm(String s, URL url, Message message,
String s1) {
return createUserPass(getBasicAuthUser(), getBasicAuthPassword());
}
});
client.getOutInterceptors().add(new LoggingOutInterceptor());
client.getInInterceptors().add(new LoggingInInterceptor());
client.getInFaultInterceptors().add(new LoggingInInterceptor());
}
public String getBasicAuthPassword() {
return basicAuthPassword;
}
public void setBasicAuthPassword(String basicAuthPassword) {
this.basicAuthPassword = basicAuthPassword;
}
public String getBasicAuthUser() {
return basicAuthUser;
}
public void setBasicAuthUser(String basicAuthUser) {
this.basicAuthUser = basicAuthUser;
}
}
Second, the service instances needs to be created and configured. To accomplish this, something similar to the following can be implemented:
`package cxf.test;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executors;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
import com.test.test.test_api.TestElement1;
import com.test.test.test_api.TestOperationManager;
import com.test.test.test_api.TestOperationManagerSoap;
import com.test.test.test_api.Type1;
import com.test.test.test_api.Type2Request;
import com.test.test.test_api.Type2Response;
/**
* Illustrates the use of the web service client code generated by the Apache CXF
* wsdl2java plug-in, as well as how to configure the services.
*
* Illustrates the use for the synchronous, "fire-and-forget", and fully
* asynchronous versions of the test service endpoint.
*/
public class TestService {
ServiceConfigurator serviceConfigurator;
public TestOperationManagerSoap service;
/**
* Get the Test service instance for use. If the instance does not create yet,
* create it.
*
* @return The service instance
*/
public TestOperationManagerSoap getService() {
if (service == null) {
service = createTestOperationManagerInstance();
}
return service;
}
/**
* Creates and returns the instance of the {@link TestOperationManager}
*
* @return` `instance of the {@link TestOperationManager}`
`*/
public TestOperationManagerSoap createTestOperationManagerInstance() {
TestOperationManager service = new TestOperationManager(
getWsdlLocation(TestOperationManager.WSDL_LOCATION));
service.setExecutor(Executors.newCachedThreadPool());
TestOperationManagerSoap testOperationManagerSoap = service
.getTestOperationManagerSoap();
getServiceConfigurator().configure(testOperationManagerSoap);
return testOperationManagerSoap;
}
/**
* Used to get the environment-specific location of the wsdl. If there is no
* concept of an environment-specific location of the wsdl, then just return
* the input codedWSDL
*/
public URL getWsdlLocation(URL codedWSDL) {
URL wsdlLocation = null;
try {
wsdlLocation = new URL("http://test.test.com/test-api/test");
} catch (MalformedURLException mue) {
wsdlLocation = codedWSDL;
}
return wsdlLocation;
}
public ServiceConfigurator getServiceConfigurator() {
return serviceConfigurator;
}
public void setServiceConfigurator(ServiceConfigurator serviceConfigurator) {
this.serviceConfigurator = serviceConfigurator;
}
}`
A few notes about this implementation:
Pretty simple. Something to the effect of the following, placed in the service class (e.g., TestService from section 5 above):
/**
* Perform the synchronous call to the web service endpoint. The method blocks
* until the web service call is completed, and the unmarshalled response is
* returned.
*
*
* @return The response from the web service call
*/
public Type2Response requestSynchronous(String type1Field1,
String type1Field2, String type1Field3, String type2Field1,
String type2Field2, String type2Field3) {
TestElement1 testElement1 = new TestElement1();
Type1 type1 = new Type1();
type1.setType1Field1(type1Field1);
type1.setType1Field2(type1Field2);
type1.setType1Field3(type1Field3);
testElement1.setType1(type1);
Type2Request type2Request = new Type2Request();
type2Request.setType2Field1(type2Field1);
type2Request.setType2Field2(type2Field2);
type2Request.setType2Field3(type2Field3);
testElement1.setType2(type2Request);
return getService().testOperation(testElement1);
}
For the asynchronous version with no callback (fire and forget), it is again very simple:
/**
* Perform the asynchronous "fire and forget" call to the web service
* endpoint. The method does not block, does not wait for the response, and
* essentially ignores the response. This is not an appropriate version of the
* method to use if there is any reason to be concerned about what the
* response might contain.
*
*
*/
public void requestAsyncFireAndForget(String type1Field1, String type1Field2,
String type1Field3, String type2Field1, String type2Field2,
String type2Field3) {
TestElement1 testElement1 = new TestElement1();
Type1 type1 = new Type1();
type1.setType1Field1(type1Field1);
type1.setType1Field2(type1Field2);
type1.setType1Field3(type1Field3);
testElement1.setType1(type1);
Type2Request type2Request = new Type2Request();
type2Request.setType2Field1(type2Field1);
type2Request.setType2Field2(type2Field2);
type2Request.setType2Field3(type2Field3);
testElement1.setType2(type2Request);
getService().testOperationAsync(testElement1);
}
Of course, in this case we cannot get the response, because we fire and forget. So we don’t block until the web service call is completed, we just continue on, with the assumption that the web service call will proceed as planned, and that we don’t care about the response.
In this case, you need to define a callback handler first. The callback handler will implement the code that must be used to process the result of the web service call, and must implement the interface AsyncHandler
/**
* Very basic callback handler for use when leveraging the asynchronous
* version of the testOperation web serivce client.
*
*/
public class TestOperationAsyncCallback implements
AsyncHandler<Type2Response> {
@Override
public void handleResponse(Response<Type2Response> res) {
System.out.println("Do something useful with this response: "
+ res.toString());
}
}
And here is the method that is used to make the call:
/**
* Perform the fully asynchronous call to the web service endpoint. The method
* does not block, and does not wait for the response. The response will be
* handled by a callback handler that implements the {@link AsyncHandler}
* interface, such as {@link TestOperationAsyncCallback} shown later in this
* class
*
*/
public void requestAsyncWithCallback(String type1Field1, String type1Field2,
String type1Field3, String type2Field1, String type2Field2,
String type2Field3) {
TestElement1 testElement1 = new TestElement1();
Type1 type1 = new Type1();
type1.setType1Field1(type1Field1);
type1.setType1Field2(type1Field2);
type1.setType1Field3(type1Field3);
testElement1.setType1(type1);
Type2Request type2Request = new Type2Request();
type2Request.setType2Field1(type2Field1);
type2Request.setType2Field2(type2Field2);
type2Request.setType2Field3(type2Field3);
testElement1.setType2(type2Request);
getService().testOperationAsync(testElement1,
new TestOperationAsyncCallback());
}
This article has illustrated how to leverage Apache CXF, Maven, and JAXB to rapidly create accurate web service client bindings. Additionally, a small framework to allow an application to quickly configure and leverage the generated bindings has been detailed. With the use of these tools, it only takes a matter of minutes to generated client side bindings for virtually any web service. Depending on the integration requirements, quite a bit more configuration is possible both within the maven configuration and within an associated JAX-WS binding file.
There is one potential issue with the wsdl2java code generator: the WSDL in question must be well-formed, without containing any elements or types that would cause name clashes in the generated code. For instance, if the WSDL in question has an element named A, and a complex type named A, the code generation will fail. If this occurs, and you are not the owner of that WSDL, a possible solution is to generate the code off of a local, modified version of the WSDL.
Apache Wicket is a component based framework for web development which uses convention over configuration.
The HTML5 specification has been in the works for several years and is getting close to reaching completion. This article gives a quick overview of what to look forward to when HTML5 is officially completed.
Bizarre JavaScript errors surfaced after upgrading Spring and Spring MVC. Heres what we found.
Insert bio here