Example of minimal Apache CXF2 web services deployment

minimalCXF2We had a requirement to add a web service to allow our partners to upload their events to our Calendar. Rather than spin up an entirely new application to handle this, we wanted to embed a very simple web service into the existing application. We wanted this to be very light weight and easy to implement.

I had used Apache Axis in the past so I decided to look into that first. To get a sample Axis2 application working (http://www.eclipse.org/webtools/community/tutorials/BottomUpAxis2WebService/bu_tutorial.html) with one simple class representing a single web service operation it created an 11MB package, dirty folder structure, admin pages to add more services, etc. Essentially a full blown web service server with admin options. I did NOT need this and it seems a little bulky. I tried to delete some of the /WEB-INF/lib JAR files but it was not obvious what was required and what wasn’t. I tried deleting the /axis2-web folder but that made it unusable. Not to mention all the config files required/generated. Let’s try something else…

I flipped over to Apache CXF2. CXF2 does not create as many unnecessary files as Axis2, and its generated configuration files are far fewer in number but it also has a gigantic “everything-and-the-kitchen-sink” package of jars at ~29MB. Since CXF2 is tied tightly into Spring and implements so many of the WS-* standards it has absolutely everything required. Again, this is too much and I only want a minimal bundle to serve up a few simple web services from within an existing Java (JSF2) application deployed on Tomcat.

Since CXF2 seems cleaner and has better documentation as to what is required in the lib directory, I decided to start with that as a baseline and start cutting away. I found that when using the Eclipse Bottom-Up-Webservice generator, the generated config brings in all of the Spring dependencies by default. So here is what I did to get that ~29MB (73 JARS) package down to ~5MB (4 required JARs) and still have the ability to serve a WSDL and operation on web service requests using JAX-WS notation in an existing project embedded in a web application, all running on Tomcat.

  1. Download CXF2 (http://cxf.apache.org/download.html) and grab the Binary and Source of the most recent version (ex: apache-cxf-2.7.2.tar.gz and apache-cxf-2.7.2-src.tar.gz). Extract the CXF binaries to the local drive (ex: C:\apache-cxf-2.7.2 and C:\apache-cxf-2.7.2-src)
  2. Download CXF2-bundle-minimal (http://mirrors.ibiblio.org/maven2/org/apache/cxf/cxf-bundle-minimal/2.7.2/) and grab the Binary and Source of the most recent version (ex: cxf-bundle-minimal-2.7.2.jar and cxf-bundle-minimal-2.7.2-sources.jar). This is a stripped down version of CXF2 for a lighter deployment and the site provides officially built packages for deployment via Maven but we can still download them and use them manually.
  3. Download Tomcat 7 (http://tomcat.apache.org/download-70.cgi) and grab the Binary of the most recent version (ex: apache-tomcat-7.0.35.tar.gz). Extract the Tomcat app server to the local drive (ex: C:\apache-tomcat-7.0.35).
  4. Open up Eclipse (in my case Juno JEE) and set the following:
    1. Window > Preferences > Web Services > CXF 2.x Preferences > CXF Runtime tab. Add a new CXF Runtime and point it to the extracted directory above (ex: C:\apache-cxf-2.7.2). Once added select it in the table and on the same tab ensure that the “Export runtime libraries to WEB-INF/lib at deployment time” at the bottom of the window is UNSELECTED as we will use a trimmed down subset for the project. Go to the Endpoint Config tab and select “Use CXF Servlet”. Apply changes
    2. Window > Preferences > Server > Runtime Environments. Add a new Tomcat 7 server and point it to the extracted directory above (ex: C:\apache-tomcat-7.0.35). Apply changes…
  5. Start a new Dynamic Web Project. From the Java EE perspective, select File > New > Dynamic Web Project, give it a name (ex: MyTestWS) and ensure the following settings:
    1. Target runtime: Apache Tomcat v7.0 à this was created in Eclipse earlier
    2. Dynamic web module version: 3.0
    3. Configuration: select the Modify button. In the Project Facets window select the “CXF 2.x Web Services” Facet and press “OK”

    Press Next until your reach the Web Module window. Ensure the “Generate web.xml deployment descriptor” option is SELECTED. Next… On the CXF Facet window ensure the CXF runtime: Apache CXF 2.7.2 (this was created in Eclipse earlier). Finish…

  6. The project is now ready. Create a new package in src (ex: my.webservice) and create a new interface class for the service (ex: GreetingService) with the following code (JAX-WS annotated):
    package my.webservice;
    
    import javax.jws.WebParam;
    import javax.jws.WebService;
    
    @WebService
    public interface GreetingService {
        public String helloWorld(@WebParam(name="name") String name);
    }

    And create the implementation class for the service (ex: GreetingServiceImpl) with the following code (JAX-WS annotated):

    package my.webservice;
    
    import javax.jws.WebService;
    
    @WebService(endpointInterface = "my.webservice.GreetingService", serviceName = "GreetingService")
    public class GreetingServiceImpl implements GreetingService {
    
        @Override
        public String helloWorld(String name) {
            return "Hello World, " + name + "!";
        }
    }
  7. In normal operation the default Servlet for CXF will initiate all of the Spring framework which will list services. We can still list our services without the Spring Framework but we have to extend a special CXF class to handle this. We will create this class to serve up the endpoint to the relative path “/GreetingService” and map it to our implementation of the service. Create a new package in src (ex: my.servlets) and create a new class extending  CXFNonSpringServlet (ex: SimpleCXFNonSpringServlet) with the following code:
    package my.servlets;
    
    import javax.servlet.ServletConfig;
    import javax.xml.ws.Endpoint;
    import my.webservice.GreetingServiceImpl;
    import org.apache.cxf.Bus;
    import org.apache.cxf.BusFactory;
    import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
    
    public class SimpleCXFNonSpringServlet extends CXFNonSpringServlet {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        public void loadBus(ServletConfig servletConfig) {
            super.loadBus(servletConfig);
            Bus bus = getBus();
            BusFactory.setDefaultBus(bus);
            Endpoint.publish("/GreetingService", new GreetingServiceImpl());
        }
    }

    Open up the web.xml file and set the new servlet mapped to the /services/* path. This path will then respond using our custom created service lister. It should look like so:

    <?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_3_0.xsd" id="WebApp_ID" version="3.0">
        <display-name>MyTestWS</display-name>
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>default.html</welcome-file>
            <welcome-file>default.htm</welcome-file>
            <welcome-file>default.jsp</welcome-file>
        </welcome-file-list>
        <servlet>
            <display-name>SimpleCXFNonSpringServlet</display-name>
            <servlet-name>SimpleCXFNonSpringServlet</servlet-name>
            <servlet-class>my.servlets.SimpleCXFNonSpringServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>SimpleCXFNonSpringServlet</servlet-name>
            <url-pattern>/services/*</url-pattern>
        </servlet-mapping>
        <session-config>
            <session-timeout>60</session-timeout>
        </session-config>
    </web-app>
  8. In from the cxf and the cxf-bundle-minimal downloads at the start, copy the jar files into the WEB-INF/lib folder:
    • cxf-bundle-minimal-2.7.2.jar (from the lone cxf-bundle-minimal install)
    • neethi-3.0.2.jar (from the C:\apache-cxf-2.7.2\lib folder)
    • wsdl4j.jar (from the C:\apache-cxf-2.7.2\lib folder)
    • xmlschema-core-2.0.3.jar (from the C:\apache-cxf-2.7.2\lib folder)
  9. Run the entire application in Tomcat 7. If you visit http://localhost:8080/MyTestWS/services/ all of the services should be listed. If you check http://localhost:8080/MyTestWS/services/GreetingService?wsdl then the wsdl is available.
  10. To test open Eclipse > Run > Launch the Web Services Explorer > WSDL Page (button in the top right) > Paste in the wsdl URL http://localhost:8080/MyTestWS/services/GreetingService?wsdl and press Go > click all the way down to the helloWorld operation > add a string parameter to the Body and enter your name (ex: Tim) and press Go.  In the response pane it should say:
    helloWorldResponse
    return (string):  Hello World, Tim!

That should be it!!!

Tim (http://tim.pinet.ca)

Advertisements

7 thoughts on “Example of minimal Apache CXF2 web services deployment

  1. @Orlando This is only to get web service output. If you wanted this displayed in an app you would need to create a web application that, in turn, calls this web service. That would be the additional step you are missing.

  2. Hi Tim,
    I’m trying this with CXF 3.0.1 and get this error:
    javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException: Could not resolve a binding for http://schemas.xmlsoap.org/wsdl/soap/
    at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:371)
    at org.apache.cxf.jaxws.EndpointImpl.publish(EndpointImpl.java:251)

    Can you offer any advice?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s