Search This Blog

Wednesday, September 24, 2008

JAXWS Example with Maven and Spring

Introduction:
For the past few months, I have been quite far away from the SOAP world with my primary focus being on REST looking at JAXWS implementors, Restlet etc. The other day, I attended the Utah Java User's Group (UJUG) meeting. The UJUG is managed superbly by Mr.Chris Maki and I quite enjoyed myself when there with the presentations. The presentations centered around how different companies implemented web services and their stack.

One company in particular has me interested in their Web service Stack. The LDS church uses JAXWS with JDK1.6. The presenter, although did not enter into details, did however mention how easy it is to create and consumer SOAP web services with JAXWS. One of the things he mentioned was that they did not need to step outside JDK in order to be able to create SOAP based WS. They did not feel the need for external impls like CXF etc.

In the projects that I have worked with SOAP, the words, simple or easy have never come to mind :-) So what was this gentleman talking about? I had to find out. Would have loved to chat with him more but could not, so here I go finding out the hard way :-).

JAXWS:
So what is JAXWS? JAXWS (Java API For XML Web Services) can be thought of as a
Java Programming API to create Web Services. JAXWS was introduced in JAVA SE 5 and
uses Annotations in the creation of end points and service clients. JAXWS 2.0 replaced or
encompassed the JAX-RPC API. For more details on the same look at this developer works article.
JAXWS uses JAXB 2.0 for data binding.

Simple JAXWS Example:
Like my JAXRS Maven/Spring examples, the JAXWS web service is implemented using a
multi module maven project with a structure as shown below:

jaxws-example
|_ client - Contains generated code from WSDL
|_ service - Contains model and business logic
|_ webapp - Contains Web service wrapper, servlet defs etc

For the example, I am using the JAXWS-Maven plugin.
The plugin has two goals that the example uses, a. wsgen, that reads a service end point
class and generates service artifacts and b. wsimport used to generate the consumer code.

The webapp maven module contains JAXB annotated DTO's like an OrderDto and LineItemDto. The
Web service is exposed via a simple wrapper class:

 @WebService
 public class OrderWebService {
   private static final Logger log = Logger.getLogger(OrderWebService.class);
   @Autowired private MapperIF beanMapper;
   @Autowired private OrderService serviceDelegate;
   ...

   public OrderDto getOrder(Long orderId) throws OrderWebException {
     Order order = null;
   
     try {
       order = serviceDelegate.getOrder(orderId);
     }
     catch (OrderNotFoundException nfe) {
       throw new OrderWebException(nfe.getMessage());
     }
    
     OrderDto orderDTO = (OrderDto) beanMapper.map(order, OrderDto.class);
     return orderDTO;
   }
   ...
}

The @WebService annotation indicates that the above class is a Web Service end point. The Service delegate
does the heart of the service work with the OrderWebService class acting only as a front. As
seen above, we are using Spring and auto-wiring for the delegate and Bean mapper. Just
one other thing to note is that we have a class called OrderWebException, this is a class
annotated with @WebFault indicating this exception is raised when there is a web fault.

I have a layer which is a webservice layer and an actual service layer as I feel there
are some advantages to such an approach. I have discussed the same in my earlier blogs.

During the maven build process, the jaxws maven plugin will come into play and generate
service artifacts. The maven plugin is configured as follows:

 1 <plugin>
 2   <groupId>org.codehaus.mojo</groupId>

 3     <artifactId>jaxws-maven-plugin</artifactId>
 4        <executions>
 5     <execution>

 6       <goals>
 7         <goal>wsgen</goal>
 8       </goals>

 9      <configuration>
10        <packageName>com.weflex.service</packageName>
11         <!-- The name of your generated source package -->
12        <sei>com.welflex.soap.ws.OrderWebService</sei>

13        <keep>true</keep>
14        <genWsdl>true</genWsdl>
15       </configuration>
16         </execution>

17      </executions>
18 </plugin>


The generator will generate the WSD and web service artifacts. The schema itself is created
at ${base.dir}/target/jaxws/wsgen/wsdl.

One point that is worth noting is that, in order to launch the web service for testing,
there is no need for a separate Servlet container, web.xml etc. JDK 1.6.X provides for
a simple container that will allow you to test the service. One can bind the service using:

@WebService 
public class OrderWebService {
   .....
  public static void main(String args[]) {
     javax.xml.ws.EndPoint.publish("http://localhost:8080/ws", new OrderWebService());
  }
}


Viola, then you connect to the service, using SOAP UI or your favorite SOAP tool and test the service.

Now the client code. Unlike in my rest examples where one wrote the client code explicitly, in this example, the client is entirely generated from the service WSDL. There is no
reason to use auto generation. Seems easy to me though.

The wsimport goal can create a Web Service client when provided a WSDL. The client project is dependent on the webapp project. Once the webapp project
is built a WSDL is created in the above mentioned target directory. The plugin on the
client is configured to point to this WSDL in order to generate client side artifacts as shown
below:

 1 <plugin>
 2   <groupId>org.codehaus.mojo</groupId>

 3   <artifactId>jaxws-maven-plugin</artifactId>
 4   <executions>
 5     <execution>

 6       <goals>
 7         <goal>wsimport</goal>
 8       </goals>

 9       <configuration>
10         <wsdlUrls>
11    <wsdlUrl>       ../../jaxws-example/webapp/target/jaxws/wsgen/wsdl/OrderWebServiceService.wsdl
12    </wsdlUrl>

13  </wsdlUrls>
14        </configuration>
15     </execution>
16    </executions>
17 </plugin>

Now that we have the client, the integration test project can run which starts the
web service and issues requests from the client to the service.

The integration test creates a client via the following call:

URL wsdlLocation = new URL(INTEGRATION_TEST_URI + "?wsdl");
ORDER_CLIENT = new OrderWebServiceService(wsdlLocation,
      new QName("http://ws.soap.welflex.com/", "OrderWebServiceService"))
         .getOrderWebServicePort();

The default generated client internal URL points to our file system. The above the URL
the same to point to an actual server instance.
Running the Example:
You need to be on JDK 1.6.X, maven 2.0.8 installed. From the root of the project directory,
issue a "mvn install" and you should see the entire project being built with the
Integration Tests running. The Project itself can be downloaded from HERE.

Thoughts:
Some of my friends will echo my thoughts with the pains they have had with Web service
creation using certain vendor tools. The promise is always, "Watch while I create a
Web Service in just two minutes!". JAXWS also tends to address performance of SOAP
WS. Take a look at the article on implementing high performance web services with jaxws.

With JAXWS, I do not have to extend any particular class in order to expose a POJO as
a Web Service. The ability to instantly test my web service without the need for an
external Servlet container starting up, +1. The ease of generating the Client code,
another +1. Maven Plugin +1. Spring Integration +1. Easy deployment without a heavy
weight container +1. The plugin however did not seem to provide for easy mapping of
name spaces to custom java packages with wsimport.

JAXWS also provides for easier than before implementation of WS-* features if you need them.

I am specially interested in the performance characteristics of JAXWS using JAXB 2.0. I have always believed that one of the major performance penalties paid with SOAP Web service have been on the marshalling/unmarshalling fronts. I need to do some performance benchmarks I guess.
Enjoy!

11 comments:

Anonymous said...

Nice article. Thank you.

Example won't build for me, tho.

Seems to be trouble resolving dependencies for spring artifact in group org.jvnet.jax-ws-commons.

JDK 1.6u10
Maven 2.0.9

Sanjay Acharya said...

Thanks. I tried to run the example and was not able to duplicate the problem you are facing. Only difference is that I am using maven 2.0.8. Are you being able to reach the repository in question? I see the following line when I run the "mvn install"

Downloading: http://download.java.net/maven/2//org/jvnet/jax-ws-commons/spring/jaxws-spring/1.8/jaxws-spring-1.8.jar

Kineas said...

Hi, same problem here. I was looking for a tutorial in JAX-WS and, surfing the web, I've found your blog.

The example won't build for me too. A problem resolving dependecies is shown, same as anonymous. I tried changing to maven 2.0.8 and the problem still persists. The log is:

Path to dependency:
1) com.welflex.example:jaxws-example-webapp:war:1.0-SNAPSHOT
2) org.jvnet.jax-ws-commons.spring:jaxws-spring:jar:1.8
3) javax.jws:jsr181-api:jar:1.0-MR1

I wasn't able to find the last package in JavaNet or central maven repository.

Cheers.

PS: Nice post anyway.

Sanjay Acharya said...

Are you running the "mvn install" from the root project? Thats what I do and I am sorry but I am not able to reproduce the dependency issue you mention. I wiped out my local repo and tried the same but the dependencies downloaded fine. I am sorry that I am not being of much help here.

Regardless, if you cannot access the jars mentioned directly you can always download the jars and install them in your local repository. The last library should be available at Downloading: https://maven-repository.dev.java.net/nonav/repository/javax.jws/jars/jsr181-api-1.0-MR1.jar
7K downloaded
Downloading: http://download.java.net/maven/2//org/jvnet/jax-ws-commons/spring/jaxws-spring/1.8/jaxws-spring-1.8.jar
26K downloaded

Kineas said...

Hi. I've found them in Jboss's repository and installed manually. Everything works fine now.

Thank you for sharing.

Anonymous said...

In pom.xml, I think that configuration should be outside of the executions element. See also https://jax-ws-commons.dev.java.net/jaxws-maven-plugin/usage.html

Jose J. García said...

Sanjay, very very good examples, thanks for sharing.

Sanjay Acharya said...

Zorno, you are very welcome :-)

Anonymous said...

The build failed with 3 missing dependencies, just like the above posts state.

I added the Maven 1 repository, and the build completed successfully.
This blog is preventing me from posting the xml... See the 4th post in this thread:

http://forums.java.net/jive/message.jspa?messageID=344471

Anonymous said...

Excellent tutorial..
It works for me! Thankq.

Anonymous said...

Nice and fine. Big thanks.