Search This Blog

Tuesday, 7 May 2013

Contract First web service with CXF

In the post on Spring web services, I had used a contract first approach for development. This being the only style supported by Spring.
JAX-WS and also CXF on the other hand support both code first and contact first approaches.
I decided to use the same wsdl file from my Spring post:
<?xml version="1.0"  encoding = "UTF-8" ?>
<wsdl:definitions xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:random="http://ws.com/Service/xsd/random-schema" 
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
        xmlns:tns="http://ws.com/Service/samplews-ns"
 targetNamespace="http://ws.com/Service/samplews-ns">

 <wsdl:types>
  <xs:schema>
   <xs:import namespace="http://ws.com/Service/xsd/random-schema"
    schemaLocation="xsd/RandomService.xsd" />
  </xs:schema>
 </wsdl:types>

 <wsdl:message name="GetRandomRequest">
  <wsdl:part element="random:GetRandomRequest" name="GetRandomRequest" />
 </wsdl:message>
 <wsdl:message name="GetRandomResponse">
  <wsdl:part element="random:GetRandomResponse" name="GetRandomResponse" />
 </wsdl:message>

 <wsdl:portType name="SampleServiceOperationsPortType">
  <wsdl:operation name="random">
   <wsdl:documentation>Returns a random value from the application
   </wsdl:documentation>
   <wsdl:input message="tns:GetRandomRequest" name="GetRandomRequest" />
   <wsdl:output message="tns:GetRandomResponse" name="GetRandomResponse" />
  </wsdl:operation>
 </wsdl:portType>

 <wsdl:binding name="SampleServiceOperationsPortTypeSoap"
  type="tns:SampleServiceOperationsPortType">
  <soap:binding style="document"
   transport="http://schemas.xmlsoap.org/soap/http" />
  <wsdl:operation name="random">
   <soap:operation soapAction="/Service/random" />
   <wsdl:input name="GetRandomRequest">
    <soap:body use="literal" />
   </wsdl:input>
   <wsdl:output name="GetRandomResponse">
    <soap:body use="literal" />
   </wsdl:output>
  </wsdl:operation>
 </wsdl:binding>

 <wsdl:service name="SampleServiceOperationsPortTypeSoap">
  <wsdl:port binding="tns:SampleServiceOperationsPortTypeSoap"
   name="SampleServiceOperationsPortTypeSoap">
   <soap:address location="randomService" />
  </wsdl:port>
 </wsdl:service>
</wsdl:definitions>
The included xsd file is as below:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified" 
    xmlns:random="http://ws.com/Service/xsd/random-schema"
    targetNamespace="http://ws.com/Service/xsd/random-schema">

    <xs:element name="GetRandomRequest">
        <xs:complexType>
            <xs:all>
                <xs:element name="name" type="xs:string" maxOccurs="1"
                    minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="GetRandomResponse">
        <xs:complexType>
            <xs:all>
                <xs:element name="value" type="xs:int" maxOccurs="1"
                    minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
    
    <xs:element name="GetRandomFault">
        <xs:complexType>
            <xs:all>
                <xs:element name="faultMessage" type="xs:string"
                    maxOccurs="1" minOccurs="1" />
            </xs:all>
        </xs:complexType>
    </xs:element>
</xs:schema>
Now that the contract has been defined we need to generate the source code from the same.
I used the wsdl2java command from CXF for the same.
>wsdl2java -ant -impl -server -d . random.wsdl
Navigate to the folder with the wsdl file and execute the command:
This results in the files being generated by CXF:

The Service Endpoint Interface generated by the tool is :
@WebService(name = "SampleServiceOperationsPortType",targetNamespace = "http://ws.com/Service/samplews-ns")
@XmlSeeAlso({ com.ws.service.xsd.random_schema.ObjectFactory.class })
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface SampleServiceOperationsPortType {

 /**
  * Returns a random value
  * 
  */
 @WebResult(name = "GetRandomResponse", 
   targetNamespace = "http://ws.com/Service/xsd/random-schema", 
   partName = "GetRandomResponse")
 @WebMethod(action = "/Service/random")
 public com.ws.service.xsd.random_schema.GetRandomResponse random(
   @WebParam(partName = "GetRandomRequest", name = "GetRandomRequest", 
    targetNamespace = "http://ws.com/Service/xsd/random-schema") 
    com.ws.service.xsd.random_schema.GetRandomRequest getRandomRequest);
}
Interesting point is that maximum annotation details is added to the interface. The documentation element details have been added to the code comments.
The Implementation is as below:
@javax.jws.WebService(serviceName = "SampleServiceOperationsPortTypeSoap", 
  portName = "SampleServiceOperationsPortTypeSoap", 
  targetNamespace = "http://ws.com/Service/samplews-ns", 
  wsdlLocation = "random.wsdl", 
  endpointInterface = "com.ws.service.samplews_ns.SampleServiceOperationsPortType")
public class SampleServiceOperationsPortTypeImpl implements
  SampleServiceOperationsPortType {

 private static final Logger LOG = Logger
   .getLogger(SampleServiceOperationsPortTypeImpl.class.getName());

 /*
  * (non-Javadoc)
  * 
  * @see
  * com.ws.service.samplews_ns.SampleServiceOperationsPortType#random(com
  * .ws.service.xsd.random_schema.GetRandomRequest getRandomRequest )*
  */
 public com.ws.service.xsd.random_schema.GetRandomResponse random(
   com.ws.service.xsd.random_schema.GetRandomRequest getRandomRequest) {
  LOG.info("Executing operation random");
  System.out.println(getRandomRequest);
  try {
   com.ws.service.xsd.random_schema.GetRandomResponse _return = null;
   return _return;
  } catch (java.lang.Exception ex) {
   ex.printStackTrace();
   throw new RuntimeException(ex);
  }
 }

}
Here the webservice annotation provides the SOAP details. The method is incomplete. I had to make some minor code changes to the below:
com.ws.service.xsd.random_schema.GetRandomResponse _return = new GetRandomResponse();
_return.setValue((int) (Math.random()*165));
There are two more files that were generated:
  1. SampleServiceOperationsPortTypeSoap – This is a web-service client built using pure JAX-WS APIs
  2. SampleServiceOperationsPortType_SampleServiceOperationsPortTypeSoap_Server – This uses the Endpoint API to publish the webservice
Now to add these files to a web project and execute it. 
I added a jax-ws endpoint bean ( a spring configuration file placed in the config folder)
<jaxws:endpoint id="randomWs" 
       implementor="com.ws.service.samplews_ns.SampleServiceOperationsPortTypeImpl" 
       wsdlLocation="random.wsdl" address="randomService">
 <jaxws:properties>
  <entry key="schema-validation-enabled" value="true" />
 </jaxws:properties>
</jaxws:endpoint>
I also added an entry for the CXFServlet in my web.xml:
<servlet>
    <servlet-name>cxf</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>cxf</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>
and tried to access the wsdl file at URL http://localhost:8080/WsdlFirst/services/randomService?wsdl
The wsdl displayed had a change at two points:
  1. It mapped the external xsd to a new URL - http://localhost:8080/WsdlFirst/services/randomService?xsd=xsd/RandomService.xsd.
  2. It modified the soap:address element's location attribute to refer to an absolute URL http://localhost:8080/WsdlFirst/services/randomService
The question that now arises is did CXF use my supplied wsdl or did it generate its own complete version.
To test the above I removed the WebResult, WebMethod and WebParam annotations.
Now if I run the code the details provided in the wsdl file is still reflected. For example the part names appear as set in the wsdl file. Also I modified the operations documentation element in my wsdl and the change was displayed.
So this means CXF uses the wsdl file specified. It will only auto-generate if one is not available.
What if you do not specify the  wsdlLocation attribute ?
I next decided to remove the attribute while retaining my wsdl file on the classpath. CXF continued to use my wsdl file. Thus the attribute can even by avoided. When resolving a wsdl request for a service 'xyz' if CXF finds a wsdl named 'xyz.wsdl' on the classpath it chooses the use the file.
To test my webservice I created a simple client:
public static void main(String[] args) {
 final String endpointAddress = "http://localhost:8080/WsdlFirst/services/randomService";
 JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();

 factory.setServiceClass(SampleServiceOperationsPortType.class); // the SEI
 factory.setAddress(endpointAddress);
 SampleServiceOperationsPortType client = (SampleServiceOperationsPortType) factory
    .create();

 GetRandomRequest randomRequest = new GetRandomRequest();
 randomRequest.setName("coolio");

 GetRandomResponse reply = client.random(randomRequest);
 System.out.println("Server said: " + reply.getValue());
}
The logs indicate successful execution:
Server said: 36

6 comments:

  1. Ηavе you eveг thought abnout
    creаting an ebook orr gueѕt authoring on other blοgs?
    I haνе a blοg baѕed on the samе ѕubjectѕ you discusѕ аnd ωould lоve to haѵe
    you ѕhare ѕοmе ѕtοгіеs/іnfoгmatiοn.
    I know my ѵiewеrs wоuld value уour work.
    If yоu аrе even гemοtely іnteresteԁ, feеl freе to senԁ mе an e-mаil.



    Feel free to surf tο my wеbpage: affordable website maintenance services

    ReplyDelete
  2. Thanks. This article was very helpful.

    ReplyDelete
  3. Nice post, very useful.. Thank you so much

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Thanks for this blog, keep sharing your thoughts like this...
    Importance of UI UX Design
    What is Web developing

    ReplyDelete