Hi,
JBoss AS7 uses the JBossWS-CXF as default webservice provider which provides most of the features coming with Apache CXF (including WS-Security, WS-Policy, WS-Addressing, WS-ReliableMessaging, basic WS-Trust, MTOM) as well as common JBossWS stack features like endpoint metrics, record management, endpoint address rewrite, etc.
In this example we will see how we can use a Complex DataType inside our CXF Based WebService and how we can enable the client side Logging for the incoming and outgoing SOAP Request/Responses, which are most desired for trouble shooting and debugging purpose.
In this example we will mainly focus on following points
Point-1). How to use the JBoss Specific Tools to build the WebServices. The details of these tools like “wsconsume” and “wsprovide” are mentioned in the following link: https://docs.jboss.org/author/display/JBWS/JAX-WS+Tools
Point-2). Where we need to place the “log4j.properties fileat the client side in order to generate a separate Log file containing the SOAP request/response.
Point-3). How to use “org.apache.cxf.interceptor.LoggingInInterceptor” and “org.apache.cxf.interceptor.LoggingOutInterceptor” at client side.
Point-4). Here we are using JBoss AS7.1.0 CR1b which can be downloaded from the following link: http://www.jboss.org/jbossas/downloads. I am using “jboss-as-7.1.0.CR1bb” in this demo.
Point-5). How to use the JAXB Annotations like “XmlAccessorType”, “XmlAccessType” and “XmlType” to define and use the Complex Datatypes inside our webservice using the “SOAPBinding.ParameterStyle.BARE”
Point-6). Source Code of this Demo is available at “https://github.com/jaysensharma/MiddlewareMagicDemos“
Developing Simple WebService
Step-1). Create a directory somewhere in your file system as “/home/userone/ComplexTypes_CXF_WebService_JBossAS7” then create another directory “src” inside the “/home/userone/ComplexTypes_CXF_WebService_JBossAS7”.
Step-2). Write a simple webservice implementation pojo class “DemoCXF.java” as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7/src”:
package ws; import javax.jws.WebParam; import javax.jws.WebMethod; import javax.jws.WebResult; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; @WebService(name = "DemoCXF", serviceName ="DemoCXFService", portName ="DemoCXFPort", targetNamespace="http://test.org") @SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE) public class DemoCXF { @WebMethod() @WebResult(targetNamespace="http://test.org",name="updatedEmployee") public Employee processEmployeeSalary(@WebParam(partName = "employee", name = "employee", targetNamespace = "http://test.org") Employee emp, @WebParam(partName = "incrementAmount", name = "incrementAmount", targetNamespace = "http://test.org") Long incrementAmount) { System.out.println("[DemoCXF] Method Invoked....processEmployeeSalary"); System.out.println("[DemoCXF] Before processing: "+emp); long incrementedSalary=emp.getEmpSalary()+incrementAmount; emp.setEmpSalary(incrementedSalary); System.out.println("[DemoCXF] After processing: "+emp); // Some Business Logic to Store the Employee's Updated Details in Database or Messaging System. return emp; } }
Step-3). Write a simple Complex Type “Employee.java” as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7/src”:
package ws; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "employee", propOrder = { "empName", "empNo", "empSalary" }) public class Employee { public String empName; public Long empNo; public Long empSalary; public String getEmpName() { return empName; } public void setEmpName(String value) { this.empName = value; } public Long getEmpNo() { return empNo; } public void setEmpNo(Long value) { this.empNo = value; } public Long getEmpSalary() { return empSalary; } public void setEmpSalary(Long value) { this.empSalary = value; } }
Step-4). Write the “web.xml” file to define the WebService as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7/src”
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <servlet> <servlet-name>DemoCXF</servlet-name> <servlet-class>ws.DemoCXF</servlet-class> </servlet> <servlet-mapping> <servlet-name>DemoCXF</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
Step-5). Write the WebService client code “Test_CXF_Client.java” as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7/src”:
package client; import java.net.URL; import javax.xml.namespace.QName; // Client side Logging related CXF APIs import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.interceptor.LoggingInInterceptor; import org.apache.cxf.interceptor.LoggingOutInterceptor; public class Test_CXF_Client { public static void main(String ar[]) throws Exception { String WSDL_URL=ar[0]+"?wsdl"; DemoCXFService service=new DemoCXFService(new URL(WSDL_URL)); DemoCXF port=service.getDemoCXFPort(); /* Following part of code is needed for client side Logging of Soap request/response */ /* We need to make sure that we place the "log4j.properties" file inside clients classpath */ Client client = ClientProxy.getClient(port); client.getInInterceptors().add(new LoggingInInterceptor()); client.getOutInterceptors().add(new LoggingOutInterceptor()); Employee emp=new Employee(); emp.setEmpNo(1000L); emp.setEmpName("MiddlewaremagicEmployee"); emp.setEmpSalary(6000L); System.out.println("nnBefore EmpNo: "+emp.getEmpNo()+", Name:"+emp.getEmpName()+", Sal:"+emp.getEmpSalary()); emp=port.processEmployeeSalary(emp,1000L); System.out.println("nnAfter EmpNo: "+emp.getEmpNo()+", Name:"+emp.getEmpName()+", Sal:"+emp.getEmpSalary()); } }
Step-5). As we are going to generate the client side logging in a separate log file at client side using CXF APIs so we will need to write a “log4j.properties” file as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7/src”:
# Direct log messages to a log file log4j.rootLogger=INFO, WSClientAppender log4j.appender.WSClientAppender=org.apache.log4j.ConsoleAppender log4j.appender.WSClientAppender.layout=org.apache.log4j.PatternLayout log4j.appender.WSClientAppender.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
NOTE: In this example we are using the “ConsoleAppender” whihc means in the Clients Console (Shell Pormpt) we will be able to see the Incoming and Outgoing soap request/response.
Step-6). Now we will write simple ANT build script “build.xml” which will build / deploy and run the webservice & client as following inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7”:
<project name="JBoss_Complex_Service" default="deploy"> <property name="jboss.home" value="/home/userone/jboss-as-7.1.0.CR1b" /> <property name="jboss.module.dir" value="${jboss.home}/modules" /> <property name="basedir" value="." /> <property name="war.dir" value="CXFComplexTypeDemoWAR" /> <property name="war.name" value="CXFComplexTypeDemo.war" /> <property name="src.dir" value="src" /> <property name="client.src.dir" value="${basedir}/clientSrc" /> <property name="output.dir" value="build" /> <property name="client.dir" value="${basedir}/clientStuff" /> <property name="client.jar.name" value="DemoCXFClient.jar" /> <path id="jboss.classpath"> <fileset dir="${jboss.module.dir}"> <include name="**/*.jar"/> </fileset> </path> <path id="client.classpath"> <fileset dir="${jboss.module.dir}"> <include name="**/*.jar"/> </fileset> <fileset dir="${client.dir}"> <include name="*.jar"/> </fileset> </path> <taskdef name="wsprovide" classname="org.jboss.ws.tools.ant.WSProvideTask"> <classpath refid="jboss.classpath"/> </taskdef> <taskdef name="wsconsume" classname="org.jboss.ws.tools.ant.WSConsumeTask"> <classpath refid="jboss.classpath"/> </taskdef> <target name="init"> <delete dir="${output.dir}" /> <mkdir dir="${output.dir}" /> <mkdir dir="${output.dir}/${war.dir}"/> <mkdir dir="${output.dir}/${war.dir}/META-INF"/> <mkdir dir="${output.dir}/${war.dir}/WEB-INF"/> <mkdir dir="${output.dir}/${war.dir}/WEB-INF/classes"/> <delete dir="${client.dir}" /> <mkdir dir="${client.dir}/META-INF"/> </target> <target name="build" depends="init"> <javac srcdir="${src.dir}" destdir="${output.dir}/${war.dir}/WEB-INF/classes" includes="*.java" excludes="Test_CXF_Client.java" classpathref="jboss.classpath"/> <copy todir="${output.dir}/${war.dir}/WEB-INF"> <fileset dir="${basedir}/src"> <include name="web.xml"/> </fileset> </copy> <wsprovide fork="false" keep="true" destdir="${output.dir}" resourcedestdir="${output.dir}/${war.dir}/WEB-INF/wsdl" sourcedestdir="${output.dir}" genwsdl="true" verbose="true" sei="ws.DemoCXF"> <classpath> <pathelement path="${output.dir}/${war.dir}/WEB-INF/classes"/> </classpath> </wsprovide> <jar jarfile="${output.dir}/${war.name}" basedir="${output.dir}/${war.dir}" compress="true" /> </target> <target name="deploy" depends="build"> <echo message="******************* Deploying *********************" /> <echo message="********** ${war.name} to ${jboss.home}/standalone/deployments **********" /> <copy file="${output.dir}/${war.name}" todir="${jboss.home}/standalone/deployments/"/> <echo message="******************* Deployed Successfully *********************" /> </target> <target name="post-deploy" > <echo message="******************* NOTE *********************" /> <echo message="***** You should be able to access your WSDL using Browser now *****" /> <echo message=" http://localhost:8080/CXFComplexTypeDemo?wsdl " /> </target> <target name="client" depends="post-deploy"> <delete dir="${client.dir}" /> <mkdir dir="${client.dir}"/> <wsconsume fork="true" keep="true" destdir="${client.dir}" sourcedestdir="${client.dir}" package="client" wsdlLocation="service.wsdl" wsdl="http://localhost:8080/CXFComplexTypeDemo?wsdl"> </wsconsume> <!-- We need to make sure that log4j.properties is present inside client's classpath--> <copy file="${src.dir}/log4j.properties" todir="${client.dir}" /> <javac srcdir="${src.dir}" destdir="${client.dir}" includes="Test_CXF_Client.java" classpathref="client.classpath"> <classpath> <pathelement location="${client.dir}"/> <path refid="client.classpath"/> </classpath> </javac> <jar jarfile="${client.dir}/${client.jar.name}" basedir="${client.dir}" compress="true" /> <!-- Cleaning Client Stuff Directory --> <delete dir="${client.dir}/client"/> <delete file="${client.dir}/log4j.properties"/> </target> <target name="run" depends="client"> <java classname="client.Test_CXF_Client" fork="true" > <classpath> <pathelement location="${client.dir}/DemoCXFClient.jar"/> <path refid="client.classpath"/> </classpath> <arg value="http://localhost:8080/CXFComplexTypeDemo"/> </java> </target> </project>
NOTE: The only change in the above file you need to do is to change the “jboss.home” directory path in the second line of the above script to point to your own JBoss AS7 directory.
Point-7). Now Start your JBoss Profile as following from inside the directory “/home/userone/jboss-as-7.1.0.CR1b/bin”:
. ./standalone.sh -c standalone-full.xml .
Step-8). Now before running your ANT script to build and deploy the above webapplication you should have the ANT as well as JAVA set in the $PATH variable of the Shell / command prompt as following:
For Unix Based OS: export PATH=/home/userone/jdk1.6.0_21/bin:/home/userone/org.apache.ant_1.6.5/bin:$PATH For Windows Based OS: set PATH=C:/jdk1.6.0_21/bin;C:/org.apache.ant_1.6.5/bin;%PATH%
Step-9). Now once the PATH is set In the command/Shell prompt you can move inside the directory “/home/userone/ComplexTypes_CXF_WebService_JBossAS7” and then run the ant to build and deploy the WebService application on your JBoss Standalone full profile, by running the command “ant deploy”
[userone@localhost ComplexTypes_CXF_WebService_JBossAS7]$ ant deploy Buildfile: build.xml init: [delete] Deleting directory /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/META-INF [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/WEB-INF [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/WEB-INF/classes [delete] Deleting directory /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff/META-INF build: [javac] Compiling 2 source files to /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/WEB-INF/classes [copy] Copying 1 file to /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/WEB-INF [wsprovide] Generating from endpoint: ws.DemoCXF [wsprovide] log4j:WARN No appenders could be found for logger (org.apache.cxf.common.logging.LogUtils). [wsprovide] log4j:WARN Please initialize the log4j system properly. [wsprovide] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. [wsprovide] java2ws -s /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build -classdir /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build -d /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemoWAR/WEB-INF/wsdl -verbose -wsdl -cp ::::::::: -wrapperbean -createxsdimports ws.DemoCXF [wsprovide] java2ws - Apache CXF 2.4.4 [wsprovide] [jar] Building jar: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/build/CXFComplexTypeDemo.war deploy: [echo] ******************* Deploying ********************* [echo] ********** CXFComplexTypeDemo.war to /home/userone/jboss-as-7.1.0.CR1b/standalone/deployments ********** [copy] Copying 1 file to /home/userone/jboss-as-7.1.0.CR1b/standalone/deployments [echo] ******************* Deployed Successfully ********************* BUILD SUCCESSFUL Total time: 5 seconds
As soon as the WebService will be deployed on the JBoss AS7 you will see the following kind of output on your JBoss Console (STDOUT):
15:53:37,164 INFO [org.jboss.as.server.deployment] (MSC service thread 1-2) Starting deployment of "CXFComplexTypeDemo.war" 15:53:37,203 INFO [org.jboss.wsf.stack.cxf.metadata.MetadataBuilder] (MSC service thread 1-5) Add Service id=DemoCXF address=http://localhost:8080/CXFComplexTypeDemo implementor=ws.DemoCXF invoker=org.jboss.wsf.stack.cxf.JBossWSInvoker serviceName={http://test.org}DemoCXFService portName={http://test.org}DemoCXFPort wsdlLocation=null mtomEnabled=false 15:53:37,204 INFO [org.jboss.ws.common.management.DefaultEndpointRegistry] (MSC service thread 1-5) register: jboss.ws:context=CXFComplexTypeDemo,endpoint=DemoCXF 15:53:37,213 INFO [org.apache.cxf.service.factory.ReflectionServiceFactoryBean] (MSC service thread 1-5) Creating Service {http://test.org}DemoCXFService from class ws.DemoCXF 15:53:37,238 INFO [org.apache.cxf.endpoint.ServerImpl] (MSC service thread 1-5) Setting the server's publish address to be http://localhost:8080/CXFComplexTypeDemo 15:53:37,247 INFO [org.jboss.wsf.stack.cxf.deployment.WSDLFilePublisher] (MSC service thread 1-5) WSDL published to: file:/home/userone/jboss-as-7.1.0.CR1b/standalone/data/wsdl/CXFComplexTypeDemo.war/DemoCXFService.wsdl 15:53:37,251 INFO [org.jboss.as.webservices] (MSC service thread 1-8) JBAS015539: Starting service jboss.ws.endpoint."CXFComplexTypeDemo.war".DemoCXF 15:53:37,260 INFO [org.jboss.web] (MSC service thread 1-8) registering web context: /CXFComplexTypeDemo 15:53:37,268 INFO [org.jboss.as.server] (DeploymentScanner-threads - 2) JBAS018565: Replaced deployment "CXFComplexTypeDemo.war" with deployment "CXFComplexTypeDemo.war"
Step-10). Now we will try to compile and run the WebService Client application by running the command “ant run”
[userone@localhost ComplexTypes_CXF_WebService_JBossAS7]$ ant run Buildfile: build.xml post-deploy: [echo] ******************* NOTE ********************* [echo] ***** You should be able to access your WSDL using Browser now ***** [echo] http://localhost:8080/CXFComplexTypeDemo?wsdl client: [delete] Deleting directory /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff [mkdir] Created dir: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff [wsconsume] Consuming wsdl: http://localhost:8080/CXFComplexTypeDemo?wsdl [wsconsume] log4j:WARN No appenders could be found for logger (org.apache.cxf.common.logging.LogUtils). [wsconsume] log4j:WARN Please initialize the log4j system properly. [wsconsume] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info. [copy] Copying 1 file to /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff [javac] Compiling 1 source file to /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff [jar] Building jar: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff/DemoCXFClient.jar [delete] Deleting directory /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff/client [delete] Deleting: /home/userone/ComplexTypes_CXF_WebService_JBossAS7/clientStuff/log4j.properties run: Feb 4, 2012 3:54:51 PM client.DemoCXFService <clinit> INFO: Can not initialize the default wsdl from service.wsdl 15:54:52,844 INFO ReflectionServiceFactoryBean:366 - Creating Service {http://test.org}DemoCXFService from WSDL: http://localhost:8080/CXFComplexTypeDemo?wsdl Before EmpNo: 1000, Name:MiddlewaremagicEmployee, Sal:6000 15:54:53,275 INFO LoggingOutInterceptor:151 - Outbound Message --------------------------- ID: 1 Address: http://localhost:8080/CXFComplexTypeDemo Encoding: UTF-8 Content-Type: text/xml Headers: {Accept=[*/*], SOAPAction=[""]} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:employee xmlns:ns2="http://test.org"> <empName>MiddlewaremagicEmployee</empName> <empNo>1000</empNo> <empSalary>6000</empSalary> </ns2:employee> <ns2:incrementAmount xmlns:ns2="http://test.org">1000</ns2:incrementAmount> </soap:Body> </soap:Envelope> -------------------------------------- 15:54:53,289 INFO LoggingInInterceptor:151 - Inbound Message ---------------------------- ID: 1 Response-Code: 200 Encoding: UTF-8 Content-Type: text/xml;charset=UTF-8 Headers: {Content-Length=[268], content-type=, Date=[Sat, 04 Feb 2012 10:24:52 GMT], Server=[Apache-Coyote/1.1]} Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns2:updatedEmployee xmlns:ns2="http://test.org"> <empName>MiddlewaremagicEmployee</empName> <empNo>1000</empNo> <empSalary>7000</empSalary> </ns2:updatedEmployee> </soap:Body> </soap:Envelope> -------------------------------------- After EmpNo: 1000, Name:MiddlewaremagicEmployee, Sal:7000 BUILD SUCCESSFUL Total time: 9 seconds
Step-11). The Generated WSDL File will look something like following :
http://localhost:8080/CXFComplexTypeDemo?wsdl
<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions name="DemoCXFService" targetNamespace="http://test.org" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://test.org" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://test.org" xmlns="http://test.org" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:complexType name="employee"> <xs:sequence> <xs:element minOccurs="0" name="empName" type="xs:string"/> <xs:element minOccurs="0" name="empNo" type="xs:long"/> <xs:element minOccurs="0" name="empSalary" type="xs:long"/> </xs:sequence> </xs:complexType> <xs:element name="employee" nillable="true" type="employee"/> <xs:element name="incrementAmount" nillable="true" type="xs:long"/> <xs:element name="updatedEmployee" nillable="true" type="employee"/> </xs:schema> </wsdl:types> <wsdl:message name="processEmployeeSalaryResponse"> <wsdl:part element="tns:updatedEmployee" name="updatedEmployee"> </wsdl:part> </wsdl:message> <wsdl:message name="processEmployeeSalary"> <wsdl:part element="tns:employee" name="employee"> </wsdl:part> <wsdl:part element="tns:incrementAmount" name="incrementAmount"> </wsdl:part> </wsdl:message> <wsdl:portType name="DemoCXF"> <wsdl:operation name="processEmployeeSalary"> <wsdl:input message="tns:processEmployeeSalary" name="processEmployeeSalary"> </wsdl:input> <wsdl:output message="tns:processEmployeeSalaryResponse" name="processEmployeeSalaryResponse"> </wsdl:output> </wsdl:operation> </wsdl:portType> <wsdl:binding name="DemoCXFServiceSoapBinding" type="tns:DemoCXF"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="processEmployeeSalary"> <soap:operation soapAction="" style="document"/> <wsdl:input name="processEmployeeSalary"> <soap:body use="literal"/> </wsdl:input> <wsdl:output name="processEmployeeSalaryResponse"> <soap:body use="literal"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="DemoCXFService"> <wsdl:port binding="tns:DemoCXFServiceSoapBinding" name="DemoCXFPort"> <soap:address location="http://localhost:8080/CXFComplexTypeDemo"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
.
.
Thanks 🙂
Middleware Magic Team