Archive for jax-ws

wsimport: Disable Wrapper Style

Default Behavior

In JAX-WS reference implementation, 'wsimport' defaults to wrapper doc/lit style when generating Java classes from WSDL. In doing so, the generated 'PortType' interface class sometimes uses 'javax.xml.ws.Holder' class as input parameters extensively. For example,

 
public void startOperation(
    XMLGregorianCalendar eventTime,
    OpConfigs opConfigs,
    Location location,
    Holder<String> status,
    Holder<String> description) {
    // TODO Auto-generated method stub
    return null;
  }
}

Disable Wrapper Style

You can use a custom binding file to disable the default wrapper style as follows if you need to.
* Create a custom binding file, e.g. CustomBinding.xml.

 
<bindings
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="OperationService.wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">
        <!-- Disable default wrapper style -->
        <enableWrapperStyle>false</enableWrapperStyle>
</bindings>

* Including 'binding' element in 'wsimport' Ant task.

 
<wsimport
  debug="true"
  verbose="${verbose}"
  keep="true"
  destdir="${generated.dir}"
  package="${src.pkg.name}.server"
  wsdl="${wsdl.file}">
  <binding dir="${basedir}/etc"
    includes="CustomBinding.xml"/>
</wsimport>
 

After setting 'enableWrapperStyle' to false, the same generated operation name in PortType class becomes:

 
public Response startOperation(TStartOperation parameters) {
  // TODO Auto-generated method stub
  return null;
}
 

For Simpler and Better Typed Binding

* Create a new JAXB binding file, e.g. simple.xjb:

 
<?xml version="1.0" encoding="UTF-8"?>
<!--
  This enables the simple binding mode in JAXB.
  See http://weblogs.java.net/blog/kohsuke/archive/2006/03/simple_and_bett.html
-->
<jaxb:bindings
  xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
  xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc">
 
  <jaxb:globalBindings>
    <xjc:simple />
  </jaxb:globalBindings>
</jaxb:bindings>
 

* Specify in wsimport Ant task:

 
<wsimport
  debug="true"
  verbose="${verbose}"
  keep="true"
  destdir="${generated.dir}"
  package="${src.pkg.name}.server"
  wsdl="${wsdl.file}">
  <binding dir="${basedir}/etc"
    includes="CustomBinding.xml,simple.xjb"/>
</wsimport>
 

* The operation now looks like this:

 
public Response startOperation(StartOperation parameters) {
  // TODO Auto-generated method stub
  return null;
}
 

Parameter name 'StartOperation' is more readable now among benefits.

References

* jaxws-ri/docs/customizations.html
* Wrapper Style
* Customizing XML Schema binding

Axis2

Apache WS Evolution

First generation: Apache SOAP
Second generation: Axis
Third generation: Axis2

Axis2 Architecture

Core Modules

XML Processing Module

*AXIOM (Axis Object Model)
- Based on PULL parser so more control over XML parsing

SOAP Processing Module

* Handlers: intercept and process part of SOAP message
- Three scopes: global, service, operation scope
* Phases: logical collection of one or more handlers
- They're handler containers
- They also order handlers
* Pipers
- InFlow
~ Last handler is Message Receiver
- OutFlow

Information Processing Module

* Description hierarchy: comes from deployment descriptors
Configuration<>-ServiceGroup<>-Service<>-Operation<>-Message
* Context hierarchy: keeps run-time data
ConfigurationContext<>-ServiceGroupContext<>-ServiceContext<>-OperationContext<>-MessageContext

Deployment Module

* Axis2 supports:
- Hot deployment
- Hot update
* Service extension by modules
A module contains:
- Handlers
- Third party libraries
- Module related resources
- Moduel config file (module.xml)
A module can be deployed as a .mar file
* Axis2 can be deployed as
- Archive file: a .aar file
- POJO deployment
- Custom deployer

Client API Module

* ServiceClient API: used to access SOAP body (payload)
sendRobust(): one way but traps exception
fireAndForget(): one way and don't care exception
sendReceive(): sunc two way
sendReceiveNonBlocking(): async two way

* OperationClient API: used to modify SOAP header
Create ServiceClient -> Create OperationClient -> Create SOAPEnvelope -> Create MessageContext -> Add SOAPEnvelope to MessageContext -> Add MessageContext to OperationClient -> Invoke OperationClient - (if response) -> Get response MessageContext form OperationClient

Transport Module

Supports:
* HTTP/HTTPS
* TCP: needs WS-Addressing
* SMTP
* JMS
* XMPP

Non Core Modules

Code Generation Module

Data Binding Module

Supports
* ADB (Axiss Data Binding)
* XMLBeans
* JaxMe: included in XMLBeans
* JibX

Axis2 Code Generation

Axis2 Code Generation

JAX-WS “HTTPS hostname wrong: should be <mysite.com>” Exception

If the "cn" of the web services server certificate does not match its host name, you'll get:
com.sun.xml.ws.client.ClientTransportException: HTTP transport error: java.io.IOException: HTTPS hostname wrong: should be <mysite.com>

You can implement your own javax.net.ssl.HostnameVerifier to suppress it. Important, remember to remove the codes for production!

 
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
 
public class TestHostnameVerifier implements HostnameVerifier {
	public boolean verify(String arg0, SSLSession arg1) {
		return true;
	}
}

In client code:

 
// Make sure java.protocol.handler.pkgs is set to "javax.net.ssl"
//System.setProperty( "java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol" );
System.setProperty( "java.protocol.handler.pkgs", "javax.net.ssl" );
Security.addProvider( new com.sun.net.ssl.internal.ssl.Provider() );
 
HelloService service = new HelloService();
HelloPort proxy = service.getHelloPort();
 
Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "https://new/endpointaddress");
ctxt.put(JAXWSProperties.HOSTNAME_VERIFIER, new TestHostnameVerifier());
 
proxy.sayHello("Hello World!");
 

Set Endpoint Address in JAX-WS Client

 
try {
  HelloService service = new HelloService (
      new URL("http://new/endpointaddress?wsdl"),
      new QName("http://example.org/hello", "HelloService "));
} catch (MalformedURLException e) {
  log.fatal(e);
}
 
HelloPort proxy = service.getHelloPort();
proxy.sayHello("Hello World!");
 

You can also use BindingProvider.ENDPOINT_ADDRESS_PROPERTY to override endpoint address. One caveat is the original endpoint used to generated the client proxy need to be up, otherwise you'll get a nasty "java.net.ConnectException: Connection refused" exception when instantiating the Service at the first place.

 
//Create service and proxy from the generated Service class.
HelloService service = new HelloService();
HelloPort proxy = service.getHelloPort();
 
Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://new/endpointaddress");
 
proxy.sayHello("Hello World!");
 

Use a local wsdl placed in classpath to create service and port, then set new end point address. This solves the issue that the original wsdl can NOT be be obtained from a live server and the live wsdl has a different service name, for example as a result of service virtualization.

 
HelloService service = new HelloService (
      this.getClass().getResource("originalHello.wsdl"),
      new QName("http://example.org/hello", "HelloService "));
 
Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://new/endpointaddress");
 
proxy.sayHello("Hello World!");