Chapter 8. Utility Services with Java

Image

8.1 Inside the Java Utility Service

8.2 Utility Service Design and Implementation

8.3 Utility Service Types

Utility services provide highly generic functionality that is separate from any domain-specific business processes and usually applicable across a range of business. This chapter explores the design and implementation of utility services with Java.

8.1 Inside the Java Utility Service

The following sections raise generation design considerations distinct to utility service architectures, and further provide a list of common utility service types.

Architectural Considerations

Utility-centric generic functionality is identified by looking for candidate services repeated in many business processes with no inherent affinity to a specific business process or business domain. In a multi-layered architecture of services wherein each layer represents an architecturally significant area of concern, the utility service layer contains services focused on providing reusable functions that address intersecting concerns of existing applications and their technical infrastructure.

Typical characteristics of utility-centric logic include:

Business Domain-Agnosticism – Utility logic has no direct dependency on business domains or processes and is generally applicable across multiple domains.

High Reusability – Utility logic tends to be naturally reusable because of its generic functional context.

Low-Level Processing – Utility logic typically provides the type of processing used by other higher-level business-centric services and processes.

Because utility services work across multiple business domains, they may need to follow a separate development and governance lifecycle potentially implemented and maintained by a separate custodian within the enterprise. The characteristics of utility logic can affect the underlying funding model of utility services, as well as the way new requirements are addressed and managed overall.

Utility services are delegated and not typically coupled with services in higher layers of the architecture directly. Decoupling service components facilitates the management of different concerns or aspects in separate locations of the architecture and independently of each other.

Utility services are not identified as part of the business process or business domain decomposition, and are instead found later in the development process. Addressing non-functional requirements (NFR) and integrating existing application logic into a new architecture can trigger the identification of utility services. NFR describes technical characteristics of the targeted solution which often require the functionality afforded through utility services. Many of the requirements, such as the need for data encryption of messages, are similar between different business domains.

For example, assume that the interaction between two business services must be reliable and offer assured delivery of messages. The standard HTTP protocol does not offer this level of support without application-level programming, so a utility service that enables the application of a reliable messaging standard, such as JMS, to the services’ communication is required. Another example is a need for a common method of logging messages and monitoring a service inventory’s health as part of an overall management solution.

Existing systems are generally considered for reuse and often cannot be easily replaced without integration into the SOA landscape. Utility services help bridge the technology gap between legacy systems and a service inventory by exposing legacy logic and functions via service contracts that comply with the service inventory’s contract standards. Wrapper utility services are one method of this form of integration.

The Java language has a standard set of APIs well suited as utility services. Java, and Java EE in particular, offers interfaces for persistence and data access, security, transactions, and XML document handling. Therefore, an initial set of utility service candidates in Java can be collected through examining the base set of standard Java APIs and evaluating each against the needs of the problem at hand.

Another way of compiling a list of utility services is to start categorizing common application areas, with each area requiring support from a set of utility services. Note that whether or not support for these areas is required by a given solution is often driven by the non-functional requirements presented in Figure 8.1.

Image

Figure 8.1 The set of required utility services can be identified by evaluating the Java language, various aspects of existing services, and non-functional requirements.

The areas of utility services can be partially derived from existing standard Java APIs and then complemented by other common function groups, such as:

Security – Common utility services can address aspects of functionality, such as authentication, encryption, credential mapping, and authorization. An example is a utility service that encrypts an arbitrary XML document using public key cryptography.

Management – The ability to track messages and services and monitor their overall health is part of a service-based management framework that can be implemented via utility services. Services can be managed through other services, such as a utility service that returns the operational state of a given business service.

Message Transport and Message Exchange Patterns – Data that flows between services may require transport over a variety of protocols. Multiple MEP message exchange patterns (MEPs) such as request/response, one-way, and publish/subscribe must be supported. This functionality is delegated to a set of utility services that are often positioned with an ESB. An example is a Publish Message service that allows an XML message to be sent to multiple subscribers asynchronously.

Message Manipulation – Integration of multiple services into a single solution often requires conversion of messages from one format into another. For example, documents may have to follow industry standard formats before they can be sent to an external partner. Data transformation services complement the utility service portfolio with this functionality, for example by supporting XML-to-XML mapping with XSLT or XQuery. This group also includes services for mapping Java objects to XML and vice versa, such as the commonly used Transform service that converts XML documents.

Tracing and Logging – Tracing and logging of information can be considered part of the management framework, although these functions can exist independently and often have high priority. Examples include various forms of logger services.

Persistence and Transactions – Java EE offers a full set of APIs for persistence (the Java Persistence API) and transactions (the Java Transaction API). The Java APIs are appropriate in their original forms for Java-only solutions. However, if non-Java components are required, establishing access to the APIs via a utility service layer may be a viable option. An example for this function is a generic Query service that returns business object data based on passed query parameters.

Registry – The decoupled nature of SOA manifests in levels of indirection between service consumers and services. For a SOAP-based Web service, a service consumer will never contain the hardcoded location or endpoint URL of the service used and instead will delegate the lookup of that address to a well-known location. The lookup of service locations and other metadata is typically contained in a set of common utility services.

For example, a Lookup Endpoint service would return the URL of a SOAP/HTTP service endpoint. REST services have not yet established the need for a centralized registry. The URI of the root resource(s) may be stored in a centralized location. A Resource Lookup service could be created for REST services to support various GET operations that retrieve the URIs of relevant resources. Alternatively, a handful of key resource URIs, or ideally one root resource URI, can be communicated to service consumers out-of-band through informal documentation that allows the hypermedia constraint to guide the REST service consumers through the network of connected resources.

Besides identifying the list of required utility services, positioning utility services in the overall architecture of the system is important because of their broad applicability and ability to be invoked in-process or out-of-process. The main approaches of making utility services available to service consumers include:

Local Java Class – Certain fine-grained performance-critical functionality is well suited to in-process invocation via a local Java invocation. For example, assume that a utility service is built to generate a unique identifier for each message sent across the system. When building an environment with a high volume of messages, the generation of the unique identifier can be placed as close to the origin of a message as possible to prevent a potential performance bottleneck.

Remote Java Component – Services that become part of a highly distributed topology with less performance-critical function and volume can be deployed as a remotely invoked Java component. Generally, the service is hosted in a Java EE-compliant application server and invoked over a communication protocol such as RMI/IIOP. Note that the standard Java RMI communication is omitted from this category, because the limitations related to the lack of a defined application server and container environment make this technology unfit for enterprise-level production environments.

Event-Driven Remote Java Component – An event-driven remote Java component is invoked asynchronously. JMS is the API used for event-driven logic. A client sends a JMS message to a queue without waiting for the message to be processed by its destination or for any response message that may be returned. JMS can be used for request/response messaging, although this method of communication is discouraged in an event-driven architecture. Request/response messaging is commonly implemented in Java EE by use of a MDB method called onMessage(), which is invoked on the corresponding queue as soon as a message arrives. The asynchronous nature of MDBs is useful for utility services.

Web Service/API – Web services are accessed across process boundaries. A SOAP or REST service can be accessed by non-Java service consumers, which make Web-based services and APIs integral to any SOA.

Figure 8.2 illustrates the different ways in which a utility service can be deployed using the four approaches discussed.

Image

Figure 8.2 Utility services can be deployed in a variety of ways to exist in the same process as the service consumer or in a separate process altogether.

Several concerns for consideration include whether a service is invoked locally or across process boundaries. Note that despite this discussion being true for Java services in general, the methods apply particularly to utility services because of their technical nature and close relationship to standard Java APIs. Cross-process invocations indicate pass-by-value semantics because object references are JVM-specific, which renders one JVM’s reference to an object meaningless to another JVM. Parameters passed into a remote method call are copied, and changing this data in the invoked method has no effect on the data inside the invoker.

The aforementioned semantics do not also apply to in-process invocations of services. For local Java invocations, if an object is passed to a method instead of a primitive, a copy of the value of the object’s reference is passed into a method. If the method uses the object’s reference to modify the object, the original object’s content will also be modified. In many cases, modifying the original object is a negative side effect. To address the modification of the invoker’s object state, copy any parameter objects inside the service implementation to guarantee that the caller’s object is not modified by local, in-process invocations.


Case Study Example

NovoBank creates a utility service named Transfer Logger that logs Funds Transfer messages. The messages can originate in a number of locations and contain the amount of the transferred sum as a non-formatted string, such as 1234. For legibility, the utility service formats the amount string into US currency before logging the message, in the form of $1,234.00. Initially, the Transfer Logger service is only available via the Web with the implementation shown in Example 8.1.

@WebService
public class TransferLogger {
  @WebMethod
  public void log(FundsTransfer fundsTransfer) {
    try {
      NumberFormat format =
        NumberFormat.getCurrencyInstance(Locale.US);
      String updatedAmount =
        format.format(new Double(fundsTransfer. getAmount()));
        fundsTransfer.setAmount(updatedAmount);
    } catch (Exception x) {...}
      // logging the message to a file goes here...
  }
}


Example 8.1

After some time, a new solution built entirely in Java is deployed into NovoBank’s IT environment, which is architected to take advantage of the Transfer Logger service in-process through a local Java call. However, a copy of the fundsTransfer object’s reference is passed into the method. The current implementation modifies the fundsTransfer object via its reference fundsTransfer.setAmount, which updates the originally passed-in fundsTransfer object. To avoid this, the implementation must be updated, as seen in Example 8.2.

@WebService
public class TransferLogger {
  @WebMethod
  public void log(FundsTransfer fundsTransfer) {
    try {
      fundsTransfer =
        (FundsTransfer)fundsTransfer.clone();
      NumberFormat format =
        NumberFormat.getCurrencyInstance(Locale.US);
      String updatedAmount = format.format(
        new Double(fundsTransfer.getAmount());
        fundsTransfer.setAmount(updatedAmount);
    } catch (Exception x) {...}
      // logging the message to a file goes here...
  }
}


Example 8.2

In the updated code, the passed object is copied before being updated. The code sample requires the fundsTransfer class to implement the clone() method and invoke the Cloneable<FundsTransfer> interface.


SOAP-based Web services are invoked across process boundaries and use an interoperable message format, such as XML. The data transported to and from a service must be mapped from Java to XML and vice versa. Certain restrictions apply contingent on the standard used for the Web service. For example, the JAX-RPC standard requires that parameters on a service method implement java.io.Serializable, since this standard does not support all XML schema constructs. JAX-WS 2.x utilizes JAXB for Java-to-XML mapping, which introduces a different set of constraints. More information on how JAX-WS leverages JAXB to map data between Java and XML was introduced in Chapter 6.

REST services are also invoked across process boundaries and use various interoperable messaging formats, such as XML, JSON, HTML FORM data, and MIME multipart messages. Data binding frameworks, such as JAXB (XML-Java) and MOXy (JSON-Java), manage the mapping between the language-neutral data types and the Java programming language types, and knowledge of the constraints imposed by these mapping technologies is imperative.

A utility service exposed as a Web-based service can be invoked by multiple service clients simultaneously, which introduces concerns about concurrency and thread-safety. For use across a wide range of service clients, utility services should be built for concurrent invocation to run within multiple threads.

For utility services built with Web-based service technologies, the JAX-WS service or JAX-RS-based resource implementations by default ensure that different requests are handled by different object instances with no special programmatic measures necessary to guarantee thread safety.

However, situations occur where the service/resource lifecycle model must change to have a single service/resource manage multiple concurrent requests. Developers must use appropriate concurrency constructs in the Java programming language to ensure correct program behavior.

Utility Service Taxonomy

The utility service model can be decomposed into further sub-categories of service models that each addresses a specific type of utility-centric functionality, as follows:

Omni Utility Services – Utility services that deliver common functionality needed across a variety of applications and solutions, such as security, logging, or auditing, are called omni utility services.

Resource Utility Services – Resource utility services encapsulate physical system resources, such as data stores and messaging resources, and are not related to REST resources.

Micro-Utility Services – Micro-utility services are fine-grained and highly specialized services that perform technical tasks, such as a service offering XML encryption.

Wrapper Utility Services – Existing legacy systems offer useful functionality that cannot be rewritten, such as mainframe applications developed in COBOL that are often not directly accessible as services. Wrapper utility services are based on the Legacy Wrapper pattern and provide access to legacy logic via standardized service contracts.

These sub-categories will be further referenced in subsequent sections.

8.2 Utility Service Design and Implementation

The following section explores design and implementation considerations for utility services.

Utility Service Design

Having an independent lifecycle means that utility services can often be designed, developed, and maintained on their own timeline. This is true both for the implementation of a service and for its interface. As per the Canonical Schema pattern, it is important to establish canonical data models across a service inventory, especially for business-centric services such as entity and task services. However, relationships between utility services and the canonical schemas are less common.

In the previous case study example, a Transfer Logger service is created to log a message every time money is transferred from one account to another. The instances of transferred money are represented by an object of type fundsTransfer, which contains information on the amount of money transferred and references to the account, such as pertinent customer information. Every time the types are updated as part of their natural evolution within the overall enterprise information model, the Transfer Logger service must also be updated, which creates an unwanted dependency with the utility service. Recall that utility services should be agnostic to any business process or domain and a particular set of business object types.

To resolve unwanted dependency on the utility services, the service interface can be designed to have no dependencies on the business objects it handles. Utility services often handle business-relevant data, but rarely contain logic specific to the nature of this data. A service that logs, persists, or transforms messages from one schema to another can be designed so that the structure of the payload of the message does not appear on its interface. Whether exposed as a Web-based service, a remote Java component, or a locally invoked Java class, the interface takes different shapes depending on how the service is deployed:

Web Service/API – Service data is captured in XML schema definitions describing the input and output parameters. A generic utility service interface has no reference to any namespace that is business domain-specific and instead uses generic types, such as <xsd:string>, <xsd:anyType>, or <xsd:hexBinary>.

Remote Java Component – The service interface is a Java interface that wraps an EJB. Each parameter of the remote interface must be serializable as per Java serialization rules, such as java.lang.String, byte[], or any other of the basic Java types that implement java.io.Serializable. The following interface definition:

public interface Logger extends java.rmi.Remote {
  public void log(java.lang.String message) throws RemoteException;
}

...illustrates an example of implementing Java serialization rules. Note that the Transfer Logger interface has no reference to an account transfer, but simply logs whatever string is passed. A real logging interface would contain additional parameters, such as indicating log levels, but will never reference business classes.

Local Java Class – The service interface is a Java interface with parameters passed to the service that do not have to be serializable, because they are passed by reference. A service invocation through a local Java invocation creates tight coupling between the service consumer and the service that should be avoided. However, the interface can now define parameters of type java.lang.Object. The service implementation logic can incorporate Java reflection to further use the passed objects.

The LocalJavaLogger class takes any arbitrary Java object as input and logs it using a standard Java Logger. The content logged is retrieved by invoking the toString() method on the object or alternatively using reflection. To identify unwanted and unnecessary dependencies of utility services upon higher-level, business domain-specific schemas and types, structure the data model into complex types that belong to different XML namespaces or Java packages.

A utility service has the highest potential of all service model types for reuse across a large number of service compositions, which results in greater need for scalability and concurrency. Most JREs support the execution of concurrent service invocations in multiple threads, requiring the service logic to be implemented in a thread-safe fashion. Detailing all of the aspects of multithreading in Java is beyond the scope of this book, but in short, multithreading affects how data is treated on the local heap and whether certain blocks of code are wrapped with the synchronized keyword. Java 5.0 introduced a package of more advanced classes and interfaces related to concurrency called java.util.concurrent, which helps developers build sophisticated concurrent applications without using low-level synchronization constructs.

If a service stores data on behalf of a particular service consumer that is establishing a form of session with another service consumer, subsequent requests from the same service consumer must run in a thread with exclusive access to this temporary information. Generally, the same rules that apply to Java servlet programming from the Service Statelessness section apply to the storage of local temporary data, because servlets are also stateless and highly concurrent with similar constraints. Minimize the temporary state held by the service implementation class and use synchronized methods where necessary, especially in mutator methods that change data.


Case Study Example

The SOA architects at NovoBank determined the need for a service that returns a list of company holidays for a specific month. In its first iteration, the service is only required by Java-based service consumers and is consequently built as a Java class that can be locally embedded into any Java application. The designer of the new Holiday Calendar service decides that returning a list of java.util.Calendar objects is most valuable to the service’s clients, and defines the following interface as:

public List<Calendar> getCompanyHolidays(int month);

The service requires an input parameter or index of the month as defined in the java.util.Calendar interface, such as Calendar.JULY. The implementation for the service uses a static table containing the list of holidays. The service operation is achieved in Example 8.3.

public List<Calendar> getCompanyHolidays(int month) {
  List<Calendar> list = new ArrayList<Calendar>();
  for (int i=0;i<holidays.size();i++) {
    Calendar c = holidays.get(i);
    if (c.get(Calendar.MONTH)== month) {
      list.add(c);
    }
  }
  return list;
}


Example 8.3

After deployment, the team at NovoBank discovers that the Holiday Calendar service introduces numerous interoperability problems.

The implementation assumes that the months are represented from 0 to 11 for January to December respectively, which is not obvious from the interface definition. Conventionally, service consumers might assume that 1 represents holidays during the month of January as the first month of the year. To resolve confusion, a comment to the XML schema definition indicating how the months are indexed can be added. Alternatively, the parameter can be made more self-explanatory by using a string like:

public List<Calendar> getCompanyHolidays(String month);

...that allows the service implementation logic to be written to support the different ways of specifying months, such as "jan," "Jan," or "January."

Even though the service uses standard Java, it leverages the java.util.List interface with generics only available in Java 5.0. Older Java environments cannot directly include this class and require a Web services invocation layer. Assuming that this service is offered as a Web service, the JAXB standard defines that the return parameter must comply with the XML schema definition, as seen in Example 8.4.

<xs:complexType name="getCompanyHolidaysResponse">
  <xs:sequence>
    <xs:element name="return" type="xs:dateTime"
      maxOccurs="unbounded" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>


Example 8.4

The List<Calendar> return type is converted into an unbounded array of elements of type dateTime in the schema. Note that this is the only interoperable method of transferring a collection of objects across a Web service interface. However, the semantics of the Java collection class are lost. Each collection class has its own way of storing and sorting elements. What is transferred across the SOAP interface is a plain array with no advanced algorithms available.

Some organizations at NovoBank still use the older JAX-RPC standard, in which mapping of the List<Calendar> type is not defined. Depending on the SOAP engine and tools used, the Holiday Calendar service may not work at all. A more compatible version of the interface:

public Calendar[] getCompanyHolidays(int month);

...is created to interoperate with NovoBank’s older JAX-RPC standards and ensure accessibility across the organization.


Utility Services and Java Editions

Let’s explore some of the relevant criteria for building utility services on each of the Java platforms introduced in Chapter 4.

Utility Services in Java SE

Utility services are business process-agnostic in nature and cover a wide range of technical functional areas. Some of these areas are considered enterprise-level by the Java community and are only addressed in the Java EE platform.

Many of the APIs defined and supported in Java SE are relevant to the design of utility services:

Security – The Java Authentication and Authorization Service (JAAS) and the Java Cryptography Extension (JCE) provide core functionality for security in a Java environment and are both included in Java SE.

Management – The Java Management Extensions API is included in Java SE, beginning with version 5.

Transformation – Parsing and transforming of XML documents are addressed in the Java API for XML Processing and related APIs, such as Streaming API for XML Processing and Transformation API for XML (TrAX). Mapping Java objects into XML documents and vice versa via APIs, such as JAXB, is also considered transformation.

Tracing and Logging – Support for tracing and logging are included in standard Java in the java.util.logging package. The open-source Jakarta Commons Logging (JCL) package, which works with any standard Java application, is currently a popular alternative.

Persistence – Java SE includes the Java Database Connectivity API, which is used to access relational databases.

Besides identifying functional areas supported by pre-defined Java APIs and implemented by standard Java packages within JRE, the way in which these are offered to potential service consumers is important. Java SE does not provide the concept of an application server, nor does it support remote communication with Java components hosted in a separate process. Consequently, if the utility services are restricted to a single Java Virtual Machine, they must be deployed as local Java components.

Utility Services in Java EE

Java EE further encompasses the notion of a secure, scalable, transactional, and distributed container that hosts Java components as part of an application server. The platform allows service capabilities to be offered across processes, instead of solely as local Java components.

In conjunction with the container runtime and associated deployment model for installing Java components into this runtime, Java EE is an umbrella for a number of additional Java technologies relevant to the identification, definition, and design of utility services:

Messaging – The Java Message Service describes a protocol and API for the reliable exchange of messages between applications. Between Web services over HTTP, JMS serves as an alternative network protocol for the exchange of messages. For SOAP-based Web services, JMS can be used as an underlying transport that is separate from the abstract service contract and specified as a binding detail in the concrete WSDL definition.

Acting as a provider of services and not just a transport protocol, the JMS includes classes and interfaces offering functionality that can add to the collection of utility services.

Transformation – To be interoperable, Web services offer an interface that receives and returns data in form of XML documents, which require back-and-forth conversion between XML documents and Java objects. Java EE defines the Java Architecture for XML Binding.

Transactions – A Java EE-compliant application server contains the logic required to include a series of executions on Java components into the same physical transaction with JTA. Transaction-related services are common utility services required within a number of automated business processes using a service inventory.

SOAP-based Web services can use certain WS-* standards to complement the JTA functionality included in Java EE by utilizing support for long-running transactions via compensation features found in specifications, such as the WS-BusinessActivity.

Registry – A naming server typically stores information about the Java components and other artifacts supported by the application server exposed via JNDI.

However, JNDI is highly Java-centric and does not satisfy requirements for storing information about services. JAXR stores information about SOAP-based Web services as part of Java EE.

Legacy Integration – The standard defines the J2EE Connector Architecture to allow embedding non-Java applications into a Java EE solution. Within JCA, connectors can be created to run in the application server process and provide seamless access to legacy environments. This architecture is most important in the context of wrapper utility services.


Note

For REST services that manage multitudes of resource representations apart from the customary XML, the Java EE platform does not provide support for binding tools between Java and such representation formats. Common JAX-RS implementations, such as Jersey, manage the formats by bundling other open-source libraries, such as EclipseLink, MOXy, and Jackson or Jettison for JSON.


When designing utility services, we need to examine the available APIs and determine which require augmentation for use as part of an enterprise service inventory. Some service functionality can require wrapping in a Web service layer for use across a network by non-Java service consumers. For example, a requirement for a generic XML-to-XML transformation service is identified for redesign as a Web service wrapped around the JAXP javax.xml.transform.Transformer class.

Java EE defines the programming model for distributed Java components as EJBs and describes how they can be accessible as Web-based services. EJBs offer scalability and built-in support for concurrent access. Services can be deployed across a number of application server instances without affecting the service logic, and grow with increased usage over time.


Note

However, Java EE 6 offers greater flexibility where special types of annotated POJOs known as managed beans offer the same level of concurrency and scalability support as EJBs. An EJB in Java EE 6 is a type of managed bean.


The lifecycle of a managed bean is handled by the container with support for additional features, such as resource injection, interceptors, and lifecycle callback methods. Unlike EJBs, managed beans do not support declarative transactions or security. Building REST utility services with managed beans can take advantage of the container features, such as automatic support for concurrency and scalability, without the need to use less useful features, such as transactions or role-based authorization checks.

Utility Services and Open-Source Frameworks

Enhancements to building Java applications once came in the form of tools and frameworks, which were commonly run as open-source efforts through simplifying the use of the underlying Java APIs and packages, or creating new ways of performing tasks unsupported by the standard set of Java specifications. Some enhancements have since become obsolete as support for the target problem was introduced to the standard platform, but other frameworks are still widely popular and undergo their own evolution and innovation.

The open-source frameworks relate to utility service design and implementation as much as the standard Java libraries within the constraints of the design principles, such as Service Reusability and Service Abstraction. Utilizing additional frameworks to fulfill the functional requirements for utility services is likely the type of reuse intended. Three popular complementary framework examples are introduced in the following sections to explore the different types of functionality relevant to the creation of utility services for an enterprise.

Spring Framework

The Spring framework provides a comprehensive set of interfaces and classes that assist in creating Java solutions. Logic coded in POJOs is configured into the Spring framework via XML-based configuration files or annotated Java objects, known as Spring beans. The framework provides a base for the presentation layer of an application through its implementation of the Spring Web model-view-controller (MVC). The Spring framework supports the following functionality relevant to utility services:

Transaction Management

Spring provides an abstraction layer on top of the existing JTAs for higher-level access to transactional functionality. Since Spring promotes the JavaBean-based approach, the abstraction layer can be added to a utility service inventory for local and remote access. For Web services, a standard, such as WS-AtomicTransaction, is required to create transactions across multiple service invocations.

Data Access Objects

Data access objects (DAOs) represent a way to group a dataset into large-grained objects, which can be transferred between layers of an application. DAOs can be mapped to messages exchanged between services, and used within a service interface as input and output messages.

Object-Relational Mapping

Accessing relational data through mapping into Java objects is another use of the Spring framework. Core standards in this context are JDBC and JPA, and alternative methods of accessing data supported by the same layer include Spring-Hibernate, JDO, and Oracle TopLink.

Creating a Spring-based utility service layer for data access allows for interfaces to be defined independent of the underlying technology used, which supports service abstraction. The object-relational data access set of interfaces are a prime candidate for becoming part of a utility service inventory.

JMS

JMS is considered the protocol binding layer of services and not necessarily as a source for services. Spring supports JMS as a transport mechanism for simple invocation of methods on JavaBeans, which provides a method of service invocation for services that are not Web services.

When using the Spring method of exposing functionality as Web services, JMS can be leveraged as a protocol for Web service invocations. Alternative APIs for reuse as utility services beyond the use of JMS include classes for retrieving the JMS destination names from JNDI, which can be exposed to find appropriate resources to which to send messages as enterprise-wide services.

JMX

JMX allows for active management and monitoring of Java applications, which encompasses its potential as a utility service. Correspondingly, Spring provides wrapper classes around JMX that enable management to Java solutions.

JCA

Similar to how other APIs within the Java platform are wrapped in a more abstract, user-friendly interface by Spring, the CCI is encapsulated to allow Spring-style access to the back-end system supported by the connector via JavaBeans defined in Spring XML configuration files.

Spring MVC

Spring MVC is a model-view, controller-based HTTP request dispatcher framework. REST services can be built by leveraging features that support the standard HTTP operations and extraction of URI template values.

Hibernate

Hibernate, which is part of the JBoss Enterprise Middleware System (JEMS) suite, is an effective framework for implementing object-relational mapping in Java and allows access to persistent, relational data by manipulating standard Java objects. Focused solely on how to write data to persistent data stores and read it back, Hibernate uses a POJO-based model augmented with XML configuration files and/or Java annotations. Just as with the Spring DAO support, the Hibernate framework offers a means of mapping relational data into objects for transfer between services, and includes helper classes more suitable for definition as utility services. These helper classes generally involve querying persistent data stores.

Commons Logging and Log4J

The Commons Logging package is part of the Apache Jakarta project and provides generic support for the logging of data within Java code. The main interface Log provides access to different logging implementations, such as the standard Java logging support. Another open-source package offering logging support is Log4J, which was also developed within an Apache project. Log4J can serve as a logging implementation in Commons Logging, because the two can be used simultaneously.

While the Log4J package has more recent widespread use, both open-source packages offer convenience classes and methods to fulfill the logging function. Logging of interactions between services is a common example of utility-centric service logic.

Utility Services as Web-Based Services

Various considerations critical to building utility services with SOAP/WSDL and REST utility services are discussed in the following sections.

Sending XML Data as a String

The interface definition must first be considered when sending XML data as a string. Since business object-specific schemas must not be referred to in the service contract to avoid dependencies between utility services and higher layers of services, generic XML schema types can be used in the WSDL definition.

Consider interpreting any incoming XML message as a string in light of the Transfer Logger service defined in the previous case study example. A fundsTransfer object was used as the payload. Together with the appropriate wrapper element, the object in schema could resemble Example 8.5.

<xs:complexType name="log">
  <xs:sequence>
    <xs:element name="arg0" type="ns2:fundsTransfer"
      minOccurs="0"
      xmlns:ns2="http://utility.services.novobank.com/"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="fundsTransfer">
  <xs:sequence>
    <xs:element name="amount" type="xs:string" minOccurs="0"/>
    <xs:element name="fromAccount" type="xs:string"
      minOccurs="0"/>
    <xs:element name="toAccount" type="xs:string"
      minOccurs="0"/>
  </xs:sequence>
</xs:complexType>


Example 8.5

A more generic version of the schema that does not introduce dependency on the fundsTransfer object is shown in Example 8.6.

<xs:complexType name="log">
  <xs:sequence>
    <xs:element name="arg0" type="xs:string" minOccurs="0"/>
  </xs:sequence>
</xs:complexType>


Example 8.6

The interface definition declares to the service consumer that the message logged should be transferred as a string. However, the service consumer must create an appropriate string from the business object, which can require further coding.

Despite assuming the message is XML, declaring it as a string results in encoding by the SOAP engine. The encoded SOAP body is presented in Example 8.7.

<soapenv:Body>
  <ns1:log>
    <arg0>&lt;?xml version=&quot;1.0&quot;&gt;&lt;ns1:log
      xmlns:ns1=&quot;http://utility.services.novobank.com/&quot;&
      gt;&lt;arg0&gt;&lt;amount&gt;1234&lt;/amount&gt;&lt;/
      arg0&gt;&lt;/ns1:log&gt;</arg0>
  </ns1:log>
</soapenv:Body>


Example 8.7

To the Web service engine, the message is a string of characters. The engine goes through the encoding effort to prevent confusion with XML. Each angle bracket, such as < and >, is replaced with an escape sequence. The encoding makes the message bigger, the execution slower, and the transferred message harder to read and debug. Even though this method provides a somewhat generic interface with no dependency on the business object, it is not a recommended way of handling XML information.

Utilizing <xsd:any/>

An alternative to the string-approach is to change the schema in the service contract to include the <xsd:any/> element, as shown in Example 8.8.

<xsd:complexType name="log">
  <xsd:sequence>
    <xsd:any processContents="skip"/>
  </xsd:sequence>
</xsd:complexType>


Example 8.8

While similar to the string definition, the changes in the service contract acknowledge that the payload of the message will contain XML data. The <xsd:any/> element serves as a wildcard indicating XML is sent without specifying the exact structure. The resulting message is smaller and easier to debug than as seen in the string method.

However, this mechanism of preventing any dependencies on the business object has disadvantages. The XML data cannot be serialized into Java helper classes. As per the JAXB specification, an <xsd:any/> element discovered in a schema is mapped to a java.lang.Object, because no information about the kind of object being transferred is available. Extra work in the JAXB binding configuration is required to allow the JAX-WS engine to serialize and deserialize the data properly. To avoid this extra effort, an alternative technique is described in the following section.

Provider-Style Web Service Logic in JAX-WS

Typically, a utility service implemented as a Web service has no prior knowledge about the data received or what data it will return to its service consumers at design-time. The only knowledge the service has is that the data handled is exchanged in XML. Converting the data into a graph of Java objects may be unnecessary if it is only received for routing to a different endpoint address or transformed from one XML format to another using an XSLT style sheet. The JAX-WS specification defines a way to create a service interface that routes the XML data directly into the service logic without any parsing or serialization.


Case Study Example

After NovoBank’s Transfer Logger service has successfully been in production for some time, the team receives requirements for a more generic version of the service functionality independent of the Transfer Account schema. The Generic Logger service interface is created to log messages without reviewing the included data. An extract from the service implementation class is provided in Example 8.9.

package com.novobank.services.utility;
import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.soap.SOAPMessage;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.WebServiceProvider;
@ServiceMode(value=Service.Mode.MESSAGE)
@WebServiceProvider()
public class GenericLoggerImpl
  implements Provider<SOAPMessage> {
  public SOAPMessage invoke(SOAPMessage source) {
    printRequest(source);
    return null;
  }
  private void printRequest(SOAPMessage request) {
    try {
      Transformer transformer = TransformerFactory.newInstance().
        newTransformer();
      transformer.transform(
        new DOMSource(request.getSOAPPart()),
        new StreamResult(System.out));
        System.out.println();
      } catch (Exception e) {
        System.out.println(e);
      }
  }
}


Example 8.9

Note that the underlying schema uses the <xsd:any/> element, as seen in Example 8.10.

<xsd:complexType name="log">
  <xsd:sequence>
    <xsd:any processContents="skip"/>
  </xsd:sequence>
</xsd:complexType>


Example 8.10


A generic service can implement the javax.xml.ws.Provider interface. The class retrieves the content of the message as an instance of javax.xml.transform.Source or as an instance of javax.xml.soap.SOAPMessage. Configuring the @ServiceMode annotation can indicate whether the entire SOAP envelope or just its payload, such as the content of the SOAP body, is passed into the implementation class.

In the previous NovoBank Generic Logger service case study example, Service.Mode.MESSAGE is specified and indicates that the entire SOAP message is passed, which is the only option unless a SOAPMessage parameter is defined. The invoke() method must be implemented within the implementation class and is called when a request to the associated service endpoint is sent. The entire received message is printed to the screen.

Another example could also log the message content to a file or database. Note that the method body returns null at the end to indicate that this class represents a one-way operation with no response message defined. In the associated WSDL document, the operation definition is illustrated in Example 8.11.

<portType name="GenericLogger">
  <operation name="log">
    <input message="tns:log"/>
  </operation>
</portType>


Example 8.11

There is no <output> element in the operation, which directly relates to the statement return null; in the service logic.

Building REST Utility Services

When building utility services with REST, the challenge is in identifying an appropriate resource that may not always be obvious.

For example, a Postal Code Validation service determines the validity of a postal code when given a country and code. Identifying a Web resource is necessary to model this as a REST service. Assuming the postal code entity is the resource itself, mapping the validate operation to any of the standard set of HTTP verbs, such as GET to return the postal code entity itself, POST to create a new postal code, PUT to update a postal code, and DELETE to remove the entity in question, is not possible.

To solve this problem, consider which HTTP method best fulfills this functionality. The operation is idempotent, making the GET method suitable. An HTTP request for validating the postal code is illustrated in Example 8.12.

#Request to validate a postal code
GET /validatepostalcode?country=CA&code=M4B1B4
Host: www.postalservices.org
#Response
HTTP/1.1 200 OK
Content-Type: text/plain
Valid


Example 8.12

The results of the invocation can be cached because postal code validation results are unlikely to change often. JAX-RS supports injecting the query parameter values into method parameters annotated with @QueryParam. The implementation of a JAX-RS resource class to perform postal code validation should be straightforward. Example 8.13 illustrates another way of extracting the query parameter values which can be useful when the prior query parameters passed in are unknown.

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

public class PostalCodeValidationResource {
  @GET
  @Path("/validatepostalcode")
  @Produces("text/plain")
  public boolean validate(@Context UriInfo info) {
    String country = info.getQueryParameters().getFirst("country");
    String code = info.getQueryParameters().getFirst("code");
    checkPostalCode(country, code);
  }
  private boolean checkPostalCode(String country, String code) {
    boolean result = false;
    //actual logic to check postal code...
    return result;
  }
}


Example 8.13

The query parameters can be programmatically obtained via calls to the UriInfo object. To retrieve a list of all query parameters passed in the URI, the UriInfo.getQueryParameters() method can be used to obtain a javax.ws.rs.core.MultiValuedMap containing the query parameters in the form of key-value pairs.

For building REST utility services with a generic JAX-RS resource implementation, java.io.InputStream or java.io.Reader can be used in the input methods and the corresponding types, such as java.io.OutputStream or java.io.Writer, are used in the output.

For complete control over input and output types, JAX-RS also provides developers with use of the custom entity providers for arbitrary representation formats or any arbitrary Java types.

For deserializing arbitrary representation formats to any Java type, JAX-RS extension APIs provides the javax.ws.rs.ext.MessageBodyReader class. The javax.ws.rs.ext.MessageBodyWriter class allows for the serializing of any arbitrary Java type to an appropriate resource representation.

Testing Considerations

All the regular service testing considerations also apply to utility services. Functional testing are often conducted via JUnit, which is directly supported by many of the available JDEs. It is often easier to write unit test cases for utility services than for other types of services, because they are technical, fine-grained, and agnostic to business processes.

Integration and system testing is more difficult, because utility services are widely reused and have a lifecycle independent from their service consumers. For example, it is not always possible to determine non-functional requirements, because the kind of load a particular service must satisfy cannot be estimated.

Determining and documenting the service levels supported in the service profile will help with testing. If the target deployment environment is known, a system test can help measure data such as the response time and throughput of a given utility service, which can be taken into consideration if the service is to be later reused by additional service consumers.

Packaging Considerations

Utility services implemented in Java are packaged according to Java packaging technology. Java components are packaged in a variety of archive formats:

JAR – The Java archive format allows multiple classes and interfaces to be bundled in one file and added to the classpath of a JVM. These classes and interfaces are then available to any code executing in that JVM’s process. A JAR file cannot expose its contents as Web-based services, so it is restricted to offering functionality for local Java invocations.

WAR – On top of regular Java classes and interfaces, the Web archive format includes deployment descriptors and artifacts that allow the file to run in the Web container of an application server. In Java EE 6, the deployment descriptor is optional.

For example, servlet logic and UI artifacts (JSPs) can be invoked from within a Web browser. A WAR file includes one or more JAR files with content available to the resulting Web application. With EJB 3.1, simplified WAR packaging allows for EJB artifacts to be bundled. Web-based services can be packaged in a WAR file, because they contain the necessary infrastructure code on top of any service logic required.

EAR – The enterprise archive file contains all of the artifacts necessary for an enterprise application to run in the Java EE container of an application server, such as Web functionality and EJBs. An EAR file can include one or more JAR files, one or more WAR files, and Web-based services.

An EAR file can offer all three types of service components relevant to Java-based utility services: local components (through embedded JAR files), remote Java components (through the remote EJB layer), and Web-based services.

Utility services can be packaged as JAR, WAR, or EAR formats depending on their design and functional requirements. Service logic contained in JAR files will be duplicated many times, since a single JAR file may be embedded into a number of WAR and EAR files.

A Java class containing service logic is usually only available on the classpath of a Web or enterprise application if it is bundled with the corresponding WAR or EAR file. A Java class or JAR file cannot be easily deployed and maintained in a central location.

8.3 Utility Service Types

This section describes a set of specialized variations of the utility service model.

Omni Utility Services

Omni utility services provide generic, cross-cutting functions, such as security, logging, eventing, and notification.

Common uses for omni utility services include:

Security – Basic functionality to secure a service-oriented environment provides the following: authentication to test the supplied credentials, such as a user id/password combination, against a list of know users; authorization to verify whether or not an authenticated user has access rights to a certain resource; digital signatures for a given document; encryption for documents/messages to only be read after being decrypted with an appropriate key; and identity federation to map credentials that represent a user’s identity between different security domains.

Logging – A Logging service should provide a context within which a certain piece of data is logged. This context can be either functional, such as aspects within the service, or transactional, such as actions executed by or on the service.

Eventing – The term “eventing” represents the ability of service components to emit, receive, and exchange information about events. Logging can be considered a special instance of eventing. The events can be highly technical in nature, such as “Could not connect to server at address x.x.x.x,” and business-oriented, such as “Item #1234 was replenished in warehouse.” Given the strong affinity of any service-oriented solution with concrete business goals, a set of omni utility services should be available and allow for the communication of business events consistently across the enterprise. This requires a well-designed interface that can address the breadth of business situations that occur and a distributed infrastructure to manage events occurring across a network, which ideally stores data relevant to these events in a central location.

Notification – For SOAP-based Web services, the OASIS Web Services Notification workgroup has published three specifications that describe the interfaces required for the exchange of event data between Web services. Utilizing a standard format and standard interfaces increases the interoperability and reusability of any service. The interfaces defined in the WS-Notification family of standards can be mapped into Java and become the base of a Java implementation.


Note

Organizations do not typically build a complete framework for Web services-based event handling from scratch, and instead utilize support in existing middleware packages.


Design Considerations

Omni utility services are broadly applicable, which emphasizes the importance of the relevant service-orientation design principles of Service Abstraction, Service Autonomy, and Service Reusability. Given that these services are reused across many service consumers, they will often be designed as Web-based services with generic, loosely typed interfaces.

Service Implementation

Omni utility services handle a large variety of payloads, and are often created using the JAX-WS Provider programming model for SOAP-based Web services or the custom entity providers, leveraging MessageBodyReader and MessageBodyWriter in JAX-RS for REST services.

Service Consumption

Omni utility services are consumed just like any other services via a generated Java proxy. Since they are typically used more than once by any given service consumer, it is recommended to instantiate proxy objects only once and cache them for later reuse.


Case Study Example

Many of the messages in NovoBank’s service inventory pass through a number of intermediaries. The increasing number of moving parts prompts the SOA architect team to create a set of services for notification. NovoBank’s Notification service is a Web service that collects information across the enterprise on messages processed, with the option to correlate the individual events to help identify bottlenecks.

The team recognizes that the WS-Notification standard addresses this type of event notification between services, and is not yet implemented in the middleware used by NovoBank. They decide to create their own temporary solution by designing the Notification Consumer service. The WS-Notification standard includes a WSDL file that defines such a service consumer. Therefore, NovoBank decides to reuse the same WSDL file for their implementation and, in particular, the portType from Example 8.14.

<wsdl:portType name="NotificationConsumer">
  <wsdl:operation name="Notify">
    <wsdl:input message="wsntw:Notify" />
  </wsdl:operation>
</wsdl:portType>

The Notify message refers back to the following XML schema definition:

<xsd:complexType name="NotificationMessageHolderType" >
  <xsd:sequence>
    <xsd:element ref="wsnt:SubscriptionReference" minOccurs="0"
      maxOccurs="1" />
    <xsd:element ref="wsnt:Topic" minOccurs="0" maxOccurs="1" />
    <xsd:element ref="wsnt:ProducerReference"  minOccurs="0"
      maxOccurs="1" />
    <xsd:element name="Message">
      <xsd:complexType>
        <xsd:sequence>
          <xsd:any namespace="##any" processContents="lax"
            minOccurs="1" maxOccurs="1"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:element>
  </xsd:sequence>
</xsd:complexType>
<xsd:element name="NotificationMessage"
  type="wsnt:NotificationMessageHolderType"/>
<xsd:element name="Notify" >
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element ref="wsnt:NotificationMessage" minOccurs="1"
        maxOccurs="unbounded" />
      <xsd:any namespace="##other" processContents="lax"
        minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>


Example 8.14

The schema is not complete, but the main parts of the service are illustrated. The Notify element contains a NotificationMessage element, followed by an optional any element indicated by the minOccurs="0" attribute. This leaves room for additional XML content to be added to the event message after the standardized portion. The NotificationMessage element is of the NotificationHolderType type, which defines some optional standard elements and a mandatory Message element containing yet another any. The content of the actual event message is undefined beyond the fact that it must be well-formed XML.

This structure is reusable as a template for other purposes, since it makes parts and elements either mandatory or optional. Using wsimport or any other tool that generates Java from WSDL, Java classes that correspond to the XML schema definition can be created. However, the NovoBank team decides to create the service implementation class based on the JAX-WS Provider programming model seen in Example 8.15.

package com.novobank.services.utility;

import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.WebServiceProvider;

@ServiceMode(value=Service.Mode.MESSAGE)
@WebServiceProvider()
public class NotificationConsumerImpl implements
  Provider<SOAPMessage> {
    public SOAPMessage invoke(SOAPMessage source) {
      logEvent(source);
      return null;
    }
    private void logEvent(SOAPMessage request) {
      try {
        // do something with the event...
      } catch (Exception e) {
        System.out.println(e);
      }
    }
}


Example 8.15

The code looks similar to the GenericLogger code used in another NovoBank service, which illustrates the fact that JAX-WS creates generic service logic with almost no dependency on its surroundings.



Case Study Example

The retail chain has requested that SmartCredit offer a utility for downloading customers’ monthly credit card charges in a PDF format. SmartCredit plans to build a Document Processor service to allow for the retrieval of monthly charges, but it must use a utility service that can consume an XML document and transform it to a specified output format. The IT team decides to build a REST utility service that can generate PDF and potentially other formats from an XML document.

SmartCredit already uses developed custom Java classes that represent PDF documents, which can be leveraged in the utility service. A skeletal representation of the custom Java type is provided in Example 8.16. Note that the detailed implementation of the logic for converting to and from PDF formats is omitted to highlight the usage of custom entity providers and not different types of document formatting logic.

package com.smartcredit.documentprocessing;
public class PDFDocument implements java.io.Serializable{
  ...//methods for reading and writing PDFs
}


Example 8.16

The resource class for the utility service is shown in Example 8.17.

@Path("documentprocessor")
public class DocumentProcessorResource {

  @GET
  @Produces("application/pdf")
  public PDFDocument getPDFDocument(InputStream in) {
    PDFDocument doc = convertXMLToPDF(in);
    return doc;
  }
  private PDFDocument converXMLToPDF(InsputStream in) {
    //logic for converting XML to PDF...
  }
}


Example 8.17

Since the output response format is a custom Java type that must be serialized into the application/pdf media type, the built-in mapping facilities in JAX-RS cannot be leveraged to perform the Java type-to-media type conversion. The JAX-RS custom entity providers, based on MessageBodyReader and MessageBodyWriter classes, can handle conversion between arbitrary Java types and other media types, which can be leveraged for the utility service. In this case, a custom entity provider must be built to serialize the PDFDocument type. The JAX-RS runtime requires an implementation of the MessageBodyWriter class to allow for serialization of the message body.

Creating a JAX-RS custom entity provider involves implementing a JAX-RS Provider. The SmartCredit custom entity provider must also produce PDF formats from XML input, which creates a class that can be seen in Example 8.18.

import javax.ws.rs.ext.MessageBodyWriter;
//...other imports
import com.smartcredit.documentprocessing.PDFDocument;

@Produces("application/pdf")
public class DocumentEntityProvider implements
MessageBodyWriter<PDFDocument>{

  @Override
  public long getSize(PDFDocument doc,
    Class<?> type,
    Type genericType,
    Annotation[] annotations,
    MediaType mediaType) {
    /* return -1 if the content length cannot be determined */

    return -1;
  }

  @Override
  public boolean isWriteable(Class<?> type,
    Type genericType,
    Annotation[] annotations,
    MediaType mediaType) {
    return PDFDocument.class.isAssignableFrom(type);
  }

  @Override
  public void writeTo(PDFDocument doc,
    Class<?> type,
    Type genericType,
    Annotation[] annotations,
    MediaType mediaType,
    MultivaluedMap<String, Object> headers,
    OutputStream stream) throws IOException,
    WebApplicationException {
    ObjectOutputStream oos = new ObjectOutputStream(stream);
      oos.writeObject(doc);
  }
}


Example 8.18

Note the following characteristics of the custom entity provider implementation:

• The DocumentEntityProvider is called by the JAX-RS runtime before the serialization of the returned resource representation, which in this case is a PDFDocument.

• Since the @Produces annotation on the custom provider matches the value of the @Produces on the resource class, the writeTo method is called.

• The writeTo method determines how the custom Java type PDFDocument is serialized to an application/pdf media type as the outbound response.

The custom entity provider is discovered by the JAX-RS runtime automatically because the provider class is annotated with the @Provider annotation.


Resource Utility Services

Resource utility services offer functionality related to the use of common distributed IT resources, such as data, network, and messaging resources. Much of this type of processing is provided by existing Java technologies that allow APIs to be wrapped as part of the utility service layer. The following sections describe resource utility services further.

Persistence/Data Access Resources

The Service Statelessness design principle refers to the delegation of state data kept by a service implementation when being used by its service consumers. In the context of resource utility services for data, the state data is persistent business data kept in a database (as per the State Repository pattern) and only accessed within a transaction. The services in this category are directly related to database access technologies, with the goal of abstracting the details of these technologies away from the service consumer.

Hibernate is an example of an open-source framework that encapsulates access to relational database tables and handles the mapping between relational data and Java objects, which includes support for collections and inheritance. With Java EE, the JPA has standardized data access to persistent relational data. Hibernate and TopLink are examples of JPA implementations. Data access services provide data. If invoked over a network and exposed as Web services, this data must be serializable into XML for network transfer in an interoperable manner, which is usually handled by a JAXB implementation. However, the SDO specification defines an alternative generic data access model that includes serialization to and from XML.

Messaging Resources

Interactions between services require support for the transfer of messages between services running in different processes. Support is offered by messaging artifacts for different types of protocols and different types of message exchange patterns.

If HTTP is utilized as the transport mechanism, as is the case for most Web-based services, a layer of HTTP-specific resource services is used to transport messages. The resource services are usually embedded within the Web service engine and are transparent to the service consumer and the service logic. Many of these engines expose lower-level APIs related to the transport of messages across HTTP, allowing direct programming against this level. Another significant transport mechanism for service interactions is JMS, which is often preferred over HTTP because it supports transactional, reliable transfer of messages. JMS also provides asynchronous message transfer and a publish/subscribe model.

Transaction Resources

This service type includes transaction resources include transactional resources and the management capabilities needed to coordinate between the resources. Coordination means that a certain protocol is executed to synchronize the transactional behavior of resources that are included in a transaction, an example of which is the two-phase commit protocol. JTA is significant for transaction resources. Spring provides wrapper APIs for handling transactions in Java, and can be a good starting point for Java-based resource utility services for transactions. If transactions are run across Web services, however, a system that can coordinate non-Java resources into a transaction is needed. Significant work has been done in the WS-* world to support both short-lived ACID transactions and long-running compensation-based transactions.

The WS-AtomicTransaction standard addresses the short-running ACID transaction scenario, which transfers a two-phase commit type of protocol into the Web services and SOAP space. Similarly, the WS-BusinessActivity standard covers a different kind of coordination protocol, addressing longer running transactions where each invoked operation can be compensated in case the transaction was not completed successfully. The two standards describe the interfaces and schemas needed for resource utility Web services engaging in transactional behavior. The underlying design patterns that the standards support are called Atomic Service Transaction and Compensating Service Transaction. Many Java implementations offer intrinsic support for these patterns.

Modeling two-phase commit transactions or recording in-flight transaction information for subsequent compensation or rollbacks in REST is not supported because it would require storing transactional state information.

Design Considerations

Resource utility services are at a relatively low level in the overall services stack. While encapsulating physical resources that all solutions must handle eventually, resource utility services should limit their exposure to areas of service logic that map business-style interfaces and capabilities into the reality of the environment in which they are running. Resource utility services often change over time and must be replaced or adjusted in the consuming service logic without affecting higher-level service contracts. For example, an entity service utilizing a persistent data store to load data offered to its service consumers will never expose the fact that the data comes from a relational database to its service consumers.

Resource utility services are deployed as Web services used over a network that affects how fine-grained their interfaces should be. When a large number of transactions are expected for a particular service, design patterns like the Service Façade pattern are leveraged to create coarse-grained interfaces and delegate “chatty” interactions into the same process, as illustrated in Figure 8.3.

Image

Figure 8.3 The resource utility service acts as a façade, encapsulating fine-grained interactions with the resource database.

For example, assume that a higher-level entity service requires customer data retrieved from a relational table. Hibernate is selected to create a mapping between the relational information and a hierarchy of Java classes. At the top of the hierarchy is a Customer class, as shown in Example 8.19.

public class Customer {
  private Name name;
  private String customerId;
  private Address address;
  private Account[] accounts;
  private Order[] orderHistory;
  public Name getName() {
    return name;
  }
  public void setName(Name name) {
    this.name = name;
  }
  public String getCustomerId() {
    return customerId;
  }
  public void setCustomerId(String customerId) {
    this.customerId = customerId;
  }
  public Account[] getAccounts() {
    return accounts;
  }
  public void setAccounts(Account[] accounts) {
    this.accounts = accounts;
  }
  public Address getAddress() {
    return address;
  }
  public void setAddress(Address address) {
    this.address = address;
  }
  public Order[] getOrderHistory() {
    return orderHistory;
  }
  public void setOrderHistory(Order[] orderHistory) {
    this.orderHistory = orderHistory;
  }
}


Example 8.19

Despite appearances, the Customer class is not a resource utility service candidate. To obtain customer information, any service consumer of the service must make repeated calls to the getter methods of the service to obtain all of the attributes relevant to the customer. Being too fine-grained will result in substandard performance, especially if the information is obtained via a Web service interface.

Instead, a service that retrieves and returns the customer object as a whole (in one step) can be designed. The Customer object is the payload of the service response message and is returned to the client as one object. If the data is accessed via a Web service interface, ensure that the objects, including complex object graphs and large collections, can be serialized into XML.

Service Implementation

Resource utility services are rarely created from scratch, and the Java EE set of standards and/or a higher-level framework (Spring or Hibernate) are often leveraged. In the implementation, be careful not to introduce any dependencies on data types that are defined in the business layer. For example, when implementing services for persistence, ensure the business objects retrievable from within your code are not directly referenced.

Service Consumption

Service consumers of resource utility services often use several in concert to retrieve data or gain access to other physical resources, which means they should be aware of potential transactional boundaries within their logic. Such service consumers must also be aware of the transactional capabilities of any resource utility service used, such as whether it supports participating in a distributed transaction or supports compensation, or whether the results of an invocation of an operation can be undone.


Case Study Example

Since many of the services in NovoBank’s inventory require some form of access to relational data, a study is conducted to determine how to best deal with this data in Java. The study concludes that JPA, coupled with the Hibernate framework, is the best option for NovoBank’s services.

The Data Session service modeled after Hibernate’s org.hibernate.Session interface is added to the resource utility service inventory. It will not be exposed as a Web service because of tight coupling with the logic that uses it for performance and efficiency purposes. Moreover, many of the required methods cannot be easily mapped into WSDL operations and the exchanged parameters cannot be mapped into XML schema.

A new Java class is created with an inner variable of type org.hibernate.Session and a number of additional convenience methods, as seen in Example 8.20.

package com.novobank.services.utility;
import java.util.Map;
import java.util.HashMap;
import java.util.UUID;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class DataSessionHandler {
  private static Map<String, Session> sessionTable =
    new HashMap<String, Session>();
  private static SessionFactory sf =
    new Configuration().configure().buildSession Factory();
  public static String createSession() {
    Session session = sf.openSession();
    String sessionID = UUID.randomUUID().toString();
    sessionTable.put(sessionID, session);
    return sessionID;
  }
  public static Session getSession(String sessionID) {
    return sessionTable.get(sessionID);
  }
  // additional static convenience methods...
}


Example 8.20


Micro-Utility Services

At the finest level of granularity of all services, micro-utility services are not usually exposed as Web services because they are invoked frequently and are performance-critical. Instead, they are usually called in-process. It still makes sense to view this functionality as services, and their reuse potential is high despite being highly specialized. If certain functionality can be executed with such efficiency in an external process, the benefits outweigh the disadvantages of higher network transfer costs.

Examples of micro-utility services are encryption and decryption, digital signatures, data transformation, and mathematical calculations. Many of these functions are already supported by standard Java libraries or open-source frameworks. The creation of XML digital signatures in Java is standardized under JSR 105, for which an implementation exists as the Apache XML Security project. Given that XML digital signatures and XML encryption are included in the WS-Security standard, however, any open-source or commercial package that supports it will include support for this functionality.

Transformation, encryption, and decryption of XML documents are relatively expensive and performance-critical functions. Appliances have recently emerged on the marketplace offering XML encryption in hardware, with better performance than Java-based software solutions. The services associated with this functionality run in a separate process and are invoked via a Web service interface. The performance benefit of hardware-based XML document manipulation may justify paying a performance penalty for the transfer of the information to and from an appliance.

Design Considerations

A micro-utility service inventory is similar to a collection of class libraries. These services offer fine-grained, highly specialized functionality usually invoked locally and generally performance-critical. Therefore, the most important design principle to apply here is Service Statelessness. In other words, ensure no state persists between service invocations and implement the service logic accordingly, which makes this as much a service implementation consideration as a service design consideration.

Service Implementation

Micro-utility services are expected to be concurrently utilized by a large number of service consumers, meaning their implementation must be thread-safe. Avoid handling temporary state where possible, and be aware of regular Java performance best practices by unit testing the service early to ensure that required service levels can be met.

Service Consumption

Micro-utility services are typically invoked locally as part of utility JAR files that package the service consumer logic. This creates a close coupling between the service consumer and the service logic, meaning that maintenance updates to the implementation of a micro-utility service cannot be easily deployed to an existing production environment. The services do not typically exist in one location, but are duplicated and distributed to each service consumer as well.


Case Study Example

Many interactions between services at NovoBank utilize XML documents. Even though the SOA architect team strives to establish a canonical message model across all services, some transformations between different schemas are still required to allow for communication with legacy systems and external business partner applications.

The team defines a micro-utility service called XMLTransform to deal with transformations in a consistent manner. The service is also exposed as a Web service for service consumers that are not performance-critical and can afford the overhead associated with the network transport.

Example 8.21 is an extract of the WSDL file representing this service.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions
  targetNamespace="http://utility.services.novobank.com/"
  name="XMLTransformService"
  xmlns:tns="http://utility.services.novobank.com/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <xsd:schema>
      <xsd:element name="transform" type="tns:transform"/>

      <xsd:complexType name="transform">
        <xsd:sequence>
          <xsd:any processContents="skip"/>
          <xsd:element name="transformName" type="xsd:string"/>
        </xsd:sequence>
      </xsd:complexType>

      <xsd:element name="transformResponse"
         type="tns:transformResponse"/>

      <xsd:complexType name="transformResponse">
        <xsd:sequence>
          <xsd:any processContents="skip"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </types>

  <message name="transform">
    <part name="parameters" element="tns:transform"/>
  </message>
  <message name="transformResponse">
    <part name="parameters" element="tns:transformResponse"/>
  </message>
  <portType name="XMLTransform">
    <operation name="transform">
      <input message="tns:transfrm"/>
      <output message="tns:transformResponse"/>
    </operation>
  </portType>
  ...
</definitions>


Example 8.21

Note that both the input and the output messages refer to any elements, meaning any arbitrary XML content can be sent in this message and some XML content will be returned. The input message includes a string that indicates the name of the transforming style sheet to be used.

Next is the Java code associated with the WSDL definition. In this case, the team uses the Provider<Source> model to define that only the payload of the message is passed into the implementation. Since the standard Java APIs for XML transformations are used, an input of type javax.xml.transform.Source is applied in Example 8.22.

package com.novobank.services.utility;

import javax.xml.ws.Provider;
import javax.xml.ws.Service;
import javax.xml.ws.ServiceMode;
import javax.xml.transform.Source;
import javax.xml.ws.WebServiceProvider;

@ServiceMode(value=Service.Mode.PAYLOAD)
@WebServiceProvider()
public class XMLTransformImpl implements Provider<Source> {
  public Source invoke(Source source) {
    String transform = parseRequest(source);
    return prepareResponse(source, transform);
  }

  private String parseRequest(Source request) {
    String transform=null;
    // parse the input XML document to retrieve the name
    // of the stylesheet to be used
    // ...
    return transform;
  }
  private Source prepareResponse(Source source,String transform) {
    Source response=null;
    // build a new XML document and store it
    // in the response message
    // ...
    return response;
  }
}


Example 8.22


Wrapper Utility Services

This type of utility service is primarily based on the application of the Legacy Wrapper pattern, whereby existing legacy logic is encapsulated by a wrapper service that exposes a standardized service contract. A wrapper utility service can be considered as an intermediary between the service consumer and the actual back-end logic. This intermediary can be located close to the service consumer, close to the back end, or in the middle between the two, such as part of an ESB as shown in Figure 8.4.

Image

Figure 8.4 Wrapper utility services can be placed close to either the service consumer or service, and be delegated into the ESB.

While the location of the actual service affects its runtime behavior, its overall design does not change significantly. Even though they encapsulate or wrap business logic in many cases, wrapper utility services are still utility services and have no dependency on the business service layer of the overall architecture. There is usually some transformation required between the data formats supported by a back end and the schemas supported by higher-level services, creating a level of indirection between business and wrapper utility services.

Design Considerations

Contrary to how services are usually identified and designed, wrapper utility service design is not focused on any identified business process activities or requirements of higher layer services, such as entity services or task services. Instead, design is solely based on the existing logic and approached bottom-up, and then mapped to any functional requirements that are raised through the regular design process.

An important design decision for a wrapper utility service is whether to support canonical data and message models, or to allow the service consumer to handle any necessary transformations. Typically, the wrapper utility service should be lightweight and not contain excessive extra logic since its only job is to make the back-end function available as a service. Instead of becoming burdened with any additional transformation or logic, its interface can be classified as one that does not conform to enterprise standards that may be in place. This also avoids any unwanted dependencies on the business object schema layer. The burden is on the service consumer to correct the returned information.

Cases occur where this method of designing wrapper utility services is unsuitable. Let’s assume, for example, that an existing back-end function is required by many components throughout a solution but does not return data whose format follows enterprise guidelines. If this function is now exposed as a service via a wrapper utility service, each service consumer must handle the transformation of the data sent to and received from this service, which can lead to unnecessary duplication of effort. In this case, the wrapper utility service should handle the transformation.

The J2EE Connector Architecture is one example of a technology that allows access to an existing back-end application, making it available as if it were a regular Java component living safely within the boundaries of the Java EE container. The native functionality is mapped to a Java interface, and all handling of non-Java data, and the required means to communicate with a back end, are delegated to the inner workings of the connector.

A JCA connector runs in the same process as the Java EE container and takes advantage of the container’s connection pooling, authentication, and transaction capabilities. However, it is only directly available to other components running in the same container, such as EJBs and servlets. The client interface is either proprietary for each particular connector or uses the record-based CCI. Therefore, a J2EE connector is usually only used locally and in pure Java solutions. The Spring framework offers a set of convenience classes around JCA that can easily be classified as wrapper utility services.

Wrapper utility services can also be designed to run tightly coupled with the back-end function they encapsulate. A commercial example is Web service interfaces that allow SOAP-based access to SAP BAPI programming interfaces. The wrapper utility services in this case are bundled directly with the SAP system via the SAP application server in NetWeaver.

Because of this tight coupling, the Service Abstraction design principle and, similarly, the Service Loose Coupling design principle are not as important for wrapper utility services as for other utility service types. Moreover, the application of the Service Autonomy design principle is limited by the characteristics of the wrapped environment. The main reason for the existence of wrapper utility services is so that a standardized contract for an existing piece of functionality can be created. With the standardized contract comes support for standardized data formats, such as Java classes in the case of JCA connector, XML schema in the case of Web-based services, and protocols (CCI for JCA connectors or SOAP or REST for Web-based services).

Service Implementation

The service logic for a wrapper utility service is highly dependent on the native API and protocol that it encapsulates. Some existing functionality may expose an external interface that can be invoked over a network. Some applications have a clearly supported documented data format, which is XML-based in some cases. The implementation of a wrapper utility service follows the existing back end and adapts to the service interface that is communicated to its service consumers.

Service Consumption

The design and runtime characteristics of wrapper utility services are heavily influenced by the existing encapsulated back end. Consumers of these services must be aware of any associated restrictions, as the services automatically inherit the service-level attributes of the existing application environment. If the wrapped system has a scheduled regular downtime for maintenance, not all of the associated wrapper utility services will be available at the same time.

Wrapper utility services are closely related to resource utility services in how they often encapsulate back-end applications with physical resources. Similar considerations regarding the transactional scope within which the service is invoked also apply. For example, whether or not a function that is wrapped by a wrapper utility service can be listed as part of a distributed transaction involving multiple resources directly depends on whether the existing back end supports this type of transaction. Note that JCA connectors implicitly support the Java EE transactional model, but are limited to what level of support is offered by the back end.


Case Study Example

NovoBank uses an existing mainframe system for handling personal accounts and customer information. The mainframe application is running on CICS. Various service implementations must access the functionality offered by this application, and the service design team decides to channel all access to the mainframe through a consistent set of wrapper utility services. After evaluating a number of technologies to implement and deploy these services, the use of a JCA connector is deemed to be appropriate for NovoBank. This connector allows access to the mainframe application directly from within the Java EE container, utilizing the CCI.

The services utilize the CCI convenience classes in the Spring framework, and apply Spring’s bean-based programming model to use the connector and expose the connector’s functionality as a Web service.


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

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