Publishing and Consuming Webservices with Grails

We’ve been working on a middle layer application that needs to translate a generic API to a proprietary API which uses our business logic to drive our local hardware.  The hardest part has been developing the webservices on an MVC framework.  Grails isn’t designed to natively consume or publish web services, but of course, there are plugins and jars available which can add that functionality.

Grails and Xfire

I was assigned to figure out how to do the top level exposing of our methods, while my teammates were working on consuming from the other service.  It was easy enough to get to work.  The Xfire plugin works quite simply.  Just install, add expose = true, and your exposing your webservices.  To everyone.  With no security.  To add security, add a couple of Spring transactional layers (wow, I trivialized, but its not easy at all when you don’t have Spring experience).  All was good, every thing was working the way I wanted.  I was interacting with Xfire and securing with Acegi.

Then we merged our branches.  My stuff broke the consuming part (done with jaxws, not sure how, I didn’t do it).

What on earth?  Why won’t this work together?

Xfire is using jaxws too.  Oh, but its a different version.  And its REALLY old.  Development on Xfire stopped before Grails was born.  Why the hell is there a plugin that uses such out of date code?  Damnit.  And there’s no simple upgrade from the Grails Xfire plugin to Apache CXF (it’s replacement).  Oh well, throw all this work out and start over.

Grails and CXF

CXF is a completely different beast in terms of what I have to do to get it to work with Grails.  No simple “grails install-plugin”.   No “add one line to expose”.  No good (Grails and CXF) tutorial.  Nobody doing what I’m trying to do.  (Well, I guess there is someone trying to do it because I find plenty of questions, similar stack traces, and a bunch of hints on where to go, but none of it was assembled together)  Hopefully, my experience can help others, and maybe someone who knows a little more about Spring, Grails, or CXF will be able to improve this solution.

Before we get too far, this solution is VERY touchy.  It requires you to recompile everytime you make a change to the source.  It gives horrible stack traces (but you’re used to that if you’re using grails).  Use it at your own risk.

First grab the jars from the CXF Apache site. There are a bunch of jars, and I don’t really know which ones are needed and which ones are not, but I found the following list and it works.  Examine WHICH_JARS if you want to be sure it works.

asm-2.2.3.jar
commons-lang-2.4.jar
commons-logging-1.1.1.jar
cxf-2.2.2.jar
cxf-manifest.jar
FastInfoset-1.2.3.jar
geronimo-activation_1.1_spec-1.0.2.jar
geronimo-annotation_1.0_spec-1.1.1.jar
geronimo-javamail_1.4_spec-1.6.jar
geronimo-jaxws_2.1_spec-1.0.jar
geronimo-jms_1.1_spec-1.1.1.jar
geronimo-servlet_2.5_spec-1.2.jar
geronimo-stax-api_1.0_spec-1.0.1.jar
geronimo-ws-metadata_2.0_spec-1.1.2.jar
jaxb-api-2.1.jar
jaxb-impl-2.1.9.jar
jaxb-xjc-2.1.9.jar
jaxen-1.1.jar
jaxws-2_1-mrel2-api.jar
jaxws-rt-2.0EA3.jar
jetty-6.1.18.jar
jetty-util-6.1.18.jar
jsr181-api.jar
neethi-2.0.4.jar
saaj-api-1.3.jar
saaj-impl-1.3.2.jar
saaj-impl-1.3.jar
velocity-1.5.jar
wsdl4j-1.6.2.jar
wss4j-1.5.7.jar
wstx-asl-3.2.8.jar
xml-resolver-1.2.jar
XmlSchema-1.4.5.jar

Once the jars are in the lib folder, an interface for the exposed class needs to be made in the /src/groovy.

package com.domain.services;

import com.domain.*;

interface Sample{
    int methOne(int arg1, String arg2)
}

Then the interface needs to be implemented by a service (well, it doesn’t have to be a service, just the class you want to expose).

import com.domain.*;

class SampleService implements com.domain.services.Sample {
    boolean transactional = true

    int methOne( int arg1, String arg2){
          return 3;
    }

Now we have the service all ready to expose, and we just need to tell Spring to do it.  We can do this with some xml in the right places.  In order to be able to find where I added the XML (so when I need to add another service interface) I created cxf-servlet.xml (don’t try to rename it. It won’t work) in web-app/WEB-INF with the following content:

<?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:simple="http://cxf.apache.org/simple"
    xmlns:soap="http://cxf.apache.org/bindings/soap"
    xsi:schemaLocation="

http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/bindings/soap

http://cxf.apache.org/schemas/configuration/soap.xsd

http://cxf.apache.org/simple

http://cxf.apache.org/schemas/simple.xsd">
    <!--create CXF service-->
    <simple:server id="samplePublishedService"
                   serviceClass="com.domain.services.Sample"
                   address="/sample">
        <simple:serviceBean>
          <bean class="SampleService" />
        </simple:serviceBean>
    </simple:server>
</beans>

This will setup the basic servlet, but doesn’t put it any where.  If desired, “address” can be a complete URL, with a different port number, and your app would serve this service there. (i.e. address=”http://localhost:523/services” would allow your SOAP calls over port 523)  However, I want to aggregate my services so a user can find the wsdl for any of my services easily.

Next, if you haven’t already, run

grails install-templates

This will create /src/templates/war/web.xml.  Add the following to that file (inside the <web-app> tags):

<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>

Then to /grails-app/conf/spring/resources.xml (or rewrite this to match the .groovy way) add

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:simple="http://cxf.apache.org/simple"
    xmlns:lang="http://www.springframework.org/schema/lang"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd
http://cxf.apache.org/simple http://cxf.apache.org/schemas/simple.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

    <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" />
    <import resource="classpath:WEB-INF/cxf-servlet.xml" />
</beans>

At this point, you should be able to run.  (You probably have to run with -Ddisable.auto.recompile=true.  Also “grails clean”ing helps solve some of the errors.) So that is where I will end.  I will post soon about Acegi integration.

Point your browser to “http://localhost:8080/testApp/ws/” (for example) to see the list of services. Links will give the wsdls.

Here is a copy of the source.



Tags:
This entry was posted on Tuesday, July 7th, 2009 at 6:49 am and is filed under Coding, Grails. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

One Response to “Publishing and Consuming Webservices with Grails”

  1. Bowlinguru » Protecting Webservices with Acegi in Grails

    [...] Publishing and Consuming Webservices with Grails 07 Jul [...]

Leave a Reply

Your comment