Introduction
This article is a continuation of my post on how to develop a RESTful web service. In this one, I am going explain the procedure to develop, build, and test a SOAP base web service. The deployment environment is OSGi container as Talend ESB 5.3.1. The bundle developed here can be deployed to any OSGi container like JBoss Fuse, ServiceMix, etc. With minor modification, it can also be deployed to Tomcat container.
There are two approaches to develop a SOAP base web service. One is called bottom-up or code first approach. With this approach, develop can write SOAP based service using annotation. The container will be able to expose the service with derived WSDL. The other approach is called Top-Down or contract first approach. Each approach has its pros and cons. The rule-of-thumb for the choice is the following:
- For large enterprise applications with SOA governance, the Top-Down approach is the choice. It enforces the controlled changes, business oriented, and language neutral.
- If the organization is small, and you need quick-to-market, the code-first/bottom-up approach is preferred. However, in this case, why don't you choose, RESTful?
The source code for this project has been pushed to gitHub at: https://github.com/garyliu1119/Gary-Liu-ESB. The following is the procedures I push the code to the github:
1 2 3 4 5 | git add camel-example-cxf-osgi git commit -m "commit the came-cxf-osg exmaple" git remote add github https: //github.com/garyliu1119/Gary-Liu-ESB.git git pull github master git push github master |
Verify The Web Service
After download the project, you can compile and deploy the project to an OSGi container by the following commands:
1 | osgi:install -s mvn:org.apache.camel/camel-example-cxf-osgi/2.10.4 |
watch the log, if there is no errors. it means the Web Service is deployed and started correctly. To verify the web service, we can enter the following URL to a browser:
1 | http: //localhost:8040/services/camel-example-cxf-osgi/webservices/incident?wsdl |
Note: Different OSGi container may use different port, here I am usng 8040. And the path, services, may vary as well. The port definition is location in the file:
1 2 | ${CONTAINER_HOME}/etc/org.ops4j.pax.web.cfg org.osgi.service.http.port=8040 |
And the path is defined in the file:
1 2 | ${CONTAINER_HOME}/etcorg.apache.cxf.osgi.cfg" org.apache.cxf.servlet.context=/services |
The file for WSDL definition is located at:
1 | src/main/resources/META-INF/wsdl/report_incident.wsdl |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <soap:header> <soap:body> <incidentid>111</incidentid> <incidentdate>2011-03-05</incidentdate> <givenname>Christian</givenname> <familyname>Mueller</familyname> <summary>Bla</summary> <details>Bla bla</details> <email>cmueller@apache.org</email> <phone>0049 69 1234567</phone> </ns2:inputreportincident> </soap:body> </soap:header></soap:envelope> |
When you invoke the web service using the above soap xml, you should get the following response:
1 2 3 4 5 6 7 | <soap:body> <code>NOK</code> </ns2:outputreportincident> </soap:body> </soap:envelope> |
If you change the givenName to Claus, you will get
1 | <code>OK</code> |
The reason for this is in our route, we look for the giveName in the message body, if it is Claus, we return code of "OK". If you look into the camel route, this will become clear.
pom.xml
The origin of this project is from Apache Camel examples. The code has been distributed with many other ESB continers. The original route is in Java DSL. I converted that to Spring DSL.
There are few interesting points should be noted about this project. The first is the pom.xml. In this file, there is a plugin to generate java code from wsdl, name wsdl2java. You can find the detailed documentation about this utility at
- https://cxf.apache.org/docs/wsdl-to-java.html
- https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html
Camel Route
Here is complete list of the file of camel-context.xml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <!--xml version= "1.0" encoding= "UTF-8" ?--> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:camel= "http://camel.apache.org/schema/spring" xmlns:cxf= "http://camel.apache.org/schema/cxf" xsi:schemalocation= " http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd <!-- <import resource= "classpath:META-INF/cxf/cxf.xml" /> --> <cxf:cxfendpoint id= "reportIncident" address= "/camel-example-cxf-osgi/webservices/incident" wsdlurl= "META-INF/wsdl/report_incident.wsdl" serviceclass= "org.apache.camel.example.reportincident.ReportIncidentEndpoint" > <!-- <bean id= "reportIncidentRoutes" class= "org.apache.camel.example.reportincident.ReportIncidentRoutes" /> <camel:camelContext id= "camel" > <camel:routeBuilder ref= "reportIncidentRoutes" /> </camel:camelContext> --> <bean id= "OK" class= "org.apache.camel.example.reportincident.OutputReportIncident" > <property name= "code" value= "OK" ></property> </bean> <bean id= "NOK" class= "org.apache.camel.example.reportincident.OutputReportIncident" > <property name= "code" value= "NOK" ></property> </bean> <camel:camelcontext id= "camel" > <camel:route> <camel:from uri= "cxf:bean:reportIncident" > <camel:convertbodyto type= "org.apache.camel.example.reportincident.InputReportIncident" > <camel:setheader headername= "Exchange.FILE_NAME" > <camel:constant>request-${date:now:yyyy-MM-dd-HHmmssSSS}</camel:constant> </camel:setheader> <camel:log message= "Show file name: ${headers.CamelFileName}" ></camel:log> <camel:log message= "Show file name: ${headers[CamelFileName]}" ></camel:log> <camel:choice> <camel:when> <camel:simple>${body.givenName} == 'Claus' </camel:simple> <camel:transform> <camel:simple>ref:OK</camel:simple> </camel:transform> </camel:when> <camel:otherwise> <camel:transform> <camel:simple>ref:NOK</camel:simple> </camel:transform> </camel:otherwise> </camel:choice> </camel:convertbodyto></camel:from></camel:route> </camel:camelcontext> </cxf:cxfendpoint></beans> |
The the above camel-context, you can see that the web service is exposed through a pojo: ReportIncidentEndpoint. It is an java inteface like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @WebService(targetNamespace = "http://reportincident.example.camel.apache.org" , name = "ReportIncidentEndpoint" ) @XmlSeeAlso({ObjectFactory.class}) @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava" , date = "2014-03-07T14:05:18.457-06:00" , comments = "Apache CXF 2.6.6" ) public interface ReportIncidentEndpoint { @WebResult(name = "outputReportIncident" , targetNamespace = "http://reportincident.example.camel.apache.org" , partName = "out" ) @WebMethod(operationName = "ReportIncident" , action = "http://reportincident.example.camel.apache.org/ReportIncident" ) @Generated(value = "org.apache.cxf.tools.wsdlto.WSDLToJava" , date = "2014-03-07T14:05:18.457-06:00" ) public OutputReportIncident reportIncident( @WebParam(partName = "in" , name = "inputReportIncident" , targetNamespace = "http://reportincident.example.camel.apache.org" ) InputReportIncident in ); } |
Note: I did not implement the interface method. The CXF library provide default immplementaion. The web service is a cxfEndpoint. The reference for