CHAPTER 3

image

Web Services

This chapter covers the Spring Web Services project. SOAP web services can be considered as completely opposite to remote procedure calls (RPCs) in terms of decoupling and performance, because SOAP provides a very high level of decoupling but not for free. The cost of this variability is performance problems that can occur because of the bloated XML protocol SOAP uses. Implementation of SOAP services is also complex. We will dive into the Spring support provided in this field and show how Spring helps to simplify this process by leveraging best practices and reusing most modern approaches.

Introduction to Web Services

The software development world is like the fashion industry: it has trends. When it became clear that distributed systems would be powering modern web sites and that the disadvantages of remote procedure calls would push these technologies to the limits, the focus of software development turned to decoupling.

Web services are highly interconnected with service-oriented architecture (SOA) , in which various pieces of functionality are separated into stand-alone service applications communicating with one another. These services are consumed by client-facing applications or other services. Such architecture allows for a separation of concerns and low coupling. On the other hand, it brings higher complexity, because a well-defined contract between the consumer and the producer of the web service needs to be specified. When all aspects of the contract are defined up front, services and applications can be developed, tested, and deployed in isolation.

RPC approaches may be considered also, but tight coupling of remoting technologies is a big problem for SOA, especially when the consumer of a web service is developed by a different team, department, or company. So communication needs to occur via a protocol that facilitates decoupling the client from the web service.

An important requirement of web services is the ability to handle different versions of the same service API. This is a killer for RPC technologies. Imagine a publicly facing API of an application and various clients consuming it. When a change or new feature needs to be implemented into the API, not all clients are keen to update—because of the additional cost for them. We may end up with two groups of clients using different contracts. Therefore, a protocol is needed, to handle versioning.

This protocol is SOAP (originally known as the Simple Object Access Protocol), which uses XML as its exchange format. SOAP is transport-protocol agnostic, so we can use HTTP, Simple Mail Transfer Protocol (SMTP), Transmission Control Protocol (TCP), User Datagram Protocol (UDP), Java Message Service (JMS), or Extensible Messaging and Presence Protocol (XMPP) as a transport protocol. Such variability doesn’t tie up the underlying network architecture. The XML payload format is flexible, platform independent, and widely adopted. All major platforms such as Java, .NET, C++, Ruby, PHP, Python, and Perl support it. Therefore, it’s a good candidate for an exchange format fulfilling SOA requirements.

The highest level of decoupling the client from the web service occurs when the client discovers the web-service capabilities and structure of the payload at runtime. XML Schema Definition (XSD) and Web Services Description Language (WSDL) can be used for this purpose. XSD defines the structure of the XML payload provided or consumed by the web service, complex types, and validation rules for certain data elements. So we can think of it as metadata for the web service payload. The web service can validate received payloads against XSD. WSDL is an XML-based description of the functionality provided by the web service. It gathers the name of the service, the service methods provided, and the signatures of the service methods (types of parameters and return values).

With this concept, the client doesn’t need to know anything about the web service up front. The XML payload can be constructed based on XSD and WSDL files on the fly. But this approach also has downsides. Constructing an XML request based on XSD and WSDL files isn’t trivial. Implementation of the web service is also more complex with such metadata rules. And when a payload is generated dynamically all the time, there are performance hits. Of course, we could omit the metadata contract of the web service and tie down the client to the static payload structure. But for such cases, there are less-verbose exchange formats (for example, JavaScript Object Notation, or JSON). Verbosity of the XML format is one of the problems pointed out by SOAP critics.

So let’s summarize the most important SOAP advantages in comparison to remoting (RPC) technologies:

  • Decoupling: Document-oriented communication allows for versioning and discoverability.
  • Interoperability: Can connect various platforms easily and operate on top of various transport protocols.

And the disadvantages include the following:

  • Complexity: SOAP is much more complicated to implement, because it requires knowledge of XSD, WSDL, XML marshalling, and the marshalling framework.
  • Performance: The verbosity of XML and the need for marshalling and verification against the schema makes it perform less well.

Java Web Services

Java is not the only platform operating on top of the SOAP protocol. .NET is also a big player in this space. But Java EE is definitely the biggest adopter of SOAP web services concepts. Many libraries have evolved in the Java ecosystem to help with web services development. Because SOAP relies on XML, it’s obvious that libraries for marshalling into XML and unmarshalling from XML are crucial for SOAP communication.

Let’s look at the XML libraries for the major programming language platforms, starting with Java. These libraries are categorized according to their different approaches to XML processing:

  • Java
    • Event-driven approach—application listens to events for certain XML elements: Simple API for XML (SAX)
    • DOM-based approach—application loads the whole XML and parses it as a tree structure: JDOM, XOM, dom4j, TrAX
    • Streaming API for XML (StAX): Introduces the concept of a cursor within an XML document that is used for reading forward and backward
    • Binding of XML to Java objects: Java Architecture for XML Binding (JAXB), Castor XML, XMLBeans, XStream, JiBX
    • .NET: XmlSerializer, System.Xml, .NET XML parser
    • Python: ElementTree, BeautifulSoup, MiniDom/PullDom
    • Ruby: Builder, Nokogiri, REXML, XmlSimple
    • Perl: Perl XML, XML::Simple
    • JavaScript: LibXmlJs

It’s obvious from this list that many possibilities exist for implementing XML-based web services. Let’s focus only on Java web services for now. We have XML messages on one hand and Java objects on the other hand. The easiest way to accomplish XML conversion is direct binding to Java objects. But this approach is not suitable in every case. For example, if we have a really big XML message that can’t be loaded into memory, we need to process it with a lower-level XML-processing library.

But for SOAP implementations, binding libraries are the best fit. Big messages in SOAP may indicate a design problem in communication. That is why most Java web services nowadays are implemented with Object to XML (OXM) binding libraries. JAXB is the main Java OXM player, because it is part of the Java EE standards. JAXB technology also is useful beyond SOAP, because it can handle JSON format.

As we described, XML marshalling and unmarshalling are happening within SOAP communication. SOAP messages have a specified high-level structure and need to conform to a formal contract (XSD/WSDL).

The mandatory high-level structure of a SOAP message used for exchanging data is as follows:

  • Envelope
  • Header: May contain metadata—for example, the data necessary for securing, routing, or business logic metadata
  • Body: Contains the message itself, and may contain the target domain where the web service is located

A contract defines the exact format for interchanged XML messages. But how do we start developing such a web service? When we have Java objects on one side and XML messages on the other side, one side needs to conform to the other side via conversion. We have two possibilities to start developing a web service:

  • Contract Last : Specify Java classes first and create a WSDL contract based on the Java classes’ structure.
  • Contract First : Create a WSDL contract up front and force the Java classes to conform to that contract.

You may expect us now to highlight the advantages and disadvantages of each approach—especially when both approaches nowadays have automatic conversion libraries. But SOA experiences show that the Contract Last approach can lead to various problems:

  • Performance: Java object graphs can easily become very big. It is not suitable for a communication protocol to send huge XML messages that would be generated by big Java object graphs.
  • Type duplication: Careful definition of XSD allows for reusability of XSD types. This is not possible when a contract is generated from Java.
  • Contract changes:
    • The basic idea of decoupling is based on protecting the client from server changes. If XSD and WSDL are automatically generated from Java interfaces, the XML contract can be a target of frequent changes by the XSD/WSDL generation algorithm.
    • When a contract is changed with the Contract Last approach, we can’t support two versions of the contract. If we use the Contract First approach, we could handle changes between versions of the contract by XSLT transformations. When messages are converted by XSLT transformation, the same Java class representation can be used. So we can support various versions of the contract at the same time with Contract First.
  • Java to XML type conversion problems:
    • It is easy to represent cyclic graphs in Java. But such a representation in XML is not trivial.
    • XSD restrictions are a useful mechanism for restricting a group of possible data values (for example, regular expressions for strings). But the Java language doesn’t contain such validation mechanisms out of the box.
    • Some Java types have special constraints or semantics. These can be interpreted in XML, but this representation can be ambiguous. ArrayList can be represented on the client as LinkedList. The picture can become even more complicated when communication occurs between two programming languages.

In light of these arguments, Contract First is considered best practice. The Contract Last development style is considered easier, but Spring, in conjunction with existing XML libraries and tooling, makes the Contract First approach easy as well.

Spring Web Services

Spring supports web services development in two ways:

  • Support for JAX-WS (formerly JAX-RPC): JAX-WS is the Java EE standard for developing Contract First and Contract Last web services. It is part of Core Spring framework.
  • Spring Web Services project: Support for Contract First web services.

Image Note  JAX-WS is beyond the scope of Enterprise Integration with Spring certification and thus also beyond the scope of this book.

The Spring Web Services project is by far the preferred way of implementing a web service. It is a stand-alone project within the Spring platform. As always, Spring Web Services doesn’t try to reinvent the wheel, but rather takes advantage of existing technologies to provide convenient abstractions and features. Let’s summarize some of these features:

  • Powerful routing of messages: We can map XML messages to endpoints via the payload root XML element, Web Services Addressing (WS-Addressing), or SOAPAction headers.
  • Wide support for XML converters: Supports DOM, SAX, StAX, JDOM, dom4j, XOM, and OXM technologies (for example, JAXB 2, Castor XML, XMLBeans, XStream, JiBX).
  • Integration with Spring Framework family: Developers use the same Inversion of Control (IoC) concepts as with all other Spring modules.
  • Support for WS-Security: Encryption, decryption of SOAP messages, and authentication for web service access are invaluable mechanisms for security.

Image Note  Support for WS-Security was removed from Enterprise Integration with Spring certification and therefore is beyond the scope of this book.

Contract Creation

Spring Web Services provides an excellent foundation for creating web services, because it facilitates only the Contract First approach. These well-defined steps indicate how to define a Spring Web Services contract:

  1. Create a sample message payload.
  2. Generate an XSD schema based on the sample message.
  3. Enhance the generated XSD if needed:
    • Amend for the desired contract structure.
    • Change XSD types for elements.
    • Extract reusable XSD types.
    • Define XSD restrictions for elements.
  4. Spring can dynamically generate WSDL based on XSD.

The first three steps are optional. A web service developer experienced with XSD may want to define a contract directly. But the simplest way is to generate it from sample messages. There is also a good chance that business experts will be able to provide sample messages with the desired XML structure.

Let’s explain these steps with a simple example. Our example service will provide user details based on a requested e-mail address. This simple domain will be used across all web services examples. The request sample message looks like Listing 3-1.

Image Note  Source-code files explaining the creation of XSD contracts are stored in the example project 0301-ws-xmlconfig-service in the directory src/main/resources.

Based on this request, the service should provide user details. The sample payload for the user details response is highlighted in Listing 3-2.

When we have the request and response messages specified, we can generate the XSD contract draft. We have various options. Because this generation is a one-time step, there is no point in automating it into a continuous integration pipeline. So you can use any tool of your choice. Various types of tools with different levels of complexity and user interaction approaches are available. Some examples include the following:

Let’s use the third option, because it doesn’t require any installation nor downloads. This tool provides various design styles for a generated XSD structure. As we don’t need reusable types, we can choose Russian Doll Design. Listing 3-3 was generated for the sample request, and Listing 3-4 for the sample response.

Image Note  XSD and WSDL languages are used extensively in web services development, and are the topic of other books. Enterprise Integration with Spring Certification focuses on Spring-related abstractions and workflows. It doesn’t dive deep into schema definitions nor web service descriptions. Therefore, detailed XSD and WSDL are beyond the scope of this book.

These generated results provide the basic foundation for our XSD contract. Developers may need to tweak this contract to cover input validation requirements and to enhance readability and code reusability. This example merges these two contracts into one file, because our contract isn’t complicated and it’s easier to work with one contract file. The enhanced contract is shown in Listing 3-5.

Let’s sum up the manual changes introduced in Listing 3-5:

  • Use xs:NCName types instead of xs:string for the first name and last name. This is useful because it excludes most special characters (which wouldn’t make sense for a person’s name).
  • Narrow down e-mail values in the contract by using regular expression restriction.
  • Specify the targetNamespace where the service will be hosted.
  • Specify UTF-8 encoding for the XSD document.

The XSD contact is a crucial piece of web service interface specification. It is a single source of truth for the web service. Designing the XSD contract correctly is important, because its structural changes can later trigger refactoring in the Java code base. Therefore, we want to have the contract reviewed by as many stakeholders as possible up front.

The created XSD contract can be used for further generation of the following:

  • Java model Plain Old Java Objects (POJOs) in case we are using a JAXB 2 XML marshaller
  • Dynamic WSDL definition for the web service

Endpoint Mapping

When the request hits our application, we need to route it to certain Java logic. This binding is called endpoint mapping in the Spring world. The endpoint refers to the class where the web service logic is defined. This class is annotated with the @Endpoint annotation.

When the class is declared to hold web service logic, we need to route messages. Spring provides various declarative endpoint-mapping mechanisms in the form of these method-level annotations:

  • @PayloadRoot: Maps the method to the root element of the request payload
  • @Action: Maps the method to the WS-Addressing Action header
  • @SoapAction: Maps the method to the SOAPAction header

With these annotations, a web services developer can effectively route SOAP messages declaratively based on various message attributes.

@PayloadRoot routing is the most popular method-mapping mechanism. The top-level (root) XML element of the SOAP message drives the routing. The @PayloadRoot annotation has two parameters:

  • localPart: Specifies the name of the root element to map. This is a mandatory parameter.
  • namespace: Specifies the namespace of the payload root element. This is an optional parameter.

So to map the example message in Listing 3-6, localPart needs to have the value UserRequest, and namespace should have the value http://localhost:10301/0301-ws-xmlconfig-service.

When a particular Java method is mapped, we need to map parameters and return the value of the Java method. Spring provides various options for signature mapping:

  • Mapping XML parser types
    • @RequestPayload: Maps the request message to the method parameter; the parameter type is derived from the underlying XML marshaller, used for XML-to-Java conversion.
    • @ResponsePayload: Maps the response message to the method’s return value; the return value type is derived from the underlying XML marshaller, used for XML-to-Java conversion.
  • @XPathParam: Used for binding parameters based on XPath expressions
    • boolean or Boolean
    • double or Double
    • String
    • org.w3c.dom.Node
    • org.w3c.dom.NodeList
  • Extracting message metadata into the method parameter (no annotation usage)
    • org.springframework.ws.context.MessageContext
    • Soap metadata (SoapMessage, SoapBody, SoapEnvelope, SoapHeader, SoapHeaderElements)
  • Mapping parameters to a custom unmarshaller (this method requires the parameter to be annotated with @RequestPayload also)

The developer can decide which information from the message is relevant for the web service and needs to be injected into a method. This list of supported signature mappings provides huge variability. A handy Spring Web Services feature is the ability to choose an XML parsing mechanism. Table 3-1 presents the types of XML parsers that Spring can automatically map in the method signature. These types always have to be annotated by @RequestPayload or @ResponsePayload, respectively.

Table 3-1. XML Parsers in Method Signature Mappings

XML Parser

Method Signature Type

TrAX

javax.xml.transform.Source, DOMSource, SAXSource, StreamSource, and StAXSource

W3C DOM

org.w3c.dom.Element

dom4j

org.dom4j.Element

JDOM

org.jdom.Element

XOM

nu.xom.Element

StAX

javax.xml.stream.XMLStreamReader and javax.xml.stream.XMLEventReader

(can be used only for method parameters and not for return values)

JAXB 2 Endpoint-Mapping Example

The most widely used marshaller is JAXB 2, which allows for generation of Java model classes from an XSD contract. Generated Java classes are annotated with special mapping annotations, which map XML elements onto POJO classes. This approach is called Object to XML Mapping (OXM) . It is similar to the famous object-relational mapping (ORM), which maps SQL DB tables onto POJOs.

Examples in this book are based on the Maven 3 build system. So jaxb2-maven-plugin is used for Java model generation; the XJC binding compiler is used to generate Java classes from the XSD contract. More information about this plug-in can be found at http://mojo.codehaus.org/jaxb2-maven-plugin/xjc-mojo.html. The Maven plug-in configuration is shown in Listing 3-7.

This configuration reads all XSD schema files from the src/main/resources directory and its subfolders. It is crucial to know that it would pick up all XSD files present in the specified directory structure and convert the XSD types/elements into Java POJOs. Therefore, the number of POJO classes depends on the number of XSD types/elements defined. If one file has five XSD types, five POJO classes will be generated for these types. The conversion will fail if any name clashes occur.

When we run this configuration with Maven goals, JAXB POJOs will be generated based on the XSD schema into the src/main/java folder:

> clean install

The generated package structure follows the namespace of the XSD schema. In our case, it is localhost._10301._0301_ws_xmlconfig_service. These POJOs basically represent the domain model of the web service. For the XSD schema in Listing 3-5, three classes are created:

  • UserRequest
  • UserDetailsReponse
  • ObjectFactory

The first two classes obviously represent the XSD elements from the contract. They contain getters and setters for subelements. The last class is a factory for creating contract elements. It can be used to create instances of POJO classes derived from XSD elements or types. It may not be needed, depending on the XSD contract. Listing 3-8 shows generated classes with their JAXB 2 annotations.

Bear in mind that this code is generated from an XSD contract. Therefore, when the contract is changed, the JAXB 2 model is regenerated. Any changes to POJO field names or types can trigger Java compiler issues in the code where they are used. This useful behavior of the Contract First approach for creating web services uses the advantages of Java’s type-safety features. If the XSD contract were generated from a Java model, the change could be missed during development. And as we all know, the sooner an issue is discovered, the cheaper the damage is.

The class ObjectFactory is a factory for all other types involved. It had wider use in earlier versions of JAXB for creation of model instances. But in recent versions of JAXB 2, the use of this class is limited, because POJO constructors can be used to create model instances now. ObjectFactory may be needed depending on the complexity of the XSD schema. If you notice that your generated ObjectFactory contains factory methods returning JAXBElement<T> types, your schema may not be directly mapped to the Java class hierarchy. Another option is that your XSD restrictions can’t be expressed by the Java type system. In such cases, you may need to create a meta-structure in the form of JAXBElement<T> types. But that is rare.

UserRequest and UserDetailResponse are domain model types that will be used in web service logic. Listing 3-9 shows an example of endpoint mapping that uses generated types in the method signature.

Developers can take advantage of the flexibility of the Spring Web Services binding mechanism and vary it with parameters.

Spring can also inject into our web service method the metadata of the message—for example, the SOAP header or message context. The message context contains all data involved in the service’s current request:

  • Request
  • Repose
  • Properties, which can be used to communicate with the interceptors and endpoint

Let’s say we want to read the SOAP header role and use the message context in web service logic now. Listing 3-10 reflects this requirement.

Routing SOAP messages based on the root XML element (@PayloadRoot) is most common. But routings also can be based on WS-Addressing (@Action) or the SOAPAction header (@SoapAction). Listing 3-11 shows a SOAPAction routing example. WS-Addressing would be similar.

Spring Context Configuration

The previous section highlighted how an XSD contract can be mapped to Java logic with the Spring Web Services framework. A class annotated with @Endpoint is a candidate for Spring component scanning. So this class can be treated as a Spring component. Various options exist for integrating it into Spring’s IoC container.

The first option is to use the Spring XML configuration shown in Listing 3-12.

This configuration uses three namespaces from Spring’s deck of XML schemas:

  • web-services: For web services configuration
  • beans: Basic Spring IoC namespace
  • context: For component scanning

The XML element <context:component-scan> scans for an endpoint defined in Listing 3-9. The <ws:annotation-driven> configuration says to Spring that we want to use Web Services annotations. The last element is the most interesting: <ws:dynamic-wsdl> is used for dynamic generation of WSDL based on the XSD contract file located in our classpath. Spring scans the classpath for the XSD schema file specified. As we are using Maven in these examples, userDetailsSchema.xsd is located in the folder src/main/resources. The location URI specifies the URL endpoint from which the generated WSDL will be served. We used the root-level location for WSDL exposure. The parameter id specifies the exposed name of the WSDL file. So when we run the server on localhost and port 10301, the final WSDL location would be http://localhost:10301/0301-ws-xmlconfig-service/userDetails.wsdl.

Dynamic WSDL generation is useful during the development stage of our web service, as requirements may change a lot. But in production we want to make sure that contact with the web service is stable. Because the generation algorithm can sometimes bring in small changes, it’s a good practice during production to use static WSDL (not generated). This configuration is shown in Listing 3-13.

The XML element <ws:static-wsdl> is used for static WSDL exposure. A prerequirement is the existence of a WSDL file in our project. In this case, the file userDetailsSchema.wsdl should be in the classpath. As we are using the Maven build system, the concrete file is src/main/resources. The parameter id is important for the final name of the exposed WSDL. In this case, it is userDetails. Therefore, if we run the server on localhost and port 10301, the final WSDL location would be http://localhost:10301/0301-ws-xmlconfig-service/userDetails.wsdl again.

This XML configuration is becoming less relevant these days because of a shift toward more-maintainable Java Spring configurations. An analogous Java configuration is shown in Listing 3-14.

The class is annotated with the standard @Configuration annotation used for Java Spring configurations, and the framework scans the current package and subpackages for Spring components. This scanning will pick up @Endpoint if web services are enabled with @EnableWs, which is the Java annotation replacement for the <ws:annotation-driven> XML element. The bean registered by the userDetails method facilitates the dynamic WSDL creation. Here we are injecting the XSD bean registered by the userDetailSchema method. Listing 3-14 is, in fact, the exact mirror of the XML configuration in Listing 3-12. Only the namespace differs, because it is located in a different example project.

A static WSDL exposure with a Java configuration is shown in Listing 3-15.

Transports

There are various ways to configure the transport layer for Spring Web Services. Each transport protocol requires certain Spring abstractions to be registered.

HTTP/HTTPS

First, there is the servlet container:

  • MessageDispatcherServlet: This is the easiest and most used mechanism.
  • DispatcherServlet: This is useful when we need to provide a SOAP endpoint alongside REST or Spring MVC endpoints. WebServiceMessageReceiverHandlerAdapter and RequestMappingHandlerAdapter create a bridge from the web service endpoints to DispatcherServlet.

Alternatively, there is the simple HTTP server, with the following as receivers:

  • SoapMessageDispatcher
  • SimpleHttpServerFactoryBean
  • SoapMessageDispatcher

JMS

With JMS, we use the following:

  • SoapMessageDispatcher
  • ActiveMQConnectionFactory
  • DefaultMessageListenerContainer

E-mail

With e-mail, we use the following:

  • SoapMessageDispatcher
  • MailMessageReceiver

XMPP

With XMPP, we use the following:

  • SoapMessageDispatcher
  • XmppConnectionFactoryBean
  • XmppMessageReceiver

As you may notice for nonservlet transports, there are a few common bean types:

  • Message factory
  • Message dispatcher
  • Message receiver
  • Connection factory (XMPP, JMS)

Image Note  Details of nonservlet transport configuration are not covered in Enterprise Integration with Spring certification. Therefore, we focus on only MessageDispatcherServlet. Information about configuration of nonservlet web service transports can be found in Spring Web Services reference documentation.

Servlet Transports

Bear in mind that MessageDispatcherServlet and DispatcherServlet are different servlet implementations. One serves SOAP endpoints, and the other serves MVC or REST endpoints. Configuring MessageDispatcherServlet in a Spring application can be done in various ways. The Spring Framework and Spring Boot projects nowadays provide various servlet descriptor wrappers to simplify web container configuration. Listing 3-16 shows a classic XML-based servlet descriptor configuration in a web.xml file.

This servlet configuration is pretty common. MessageDispatcherServlet is used as a servlet class. If we didn’t specify the contextConfigurationLocation parameter, Spring would try to find the Spring configuration in the file {servlet-name}-servlet.xml. But in this case, we used the explicit approach with the web-service-config.xml context configuration from the classpath (located in the directory src/main/resources).

The parameter transformWsdlLocations is false by default. If it’s true, Spring treats the location element in the WSDL as a relative address and transforms the WSDL location element according to the URL received from the client. So by default Spring does not do this transformation, and the WSDL location element is hard-coded. But hard-coding is not suitable for professional software development, in which the same application artifact is promoted to various environments (for example, development, QA, performance, and production environments). So we want to have transformWsdlLocations configured to true nearly always.

Listing 3-16 includes two remaining elements of servlet configuration. The load-on-startup configuration tells the servlet container that we don’t want to use the lazy initialization feature. So our servlet is initialized at the start of the servlet container. Otherwise, the servlet would be initialized when the first request arrives. Finally, the servlet-mapping element specifies the relative root URL of the servlet.

In the Java world, a big shift is occurring from XML configuration toward annotations. The Spring Web Services project is no exception in this field. Enhanced support for Servlet 3.0 Java configuration was introduced in version 2.2.

The example in Listing 3-17 has a similar effect as the web.xml configuration in Listing 3-16.

The AbstractAnnotationConfigMessageDispatcherServletInitializer class is an abstract implementation of WebApplicationInitializer, which is the interface helping with programmatic servlet configuration in the Spring world. Spring automatically seeks WebApplicationInitializer implementations if a servlet container is in the classpath.

MessageDispatcherServlet is configured by Spring under the hood when the AbstractAnnotationConfigMessageDispatcherServletInitializer implementation is detected in the classpath. The Spring servlet context configuration is defined in the ServerConfiguration class, which uses the @Configuration annotation to define a Java-based Spring context. We don’t need the root Spring context for this example, so we can return null from getRootConfigClasses(). The last piece of this servlet configuration is the servlet URL mapping, which defines the relative address where the servlet will be located. But the default AbstractAnnotationConfigMessageDispatcherServletInitializer uses the /services value defined in one of the servlet initializer parent classes. Our example uses /*.

Listing 3-17 highlights a relatively new approach for Spring Web Services servlet configuration. But an even more modern approach introduced by the Spring Boot project is shown in Listing 3-18.

The @ComponentScan, @Configuration annotation should already be familiar. This Spring configuration class scans the current package and subpackages for Spring components. @EnableWs turns on Spring Web Services support.

One additional annotation is @EnableAutoConfiguration. It’s one line, but its impact is very big. Spring Boot takes a look at the set of dependencies used by our application and guesses the desired Spring configuration based on it. It creates a few default beans out of the box. In this case, we need it for autoconfiguration of the embedded servlet container.

A key class of this new mechanism is ServletRegistrationBean, which is effectively a wrapper for servlet configuration. So when it’s registered into the Spring container, Spring registers the wrapped servlet into the servlet container. The effect of this API is that the developer feels that the servlet configuration is being registered into the Spring context, and not the other way around. It turns upside-down the integration mechanism of the servlet container and Spring context from the API point of view. In Listing 3-18, MessageDispatcherServlet is our desired servlet implementation. An important aspect of this concept is that we need to configure the applicationContext property. In this case, we use the application context injected by Spring, which is in fact the context where this bean is registered.

When we have Spring/servlet configuration turned upside-down, we can register this bean alongside other beans needed for serving web services. Listing 3-18 shows the least verbose Spring Web Services configuration, which uses static WSDL exposure (previously explained).

Web Service Client

The focus of the previous sections was exposing Spring Web Services. Spring also provides support for consuming web services. This approach to client-side development is also focused on Contract First. And again, there is no need to create a client-side Java model manually.

Spring’s preferred XML marshaller on the client is JAXB 2, as well as on the server. As our examples are based on the Maven build system, Listing 3-19 shows how to generate JAXB 2 model classes from WSDL located on a remote server.

maven-jax2b-plugin is hooked to generate the Maven goal, part of the default life-cycle phase. The target package for the generated classes is specified by the generatePackage element in the plug-in configuration. This generation relies on a schema to be accessible at http://localhost:10301/0301-ws-xmlconfig-service/wsdl/userDetails.wsdl. So it’s handy when the target web service is already live.

But when we are developing a web service alongside a client consumer, the WSDL is not necessarily online. A nonexistent remote schema causes problems when we want to automate the build as part of a continuous integration/delivery/deployment pipeline. Therefore, there is also an option to use a local WSDL file, as highlighted in Listing 3-20.

Model classes are generated into the package net.lkrnac.book.eiws.chapter03.ws.xmlconfig.model. They are generated based on a WSDL schema located in the file userDetails.wsdl and the directory src/main/schemas. The client model looks just like classes generated on the server counterpart in Listing 3-8. Similar to server model generation, we take advantage of type-safety Java features on the client as well. It is obvious that the most important part of the client/server interface is an XSD/WSDL contract from which models on both sides are generated.

The central class for client Spring Web Services support is WebServiceTemplate. It covers a lot of responsibilities:

  • Sending and receiving messages
  • Calling marshaller/unmarshaller for converting messages
  • Can be configured to intercept messages
  • Can be configured to translate errors generated on the server into client-side runtime exceptions

Apart from SOAP, WebServiceTemplate can work also with a Plain Old XML payload and can operate on top of the same transport protocols as the server: HTTP, e-mail, JMS, and XMPP. Similar to server transport Spring Web Services support, the client counterpart also provides a set of bean wrappers for each one. The central interface is WebServiceMessageSender, which defines the message sender implementation for each protocol:

  • HTTP
    • Default configuration for HTTP protocol—HttpUrlConnectionMessageSender
    • Apache HttpComponents— HttpComponentsMessageSender
  • JMS—JmsMessageSender and JMS message factory (for example, ActiveMQConnectionFactory)
  • E-mail— MailMessageSender
  • XMPP— XmppMessageSender and XmppConnectionFactoryBean

These can be created directly via a Java constructor and has three mandatory fields: defaultUri, marshaller, and unmarshaller.

In our examples, we will be focusing on HTTP, which is the most common protocol for web services. First, the client Spring context configuration example in Listing 3-21 shows an XML configuration.

Here we are using two Spring XML namespaces: context and beans. Context is used for component scanning the bean, using WebServiceTemplate in the package net.lkrnac.book.eiws.chapter03.ws.xmlconfig.client. Two beans are needed for web service consumption. One of the beans is our famous webServiceTemplate. It needs to have the defaultUri property specified, which is nothing other than the address where the web service is hosted. Two other mandatory properties of WebServiceTemplate are marshaller and unmarshaller. These are both served by the marshaller bean. It is registered as a separate bean and used for XML-to-Java POJOs conversion. We can observe from earlier POJOs generation that JAXB 2 is our marshaller of choice (the implementation class is Jaxb2Marshaller).

Nearly the same configuration written in Java is shown in Listing 3-22. The only differences are the web service URI and marshaller context path.

This example is Spring’s default client support for web services. WebServiceTemplate uses the HttpUrlConnectionMessageSender bean under the hood. This bean is created automatically by Spring and uses the standard Java SE HTTP client implementation from the java.net package. But some advanced use cases can force you to use the Apache HttpComponents project for client HTTP access. Additional features of Apache HttpComponents are as follows:

  • Client-side authentication
  • HTTP state management
  • HTTP connection pooling

Listing 3-23 shows an example of an Apache HttpComponents client.

The Apache HttpComponents wrapper (HttpComponentsMessageSender) needs to be configured as a messageSender property into WebServiceTemplate. A similar approach for registering special message senders would apply for non-HTTP protocols also.

So now that you know how to create WebServiceTemplate, let’s take a look at its use in Listing 3-24.

The method getUserDetails uses an injected WebServiceTemplate to send request and receive response POJOs via the method marshalSendAndReceive. You may notice that this method has four responsibilities: it marshals the message, sends it to the client, waits for a response, and unmarshals it. For marshalling and unmarshalling, it uses the instance of un/marshaller we configured during the creation of WebServiceTemplate. Request and response POJOs were generated by maven-jax2b-plugin in Listing 3-19.

If we need to use a custom marshalling algorithm, we can use the WebServiceTemplate method sendAndReceive. This method sends a message and waits for a response, leaving the marshalling concerns to the caller logic.

Listing 3-24 covers routing based on the payload root XML element. In case of the SOAPAction header routing, we would need to specify the target SOAP action. It is highlighted in Listing 3-25.

To specify which SOAP action we want to execute on the server, SoapActionCallback with the action name is passed into WebServiceTemplate. In case of WS-Addressing, we would use ActionCallback instead of SoapActionCallback.

Intercepting Messages

Intercepting messages is a powerful mechanism for fulfilling various business requirements. Spring supports two types of interceptions:

  • Server interception
  • Client interception

Each one is composed of different phases. These types of interception touchpoints can be configured on the client as well as on the server (in the following order):

  1. Handle request—occurs before the message is sent from the client or processed on the server
  2. Handle response— occurs before sending a response from the server to the client
  3. Handle fault— occurs on the server and client, but only when an error occurs in web service logic
  4. After completion—is usually used for releasing resources, because it will occur under all circumstances

The first three intercepting phases can terminate further processing of a request by returning a false value. A false value indicates a problem in the processing chain, whereas a true value indicates success of the intercepting phase.

Developers using the Spring Web Services project can take advantage of existing interceptor implementations or can create custom implementations.

Server-Side Interception

Server-side interception is covered by the interface org.springframework.ws.server.EndpointInterceptor. Various implementations of this interface are offered by Spring Web Services out of the box:

  • Logging interceptorsPayloadLoggingInterceptor, SoapEnvelopeLoggingInterceptor
  • Payload-handling interceptorsPayloadValidatingInterceptor, PayloadTransformingInterceptor
  • Security interceptorsXwsSecurityInterceptor, Wss4jSecurityInterceptor

Apart from built-in interceptors, we can create custom implementations of EndpointInterceptor. An example is shown in Listing 3-26.

This interceptor is a normal Spring bean implementing EndpointInterceptor. Each intercepting method will be called according to the interception phases’ life cycle explained at the beginning of this section. An abstract implementation of EndpointInterceptorAdapter also exists for convenience, when a developer doesn’t need to use all interception touchpoints. This abstract implementation simply returns true for touchpoints that can interrupt processing.

An important aspect of this mechanism are injected parameters; via the messageContext parameter, we are able to inspect and manipulate the message. The second parameter is of type Object, because it represents a mapped endpoint bean that serves the message.

Let’s explore how to configure interceptors in Listing 3-27.

This example shows an XML configuration of interceptors. Spring namespace registration, component scanning, and annotation declaration should already be familiar from earlier sections of this chapter.

The new part of this example is an element of the web-services namespace: <ws:interceptors>. It registers interceptors for web services. Under this XML element, we can register a reference (using the ref element) or declare a new bean (using the bean element) of the global interceptor. Such an interceptor will be applied for all endpoints belonging to this Spring context. In this case, we used Globalnterceptor, which is a class just like UserInterceptor from Listing 3-27.

The nested element payloadRoot binds interceptor(s) to a concrete endpoint specified by namespaceUri and localPart parameters. This is handy when we need to have different interception logic for different endpoints. We would use the soapAction element instead of payloadRoot if we were using SOAPAction routing. In this case, we registered two interceptors for endpoints with the namespace http://localhost:10301/0301-ws-xmlconfig-service and the local part UserRequest. The first one is our custom UserInterceptor, and the second is loggingInterceptor, which is a bean of type PayloadLoggingInterceptor. Of course, we can use custom as well as built-in interceptor implementations within bean declarations or as bean references.

Finally, we registered two more interceptors: one for logging SOAP envelopes, and one for validating requests against the XSD contract. The last three interceptors are implementations provided by the Spring Web Services project out of the box. We just need to register them accordingly. For validation schema interceptors, we need to specify the location of the schema.

The Java configuration is shown in Listing 3-28.

The UserInterceptor and GlobalInterceptor beans are component scanned and autowired into this configuration class. In the method addInterceptors, we are registering interceptors into an injected list. The first two interceptors are wrapped into PayloadRootSmartSoapEndpointInterceptor. This wrapper makes the given interceptor effective only for endpoints with the namespace http://localhost:10305/0305-ws-interceptor-service and the local part UserRequest. We registered our custom UserInterceptor and PayloadLoggingInterceptor this way.

The remaining interceptors are global, which means they apply to all endpoints handled in this service. In this case, we registered two custom, two logging, and one validation interceptor. Built-in interceptors sometimes have mandatory parameters. In this case, the validation interceptor needs to have access to the XSD schema. This configuration is effectively the same as the XML configuration in Listing 3-27.

Client-Side Interception

An interesting part of Spring Web Services interception support is on the client side. The main interface ClientInterceptor signature is similar to the server-side EndpointInterceptor. The only difference is the missing endpoint bean instance injection into the interception touchpoints, because there aren’t any endpoints on the client. There isn’t a convenient abstract implementation of this interface. So we need to implement all touchpoints in a custom client interceptor. The only out-of-the-box interceptor provided by Spring Web Services is PayloadValidatingInterceptor. Client interceptors are registered into the WebServiceTemplate bean.

Listing 3-29 shows an example of a custom client-side interceptor.

Instead of using simple logging example logic, a developer can place into each interception touchpoint custom logic that matches the requirements of his web service. Listing 3-30 shows an example configuration.

We are registering the interceptors array into the WebServiceTemplate field called interceptors. The XML configuration would be similar, as we use standard Spring dependency injection XML elements such as bean, property, and ref. See Listing 3-31.

Error Handling

The Spring Web Services project enables easy translation of errors penetrated from web service logic. Similar to interception support, error handling is divided into the server and client side also.

Server-Side Error Handling

Lax error handling is often a source of serious security vulnerabilities that expose implementation details of a web service. When an exception bubbles up from the web service logic to the servlet container, it commonly sends a failed message with information about the web server and stack trace as a response message. The attacker can analyze (based on such a message) the type of web server, version, platform the web service is running on, and the frameworks used. Such data can be further used to create more-targeted attacks. This problem is number 6 in the Open Web Application Security Project (OWASP) Top 10 list of security risks.

Translation of exceptions on the server is useful for hiding these implementation details. The idea is that Spring will use registered exception resolvers to create a generic response instead of exposing sensitive information to the client. MessageDispatcher scans the classpath for implementations of the EndpointExceptionResolver interface. It has only one method called resolveException. This interface can be used in various ways:

  • A custom implementation fulfills special requirements of error translation.
  • SoapFaultMappingExceptionResolver maps each exception to a particular SOAP fault with an explicit error message.
  • SimpleSoapExceptionResolver translates all exceptions into the SOAP client error with an exception message as a response payload.

Last but not least is the @SoapFault exception. It is used to mark the exception type, so that the exception can be translated into a message. The SOAP fault is specified in annotation parameters in such a case. All these mechanisms allow for higher security standards, but at the same time for variability to inform the client about known problems.

There aren’t any specific XML elements for error handling. Therefore, we will focus only on Java examples. With XML, we would use standard DI elements such as bean, property, and ref. Listing 3-32 shows how to register SoapFaultMappingExceptionResolver.

The exception resolver is registered in the method exceptionResolver. We first create an object of type SoapFaultMappingExceptionResolver. Then we register the default SOAP fault. This is used for every error unless a more specific mapping exists. Specific mappings are registered as an array of Properties type into the field exceptionMappings of SoapFaultMappingExceptionResolver. In this case, we map IllegalStateException to the client SOAP error. Method setOrder specifies the order in which mappings are processed when an exception occurs.

If we prefer SimpleSoapExceptionResolver or a custom exception resolver implementation, it would be created via a constructor and registered as a Spring bean in a similar way.

Listing 3-33 shows how to use the @SoapFault annotation.

When this exception percolates to a Spring Web Services container, it’s translated into a client SOAP fault. This is specified by the annotation parameter faultCode. In this case, we mapped CustomErrorException to the client SOAP error. The exception message field is used as a response payload. If we would like to explicitly specify a generic error message for an exception of this type, we would define the annotation parameter faultStringOrReason.

Client-Side Error Handling

Client-side error translation is in place for the translation of SOAP messages into exceptions in client code. The central interface is called FaultMessageResolver and needs to be registered as a property of WebServiceTemplate. There are two out-of-the-box implementations:

  • SoapFaultMessageResolver translates the error into SoapFaultClientException.
  • SimpleFaultMessageResolver translates the error message into WebServiceFaultException.

We can also provide a custom implementation of FaultMessageResolver, as shown in Listing 3-34.

Out-of-Container Testing

Various approaches exist for automatically testing web services. Unit tests usually don’t create Spring’s context instance. A class or group of classes are tested in isolation, and the test fakes dependencies. These tests are useful as documentation of a developer’s intentions of how the module should work. Such testing is particularly useful for sanitizing the business logic of the web service or web service consumer.

In another type of testing, we automatically deploy the web service to a target environment and execute a test suite, which would perform or consume real requests. This is heading toward an end-to-end level of testing. Because deployment steps are needed in the build pipeline, these tests may take some time. Therefore, this type of testing is commonly not triggered as often as unit testing.

Slightly more suitable is simulating a deployment environment during the build process. Examples are tomcat7-maven-plugin or gradle-maven-plugin, which enable us to build a full artifact of our application and deploy it to an embedded servlet container.

Additionally, Spring Web Services provides support for integration testing of full or partial Spring configurations. It can be integrated with a unit-testing framework of your choice (most commonly JUnit or TestNG). This testing support is handy, because it covers integrated Spring configuration and doesn’t need to use a servlet container (or other type of resource-intensive environment) at the same time.

Server-Side Testing Support

When we are testing a web service server, we need to send requests to the web service under test, receive the request, and verify a response. The Spring Web Services project provides helpers to fulfill these tasks easily:

  • MockWebServiceClient is used to simulate the client and for sending requests.
  • org.springframework.ws.test.server.RequestCreator/org.springframework.ws.test.server.RequestCreators helps with creation of request messages.
  • org.springframework.ws.test.server.ResponseActions/org.springframework.ws.test.server.ResponseMatchers provides support for response verification.

In this example, we’ll use a testing request from Listing 3-35 and a response located in Listing 3-36.

Let’s explore service-side testing support in Listing 3-37.

TestNG is a testing framework used for powering the test. To integrate it with the Spring context, we need to inherit the test class from AbstractTestNGSpringContextTests. This class is part of the Spring Core test module. We are testing the Spring configuration defined by the class ServerConfiguration from the example project 0303-ws-boot-service. This configuration was already explained in earlier sections of this chapter. To create the mock client, we can use an autowired application context instance.

The next step is the creation of the request. We create it based on the test file testRequest.xml with the help of RequestCreator and RequestCreators. Now we are ready to perform virtual requests. We do this by calling the method sendRequest on the mock client instance. It returns a virtual response. Finally, we verify that the web service responded with the expected result and message payload. The ResponseMatchers class has a lot of helper verification methods, so we are capable of testing complex behaviors.

Client-Side Testing Support

When we are testing a web service client, we need to use the test client to perform requests first. Then we receive responses from the fake server and verify that they are represented on the client as expected. The classes that help us with it are as follows:

  • MockWebServiceServer is used to simulate the client and to send requests.
  • org.springframework.ws.test.client.ResponseCreator/org.springframework.ws.test.client.ResponseCreators provides support recording the expected responses into the fake server. It will be used to send responses from the fake server.
  • org.springframework.ws.test.client.RequestMatchers provides support recording the expected requests into the fake server. It will be used to verify that the client sent the correct payload in the request.

Let’s explore the testing support in Listing 3-38.

Similar to the server-side testing example in Listing 3-37, TestNG is used as a testing framework. The test uses WsBootClientConfiguration as a configuration under test. For creation of a fake server, we need to autowire an instance of WebServiceTemplate. Then we record requests that are expected from the client and responses provided by the fake server.

The testing client instance was injected from the Spring testing configuration. We perform the client call as it would be done from our client application. In the verification phase, we need to verify that the client correctly represented responses from the fake server. Last but not least, we need to verify that the requests sent from the test client to the fake server were correct. This is done by a method called verify on the fake server instance.

Summary

In this chapter, we briefly compared concepts for creating web services and distributed applications and the advantages that arise from decoupling services in comparison to remoting technologies. Then we presented XML marshalling technologies that can be integrated into Spring’s ecosystem.

We also explored the benefits of a Contract First approach for building web services with the Spring Web Services project. This approach helps abstract away the complexity of developing web services and makes it much more approachable by providing APIs to follow best practices on top of existing libraries and protocols. It enables us to create SOAP-based web services that can communicate via HTTP, JMS, e-mail, and XMPP transport protocols.

We showed how to optimally start developing a web service contract based on communication requirements. The message payload is the starting line for generating an XSD contract draft. Subsequently, the JAXB 2 Java model can be generated based on this contract. Configuring a container environment and Spring configuration with use of XML and Java contexts was also covered.

Routing web service messages is the backbone of productive development with Spring. We can benefit from routing based on the payload root element, WS-Addressing, and the SOAPAction header.

Client-side support is a crucial part of Spring offerings because it allows us to generate a Java model from an XSD contract. Finally, the chapter explained how to intercept and handle errors and to test Spring configurations in isolation and more effectively.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.239.103