We need to create web.xml and a Spring application context file.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="services" version="2.5">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/appContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Nothing surprising here. The main thing worth mentioning is that we're using the
CXFServlet to process all requests, which we're assuming are all requests
for web services.
As you might guess from the web.xml file above, we need to create
a Spring application context file called appContext.xml.
<?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"
xmlns:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
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
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd"
default-autowire="byName">
<!-- 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" />
<!-- Enable message logging using the CXF logging feature -->
<cxf:bus>
<cxf:features>
<cxf:logging/>
</cxf:features>
</cxf:bus>
<!-- The service bean -->
<bean id="contactUsServiceImpl" class="contactus.ContactUsServiceImpl"/>
<!-- Aegis data binding -->
<bean id="aegisBean"
class="org.apache.cxf.aegis.databinding.AegisDatabinding"
scope="prototype"/>
<bean id="jaxws-and-aegis-service-factory"
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>
<!-- Service endpoint -->
<!-- See http://incubator.apache.org/cxf/faq.html regarding CXF + Spring AOP -->
<jaxws:endpoint id="contactUsService"
implementorClass="contactus.ContactUsServiceImpl"
implementor="#contactUsServiceImpl"
address="/contactus">
<jaxws:serviceFactory>
<ref bean="jaxws-and-aegis-service-factory"/>
</jaxws:serviceFactory>
</jaxws:endpoint>
</beans>
Let's talk about appContext.xml.
The first three imports just import some bean definitions from the CXF JAR file. I don't know the details and I don't think we're supposed to care about the details.
The bus config just puts logging in. That's optional. If you run this in a production environment you probably want to turn that off because it's verbose.
The service bean definition is just telling Spring about the service bean we wrote. We're going to put a web service endpoint in front of it.
The next piece on Aegis specifies the databinding mechanism we want to use for mapping back and forth between Java and XML. I mentioned earlier that I tried using the default JAXB but was having problems getting it to work, so someone recommended to me to try Aegis and that worked like a charm.
Finally we define the web service endpoint. The #contactUsServiceImpl
piece connects the endpoint up to the service bean. (Yes, the hash mark is required.)
We use the JAX-WS/Aegis service factory that we defined earlier so we can use the Aegis
databinding.
I'm not using transactions or persistence in my simple service bean. In a realistic
service you are going to have transactions and persistence. To make CXF play nicely with
AOP (and hence Spring's declarative transactions) you are going to need to include the
implementorClass attribute as I've done. For more details on that see the
CXF FAQs at http://incubator.apache.org/cxf/faq.html.
Though it in no way reflects a fault with Eclipse itself, if you are using Eclipse 3.3
you may be getting some annoying validation errors in the IDE, such as
"cvc-complex-type.2.4.c: The matching wildcard is strict, but no
declaration can be found for element 'jaxws:endpoint'." This is because the
schema locations for the http://cxf.apache.org/core and
http://cxf.apache.org/jaxws keys don't actually point at real XSDs,
so the validator won't recognize elements from the cxf and jaxws
namespaces. (That's why I say it isn't Eclipse's fault.) This doesn't create a problem
with the actual build, but the IDE will complain as it tries to validate the Spring app
context files against the missing XSDs. I haven't yet discovered the "right" solution to
this problem (I'd be delighted to have somebody tell me) but there are three workarounds
I know about:
http://www.springframework.org/schema/beans key to
the http://www.springframework.org/schema/beans/spring-beans.xsd location
(notice that I've removed the -2.5 part). That gets rid of the validation
errors though now you're using the Spring 2.0 beans schema. That may be OK for you.There may be something you can do with Window → Preferences → Web and XML →
XML Catalog but I didn't see it: even if you put the core.xsd and jaxws.xsd
in the catalog, those XSDs in turn reference other schema locations that don't resolve to an XSD
and it looks like a lot of work to me.
Anyway as I say if somebody knows the right way to handle this please let me know and I'll gladly update the article and credit you. :-)