JAX-WS WebServices with Apache CXF
Bhaskar S | 12/24/2011 |
Introduction
Applications in an Enterprise are built over time and often build with different technologies running on heterogeneous hardware platforms. For example, the Accounts Department may have built its Interest Calculator System using Java running on Linux platform, while the Sales Department may have built its Customer Sales System using C# running on the Microsoft platform. Sooner or later there is a need to integrate these application systems for better Customer service. How do we integrate these two systems ? The answer is WebServices.
A WebService is nothing more than a client application sending a well defined request (with data) over HTTP to a server application and receiving a well defined response. That said, how do we make the C# application communicate with Java application ? The answer is using a platform-independent standard called SOAP. SOAP stands for Simple Object Access Protocol and is an XML based protocol for applications to exchange information over HTTP. And finally, how do we expose the service interface to the client ? The answer again is using a platform-independent standard called WSDL. WSDL stands for Web Services Description Language and is an XML document that describes the services offered and how to access them.
For building WebServices and Service Clients in Java, we use JAva API for XML Web Services (JAX-WS). JAX-WS is an API specification that aims to simplify WebServices development through the use of Java Annotations. Apache CXF is one of the popular open-source frameworks that implements the JAX-WS specification.
Setup
For our examples, we will need the following open-source products:
Java SE 6 and above (http://java.oracle.com/)
Apache CXF 2.5.1 (http://cxf.apache.org/)
Apache Tomcat 6.0.35 (http://tomcat.apache.org/)
For the samples, I downloaded and set them up in the respective directories as follows:
$HOME/Programs/apache-cxf-2.5.1
$HOME/Programs/apache-tomcat-6.0.35
We will develop two types of samples – one is a standalone WebService using just Apache CXF and the other is using Apache CXF that is deployed inside the Apache Tomcat web container.
NOTE :: All the setup is on a Linux desktop. It should be easy to setup on Windows too
We you download and extract Apache CXF in the directory “$HOME/Applications/apache-cxf-2.5.1”, you will see a directory called “lib” that contains jar files provided with the CXF distribution. For developing JAX-WS based WebServices, we only need the following jars:
commons-logging-1.1.1.jar
cxf-2.5.1.jar
geronimo-activation_1.1_spec-1.1.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-jaxws_2.2_spec-1.1.jar
geronimo-servlet_2.5_spec-1.1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-ws-metadata_2.0_spec-1.1.3.jar
jaxb-api-2.2.3.jar
jaxb-impl-2.2.4-1.jar
jetty-continuation-7.5.3.v20111011.jar
jetty-http-7.5.3.v20111011.jar
jetty-io-7.5.3.v20111011.jar
jetty-server-7.5.3.v20111011.jar
jetty-util-7.5.3.v20111011.jar
msv-core-2011.1.jar
neethi-3.0.1.jar
stax2-api-3.1.1.jar
woodstox-core-asl-4.1.1.jar
wsdl4j-1.6.2.jar
xmlschema-core-2.0.1.jar
The samples we develop will be located in the following directory: $HOME/Projects/Java/CXF-WebServices
This project directory will contain the following sub-directories:
bin
src
resources
build
There are two approaches for developing WebServices:
Code-first approach
Contract-first approach
With the Code-first approach, we first develop the code for implementing the WebServices and then expose the WSDL.
With the Contract-first approach, we first start with the WSDL and then generate code from the WSDL using the tools provided by the Apache CXF framework.
We will begin with the Code-first approach to developing WebServices using the Apache CXF Framework.
Code-first Standalone WebServices
Our first example will be a standalone JAX-WS WebService using just the Apache CXF Framework. When we say standalone WebServices, it means that we will not be hosting it in a Web container like Apache Tomcat. The Apache CXF Framework by default uses Jetty server for exposing services. For our example, we choose the simple interest calculator and expose it as a WebService.
The first step is to define the WebService interface. In the JAX-WS parlance, it is called as the Service Endpoint Interface (SEI). SEI is nothing more than a Java interface that defines the business method(s) to be exposed as remote service(s).
The following defines WebService interface for the simple interest calculator:
Next step is to implement the WebService interface SimpleInterestCalculator.
The following Java class defines the service implementation:
Notice the use of the Java annotation @WebService on both the interface as well as the implementation class. The purpose of this annotation is to mark the interface as the WebService interface and to mark the implementation class as the service endpoint. This annotation is declared right above the interface or the class.
The final step is to publish the implementation of SimpleInterestCalculator as a remote WebService, which can be accessed by clients via HTTP endpoint URL.
The following Java class uses the static “publish()” method of the JAX-WS javax.xml.ws.Endpoint class to publish the implementation of SimpleInterestCalculator as a Web Service on the specified HTTP endpoint URL:
NOTE :: By default, the Apache CXF Framework uses embedded Jetty web server to expose our WebService when we call the function Endpoint.publish(endpoint, implementor)
Now that we have implemented our first WebService, we need to start it up so we can use it. To start our WebService, let us create a script called “SimpleInterestService.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorService
Open a Terminal window and execute the script “bin/SimpleInterestService.sh” from the directory $HOME/Projects/Java/CXF-WebServices and we should see something as follows:
Ready to start SimpleInterestCalculator jax-ws service
Dec 26, 2011 12:29:24 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://standalone.jaxws.cxf.polarsparc.com/}SimpleInterestCalculatorImplService from class com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorImpl
Dec 26, 2011 12:29:24 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be http://localhost:8080/SimpleInterestCalculator
2011-12-26 12:29:24.739:INFO:oejs.Server:jetty-7.5.3.v20111011
2011-12-26 12:29:24.805:INFO:oejs.AbstractConnector:Started SelectChannelConnector@localhost:8080 STARTING
2011-12-26 12:29:24.820:INFO:oejsh.ContextHandler:started o.e.j.s.h.ContextHandler{,null}
You can get the WSDL for this service by accessing the following URL: http://localhost:8080/SimpleInterestCalculator?wsdl
The following is the WSDL:
<wsdl:definitions xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://standalone.jaxws.cxf.polarsparc.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="SimpleInterestCalculatorImplService"
targetNamespace="http://standalone.jaxws.cxf.polarsparc.com/">
<wsdl:types>
<xsd:schema xmlns:tns="http://standalone.jaxws.cxf.polarsparc.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="unqualified"
targetNamespace="http://standalone.jaxws.cxf.polarsparc.com/">
<xsd:element name="Exception" type="tns:Exception"/>
<xsd:complexType name="Exception">
<xsd:sequence>
<xsd:element minOccurs="0" name="message" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="calculateSimpleInterest" type="tns:calculateSimpleInterest"/>
<xsd:complexType name="calculateSimpleInterest">
<xsd:sequence>
<xsd:element name="arg0" type="xsd:int"/>
<xsd:element name="arg1" type="xsd:double"/>
<xsd:element name="arg2" type="xsd:double"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="calculateSimpleInterestResponse"
type="tns:calculateSimpleInterestResponse"/>
<xsd:complexType name="calculateSimpleInterestResponse">
<xsd:sequence>
<xsd:element name="return" type="xsd:double"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="Exception">
<wsdl:part element="tns:Exception" name="Exception"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterest">
<wsdl:part element="tns:calculateSimpleInterest" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterestResponse">
<wsdl:part element="tns:calculateSimpleInterestResponse" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:portType name="SimpleInterestCalculator">
<wsdl:operation name="calculateSimpleInterest">
<wsdl:input message="tns:calculateSimpleInterest" name="calculateSimpleInterest"/>
<wsdl:output message="tns:calculateSimpleInterestResponse"
name="calculateSimpleInterestResponse"/>
<wsdl:fault message="tns:Exception" name="Exception"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SimpleInterestCalculatorImplServiceSoapBinding"
type="tns:SimpleInterestCalculator">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="calculateSimpleInterest">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="calculateSimpleInterest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="calculateSimpleInterestResponse">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SimpleInterestCalculatorImplService">
<wsdl:port binding="tns:SimpleInterestCalculatorImplServiceSoapBinding"
name="SimpleInterestCalculatorImplPort">
<soap:address location="http://localhost:8080/SimpleInterestCalculator"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
The WSDL wsdl:types element describes all the data types used in the web service. It uses XML schema to define the data types.
The WSDL wsdl:message element describes the request and response data that is exchanged between the service provider and the client. There are two types of messages: one is the input message which wraps the parameters passed to a service (request) and the other is the output message which wraps the return result (response) from the service.
The WSDL wsdl:portType element describes the various operations offered by a web service. In our example, it is just the one operation called “calculateSimpleInterest”. For each of the operations, it lists the input and output messages exchanged.
The WSDL wsdl:binding element describes the message format and transport specifics used by the web service. In our example, we are using SOAP over HTTP.
And, finally the WSDL wsdl:service element indicates the endpoint address of the web service. In our example, it is “http://localhost:8080/SimpleInterestCalculator”.
Now that we have deployed our WebService, we need a client to invoke our service. The following Java class defines the client implementation:
// [1] : The Service object is like a factory object that allows for the creation of client proxy for the web service.
// [2] : The getPort() method on the Service object returns the client proxy for the web service.
To test our web service client, let us create a script called “SimpleInterestClient.sh” in the “bin” directory as follows:
cd ~/ProductHome/bin/cmd
NOTE :: The client takes three command line arguments: months, amount and rate
Open a Terminal window and execute the script “bin/SimpleInterestClient.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Dec 26, 2011 1:54:27 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://standalone.jaxws.cxf.polarsparc.com/}SimpleInterestCalculatorImplService from WSDL: http://localhost:8080/SimpleInterestCalculator?wsdl
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
CAUTION :: If the interface is not annotated as a Web service, we will encounter the following exception: javax.xml.ws.WebServiceException: Could not find wsdl:binding operation info for web method calculateSimpleInterest
Now, lets try another example. Execute the script “bin/SimpleInterestClient.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments -12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Dec 26, 2011 1:55:14 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://standalone.jaxws.cxf.polarsparc.com/}SimpleInterestCalculatorImplService from WSDL: http://localhost:8080/SimpleInterestCalculator?wsdl
java.lang.Exception: Invalid parameter: months
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshallException(JAXBEncoderDecoder.java:456)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:595)
at org.apache.cxf.jaxb.io.DataReaderImpl.read(DataReaderImpl.java:156)
at org.apache.cxf.interceptor.ClientFaultConverter.processFaultDetail(ClientFaultConverter.java:153)
at org.apache.cxf.interceptor.ClientFaultConverter.handleMessage(ClientFaultConverter.java:80)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:107)
at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:799)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1627)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1494)
at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1402)
at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:649)
at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:533)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:463)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:366)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:319)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:88)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
at $Proxy19.calculateSimpleInterest(Unknown Source)
at com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorClient.main(SimpleInterestCalculatorClient.java:26)
We specified an invalid month and hence the service rejected our request.
Hurray !!! We have successfully deployed and tested our first web service using Apache CXF.
Now, let us revisit the WSDL for our web service. From the WSDL discussed above, we see that the Apache CXF Web Service framework assigned all the names. For example, in wsdl:types, the data elements are named “arg0”, “arg1”, and “arg2” respectively. Similarly, in wsdl:service, the service is named “SimpleInterestCalculatorImplService” and the port “SimpleInterestCalculatorImplPort”. To make the names more human friendly, we can specify the appropriate names in the service interface as well as the service implementation using annotations. We will use the same example as above to explore the annotations.
The following defines Web Service interface for the simple interest calculator:
Notice the use of the annotation @WebService which is defined by javax.jws.WebService to specify some attributes. The following are the list of attributes one can specify using the @WebService annotation:
Attribute | Description |
---|---|
name | Specifies the name of the service interface and maps to the name attribute of the WSDL wsdl:portType element |
targetNamespace | Specifies the target namespace for the service |
serviceName | Specifies the name of the remote service and maps to the name attribute of the WSDL wsdl:service element |
wsdlLocation | Specifies the URI where the service WSDL is located |
portName | Specifies the name of the publised endpoint and maps to the name attribute of the WSDL wsdl:port element |
For our example SEI, we have specified the name and targetNamespace attributes. For the name attribute, we have specified “SimpleInterestCalculator” and for the targetNamespace, we have specified an appropriate namespace of “http://polarsparc.com/” instead of the default namespace of “http://standalone.jaxws.cxf.polarsparc.com/”.
Also, notice the use of the annotation @WebParam which is defined by javax.jws.WebParam to specify the name attribute. The following are the list of attributes one can specify using the @WebParam annotation:
Attribute | Description |
---|---|
name | Specifies the name of the parameter |
targetNamespace | Specifies the target namespace for the parameter |
mode | Specifies the mode (IN, OUT, INOUT) of the parameter. The default is IN |
For our example SEI, we have specified the name attribute. So instead of the default parameter names of “arg0”, “arg1”, and “arg2”, it will be the more friendlier parameter names of “months”, “amount”, and “rate” respectively.
Next step is to implement the Web Service interface SimpleInterestCalculator2. The following Java class defines the service implementation:
Notice the use of the Java annotation @WebService to specify the target namespace, the service name and the port name.
The following class publishes the implementation of SimpleInterestCalculator2 as a remote Web Service:
To start our enhanced web service, let us create a script called “SimpleInterestService2.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorService2
Kill the script “SimpleInterestService.sh” if it is still running.
Open a Terminal window and execute the script “bin/SimpleInterestService2.sh” from the directory $HOME/Projects/Java/CXF-WebServices and we should see something as follows:
Ready to start SimpleInterestCalculator2 jax-ws service
Dec 26, 2011 2:53:16 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculator2
Dec 26, 2011 2:53:17 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be http://localhost:8080/SimpleInterestCalculator2
2011-12-26 14:53:17.220:INFO:oejs.Server:jetty-7.5.3.v20111011
2011-12-26 14:53:17.257:INFO:oejs.AbstractConnector:Started SelectChannelConnector@localhost:8080 STARTING
2011-12-26 14:53:17.271:INFO:oejsh.ContextHandler:started o.e.j.s.h.ContextHandler{,null}
Now, let us get the WSDL for this service by accessing the following URL: http://localhost:8080/SimpleInterestCalculator2?wsdl
The following is the WSDL:
<wsdl:definitions xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://polarsparc.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="SimpleInterestCalculatorService"
targetNamespace="http://polarsparc.com/">
<wsdl:types>
<xsd:schema xmlns:tns="http://polarsparc.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="unqualified"
targetNamespace="http://polarsparc.com/">
<xsd:element name="Exception" type="tns:Exception"/>
<xsd:complexType name="Exception">
<xsd:sequence>
<xsd:element minOccurs="0" name="message" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="calculateSimpleInterest2" type="tns:calculateSimpleInterest2"/>
<xsd:complexType name="calculateSimpleInterest2">
<xsd:sequence>
<xsd:element name="months" type="xsd:int"/>
<xsd:element name="amount" type="xsd:double"/>
<xsd:element name="rate" type="xsd:double"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="calculateSimpleInterest2Response"
type="tns:calculateSimpleInterest2Response"/>
<xsd:complexType name="calculateSimpleInterest2Response">
<xsd:sequence>
<xsd:element name="return" type="xsd:double"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="Exception">
<wsdl:part element="tns:Exception" name="Exception"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterest2">
<wsdl:part element="tns:calculateSimpleInterest2" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterest2Response">
<wsdl:part element="tns:calculateSimpleInterest2Response" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:portType name="SimpleInterestCalculator">
<wsdl:operation name="calculateSimpleInterest2">
<wsdl:input message="tns:calculateSimpleInterest2" name="calculateSimpleInterest2"/>
<wsdl:output message="tns:calculateSimpleInterest2Response"
name="calculateSimpleInterest2Response"/>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SimpleInterestCalculatorServiceSoapBinding"
type="tns:SimpleInterestCalculator">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="calculateSimpleInterest2">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="calculateSimpleInterest2">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="calculateSimpleInterest2Response">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SimpleInterestCalculatorService">
<wsdl:port binding="tns:SimpleInterestCalculatorServiceSoapBinding"
name="SimpleInterestCalculatorPort">
<soap:address location="http://localhost:8080/SimpleInterestCalculator2"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
This version of the WSDL has more human friendly names compared to the previous version.
The following Java class defines the client implementation using SimpleInterestCalculator2:
To test our enhanced web service client, let us create a script called “SimpleInterestClient2.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorClient2 $1
$2 $3
NOTE :: The client takes three command line arguments: months, amount and rate
Open a Terminal window and execute the script “bin/SimpleInterestClient2.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Dec 26, 2011 2:54:56 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from WSDL: http://localhost:8080/SimpleInterestCalculator2?wsdl
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
Until now we have been passing individual parameters to our Simple Interest WebService method “calculateSimpleInterest”. What if we want to use an object to encapsulate the simple interest details ? Let us check that option as well.
The following Java class defines the SimpleInterestDTO object implementation:
Notice the use of the Java annotation @XmlRootElement to specify the name and as well as the namespace. This is the annotation from Java Architecture for XML Binding (JAXB). JAXB allows us to convert a Java object into XML and vice-versa. Internally, Apache CXF Framework uses JAXB for converting data between Java and XML. When the WebService call is made, the SimpleInterestDTO object is serialized into XML using JAXB and transported as SOAP over HTTP. On the service provider end, Apache CXF converts the XML data back into the SimpleInterestDTO object using JAXB before invoking the service method. The annotation @XmlRootElement maps the SimpleInterestDTO object to the XML root element “<dto>”. The attributes of the SimpleInterestDTO object are by default mapped as XML elements with the same name as the attributes in the object.
The following defines WebService interface using the SimpleInterestDTO object:
Next step is to implement the WebService interface SimpleInterestCalculator3. The following Java class defines the service implementation:
The following class publishes the implementation of SimpleInterestCalculator3 as a remote WebService:
To start our WebService using the SimpleInterestDTO object, let us create a script called “SimpleInterestService3.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorService3
Kill the script SimpleInterestService2.sh if it is still running.
Open a Terminal window and execute the script “bin/SimpleInterestService3.sh” from the directory $HOME/Projects/Java/CXF-WebServices and we should see something as follows:
Ready to start SimpleInterestCalculator3 jax-ws service
Dec 27, 2011 8:07:38 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculator3
Dec 27, 2011 8:07:38 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be http://localhost:8080/SimpleInterestCalculator3
2011-12-27 20:07:38.975:INFO:oejs.Server:jetty-7.5.3.v20111011
2011-12-27 20:07:39.012:INFO:oejs.AbstractConnector:Started SelectChannelConnector@localhost:8080 STARTING
2011-12-27 20:07:39.025:INFO:oejsh.ContextHandler:started o.e.j.s.h.ContextHandler{,null}
Now, let us get the WSDL for this service by accessing the following URL: http://localhost:8080/SimpleInterestCalculator3?wsdl
The following is the WSDL:
<wsdl:definitions xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://polarsparc.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="SimpleInterestCalculatorService"
targetNamespace="http://polarsparc.com/">
<wsdl:types>
<xs:schema xmlns:tns="http://polarsparc.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="unqualified"
targetNamespace="http://polarsparc.com/">
<xs:element name="SimpleInterestDTO" type="tns:simpleInterestDTO"/>
<xs:complexType name="simpleInterestDTO">
<xs:sequence>
<xs:element name="amount" type="xs:double"/>
<xs:element name="months" type="xs:int"/>
<xs:element name="rate" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Exception" type="tns:Exception"/>
<xs:complexType name="Exception">
<xs:sequence>
<xs:element minOccurs="0" name="message" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:element name="calculateSimpleInterest3" type="tns:calculateSimpleInterest3"/>
<xs:complexType name="calculateSimpleInterest3">
<xs:sequence>
<xs:element minOccurs="0" name="dto" type="tns:simpleInterestDTO"/>
</xs:sequence>
</xs:complexType>
<xs:element name="calculateSimpleInterest3Response"
type="tns:calculateSimpleInterest3Response"/>
<xs:complexType name="calculateSimpleInterest3Response">
<xs:sequence>
<xs:element name="return" type="xs:double"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="Exception">
<wsdl:part element="tns:Exception" name="Exception"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterest3">
<wsdl:part element="tns:calculateSimpleInterest3" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterest3Response">
<wsdl:part element="tns:calculateSimpleInterest3Response" name="parameters"></wsdl:part>
</wsdl:message>
<wsdl:portType name="SimpleInterestCalculator">
<wsdl:operation name="calculateSimpleInterest3">
<wsdl:input message="tns:calculateSimpleInterest3" name="calculateSimpleInterest3"/>
<wsdl:output message="tns:calculateSimpleInterest3Response"
name="calculateSimpleInterest3Response"/>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SimpleInterestCalculatorServiceSoapBinding"
type="tns:SimpleInterestCalculator">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="calculateSimpleInterest3">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="calculateSimpleInterest3">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="calculateSimpleInterest3Response">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SimpleInterestCalculatorService">
<wsdl:port binding="tns:SimpleInterestCalculatorServiceSoapBinding"
name="SimpleInterestCalculatorPort">
<soap:address location="http://localhost:8080/SimpleInterestCalculator3"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
In this version of the WSDL, notice the complex type named calculateSimpleInterest3 - it indicates that an object of type SimpleInterestDTO is passed to the service method.
The following Java class defines the client implementation using SimpleInterestCalculator3:
To test our WebService client using the SimpleInterestDTO object, let us create a script called “SimpleInterestClient3.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.standalone.SimpleInterestCalculatorClient3 $1
$2 $3
NOTE :: The client takes three command line arguments: months, amount and rate
Open a Terminal window and execute the script “bin/SimpleInterestClient3.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Dec 27, 2011 8:07:59 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from WSDL: http://localhost:8080/SimpleInterestCalculator3?wsdl
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
Until now all our examples were standalone JAX-WS WebServices that used the embedded Jetty server in the Apache CXF Framework for exposing our services. We will now look at exposing our WebSevice via the Apache Tomcat Web container.
Code-first WebServices in Tomcat
Until now all our examples were standalone JAX-WS WebServices that used the embedded Jetty server in the Apache CXF Framework for exposing our services.
In this section, we will expose our WebSevice via the Apache Tomcat Web container. The way to deploy our WebService in Tomcat is to create and deploy a Web Application Archive (war) file. All Web Application Archives follow a well defined directory structure which is as follows:
WEB-INF : This directory will contain deployment descriptors and other resource files
WEB-INF/classes : This directory will contain all the class files that belong to the application
WEB-INF/lib : This directory will contain all the dependent jars
The Web Application Archive (file with .war extension) is nothing more than a JAR file of the above specified directory structure with all the files.
The first thing we need for the Web Application is the XML deployment descriptor called web.xml.
The following is the resources/web.xml for our CXF WebServices deployment in Tomcat:
The Web deployment descriptor XML file is stored in the “resources” directory of our project.
By default, the Apache CXF Framework uses Spring to initialize and wire-up the various components and extensions to make the runtime environment ready for use. When we deploy Apache CXF in Tomcat, there needs to be some component that can kick start Spring to bring the CXF runtime alive. That is exactly what the servlet listener class org.springframework.web.context.ContextLoaderListener does. But how does the ContextLoaderListener know what components and extensions of CXF to load ? This is where the XML configuration file cxf-servlet.xml comes into play.
The following is the resources/cxf-servlet.xml for our CXF WebServices deployment in Tomcat:
As we can see, the cxf-servlet.xml configuration file imports the necessary CXF XML configuration file cfx.xml. In addition, this is also the configuration file where we specify all the service endpoints (jaxws:endpoint) we want enabled and exposed. In our example, we have specified the financial calculator SimpleInterestCalculator to be exposed as the service endpoint. This configuration XML file is stored in the “resources” directory of our project.
The last piece of the puzzle is the org.apache.cxf.transport.servlet.CXFServlet. It serves as an adapter that intercepts incoming requests and routes to the appropriate service endpoints.
The following defines the SEI for the simple interest calculator:
The following Java class defines the service implementation:
To create the Web Application Archive (file with .war extension) for deployment into Tomcat, we use the ANT build script.
The following is the ANT build XML build.xml:
When we run the ANT build script, it creates the deployable WAR file called CXFWebServices.war in the project “build” directory. To deploy this WAR file in Tomcat, simply copy the WAR file in the Tomcat “webapps” directory. Remember from Part-1 that we had setup the Apache Tomcat web container in the directory $HOME/Programs/apache-tomcat-6.0.35.
After we copy CXFWebServices.war into $HOME/Programs/apache-tomcat-6.0.35/webapps directory and start the Tomcat server, we see the following in the Tomcat log file:
Dec 30, 2011 10:19:09 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments
was not found on the java.library.path: /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/server:
/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64:/usr/lib/jvm/java-6-sun-1.6.0.26/jre/../lib/amd64:
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Dec 30, 2011 10:19:09 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
Dec 30, 2011 10:19:09 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 427 ms
Dec 30, 2011 10:19:09 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Dec 30, 2011 10:19:09 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.35
Dec 30, 2011 10:19:09 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor manager.xml
Dec 30, 2011 10:19:10 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor host-manager.xml
Dec 30, 2011 10:19:10 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive CXFWebServices.war
Dec 30, 2011 10:19:10 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Dec 30, 2011 10:19:10 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Fri Dec 30 22:19:10 EST 2011]; root of context hierarchy
Dec 30, 2011 10:19:10 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/cxf-servlet.xml]
Dec 30, 2011 10:19:10 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Dec 30, 2011 10:19:10 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf-servlet.xml]
Dec 30, 2011 10:19:10 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in
org.springframework.beans.factory.support.DefaultListableBeanFactory@35f38fc6: defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,
org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,org.apache.cxf.bus.spring.BusExtensionPostProcessor,
org.apache.cxf.binding.soap.SoapBindingFactory,org.apache.cxf.binding.soap.SoapTransportFactory,
org.apache.cxf.binding.soap.customEditorConfigurer,SimpleInterestCalculator]; root of factory hierarchy
Dec 30, 2011 10:19:10 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Dec 30, 2011 10:19:11 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /SimpleInterestCalculator
Dec 30, 2011 10:19:11 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 1212 ms
You can get the WSDL for this service by accessing the following URL: http://localhost:8080/CXFWebServices/services/SimpleInterestCalculator?wsdl
The following is the WSDL:
<wsdl:definitions xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://polarsparc.com/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
name="SimpleInterestCalculatorService"
targetNamespace="http://polarsparc.com/">
<wsdl:types>
<xs:schema xmlns:tns="http://polarsparc.com/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
attributeFormDefault="unqualified"
elementFormDefault="unqualified"
targetNamespace="http://polarsparc.com/">
<xs:element name="calculateSimpleInterest" type="tns:calculateSimpleInterest"/>
<xs:element name="calculateSimpleInterestResponse"
type="tns:calculateSimpleInterestResponse"/>
<xs:complexType name="calculateSimpleInterest">
<xs:sequence>
<xs:element name="months" type="xs:int"/>
<xs:element name="amount" type="xs:double"/>
<xs:element name="rate" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="calculateSimpleInterestResponse">
<xs:sequence>
<xs:element name="interest" type="xs:double"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Exception" type="tns:Exception"/>
<xs:complexType name="Exception">
<xs:sequence>
<xs:element minOccurs="0" name="message" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="calculateSimpleInterest">
<wsdl:part element="tns:calculateSimpleInterest" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="Exception">
<wsdl:part element="tns:Exception" name="Exception"></wsdl:part>
</wsdl:message>
<wsdl:message name="calculateSimpleInterestResponse">
<wsdl:part element="tns:calculateSimpleInterestResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="SimpleInterestCalculator">
<wsdl:operation name="calculateSimpleInterest">
<wsdl:input message="tns:calculateSimpleInterest" name="calculateSimpleInterest">
</wsdl:input>
<wsdl:output message="tns:calculateSimpleInterestResponse"
name="calculateSimpleInterestResponse"></wsdl:output>
<wsdl:fault message="tns:Exception" name="Exception"></wsdl:fault>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SimpleInterestCalculatorServiceSoapBinding"
type="tns:SimpleInterestCalculator">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="calculateSimpleInterest">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="calculateSimpleInterest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="calculateSimpleInterestResponse">
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault name="Exception" use="literal"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SimpleInterestCalculatorService">
<wsdl:port binding="tns:SimpleInterestCalculatorServiceSoapBinding"
name="SimpleInterestCalculatorPort">
<soap:address
location="http://localhost:8080/CXFWebServices/services/
SimpleInterestCalculator"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
The following Java class defines the client implementation using the Service Endpoint Interface (SEI) SimpleInterestCalculator4:
To test our web service client, let us create a script called “SimpleInterestClient4.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculatorClient4 $1
$2 $3
Open a Terminal window and execute the script “bin/SimpleInterestClient4.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Dec 30, 2011 10:46:39 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from WSDL: http://localhost:8080/CXFWebServices/services/SimpleInterestCalculator?wsdl
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
Yippy !!! We have successfully deployed and tested out WebService in Tomcat.
As we indicated earlier, the Apache CXF Framework uses Spring to initialize and wire-up the various components. That said, we can simplify the client code by using Spring configuration.
The following is the Spring configuration file cxf-http-client.xml that we will use with our simplified client code:
As we can see, the cxf-http-client.xml Spring configuration file indicates the Service Endpoint Interface (SEI) and the WSDL location in the client factory. This configuration file is stored in the “resources” directory of our project.
The following Java class defines the simplified client implementation using Spring for configuration and using the Service Endpoint Interface (SEI) SimpleInterestCalculator4:
Notice the use of Spring ClassPathXmlApplicationContext to load the Spring based client configuration file.
To test our simplified web service client, let us create a script called “SimpleInterestClient5.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculatorClient4 $1
$2 $3
NOTE :: The client script includes the spring framework related dependency jars
Open a Terminal window and execute the script “bin/SimpleInterestClient5.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments resources/cxf-http-client.xml (config), 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Jan 3, 2012 5:21:39 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@2a0ecd7e: startup date [Tue Jan 03 17:21:39 EST 2012]; root of context hierarchy
Jan 3, 2012 5:21:39 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [resources/cxf-http-client.xml]
Jan 3, 2012 5:21:39 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@220ca470: defining beans [client,clientFactory]; root of factory hierarchy
Jan 3, 2012 5:21:40 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculator4Service from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
Thus far the consumer (client) and the service provider (WebService) have been communicating over HTTP. In the following section, we will use HTTP over SSL (HTTPS) for communication between the consumer (client) and the service provider (WebService).
The following are the steps to enable SSL over HTTP:
Step. 1
Generate our own SSL certificate and store it in a keystore. For this, we will use the Java keytool command.
The following is the command to generate the necessary SSL certificate in a keystore file called cxfcerts.jks which is stored in the directory “resources”. Run this command from the project directory $HOME/Projects/Java/CXF-WebServices:
keytool -gentkey -alias cxfcerts -keystore resources/cxfcerts.jks -keypass cxfkey -storepass csfkey
The keytool command will prompt you with a series of questions before generating the SSL certificate in the specified keystore file.
The following is a screenshot of executing the keytool command:
Step. 2
Copy the SSL keystore file “resources/cxfcerts.jks” to the “webapps” directory under the Tomcat installation directory.
Step. 3
Enable HTTPS protocol in the Tomcat server configuration. The Tomcat server configuration is defined in the XML file “conf/server.xml”. Open the file “conf/server.xml” in an editor and locate the following line(s):
Uncomment the XML element “<Connector>” and modify it to look like the following line(s):
Notice the additional attributes that specify the keystore file and the keystore password we used in the keytool command.
Step. 4
Update the Web Application XML deployment descriptor called web.xml.
The following is the modified web.xml for our CXF WebServices deployment in Tomcat that will use SSL over HTTP:
Notice that we are using the URL prefix “/secure-services/*” for SSL.
Step. 5
Rebuild the WAR file called CXFWebServices.war and deploy it into our Tomcat “webapps” directory.
Step. 6
Start the Tomcat server and we see the following in the Tomcat log file:
Jan 5, 2012 9:08:04 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments
was not found on the java.library.path: /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/server:
/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64:/usr/lib/jvm/java-6-sun-1.6.0.26/jre/../lib/amd64:
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Jan 5, 2012 9:08:04 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
Jan 5, 2012 9:08:04 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8443
Jan 5, 2012 9:08:04 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 645 ms
Jan 5, 2012 9:08:04 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Jan 5, 2012 9:08:04 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.35
Jan 5, 2012 9:08:04 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor manager.xml
Jan 5, 2012 9:08:04 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor host-manager.xml
Jan 5, 2012 9:08:04 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive CXFWebServices.war
Jan 5, 2012 9:08:04 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Jan 5, 2012 9:08:04 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Thu Jan 05 15:08:04 EST 2012]; root of context hierarchy
Jan 5, 2012 9:08:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/cxf-servlet.xml]
Jan 5, 2012 9:08:05 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Jan 5, 2012 9:08:05 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2031688f:
defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,
org.apache.cxf.bus.spring.BusExtensionPostProcessor,SimpleInterestCalculator]; root of factory hierarchy
Jan 5, 2012 9:08:05 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Jan 5, 2012 9:08:06 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /SimpleInterestCalculator
Jan 5, 2012 9:08:06 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 1149 ms
Jan 5, 2012 9:08:06 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Thu Jan 05 15:08:06 EST 2012]; parent: Root WebApplicationContext
Jan 5, 2012 9:08:06 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [jndi:/localhost/CXFWebServices/WEB-INF/cxf-servlet.xml]
Jan 5, 2012 9:08:06 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Jan 5, 2012 9:08:06 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@636f2067:
defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,
org.apache.cxf.bus.spring.BusExtensionPostProcessor,SimpleInterestCalculator]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@2031688f
Jan 5, 2012 9:08:06 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Jan 5, 2012 9:08:06 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /SimpleInterestCalculator
Jan 5, 2012 9:08:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory ROOT
Jan 5, 2012 9:08:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory docs
Jan 5, 2012 9:08:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory examples
Jan 5, 2012 9:08:06 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
Jan 5, 2012 9:08:06 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8443
Jan 5, 2012 9:08:06 PM org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
Jan 5, 2012 9:08:06 PM org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/13 config=null
Jan 5, 2012 9:08:06 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 1863 ms
You can get the WSDL for this service by accessing the following URL: https://localhost:8443/CXFWebServices/services/SimpleInterestCalculator?wsdl
As we can see from the Tomcat log output, the SSL transport on port 8443 has successfully started and so is our CXF WebService.
Step. 7
Create a new Spring configuration file for our WebService client to use SSL.
The following is the Spring configuration file cxf-https-client.xml that we will use with our simplified client code:
As we can see, the cxf-https-client.xml Spring configuration file specifies the various SSL parameters and the SSL based WSDL location in the client factory. This configuration file is stored in the “resources” directory of our project.
Step. 8
Test the WebService client using SSL. Open a Terminal window and execute the script “bin/SimpleInterestClient5.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments resources/cxf-https-client.xml (config), 12 (months), 10000 (amount), and 2.75 (rate) and we should see something as follows:
Jan 5, 2012 9:37:54 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4def8cf3:
startup date [Thu Jan 05 15:37:54 EST 2012]; root of context hierarchy
Jan 5, 2012 9:37:55 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [resources/cxf-https-client.xml]
Jan 5, 2012 9:37:55 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Jan 5, 2012 9:37:55 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@69d95da8:
defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,
org.apache.cxf.bus.spring.BusExtensionPostProcessor,client,clientFactory,*.http-conduit]; root of factory hierarchy
Jan 5, 2012 9:37:55 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculator4Service from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Amount: 10000, Rate: 2.75, Months: 12, Interest: 275.00
Hooray !!! We did it !!! We have successfully tested the SSL (HTTPS) communication between the consumer (client) and the service provider (WebService).
There are some quirks that we should be aware of with the client specific Spring configuration file cxf-https-client.xml.
Remember in Step 1, we created our own self-signed SSL certificate. As a result, if we do not include the attribute disableCNCheck=”true” on the <http:tlsClientParameters> element, we will encounter the following exception: Caused by: java.io.IOException: IOException invoking https://localhost:8443/CXFWebServices/secure-services/SimpleInterestCalculator?wsdl: The https URL hostname does not match the Common Name (CN) on the server certificate. To disable this check (NOT recommended for production) set the CXF client TLS configuration property "disableCNCheck" to true.
If we do not include <import resource=”classpath:META-INF/cxf/cxf.xml”/>, we will encounter the following exception: Caused by: javax.net.ssl.SSLHandshakeException: SSLHandshakeException invoking https://localhost:8443/CXFWebServices/secure-services/SimpleInterestCalculator?wsdl: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Contract-first WebServices
All our examples thus far was based on the Simple Interest Calculator. For the Contract-first WebServices, we will use the example of the Monthly Mortgage Payment Calculator. In the Contract-first WebServices, we start with the WSDL and then generate code using the tool(s) that come with the Apache CXF Framework.
We will first define a WSDL contract for the Monthly Mortgage Payment Calculator. The following is the WSDL XML file called resources/MonthlyMortgageCalculator.xml located in the “resources” directory of our project:
As we can see the Monthly Mortgage Payment Calculator interface defines one operation called “calculateMonthlyMortgage” which accepts an input parameter of type “MonthlyMortgageRequest” and returns an output of type “MonthlyMortgageResponse”.
The type “MonthlyMortgageRequest” is a complex data type that encapsulates 3 primitive data types “int” for years, “double” for principal amount, and “double” for the interest rate.
The type “MonthlyMortgageResponse” is a complex data type that encapsulates the primitive data type “double” for the monthly payment.
Now that we have defined the Monthly Mortgage Payment Calculator WebService interface, the next step is to generate Java code from this WSDL file. To do that, we will use the tool called wsdl2java that is supplied with the Apache CXF Framework. We can either use the command line tool or use an ANT task which can be called from the ANT build script. We choose the option to use the ANT task.
The following is our modified ANT build XML build.xml:
We have highlighted the sections that were added to the build.xml file to enable code generation for the given WSDL resources/MonthlyMortgageCalculator.xml using the Apache CXF Framework provided ANT task org.apache.cxf.tools.wsdlto.WSDLToJava. We have specified a number of options for the ANT task, which are described in the following table:
Options | Description |
---|---|
-p | This option specifies the package name to
be
used for the generated code. If not specified, it will use the value of
the targetNamespace attribute from the WSDL |
-keep | This option specifies that the code generator not overwrite any exisiting files |
-impl | This option specifies that code be generated for the Service Endpoint Interface implementation |
-d | This option specifies the directory where the code is to be generated. For our case we have specified it to be the “src” directory |
When we execute the ANT build script for the target “generated”, we will see the following files in the “com/polarsparc/cxf/jaxws/generated” directory under “src”:
MonthlyMortgageCalculator.java
MonthlyMortgageRequest.java
MonthlyMortgageResponse.java
ObjectFactory.java
MonthlyMortgageCalculatorImpl.java
MonthlyMortgageCalculatorService.java
package-info.java
The 1st file MonthlyMortgageCalculator.java is the Service Endpoint Interface (SEI), which looks as follows:
The 2nd file MonthlyMortgageRequest.java is the Request Wrapper object, which looks as follows:
The 3rd file MonthlyMortgageResponse.java is the Response Wrapper object, which looks as follows:
The 4th file ObjectFactory.java is the factory object for creating the request and response wrapper objects, which looks as follows:
The 5th file MonthlyMortgageCalculatorImpl.java is an empty skeleton for the Service Endpoint Interface (SEI) implementation class, which looks as follows:
The remaining files are not that important for our Monthly Mortgage Calculator service.
As we indicated earlier, the 5th file MonthlyMortgageCalculatorImpl.java is just a skeleton of the implementation class. We need to modify it by filling in the missing logic. The following is the modified MonthlyMortgageCalculatorImpl.java file:
We have highlighted the sections that were added to the MonthlyMortgageCalculatorImpl.java file.
Now that we have all the pieces, we will rebuild the WAR file called CXFWebServices.war and deploy it into our Tomcat “webapps” directory.
When we restart the Tomcat server, we see the following in the Tomcat log file:
Jan 7, 2012 5:50:02 PM org.apache.catalina.core.AprLifecycleListener init
INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments
was not found on the java.library.path: /usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64/server:
/usr/lib/jvm/java-6-sun-1.6.0.26/jre/lib/amd64:/usr/lib/jvm/java-6-sun-1.6.0.26/jre/../lib/amd64:
/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
Jan 7, 2012 5:50:03 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8080
Jan 7, 2012 5:50:03 PM org.apache.coyote.http11.Http11Protocol init
INFO: Initializing Coyote HTTP/1.1 on http-8443
Jan 7, 2012 5:50:03 PM org.apache.catalina.startup.Catalina load
INFO: Initialization processed in 860 ms
Jan 7, 2012 5:50:03 PM org.apache.catalina.core.StandardService start
INFO: Starting service Catalina
Jan 7, 2012 5:50:03 PM org.apache.catalina.core.StandardEngine start
INFO: Starting Servlet Engine: Apache Tomcat/6.0.35
Jan 7, 2012 5:50:03 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor manager.xml
Jan 7, 2012 5:50:03 PM org.apache.catalina.startup.HostConfig deployDescriptor
INFO: Deploying configuration descriptor host-manager.xml
Jan 7, 2012 5:50:03 PM org.apache.catalina.startup.HostConfig deployWAR
INFO: Deploying web application archive CXFWebServices.war
Jan 7, 2012 5:50:04 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Jan 7, 2012 5:50:04 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Sat Jan 07 17:50:04 EST 2012]; root of context hierarchy
Jan 7, 2012 5:50:04 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from ServletContext resource [/WEB-INF/cxf-servlet.xml]
Jan 7, 2012 5:50:04 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Jan 7, 2012 5:50:05 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@61128f5a:
defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,
org.apache.cxf.bus.spring.BusExtensionPostProcessor,SimpleInterestCalculator,MonthlyMortgageCalculator];
root of factory hierarchy
Jan 7, 2012 5:50:05 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Jan 7, 2012 5:50:05 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /SimpleInterestCalculator
Jan 7, 2012 5:50:05 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}MonthlyMortgageCalculatorService from WSDL:
/home/bswamina/Projects/Java/CXF-WebServices/resources/MonthlyMortgageCalculator.wsdl
Jan 7, 2012 5:50:05 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /MonthlyMortgageCalculator
Jan 7, 2012 5:50:05 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 1292 ms
Jan 7, 2012 5:50:05 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Sat Jan 07 17:50:05 EST 2012];
parent: Root WebApplicationContext
Jan 7, 2012 5:50:06 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from URL [jndi:/localhost/CXFWebServices/WEB-INF/cxf-servlet.xml]
Jan 7, 2012 5:50:06 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [META-INF/cxf/cxf.xml]
Jan 7, 2012 5:50:06 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@5585c0de:
defining beans [cxf,org.apache.cxf.bus.spring.BusWiringBeanFactoryPostProcessor,org.apache.cxf.bus.spring.Jsr250BeanPostProcessor,
org.apache.cxf.bus.spring.BusExtensionPostProcessor,SimpleInterestCalculator,MonthlyMortgageCalculator]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@61128f5a
Jan 7, 2012 5:50:06 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
INFO: Creating Service {http://polarsparc.com/}SimpleInterestCalculatorService from class com.polarsparc.cxf.jaxws.webapp.SimpleInterestCalculator4
Jan 7, 2012 5:50:06 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /SimpleInterestCalculator
Jan 7, 2012 5:50:06 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}MonthlyMortgageCalculatorService from WSDL:
/home/bswamina/Projects/Java/CXF-WebServices/resources/MonthlyMortgageCalculator.wsdl
Jan 7, 2012 5:50:06 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /MonthlyMortgageCalculator
Jan 7, 2012 5:50:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory ROOT
Jan 7, 2012 5:50:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory docs
Jan 7, 2012 5:50:06 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory examples
Jan 7, 2012 5:50:06 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8080
Jan 7, 2012 5:50:06 PM org.apache.coyote.http11.Http11Protocol start
INFO: Starting Coyote HTTP/1.1 on http-8443
Jan 7, 2012 5:50:06 PM org.apache.jk.common.ChannelSocket init
INFO: JK: ajp13 listening on /0.0.0.0:8009
Jan 7, 2012 5:50:06 PM org.apache.jk.server.JkMain start
INFO: Jk running ID=0 time=0/14 config=null
Jan 7, 2012 5:50:06 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2839 ms
As we can see from the Tomcat log output, our Monthly Mortgage Payment service has successfully started.
The following Java class defines the client implementation using the Service Endpoint Interface (SEI) MonthlyMortgageCalculator:
To test our web service client, let us create a script called “MortgageCalculator.sh” in the “bin” directory as follows:
CP=.:./build/classes:$CXF_HOME/lib/commons-logging-1.1.1.jar:
$CXF_HOME/lib/cxf-2.5.1.jar:$CXF_HOME/lib/geronimo-activation_1.1_spec-1.1.jar:
$CXF_HOME/lib/geronimo-annotation_1.0_spec-1.1.1.jar:
$CXF_HOME/lib/geronimo-jaxws_2.2_spec-1.1.jar:
$CXF_HOME/lib/geronimo-servlet_2.5_spec-1.1.2.jar:
$CXF_HOME/lib/geronimo-stax-api_1.0_spec-1.0.1.jar:
$CXF_HOME/lib/geronimo-ws-metadata_2.0_spec-1.1.3.jar:
$CXF_HOME/lib/jaxb-api-2.2.3.jar:
$CXF_HOME/lib/jaxb-impl-2.2.4-1.jar:
$CXF_HOME/lib/jetty-continuation-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-http-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-io-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-server-7.5.3.v20111011.jar:
$CXF_HOME/lib/jetty-util-7.5.3.v20111011.jar:
$CXF_HOME/lib/msv-core-2011.1.jar:
$CXF_HOME/lib/neethi-3.0.1.jar:
$CXF_HOME/lib/stax2-api-3.1.1.jar:
$CXF_HOME/lib/woodstox-core-asl-4.1.1.jar:
$CXF_HOME/lib/wsdl4j-1.6.2.jar:
$CXF_HOME/lib/xmlschema-core-2.0.1.jar
java -cp $CP
com.polarsparc.cxf.jaxws.webapp.MonthlyMortgageCalculatorClient $1 $2 $3
Open a Terminal window and execute the script “bin/MortgageCalculator.sh” from the directory $HOME/Projects/Java/CXF-WebServices with arguments 15 (years), 150000 (amount), and 3.75 (rate) and we should see something as follows:
Jan 7, 2012 6:07:07 PM org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromWSDL
INFO: Creating Service {http://polarsparc.com/}MonthlyMortgageCalculatorService from WSDL:
http://localhost:8080/CXFWebServices/services/MonthlyMortgageCalculator?wsdl
Amount: 150000, Rate: 3.75, Years: 15, Payment: 1090.83
YES !!! We have successfully deployed and tested our Contract-first WebService in Tomcat.
With this we conclude our journey into JAX-WS WebServices with Apache CXF Framework.