Showing posts with label contract-first. Show all posts
Showing posts with label contract-first. Show all posts

Saturday, February 6, 2010

Bookmark and Share

JAX-WS as Java's standard API for web services comes with a close integration of JAXB as XML binding framework. More precisely JAXB is actually the only binding technology directly supported by JAX-WS. As JAXB basically does its job pretty well, that's nothing bad per se.

Nevertheless there might be situations where you would like to integrate JAX-WS with other XML binding solutions such as Apache XmlBeans.

For instance you might have used another web service stack and binding framework in the past and now want to migrate your services to JAX-WS. Depending on the number of services, changing your XML binding framework can create quite a lot of work, as you have to rewrite the mapping code which converts between the types generated by the binding framework and your domain objects.

Provider-based endpoints

The key idea for integrating JAX-WS with another binding framework is to create a Provider based endpoint for your web service. Contrary to standard SEI (service endpoint interface) implementations, which solely operate on JAXB generated classes, a provider endpoint has access to the raw XML of incoming and outgoing messages.

That way you can take care of XML marshalling yourself, e.g. by converting incoming XML messages into their corresponding XmlBeans types and the other way around.

In the following a web service for a video rental store shall be built. The service has a single operation, getMovieById, with request and response types in an accompanying XML schema (the service's WSDL and all other sources for this post can be found in my Git repository):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<schema 
    targetNamespace="http://www.gunnarmorling.de/moapa/videorental/types"
    xmlns="http://www.w3.org/2001/XMLSchema"
    xmlns:videorental="http://www.gunnarmorling.de/moapa/videorental/types">

    <element name="GetMovieByIdRequest" type="videorental:GetMovieByIdRequest" />
    <element name="GetMovieByIdResponse" type="videorental:GetMovieByIdResponse" />

    <complexType name="GetMovieByIdRequest">
        <sequence>
            <element name="Id" type="long" />
        </sequence>
    </complexType>

    <complexType name="GetMovieByIdResponse">
        <sequence>
            <element type="videorental:Movie" name="Movie" minOccurs="0" />
        </sequence>
    </complexType>

    <complexType name="Movie">
        <sequence>
            <element name="Id" type="long" />
            <element name="Title" type="string" />
            <element name="RunTime" type="int" />
        </sequence>
    </complexType>
</schema>

XmlBeans will generate two document types for the elements (GetMovieByIdRequestDocument, GetMovieByIdResponseDocument) as well as types for each of the complex types.

For the service implementation I recommend to work only with the classes representing the complex types (instead of the document types). An interface representing the service's port type and its implementation therefore might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public interface VideoRentalPortType {

    public GetMovieByIdResponse getMovieById(GetMovieByIdRequest request);

}

...

public class VideoRentalPort implements VideoRentalPortType {

    private MovieRepository movieRepository = new MovieRepositoryMockImpl();

    public GetMovieByIdResponse getMovieById(GetMovieByIdRequest request) {

        GetMovieByIdResponse response = GetMovieByIdResponse.Factory.newInstance();

        de.gmorling.moapa.videorental.domain.Movie movie = movieRepository.getMovieById(request.getId());

        if(movie != null) {
            response.setMovie(convert(movie));
        }       

        return response;
    }

    private Movie convert(de.gmorling.moapa.videorental.domain.Movie movie) {

        Movie theValue = Movie.Factory.newInstance();

        theValue.setId(movie.getId());
        theValue.setTitle(movie.getTitle());
        theValue.setRunTime(movie.getRunTime());

        return theValue;
    }
}

This looks pretty similar to a typical SEI-based implementation. The only difference is, that the types in use are generated by XmlBeans instead of JAXB. The implementation is straight forward. It takes the movie id from the incoming request, invokes some backend business service and creates a GetMovieByIdResponse from the result.

Next we need to create the actual Provider implementation which will be called by the JAX-WS runtime, whenever the service is invoked:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@WebServiceProvider
@ServiceMode(value=Mode.MESSAGE)
public class VideoRentalProvider implements Provider<SOAPMessage> {

    private VideoRentalPortType videoRentalPort = new VideoRentalPort();

    public SOAPMessage invoke(SOAPMessage request) {

        try {

            Node root = request.getSOAPBody().getFirstChild();

            if(root.getNodeName().contains(GetMovieByIdRequest.class.getSimpleName())) {

                GetMovieByIdRequestDocument requestDocument = GetMovieByIdRequestDocument.Factory.parse(root);
                GetMovieByIdResponseDocument responseDocument = GetMovieByIdResponseDocument.Factory.newInstance();
                responseDocument.setGetMovieByIdResponse(videoRentalPort.getMovieById(requestDocument.getGetMovieByIdRequest()));

                return createSOAPMessage(responseDocument);
            }
            else {
                throw new UnsupportedOperationException();
            }
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

    private SOAPMessage createSOAPMessage(XmlObject responseDocument) {

        try {

            SOAPMessage message = MessageFactory.newInstance().createMessage();
            Node node = message.getSOAPBody().getOwnerDocument().importNode(responseDocument.getDomNode().getFirstChild(), true);
            message.getSOAPBody().appendChild(node);
            return message;
        }
        catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}

A provider implementation must be annotated with @WebServiceProvider. By binding the type parameter to SOAPMessage (a comparison with the alternatives Source and DataSource can be found here) and specifying Mode.MESSAGE within the @ServiceMode annotation we achieve, that we can access complete SOAP messages within the implementation.

The Provider interface defines only one method, invoke(). This method is called whenever any of the service's operations is invoked. Therefore we first have to determine the currently invoked operation, which can be done by peeking at the node name of the first body element.

In case of the getMovieById operation then a GetMovieByIdRequestDocument instance representing the contents of the message body is created using a XmlBeans-generated factory class. From that document we retrieve the actual request element and pass it to the port implementation.

The result is put into a new GetMovieByIdResponseDocument instance, which itself is finally wrapped into a SOAPMessage by importing the root DOM node into the message body.

Conclusion

Creating Provider-based endpoints makes it possible to use JAX-WS together with binding frameworks other than JAXB. Whether it should be done depends as always on the specific circumstances.

For new projects working with SEI endpoints and therefore JAXB should be preferred. The JAX-WS tooling will smoothly generate JAXB binding classes as well as port type interfaces, making the creation of new web services pretty simple.

In migration scenarios things can be different though. Re-writing a whole lot of mapping code originally created for another binding framework such as XmlBeans can be quite a time-consuming and error-prone task. In that case implementing Provider-based services can be an interesting alternative.

When pursuing that approach some performance tests should be executed first. A short load test on my machine didn't show significant execution time differences between JAXB/XmlBeans for the video rental service. But things might look different for larger message types due to the involved DOM manipulations.

Thursday, April 9, 2009

Bookmark and Share

JAX-WS based web services normally run on a JEE web container or application server. That's great for production purposes, as those servers provide a very high level of scalability, security infrastructure, monitoring facilities etc.

But during development repeated deployments to a JEE container can be rather time consuming. JAX-WS' Endpoint class provides an interesting alternative here, as it allows to host JAX-WS web services within plain Java SE applications.

A web service hosted that way can very easily be launched and debugged from within your IDE for instance, which is great for development and testing purposes.

If you go with the code-first approach for web service development, things are really straight-forward:

1
2
3
4
5
6
7
8
9
10
11
public class SimpleServer {

    public static void main(String[] args) throws Exception {

        Endpoint endpoint = Endpoint.create(new ExamplePort());
        endpoint.publish("http://localhost:8080/example_app/ExampleService");

        System.out.println("Published service at http://localhost:8080/example_app/ExampleService");
    }

}

All you have to do is to create the end point by passing an instance of the port to be published (a class annotated with @WebService) and call the end point's publish() method.

Things are slightly more complicated if you prefer the contract-first web service style, meaning you start building your service with a WSDL file describing the service's interface and then generate service/port classes for it. In that case you have to specify the service's WSDL file as well as the QNames of the service and port to be published:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class SimpleServer {

    public static void main(String[] args) throws Exception {

        URL wsdlResource = 
            SimpleServer.class.getResource("/WEB-INF/wsdl/example.wsdl");

        List<Source> metadata = new ArrayList<Source>();

        Source source = new StreamSource(wsdlResource.openStream());
        source.setSystemId(wsdlResource.toExternalForm());
        metadata.add(source);

        Map<String, Object> properties = new HashMap<String, Object>();

        properties.put(
            Endpoint.WSDL_PORT, 
            new QName(
                "http://www.yournamespace.com/example",
                "ExamplePort"));
        properties.put(
            Endpoint.WSDL_SERVICE, 
            new QName(
                "http://www.yournamespace.com/example",
                "ExampleService"));

        Endpoint endpoint = Endpoint.create(new ExamplePort());
        endpoint.setMetadata(metadata);
        endpoint.setProperties(properties);

        endpoint.publish("http://localhost:8080/example_app/ExampleService");

        System.out.println("Published service at http://localhost:8080/example_app/ExampleService");
    }

}

Having launched the service it's a good idea to inspect the WSDL published by the JAX-WS runtime (e.g. at http://localhost:8080/example_app/ExampleService?WSDL). When using the JAX-WS reference implementation (RI) the file always starts with the comment "Published by JAX-WS RI ...", but in the contract-first scenario this shouldn't be followed by "Generated by JAX-WS RI ...".

JAX-WS RI is rather picky with its configuration, and if something is wrong, the runtime silently switches over to code-first mode, meaning the WSDL file is not the original one but was dynamically created from the port class. A common reason for this is not specifying the service and port QNames properly.

Here it would be great to have some kind of "force contract-first mode" option, which prevents the service from starting at all, if something is wrong with the configuration.

Saturday, March 7, 2009

Bookmark and Share

My recently published article "Web Services: Auf die Plätze, fertig, los!" is now available for download as PDF file (it originally appeared in edition 3/09 of the German-speaking journal Java Magazin). I'd be happy on any comments and feedback.

Saturday, February 7, 2009

Bookmark and Share

I am so happy – this week my first article ever in a printed publication appeared. It's named "Web Services: Auf die Plätze, fertig, los!" and is published in edition 3/09 of the German-speaking journal Java Magazin.

The article (written by my colleague Ralf Ebert and me), is a tutorial, that shows how to get started with building contract-first web services using JAX-WS and Maven. We take the reader by the hand and show from scratch, how to create a new Maven project, how to write an XML schema for the service and how to run the web service using the Jetty plugin for Maven.

To make things easier, we don't make the reader write the WSDL for the service by hand, but rather provide a Maven plugin, that creates the WSDL from the XSD with the request/response types relying on some naming conventions. That greatly simplifies things (as writing WSDLs is pretty time-consuming and error prone), while keeping full control over the WSDL, being the big advantage of the contract-first approach for creating web services.

I will show in detail, how to make use of that plugin – named Maven WSDL builder plugin – in a subsequent post.

Having a basic examplary service up and running, the article continues by showing how to perform schema validation of web service requests and responses. Finally we explain how to implement cross-cutting concerns using handler chains.

So if you ever wanted to get started with contract-first web services the real simple way, don't hesistate and get your copy of Java Magazin now and let us know your opinion on the article.