del.icio.us Digg DZone Reddit StumbleUpon
Make Web Services Transparent with Spring 2.5 and Apache CXF 2.0 - Willie Wheeler
« Previous | 1 | 2 | 3 | 4 | Next »

Creating the Spring Application Context

This is where all the good stuff is: in the Spring application context.

Spring application context: myapp-servlet.xml

Put this file at /WEB-INF/myapp-servlet.xml so that it's where DispatcherServlet expects to find it. The myapp- part needs to match the value specified for <servlet-name> above. (There's a way to change this but I'm not worried about that for this sample app.)

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <!-- Configure CXF to use Aegis data binding instead of JAXB -->
    <bean id="aegisBean"
          class="org.apache.cxf.aegis.databinding.AegisDatabinding"
          scope="prototype"/>
    <bean id="jaxwsAndAegisServiceFactory"
          class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
          scope="prototype"> 
        <property name="dataBinding" ref="aegisBean"/>
        <property name="serviceConfigurations">
          <list>
            <bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
            <bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>
            <bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/> 
          </list>
        </property>
    </bean>

    <!-- Factory to create the dynamic proxy -->
    <bean id="contactUsFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass" value="contactus.ContactUsService"/>
        <property name="address"
                  value="http://localhost:8080/cxf-service-example/contactus"/>
        <property name="serviceFactory" ref="jaxwsAndAegisServiceFactory"/>
    </bean>
    
    <!-- Web service dynamic proxy -->
    <bean id="contactUsService"
          class="contactus.ContactUsService"
          factory-bean="contactUsFactory"
          factory-method="create"/>
    
    <!-- Controllers -->
    <bean class="myapp.ViewMessagesController">
        <property name="contactUsService" ref="contactUsService"/>
    </bean>
    
    <!-- View resolvers -->
    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

There's lots to talk about here. Let's do it bean-by-bean:

aegisBean: CXF supports different databinding mechanisms (i.e., mechanisms for mapping back and forth between Java and XML). The default is JAXB but I was never able to get that to work for complex data types like contactus.Message. So instead I used Aegis (which is bundled with CXF) and it works great.

jaxwsAndAegisServiceFactory: This factory creates service models either based on a service's WSDL or else based on the structure of the service class. These service models are in turn used by the service proxy factory, which creates your client-side web-service-aware dynamic proxies. Anyway, you don't have to worry too much about jaxwsAndAegisServiceFactory; the only reason we're including it is that we need to be able to tell it to use Aegis databinding.

contactUsFactory: This is a factory that generates the dynamic proxies we've been talking about. These proxies implement the contactus.ContactUsService interface, and this is what gives us the transparency referenced in the title of this article: the client application has no idea that it's working with web services at all (other than in the configuration). If you wanted to, you could collapse the client app and the contactus.ContactUsServiceImpl backend and remove the web service altogether. That's part of the beauty of Spring (and of Java interfaces and dynamic proxies).

We specify not only the service class but also the web service endpoint address and the service factory. Again the only reason we have to deal with the service factory at all is that we're using the non-default Aegis databinding. If you're able to get it working with JAXB then more power to you. (And I'd be interested in hearing about it!)

IMPORTANT: You will need to set the host, port and context path in the address property to match your web service deployment address. The one in the config file is the one I'm using but you're probably using at least a different context path.

The other beans are just Spring MVC beans. Nothing we need to worry about here.

We're now ready to try it out!

Social bookmarks: del.icio.us Digg DZone Reddit StumbleUpon
« Previous | 1 | 2 | 3 | 4 | Next »

Comments (31)

Good stuff. Did you ever get JAXB working?
By dj on Mar 14, 2008 at 11:09 AM PDT
Thanks DJ. Nope, though I didn't try after I wrote the article. JAXB worked fine as long as I stuck with simple data types. You can try it yourself by just removing the serviceFactory injection from the contactUsFactory in myapp-servlet.xml. Maybe you'll have better luck than I did. If so I'd be interested to hear.
By Willie Wheeler on Mar 14, 2008 at 10:36 PM PDT
Is there a URL that I can get your example source code?
Thanks!
By XML on Mar 21, 2008 at 5:34 AM PDT
Hi XML. I added the source code for the services and client app to the Resources section at the end of the article. I did not include the third-party JARs since those mostly come from the CXF distribution itself.
By Willie Wheeler on Mar 21, 2008 at 11:28 PM PDT
Tried the tutorial, it works great as is
1. except the one JAR file as JDOM1.0.Jar needs to be added as mentioned in your earlier tutorial.
2. JSP file does not print anything, but I am getting the output in Console.
${message.lastNameFirstName} (${message.email}): ${message.text}
By Mark on Apr 4, 2008 at 1:06 PM PDT
It works with Spring 2.5.2 using JBoss 4.3 CXF stack (not with the default included 2.0.4, but with 2.0.5).
By Marek Lange on Apr 9, 2008 at 2:17 AM PDT
I cant get the client to work with spring 2.5.x, there is a very annoying bug for me:

java.lang.NoSuchMethodError: setConfigLocation
org.springframework.web.servlet.FrameworkServlet.c reateWebApplicationContext(FrameworkServlet.java:3 98)....

I changed the client for spring 2.0.x and it works.
By Isaias on May 8, 2008 at 7:53 AM PDT
Not sure exactly what you're running into but the API changes from time to time. The setConfigLocation() method appears in Spring 2.5.2; compare the Spring 2.5.1 API

http://static.springframework.org/spring/docs/2.5.1/api/org/springframework/web/context/ConfigurableWebApplicationContext.html

with the Spring 2.5.2 API

http://static.springframework.org/spring/docs/2.5.2/api/org/springframework/web/context/ConfigurableWebApplicationContext.html

When I wrote the article I was using the Spring 2.5.0 distribution. If you aren't already doing so I would try using the latest versions of Spring and CXF and see if you have any luck. Sounds like you aren't the only one who's had this issue though:

http://forum.springframework.org/showthread.php?p=179390
By Willie Wheeler on May 9, 2008 at 8:43 PM PDT
Hi, I'm beginner and I have a problem with client :(
I use tomcat 6.0.x, spring 2.5.4 and CXF 2.1

org.apache.cxf.binding.soap.SoapFault: Error reading XMLStreamReader.
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:70)
at org.apache.cxf.binding.soap.interceptor.Soap11FaultInInterceptor.handleMessage(Soap11FaultInInterceptor.java:35)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:221)
..........
..........
Thanks for help
By Petross on May 15, 2008 at 5:53 PM PDT
how to access a webservice from another webservice
By kiran on Sep 2, 2008 at 4:51 AM PDT
Thanks for the article! I created a maven build so that folks can run (and change) the code instantly. See: http://www.jroller.com/brodkin/entry/a_maven_project_for_willie
By Sam Brodkin on Sep 3, 2008 at 5:44 AM PDT
Great Article, indeed cxf works well with aegis...until you dont use circular references in your model...
By Sylvain on Sep 11, 2008 at 3:09 AM PDT
Great Article. Thanks.
I want to use a Java client for this rather than the web client. What should I do?

Regards,
Srikanth
By Srikanth Vishwanath on Nov 4, 2008 at 3:36 AM PST
Hi Srikanth. Just inject the service bean proxy into your Java client and you should be in business. So to use the example from the article, you'd inject the contactUsService (the service bean proxy) into your Java class.
By Willie Wheeler on Nov 4, 2008 at 6:38 AM PST
The only thing that strikes me as bad is the fact that we have to place a server side jar on the client side which kinda seems to defeat the purpose of using web services in the first place. By introducing a dependency and that too by placing jars your going to run into maintenance nightmares when you want to change/upgrade your server and you need all your clients to upgrade along with you
By Nikhil on Nov 8, 2008 at 7:10 AM PST
Thanks for the excellent articles.

I get the following error when I compile against a new WSDL.

Rpc/encoded wsdls are not supported in JAXWS 2.0

Is there any way that I can use CXF with RPC encoded WSDLs??
By Steve Daly on Dec 1, 2008 at 9:05 AM PST
Haven't run into it myself but here's a forum post I found about it:

http://www.nabble.com/Rpc-encoded-wsdls-are-not-supported-in-JAXWS-2.0-error-td20420617.html
By Willie Wheeler on Dec 1, 2008 at 11:34 PM PST
Hi,
I've just tried your code without AEGIS and got it working without any problem. Maybe that's because I'm using CXF 2.1.3 ?
By Frédéric ESNAULT on Dec 17, 2008 at 6:50 AM PST

Great articles Willie!

I tried following both tutorials and got it working but for some reason, if the client is spring injected, its method which is supposed to return a DataHandler for binary data is returning null.

I tried this programmatically instead of using Spring and it works perfectly. Would you have any ideas why?

By Jarell on Feb 24, 2009 at 5:32 AM PST

@Jarell: Can you post the relevant part of your app context file? (Please use the <pre> tag to format it.)

By Willie Wheeler on Feb 24, 2009 at 6:58 AM PST

Hi Willie,

Here's the programmatic way:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.setServiceClass(JasperWebService.class);
factory.setAddress("http://localhost:8081/JasperWebServiceApp/jasper");
JasperWebService client = (JasperWebService) factory.create();
DataHandler dh = client.generateCommsTreeSample();
fos = new FileOutputStream(filePath);
dh.writeTo(fos); 

Here's the Spring Config when I try to inject the client, (I'm using JSF support of Spring to inject it to the backing bean):

<bean id="JasperWebServiceFactory" 
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
   <property name="serviceClass" 
        value="com.hp.bcp.jasperws.contract.service.JasperWebService"/>
   <property name="address" 
        value="http://localhost:8081/JasperWebServiceApp/jasper"/>
   <property name="serviceFactory" 
        ref="jaxwsAndAegisServiceFactory"/>
</bean>


<bean id="jasperWebService"
          class="com.hp.bcp.jasperws.contract.service.JasperWebService"
          factory-bean="JasperWebServiceFactory"
          factory-method="create"/>

<bean id="aegisBean"
           class="org.apache.cxf.aegis.databinding.AegisDatabinding"
           scope="prototype"/>

<bean id="jaxwsAndAegisServiceFactory"
           class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
           scope="prototype"> 
   <property name="dataBinding" ref="aegisBean"/>
   <property name="serviceConfigurations">
   <list>
     <bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
     <bean class="org.apache.cxf.aegis.databinding.AegisServiceConfiguration"/>
     <bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/> 
   </list>
   </property>
</bean> 
By Jarell Mallari on Feb 24, 2009 at 11:08 PM PST

The server seems to work just fine. However, I'm having accessing the client.

Once i set up everything and when i try to access using the client web site(e.g. http://localhost:9006/CXFClient/viewMessages.do)

I'm having this error from the client console:

16:49:35,813 WARN [PageNotFound] No mapping found for HTTP request with URI [/CXFClient/viewMessages.do] in DispatcherServlet with name 'myapp'

please help

By nel on Apr 15, 2009 at 5:11 PM PDT

Very usefull article,

I get the server to run without any problems, however, the client is a pain in the ass.

I'm using Java 6, TomCat 6, Spring 2.5 and CXF 2.2

I get the following exception when i start the client:

print("Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:387)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:971)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:246)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:168)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:435)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:409)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:537)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:192)
at org.springframework.beans.factory.annotation.InjectionMetadata.injectMethods(InjectionMetadata.java:117)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java:302)
... 56 more"); 

I solved the problem by adding the following code the CXF Spring config:

print("<!-- Load CXF modules from cxf.jar -->
<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" />"); 
By Chris Gunnink on Apr 25, 2009 at 7:21 AM PDT

Hmm, weird input form...

The Exception message was:

"Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined".

By Chris Gunnink on Apr 25, 2009 at 7:23 AM PDT

i have this exception:

java.lang.NoSuchMethodError: org.apache.ws.commons.schema.XmlSchema.getTypeByName

for jar XmlSchema-1.3.2

what this ? i use be tomcat-6 and jboos

By Gilberto Santos on May 12, 2009 at 6:33 AM PDT

Hi Willie,

I managed to get it working with JAXB by moving to cxf 2.2.3 and Spring 2.5 (or higher).

The change to the myapp-servlet.xml is as follows:

<jaxws:client id="contactUsService"
              serviceClass="contactus.ContactUsService"
              address="http://localhost:8080/Cxf/contactus" />

<!-- Controllers -->
<bean class="myapp.ViewMessagesController">
    <property name="contactUsService" ref="contactUsService"/>
</bean>

<!-- View resolvers -->
<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean> 

Please note that CXF and Spring will not work on Websphere 7.0x because the websphere jax-ws engine does not honor spring bean dependencies for web service endpoints. (i.e. the WAS engine instantiates the web service impl every time a call is made, bypassing the already instantiated one in spring (assuming you are using a singleton)) This is true even if you turn off annotation scanning and revert to the 2.4 servlet specification. I am working with IBM on fixing the issue.

By Terry Trippany on Sep 10, 2009 at 8:44 AM PDT

As a follow up on the WAS 7.0 issue the fix is simple. It is a matter of telling the container not to search the source code for annotations relying instead on the web.xml file. This is accomplished by by adding metadata-complete="true" to the web.xml as shown in the following example:

It will also decrease startup time but you must make sure to include all your resources in the web.xml.

  • Trip
By Terry Trippany on Sep 11, 2009 at 9:36 AM PDT

Sorry, the blog stripped my example:

web-app
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app 25.xsd" metadata-complete="true"
version="2.5">

By Terry Trippany on Sep 11, 2009 at 9:39 AM PDT

Hi Willie

Great tutorial.

This is the first time i try the CXF Web Service with Spring. I am using Eclipse and Tomcat 6. Your previous tutorial works fine on my environment. When trying this one, I copied your code and followed the steps, but every time I deploy the app on my Tomcat, I got this error below. Could you help?

SEVERE: StandardWrapper.Throwable java.lang.NoSuchMethodError: org.springframework.web.context.ConfigurableWebApplicationContext.setConfigLocation(Ljava/lang/String;)V at org.springframework.web.servlet.FrameworkServlet.createWebApplicationContext(FrameworkServlet.java:398) at org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:316) at org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:282) at org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:126) at javax.servlet.GenericServlet.init(GenericServlet.java:212) at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1172) at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:992) at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:4058) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4371) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardHost.start(StandardHost.java:719) at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045) at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443) at org.apache.catalina.core.StandardService.start(StandardService.java:516) at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) at org.apache.catalina.startup.Catalina.start(Catalina.java:578) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288) at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)

By Alex on Nov 9, 2009 at 5:04 AM PST

The Pandora pandora schmuck myth first Pandora Armreifenappears in lines Pandora Halsketten of Hesiod's poem in Pandora Charms epic meter, the Theogony (ca. 8th?7th centuries BC), without ever giving the woman a name. After humans Pandora Sets have received thethe myth is a rosetta stone kind of theodicy, addressing the question pop information, web easy get, sports fashion, news-fashionof why there is evil in the world. In the seventh hot-winter century BC, Hesiod, both in his Theogony (briefly, without naming Pandora outright rosetta stone language, rosetta stone spanish, abercrombie and fitch , Abercrombie Fitch

By pandora schmuck on Aug 30, 2010 at 11:14 PM PDT

Post a comment

Your name:
Your e-mail address (won't be displayed):
Your web site (optional):
example: www.xyz.com
Your comment:
Preview:
By You
Please help us reduce comment spam:
Spring Annotations RefCard
Check out the new DZone Spring Annotations Refcard by Craig Walls!

What's New?

2009-08-30 - Check out my two-part series on DZone: Spring Integration: A Hands-On Tutorial.
2009-03-25 - My new article Getting Started with Spring Batch 2.0 is available on DZone.
Home | Consulting | Tech Articles | Mailing List | Contact | Spring Blog
Copyright © 2008 Wheeler Software, LLC.