Hi, I am looking for Spring/Summer - 2015 Internships. I am also interested in full time opportunities starting June 2015. My Resume

Search This Blog

Loading...

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

1 comment:

  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