Lightweight Composite Service Development with SCA and Spring


by Berthold Maier and Nicolas Fonnegra


Published October 2011

Contents

Downloads

Oracle SOA Suite
DecoupledSpringPaper.zip
(Includes sample code for the components described in this article.)

Audience

This paper is intended for software developers and software architects who implement and design service-oriented Spring applications. It is also intended for Oracle SOA Suite developers using the Service Component Architecture (SCA) Composite to expose their existing business logic to the service world. Readers should be familiar with service-oriented architecture (SOA) principles, WebService, Java, Spring, XSLT, and Oracle SOA Suite 11g.

Introduction

Rod Johnson first introduced the Spring Framework at the end of 2002, in his book Expert One on One J2EE, as a lightweight alternative to the Enterprise Java Beans (EJB) architecture [Wiki]. Since then, different companies and organizations have developed Spring-based applications, defining several beans that are in charge of business logic. Many of these beans were intended as decoupled components. However, in order to use independently developed components for new applications or for the development of business services, most developers tend to couple these Spring components very tightly when they apply configuration changes and they implement extra adapter logic for the communication among these components. Using shared business objects, common libraries, or common message types means the interface and data types can't be changed easily without affecting several other pieces. As a result, the Spring beans are tightly coupled and governance and reuse is difficult.

To solve this problem, the component development approach can be combined with the SCA standard to produce a more extensible alternative. With SCA, we have a flexible and lightweight model for assembling independent components and applying SOA principles. Keep in mind that with SCA, we are not forced to use Web services over SOAP or any other dedicated technology; Spring beans are enough. We can simply compose lightweight Spring beans and call them from another independent application or service without large overhead in the SOA environment.

The main propose of this article is to present a lightweight SOA-based design involving totally decoupled Spring components that interact using SCA. This paper is divided in two main parts. The first part shows the design and describes its main components, and the second part illustrates a test scenario, with the corresponding source code for the Oracle SCA engine (a component of Oracle SOA Suite). In the Conclusion, the result is analyzed with its implications and its scope.

Architecture

Component Design

The main idea behind this approach is to provide a way to define two or more complete, independent Spring components that communicate with each other without relying on external or third-party objects. This approach follows service design principles, and it allows the easy interchange and reuse of defined Spring components without having to modify their source code or other external entities. To accomplish this objective, it is first necessary to understand the design pattern that Spring components should follow before defining the communication between them.

Every Spring component can define two interfaces: one for receiving incoming messages and, optionally, another to send messages. Each transport object—and consequently, each interface—should belong to one and only one component and should remain unknown to the rest of the components. All the components should remain entirely decoupled even at the physical layer and, therefore, class files should not be shared among them. Figure 1 shows the class diagram for this approach.

Figure 1
Figure 1

The class diagram in Figure 1 shows a parallel between two independent Spring components that are composed inside the application to add new functionality. Although both components are developed by completely different teams or companies and they follow identical design principals, it is important to mention again that neither component shares any of its objects or configurations.

The component design can be divided into two interaction flows: receiving an object from the exposed function and returning back an object after finishing the internally implemented business logic. Other exchange patterns are possible.

The receiving part is composed of one receiving interface, two transport objects, and one implementing class for the internal logic. In our example, the receiver interface (IReceiverA or IReceiverB in Figure 1) defines an input method that uses the two transport objects, one to actually receive the message (ReceivedObject) and the other to return a result to the sender (ReturnedObject). These two objects represent the actual information that is being transferred and both of them should follow the JavaBeans convention. The class implements the receiver's interface and should perform—or delegate to other Spring beans—the business logic. If the component needs to send a message to another Spring component, it should also define the second, optional part of the design.

The second part of the design is similar to the first part. It contains one sender interface, two transport objects but no implementing class. The difference here is the relationship between the implementing class (ReceiverImplA or ReceiverImplB in Figure 1) and the two interfaces. ReceiverImpl is an IReceiver and has (again, optionally) an ISender. If the component doesn't communicate with other Spring components, it shouldn't have an ISender interface. Like the IReceiver, the ISender interface defines a sending method and uses two transport objects, one to send the information and one to obtain the result.

The main question that arises now is this: How can you expose a new business service interface when you are using these two beans, and how does the information travel from one component to another? In other words, how are the two components bound together in a lightweight service-based way without overwhelming the SOA?

Implementation

Thanks to the new SCA architecture in Oracle SOA Suite 11g, with the Spring-based implementation of the SCA container, it is now possible to leave this implementation to one of the SOA components. For this paper, the SCA Mediator component will implement the ISender interface and it will visually transport the messages from one Spring component to another, doing the necessary transformations externally in order to adapt the component-specific messages. By doing this, each component can communicate with the others without having to rely on their implementation. Figure 2 shows how this design looks in SCA.

Figure 2
Figure 2

Figure 2 shows a composite implementing the design from Figure 1. Inside there are three Spring components and one mediator.  These components are the ones that are going to be shown in the test scenario.

The BookstoreShipping Spring component exposes a Web Services Description Language (WSDL) interface based on its IReceiver interface and uses its ISender interface to communicate with the mediator. The mediator implements this ISender interface and uses the IReceiver interface from the inlandShipping component and the overseasShipping component to transfer and route the messages.

This model is achieved by using some of the Spring application context features and the SCA Spring extension. Since each Spring component is independent, each should have its own application context configuration file. Similar to any Spring application, the configuration file defines the bean and its properties. If the component sends information to another component, one of the properties should be a reference to the destination. In other words, the ISender property should be injected like this:


<bean id="receiverA" class="com.decoupledspring.demo.ReceiverImplA">
 ...
   <property name="senderProperty"  ref="sender"/>  ...
</bean>


The configuration file for the ReceiverA bean will specify that the sender property uses a reference to a sender object, but it doesn't define it. The definition of the sender object will be done in the SCA composite itself.

The composite defines several Spring application configuration files, one for each Spring component. If the component doesn't have an ISender interface, it should only import the component's application context and declare its exposed service. If the component has an ISender interface, it should import the application context and extend it, using some SCA extensions like this:


<sca:reference name="sender" type="com.decoupledspring.demo..ISenderA"/> 


This extension makes available a bean called sender and it can be injected to the sender property defined previously. The composite will then define a wire in order to bind the SCA reference to the mediator:


<wire>
    <source.uri>ReceiverASCA/sender</source.uri>
    <target.uri>MediatorSCA/MediatorSCA</target.uri>
 </wire>

Test Scenario

For this test scenario, a sample shipping process for a bookstore was implemented. The bookstore relies on two external shipping companies. If the order is in the same country, the inland shipping company is called, but if the order require overseas shipping, the overseas shipping company is called.

When the bookstore's shipping department receives an order for a book, it forwards the order to the SCA Mediator component, which then decides if the overseas or the inland shipping component will be used and performs the necessary transformations, depending on the case.

The bookstore and the shipping components follow the design explained in Figure 1. So all the components implement the IReceiver interface, but only the bookstore has a reference to the ISender interface.

This test scenario was developed using Oracle JDeveloper 11g with the additional Oracle SOA Suite plug-in.

Spring Development

The first step that was done for the test scenario was to create the independent Spring components following the design explained earlier. To ensure the components were truly independent, the three components were coded in separate projects inside the same application. The components could also have been developed in separate applications, because in the end, all were exported to JAR files and imported into the SCA composite.

The bookstore component mentioned above had to implement all of the following Java artifacts: ReceivedObject, ReturnedObject, IReceiver, ReceiverImpl, ISender, SendedObject, and ObtainedObject. This component was divided into three parts. The first part receives the order, the second part forwards the order, and the third part binds the other two parts while performing some light business logic.

The ReceivedObject artifact's BookOrder object is a simple transport object that represents the incoming order with the following attributes: package com.decoupledspring.demo.bookstore.model. 


public class BookOrder {
  
  private String bookName;
  private String isdn;
  private String price;
  private boolean inland;
  private String address;
  private String buyer;
  private String country   
 .....


OrderResult, the class representing the ReturnedObject, is also a transport object following the JavaBeans convention. It has only one attribute to handle the shipping status, which is either success or failure.

After generating both of these objects, IBookShipping (the IReceiver interface) was defined, exposing the method for receiving an order (an instance of the ReceivedObject class) and returning the respective result (an instance of the ReturnedObject class).


public interface IBookShipping {
  
  public OrderResult shipBook(BookOrder bookOrder);
}


The sending part is very similar to the receiving part. It requires the definition of two transport objects, BookShippingOrder (SendedObject) and BookShippingResult (ObtainedObject), and one interface called IShippingOrder (ISender). BookShippingOrder represents the shipping order with the following attributes:


public class BookShippingOrder {
  
  private String bookName;
  private String isdn;
  private String priceinEuros;
  private String priceinDollars;
  private boolean inland;
  private String shippingAddress;
  private String buyer;
  private String shippingCountry;


Again, BookShippingResult has one attribute representing the success or failure of the shipping, and IShippingOrder defines a method with one instance of BookShippingOrder as input and an instance of BookShippingResult as output.


public interface IShippingOrder {
  
  public BookShippingResult sendShippingOrder(BookShippingOrder bookShippingOrder);
}


The third part involved defining the ReceiverImpl (called BookShippingImpl in this test case). It implements the IBookShipping interface and has a reference to the IShippingOrder interface. The main purpose of this class is to perform some business logic before the order is sent.


public class BookShipping Impl implements IBookShipping {
  
  private IShippingOrder shippingOrder;
  
  public BookShippingImpl() {
    super();
  }

  public OrderResult shipBook(BookOrder bookOrder) {
    BookShippingOrder bookShippingOrder= new BookShippingOrder();
    bookShippingOrder.setBookName(bookOrder.getBookName());
    bookShippingOrder.setBuyer(bookOrder.getBuyer());
    bookShippingOrder.setInland(bookOrder.isInland());
    bookShippingOrder.setIsdn(bookOrder.getIsdn());
    bookShippingOrder.setPriceinDollars(this.getPriceinDollars(bookOrder.getPrice()));
    bookShippingOrder.setPriceinEuros(bookOrder.getPrice());
    bookShippingOrder.setShippingAddress(bookOrder.getAddress());
    bookShippingOrder.setShippingCountry(bookOrder.getCountry());
    BookShippingResult result= shippingOrder.sendShippingOrder(bookShippingOrder);
    OrderResult orderResult = new OrderResult();
    orderResult.setResult(result.getShippingResult());
    return orderResult;
  }


After generating all the artifacts, the only thing left to do, which was crucial, was to define this service as a Spring bean with its corresponding application context definition:


   <bean id=" bookstore " class="com.decoupledspring.demo.bookstore.service.impl.BookShippingImpl">
    <property name="shippingOrder"  ref="shippingOrder"/>
  </bean>


For the interaction the bookstore bean must declare a reference to the shippingOrder bean, but for decoupling reasons this reference linkage is not done inside the Spring XML definition. It will be defined later in the SCA composite. After all these tasks were done, the bookstore was exported to a JAR file in order to include it later in Oracle SOA Suite 11g

The inland and overseas shipping components are very similar to the bookstore component. The main difference is that they include only one receiving part, because they do not need to forward the order. In other words, they define only the ReceivedObject, ReturnedObject, IReceiver, and ReceiverImpl artifacts.

In the inland component, the ReceivedObject is called InlandShippingOrder and has the following attributes:


public class InlandShippingOrder {
  
  private String bookName;
  private String price;
  private String shippingAddress;
  private String buyer;


The InlandShippingResult class (ReturnedObject) has only one attribute, which represents the result of the transaction. The IReceiver interface, or IInlandShipping, in this case, declares a method with the InlandShippingOrder as input and the InlandShippingResult as an output:


public interface IInlandShipping {
  
  public InlandShippingResult sendBook(InlandShippingOrder inlandShippingOrder);
}


The IInlandShipping class (InlandShippingImpl) implements very simple business logic, returning only a success message. In a more complex scenario, it could store the order in a database or call an external Web service, but that was not done for this test scenario.


public class InlandShippingImpl implements IInlandShipping{
  public InlandShippingImpl() {
    super();
  }

  public InlandShippingResult sendBook(InlandShippingOrder inlandShippingOrder) {
    String result = "Book " + inlandShippingOrder.getBookName();
    result+=" successfully deliver to " +inlandShippingOrder.getBuyer();
    InlandShippingResult shippingResult= new InlandShippingResult();
    shippingResult.setShippingResult(result);
    return shippingResult;
  }
}


The Spring definition for this component is also very simple, since it doesn't have a reference to any outside elements:


<bean id="inlandShipping" class="com.decoupledspring.demo.inland.service.impl.InlandShippingImpl"/>


In the overseas component, the ReceivedObject was named OverseasShippingOrder and it defined the following attributes:

 
public class OverseasShippingOrder {
  private String bookName;
  private String priceinEuros;
  private String priceinDollars;
  private String shippingAddress;
  private String buyer;
  private String shippingCountry;


As with the other components, the ReturnedObject, or OverseasShippingOrder, has only one attribute,which returns the result of the transaction. The IOverseasShipping object, representing the ISender interface, uses both objects to define the method for receiving the order:


public interface IOverseasShipping {
  
  public OverseasShippingResult deliverBook(OverseasShippingOrder shippingOrder);
  
}


The OverseasShippingImpl class implements the IOverseasShipping interface, and it is responsible for the overseas shipping component's business logic. The logic implemented here is, again, very simple, since the purpose of the test scenario is just to show how to integrate the components, not to show their inner behavior: 


public class OverseasShippingImpl implements IOverseasShipping{
  public OverseasShippingImpl() {
    super();
  }

  public OverseasShippingResult deliverBook(OverseasShippingOrder shippingOrder) {
    String result= shippingOrder.getBookName() +" successfully deliver to "; 
    result+=shippingOrder.getShippingAddress();
    result+=" at " + new Date();
    OverseasShippingResult osr= new OverseasShippingResult();
    osr.setResult(result);
    return osr;
  }
}


The Spring definition for this component is similar to the one used for the inland component, because, again, it doesn't have any external reference:


<bean id="overseasShipping" class="com.decoupledspring.demo.overseas.service.impl.OverseasShippingImpl"/>


Both components were also exported to JAR files so they could be used as libraries in the SCA composite.

The SCA Composite

The implementation of the SCA composite involves two major activities: defining the mediator and importing and binding the Spring components. The Spring components have already been exported to JAR files, so it is now necessary to import them in two places.

The first is the typical import of a library in an Oracle JDeveloper project, in this case, to an SCA composite project. The second is the SCA lib directory, located at <SCA-Project-Home>/SCA-INF/lib, in which <SCA-Project-Home> represents the path where the SCA composite was saved. The three JAR files have to be copied manually to this directory and afterward Oracle JDeveloper has to be restarted.

Once the JAR files have been imported, the Spring components can be defined inside the SCA composite. To achieve this, it is necessary to define three Spring beans inside the composite, one for each of the already developed components.

Generally, when a Spring bean is added inside the SCA composite, Oracle JDeveloper provides the option to generate a new empty configuration file or to use an existing one. Although the bookstore components already have Spring configuration files, new empty ones will be generated so they can be used as wrappers, which makes it possible to add the SCA Spring extensions. 

The Spring bean configuration file modification consists of three parts. The first part imports the already defined beans (bookstore, inlandShipping, and overseasShipping).

The second part defines an exposed service for each component. In the bookstore bean, the exposed service is based on the IBookShipping interface. For the inlandShipping and overseasShipping beans, the service is based on the IInlandShipping and the IOverseasShipping interfaces, respectively. The inlandShipping configuration file should look similar to this:

  
<!--Spring Bean definitions go here-->
  <import resource="classpath:com/decoupledspring/demo/inland/resources/inlandShipping.xml"/>
  <sca:service name="InlandShipping" target="inlandShipping"
               type="com.decoupledspring.demo.inland.service.api.IInlandShipping"/>
</beans>


The third—and most interesting part—is the definition of the external service (shippingOrder, in this case) being consumed by the bookstore bean. As already mentioned, the bookstore component doesn't know which component implements the IShippingOrder interface.

Now, this implementation is going to be declared as an SCA reference, and it will be wired up inside the SCA composite: 


  <!--Spring Bean definitions go here-->
  <import resource="classpath:com/decoupledspring/demo/bookstore/resources/bookstore.xml"/>
  <sca:service name="BookstoreService" target="bookstore"
               type="com.decoupledspring.demo.bookstore.service.api.IBookShipping"/>
   <sca:reference name=" shippingOrder " type="com.decoupledspring.demo.bookstore.service.api.IShippingOrder"/>
</beans>


After the three beans have been defined, the SCA composite should look similar to Figure 3.

Figure 3
Figure 3

The final part is to add the mediator to the SCA composite, drag the components' arrows to bind them together, and define the necessary transformations between messages.

Once the mediator is added, the left arrow of the inlandShipping bean and the left arrow from the overseasShipping bean need to be dragged to the right arrow from the mediator, making both of these components destinations of the mediator's messages.

Similarly, the right arrow from the bookstore bean must be dragged to the left arrow of the mediator, making the bookstore the source of the messages and making the mediator the implementation of the shipping order reference.

The generated code shows how the binding was done:


<wire>
	<source.uri>BookstoreSCA/shippingOrder</source.uri>
    <target.uri>MediatorBookstore/MediatorBookstore</target.uri>
</wire>


The last step is the definition of the transformations, so the information sent by the bookstore component can be received in the shipping components. The transformations and the routing rules (remember, some shipping orders are for inland clients and other are for overseas clients) are defined in the mediator, using XSLT and XPath expressions, respectively. Once this has been done, the SCA composite can be deployed and tested inside the Oracle Fusion Middleware.

Conclusion

The SCA architecture gives software developers the opportunity to design and implement their enterprise applications based on technology-independent Java classes and to offer them as interchangeable and reusable components with all the benefits of SOA. These components can be the standard components from the Oracle SOA Suite (for example, BPEL Process, HumanTask, and Business Rules Engines) or self-developed components, such as Spring components. The main idea behind this architecture is to allow the software developer to compose any component without incurring huge development and maintenance costs.

The inclusion of a Spring bean inside a composite is done in a straightforward manner, requiring only the definition of an application bean configuration file. The SCA Spring capability adds two new tags to the Spring framework: sca-reference and sca-service. With these new tags it is now possible to integrate the Spring components with SOA components, such as Business Process Execution Language (BPEL) and Business Process Model and Notation (BPMN) processes and mediators, and later decorate the required protocol, such as POJO, RMI, SOAP, and so on, to the exposed interface. .

By taking advantage of the SCA Mediator Component features, such as message and bean object transformation and message routing, it is possible to define a Spring bean integration scenario in which several decoupled Spring components are defined and integrated and then to maintain them independently from each other. The Spring beans are able to communicate without sharing object definitions or any other resources.

With the help of the Oracle SOA Suite's SCA architecture and its native Spring support, developed Spring components from different production applications can be reused and orchestrated without having to develop new versions of them. The SCA composite is the main "glue" among the components, and the software developer can focus on the functionality of the Spring components rather than wasting time figuring out the dependencies among the components. Furthermore, powerful capabilities of the Oracle SOA Suit, such as fault policies, dynamic routing, visual design, and composed module-based deployment, can be added to Spring projects, making them more flexible and robust.

References