Showing posts with label CXF. Show all posts
Showing posts with label CXF. Show all posts

Tuesday, January 13, 2015

Spring CXF endpoint configuration in pure Java

A Spring CXF endpoint configuration in XML looks something like below.

 <import resource="classpath:META-INF/cxf/cxf.xml" />  
 <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />  
 <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
        
 <bean id="soapServiceImpl" class="SoapServiceImpl"/>  
   
 <jaxws:endpoint id="soapServiceEndpoint"  
      implementor="#soapServiceImpl" address="/soapService">  
 </jaxws:endpoint>  

To do this configuration in pure Java, simply annotate the web service with @Service and make a configuration file like below.
 import org.apache.cxf.Bus;  
 import org.apache.cxf.jaxws.EndpointImpl;  
 import org.springframework.context.annotation.Bean;  
 import org.springframework.context.annotation.Configuration;  
 import org.springframework.context.annotation.ImportResource;  
 import javax.xml.ws.Endpoint;  
   
 @Configuration  
 @ImportResource({  
     "classpath:META-INF/cxf/cxf.xml",  
     "classpath:META-INF/cxf/cxf-extension-soap.xml",  
     "classpath:META-INF/cxf/cxf-servlet.xml" })  
 public class CXFConfig {  
   
   @Bean  
   public Endpoint soapServiceEndpoint(Bus cxfBus, SoapService soapService) {  
     EndpointImpl endpoint = new EndpointImpl(cxfBus, soapService);  
     endpoint.setAddress("/soapService");  
     endpoint.publish();  
     return endpoint;  
   }  
 }  

If component scan of @Service, @Repository and @Component is not already active this can be done be adding:
 <context:component-scan annotation-config="true" base-package="package.to.scan" />  

Tuesday, January 21, 2014

JBoss 4.2.2 to Wildfly8/Jboss7 CXF migration

Prelude

Recently I was facing a migration project, migrating from from JBoss 4.2.2 to Wildfly 8. The old project was using Spring 2.5.5, Hibernate 3.6.7 and CXF 2.2.7.
Everything except CXF was fairly easy to migrate. Wildfly 8 comes with CXF 2.7.7 bundled and I wanted to use the managed dependencies. 
I have made this post to hopefully help other people facing a migration from an older JBoss to a more resent version.

Before starting any migration I would strongly recommend making sure you have at least some smoke tests of all services.
For old legacy systems no test might exist, but making a few smoke tests that exercise as much as possible of the service could work.   
An excellent tool for testing SOAP web service is SoapUI.

I have added short description of the project configuration in the bottom of the post.

Happy reading! 


Here we go!

First I made sure all CXF dependencies was removed from the war file, but WSDL and XSD files were causing a problem.


First try deploying, resulted in some problems loading the CXF beans from cxf-beans.xml.


ERROR [org.springframework.web.context.ContextLoader] (MSC service thread 1-5) Context initialization failed: org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Failed to import bean definitions from URL location [classpath:/cxf-beans.xml]
Offending resource: ServletContext resource [/WEB-INF/classes/applicationContext.xml]; nested exception is org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://cxf.apache.org/jaxws]
Offending resource: class path resource [cxf-beans.xml]
        at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68) [spring-2.5.5.jar:2.5.5]
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85) [spring-2.5.5.jar:2.5.5]
        at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:76) [spring-2.5.5.jar:2.5.5]
I solved this by completely removing cxf-beans.xml and let Wildfly manage the services.
Unfortunately this didn't solve all my problems. Now I was facing problems with the XSD's.
We used to create web services by, first creating a WSDL and XSD files and then running wsdl2jave, generating model and interface. 

Caused by: org.apache.ws.commons.schema.XmlSchemaException: Unable to locate imported document at '/ServiceObject.xsd', relative to 'vfs:/C:/Development/wildfly-8.0.0.CR1/bin/content/project.war/WEB-INF/classes/ServiceV1.wsdl#types1'.

After hours of googling about the problem, I decided to get rid of all WSDL and XSD's and let Wildfly handle everything. I removed the wsdlLocation = "classpath:ServiceV1.wsdl", from @WebService annotation and deleted alle WSDL and XSD's from the project. 

First success! The project was now able to deploy. However Wildfly was not showing any registered services in the console :(. 
The problem here was, that I was using standalone-full.xml, which did not have web services enabled by default.
Easy solved by adding:

<extension module="org.jboss.as.webservices">   
and
<subsystem xmlns="urn:jboss:domain:webservices:1.2">
 <modify-wsdl-address>true</modify-wsdl-address>
 <wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host>
 <endpoint-config name="Standard-Endpoint-Config"/>
 <endpoint-config name="Recording-Endpoint-Config">
  <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
   <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
  </pre-handler-chain>
 </endpoint-config>
 <client-config name="Standard-Client-Config"/>
</subsystem>   

to standalone-full.xml.

The Wildfly console was now showing all my services. Wohoo!
However my newly gained success only lasted until i clicked the WSDL link :(.
I was now presented to a nice stack trace...
Exception handling request to /project/ServiceV1: java.lang.AbstractMethodError: org.apache.xerces.dom.DeferredDocumentImpl.setXmlStandalone(Z)V
If you have any dependencies to xerces either remove it if possible or use the version provided by Wildfly.

A quick check list to run through: 
  • Remove all WSDL and XSD files
  • Remove wsdlLocation in @WebService element
  • If Services was previously mapped using a name in the WSDL, change "serviceName" to reflect the right name of the service
  • Service endpoints can be found in the web console: http://localhost:9990/console/App.html#webservice-runtime
  • Make sure model classes is properly decorated with XML annotations according to XSD's. If originally generated from XSD's, it should not be a problem
  • Make sure xerces dependency is managed by Wildfly

Sample project configuration

The project structure was looking something like this:

war \
---- WEB-INF \
-------- web.xml
-------- lib \
------------ cxf-2.2.7.jar
-------- classes \
------------ applicationContext.xml
------------ cxf-beans.xml
------------ ServiceV1.wsdl
------------ ServiceObjectV1.xsd
------------ com.blogspot.jpdevelopment.v1.ServiceV1.class
------------ com.blogspot.jpdevelopment.v1.ServiceV1Impl.class
------------ com.blogspot.jpdevelopment.v1.ServiceObjectV1.class   

Web service implementation was looking like this:
@WebService(name = "ServiceV1",
   serviceName = "ServiceV1",
   portName = ServiceV1LOCAL",
   targetNamespace = "http://schema.jpdevelopment.blogspot.com/v1",
   wsdlLocation = "classpath:ServiceV1.wsdl",
   endpointInterface = "com.blogspot.jpdevelopment.v1.ServiceV1")

public class ServiceV1Impl implements ServiceV1 {            

And the interface:
@WebService(targetNamespace = "http://schema.jpdevelopment.blogspot.com/v1", name = "ServiceV1")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public interface ServiceV1 {

CXF was started in web.xml
<web-app>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>WEB-INF/classes/applicationContext.xml</param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <listener>
  <listener-class>com.blogspot.jpdevelopment.ContextLoadListener</listener-class>
 </listener>
 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <display-name>CXF Servlet</display-name>
  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/ws/*</url-pattern>
 </servlet-mapping>
</web-app> 

Web service end points was configured in cxf-beans.xml
<bean name="serviceV1Impl" class="com.blogspot.jpdevelopment.v1.ServiceV1Impl">
 <property name="someService" ref="someService" />
</bean>

<jaxws:endpoint id="serviceV1"
 implementor="#serviceV1Impl" address="/serviceV1"
 wsdlLocation="classpath:ServiceV1.wsdl">
</jaxws:endpoint>  

XSD's was referenced inside the WSDL with relative paths, because the project should be deployable to both test and production with any modifications to the generated was file.