This is a 2nd article comparing some of the Oracle SOA Suite 12c features with JBoss Fuse Service Works. The 1st article was focusing on the BPEL development while this 2nd part is demonstrating how to use both tools in developing intra-composites routing. So, this article compares the Oracle Mediator included in the Oracle SOA Suite 12c release with Switchyard 2.0 included in JBoss FSW 6.0.0.GA. For the purposes of the presented samples, I’m using :
- the Oracle Fusion Middleware 12.1.3.0.1 Appliance (http://www.oracle.com/technetwork/community/developer-vm/index.html#soa12), a prebuilt VirtualBox virtual machine containing WebLogic 12c, Oracle Database XE 12 and Oracle SOA Suite 12c
- the JBoss Fuse Service Works 6.0.0.GA distribution downloaded from the RedHat customer portal (https://access.redhat.com/jbossnetwork/restricted/listSoftware.html?product=jboss.fuse.serviceworks&downloadType=distributions)
Both packages require registration in order to be downloaded.
The project
In order to compare the two product stack I’m using a simple use case :
- a JAX-WS web service accepting an XSD based grammar is exposed to process purchase orders ;
- a mediator transforms the associated SOAP payload in an appropriated format as required for the persistence service ;
- a persitence service persist the purchase orders into the persistence store.
The following diagram illustrates this process.
Using SOA Suite and JDeveloper
To provide a solution to the use case above with SOA Suite and JDeveloper is quite straightforward. I need to create a new application and a new SOA project in JDeveloper. This results in the creation of a new SCA composite. Here, using the JDeveloper Component Palette, I create an SCA service in the « Exposed Services » pane of the SCA Editor. In order to do this I choose the component named « SOAP » in the Components pane and I drag it on the canvas, in the « Exposed Services » pane. The service wizard opens and let me choose the following properties :
- The name of the service
- The SCA type service or reference (here a service)
- The WSDL URL. Here I can use an existent WSDL located either on the local file system, or deployed on a WebLogic application server, or in the code repository (MDS – MetaData Store), or in an UDDI/WSIL repository. If I don’t have an existent WSDL, which will be our use case here, the tool will generate it automatically, based on the input and output flows, described by an XSD grammar. In this case, I will use the XSD file named po.xsd, located in my project’s Schema directory. I can also choose the generated service type as being one way, synchronous and asynchronous. I choose one-way. I need to configure the location, on the local file system, where the generated WSDL will be saved, its port type and the transaction propagation option which, in this case, will be NEVER.
The service wizard will generate the required JAX-WS web service and its associated WSDL. Now it’s time to create the mediator.
From the Components pane of the SCA Editor I select the Mediator component and I drag it on the Components pane of the SCA Editor. This will be all I need to do, at this stage, as far as the mediator is concerned.
Now I attack the last element of my design, the database adapter. From the Components pane I select the Database Adapter component and I drag it on the References pane of the SCA Editor. The Database Adapter wizard which opens let me configure the following properties :
- The JDBC connection to be used to access my database. I’ll use the Oracle XE 11g database which comes with the appliance. Here, besides the JDBC connection name which has a meaning only in JDeveloper, I need to provide a JNDI name that I will use later in order to configure the associated datasource in the application server.
- The operation type to be performed to the database which could be : calling a stored procedure or performing one of the following SQL requests :
- insert or update
- delete
- select
- query by example
- The set of tables to be used by the operations configured above. In this example I’m using a single table, named ORDERS, that I created previously.
- When several database tables are to be used then their relationship may be also selected and configured. This doesn’t happen in this case as there is only one database table.
- The columns of the database table which are to be used in the operations described above. Given that in my case I’m just inserting data into the database, I check all the defined columns but the primary key which will be initialized based on a native Oracle sequence number. Accordingly, the next step of the wizard is to allow me to create this sequence number, or to select it if it already exists, and to associate it with the database table’s primary key.
- The last thing to do is to configure some obscure JCA endpoint properties and the best choice, at least at this stage, is to accept the default values.
All the required components are in place now, as the figure below shows and we need to wire them now. This is done very easy by dragging arrows between components, first between the inboud web service and the mediator, next between the mediator and the outbound database adaptor.
Our mediator needs to perform transformations such that to adapt and mediate the content of the SOAP payload to the WSDL based database adapter input. Double-clicking the mediator, the Mediator Editor opens and let us define the required transformation, as follows :
- In the Transformation section of the Mediator Editor, there is an icon serving to handle an XSL mapping file describing the transformation required between the left and the right side of the mediator. We may use an existent mapping file, if we have one, or create a new one. In this case we’ll create a new one as we don’t have any.
- The XSLT Map Editor opens and allows us to define the required transformation. On the left side of the editor we have the inbound data, the PurchaseOrder structure as described by the input XSD file, while on the right side of the editor we have the outbound data, a collection of Orders types, as defined by the WSDl automatically created as an interface of the Database adaptor service. To map the inbound elements to the outbound one sis very simple, just click an element on the left side and drag it on the corresponding element on the right side. The image below shows the resulting mapping.
That’s all folks. Now, I just need to deploy my project on the application server by right-clicking it and selecting the « Deploy » option. The deployment should work without error and, once terminated, I can use the Oracle Enterprise Manager console to test it.
Using JBoss FSW and JBoss Developer Studio
To do the same exercise with JBoss FSW and JBoss Developer Studio, one needs to create first a switchyard project. Here, in the switchyard project configuration wizard, we can choose the implementation support, the gateway bindings and the test frameworks to be used with our switchyard composite. For our use case we will choose a bean implementation, the JPA and SOAP gateway bindings and the http test mixin.
Our new SCA composite gets created and we can start our design. The first thing to do is to expose our JAX-WS service, as we did previously. In the Palette pane, I select the Service element in the Core category and I drag it on the canvas. An SCA service is automayically created and positioned on the left side of the canvas. Here I need to choose an interface type between the following :
- A Java interface
- A WSDL
- An ESB interface requiring to configure an input, an output and a fault type
This is the first surprize because, as oposed to the SOA Suite, here we need a WSDL for our service and we cannot create it on the fly. A WSDL is not a business artifact but a technical one. While it is mandatory for exposing JAX-WS web services, we cannot expect that business analysts, who are the final target of these kind of tools, use and understand it. Instead, business analysts are concerned by the enterprise data, expressed by XSD grammars and the tool should be able to generate the required WSDL based on this XSD grammar. It’s already difficult to train business analyst such that they succeed to express their enterprise data in the form of XSD grammars, if additionally we need to get them authoring WSDL contracts, then we need to transform them into real developers. So we got stucked here even before having started.
Okay, let’s suppose we have a WSDL for the service we will expose, which is a very unlike case. I’m not talking here about an external service, having a clear defined contract, but about an ad-hoc internal service, which contract shall be made on the fly, based on the service input, output and fault specifications. In my case, in order to ba able to continue my implementation, I just used the WSDL created previously by SOA Suite, which I ammenended such that to adapt it to my needs here. I stored it in the project and now I can use it, by choosing WSDL as the type of the interface and browsing the project to select the required file. I need to give a name to the service, like OrderWebService and that’s it, my service is created.
Now I need to add a binding to my service and, in this case, the binding is a SOAP one. So, in the Palette I select the SOAP binding and I drag it onto my service. Here, in the SOAP Binding Details window, the only mandatory field is the WSDL Url which is automatically initialized with the location of the WSDL used as the service interface during the service creation step. I also need to give an arbitrary name to this binding, a context path and a service port. In my case the context path is « orders » and the server port is 18001. This means that my service could be tested, using the http mixin, using the URL localhost :180001/orders.
The next step is to create an SCA component associated to my service. This is done by clicking the Component element in the Palette pane, the Core category, and draging it on the canvas. The new created component is automatically placed in the middle on the canvas. I gave it the name OrderComponent. Now, I need to provide an implementation to this component and this implementation will be a CDI bean. Hence, in the Implementation category of the Palette I click the Bean element and I drag it onto my OrderComponent component. The Bean Implementation details dialog opens and here I have to create a Java class which is the implementation of the OrderComponent component. If this class already exists, I can browse for it but, in my case it doesn’t, so by clicking on Bean Class link, I can create it. So, I click the link and the New Bean Service Class dialog opens. Here, the interface type is automatically configured as being Java and, the first thing I need to do, is to configure the service interface. Again, if I have an already existent interface then I can browse for it but, in my case, I haven’t, so I will creatre it by clicking the Interface link. The Java Interface dialog opens and here I create an interface named OrderService. Back to the New Bean Service Class dialog, the name of my implementation is automatically proposed OrserServiceBean, which I will accept. The Component OrderComponent, together with its associated Java interface and implementation, respectivelly OrderService and OrderServiceBean, are created.
To resume, I have now an SCA service, named OrderWebService, bound to a SOAP service, having OrderProcessing.wsdl as a contract. The body of this service is the SCA component named OrderComponent, which implements the interface named OrderService. The OrderComponent is implemented as a CDI bean named OrderServiceBean. This is the class to which my SCA service, Order WebService, delegates all the operations it is responsible of. So, this class is the class which will receive a purchase order and will get it persistent, hence I have to provide the required code here.
The good news is that, as opposed to my previous implementation using SOA Suite, where I was using a Database Adapter, here I will use JPA (Java Persistence API). Oracle SOA Suite doesn’t have a JPA adapter and the standard way to implement persistence is using the Database Adapter, which is not JPA compliant. But this is not the case of FSW so let’s take advantage of the full JPA support.
The way that FSW provides JPA support is very straightforward. The only thing one needs to do is to have an interface and to use the with it FSW out-of-the-box JPA binding facility. In our case, what we want to achieve is to be able to persist instances of our purchase orders. So, we need to provide a JPA entity to model the purchase order and to use it as the value of the <jpa :entityClassName /> element, belonging to the JPA binding. To do that, I select the Reference widget in the Core tab of the Palette and I drag it on the right side of the canvas. The New Reference wizzard is displayed. Here, I need first to define the interface to the service I’m referencing. I have the choice between a Java, a WSDL or an ESB interface. I will chose the Java interface and I can either select an existent or create a new one. I don’t have any existing interface hence I’ll create a new one by clicking the link labeled Interace. The code below shows the interface I have created.
package fr.simplex_software.soa.order_service;
public interface OrderRepository
{
public void persistOrder (PurchaseOrderEntity purchaseOrder);
}
It has only one mthod, called persistOrder which, as its name implies, will persist the purchase order entity passed as an argument. The listing below shows the PurchaseOrderEntity.
package fr.simplex_software.soa.order_service;
import java.math.*;
import javax.persistence.*;
import fr.simplex_software.soa.order_service.jaxb.*;
@Entity
@Table(name="ORDERS")
public class PurchaseOrderEntity
{
private Long id;
private String custID;
private String orderId;
private String productName;
private String itemType;
private BigDecimal price;
private BigDecimal quantity;
private String status;
private String ccType;
private String ccNumber;
public PurchaseOrderEntity()
{}
public PurchaseOrderEntity (PurchaseOrder po)
{
this.ccNumber = po.getCcNumber();
this.ccType = po.getCcType();
this.custID = po.getCustID();
this.itemType = po.getItemType();
this.orderId = po.getOrderId();
this.price = po.getPrice();
this.productName = po.getProductName();
this.quantity = po.getQuantity();
this.status = po.getStatus();
}
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ORDER_REF_ID", unique=true, nullable=false)
public Long getId()
{
return id;
}
@Column(name="CUSTOMER_ID")
public String getCustID()
{
return custID;
}
public void setId(Long id)
{
this.id = id;
}
public void setCustID(String value)
{
this.custID = value;
}
@Column(name="ORDER_ID")
public String getOrderId()
{
return orderId;
}
public void setOrderId(String value)
{
this.orderId = value;
}
@Column(name="PRODUCT_NAME")
public String getProductName()
{
return productName;
}
public void setProductName(String value)
{
this.productName = value;
}
@Column(name="ITEM_TYPE")
public String getItemType()
{
return itemType;
}
public void setItemType(String value)
{
this.itemType = value;
}
@Column(name="PRODUCT_PRICE")
public BigDecimal getPrice()
{
return price;
}
public void setPrice(BigDecimal value)
{
this.price = value;
}
@Column(name="PRODUCT_QUANTITY")
public BigDecimal getQuantity()
{
return quantity;
}
public void setQuantity(BigDecimal value)
{
this.quantity = value;
}
@Column(name="CREDIT_CARD_STATUS")
public String getStatus()
{ return status;
}
public void setStatus(String value)
{
this.status = value;
}
@Column(name="CREDIT_CARD_TYPE")
public String getCcType()
{
return ccType;
}
public void setCcType(String value)
{
this.ccType = value;
}
@Column(name="CREDIT_CARD_NUMBER")
public String getCcNumber()
{
return ccNumber;
}
public void setCcNumber(String value)
{
this.ccNumber = value;
}
}
The code above shows a simple JPA entity defining a few columns and an auto-increment ID field. We’re using a MySQL database as JPA data-store. Now, our SCA component, named OrderComponent, will reference a service via an SCA reference, named OrderRepositoryReference, which binds to the FSW out-of-the-box JPA implementation the OrderRepository interface. The image below shows our SCA composite.
The relevant XML content of this SCA composite is shown below :
<?xml version="1.0" encoding="UTF-8"?>
<sy:switchyard xmlns:bean="urn:switchyard-component-bean:config:1.1" xmlns:jpa="urn:switchyard-component-camel-jpa:config:1.1" xmlns:sca="http://docs.oasis-open.org/ns/opencsa/sca/200912" xmlns:soap="urn:switchyard-component-soap:config:1.1" xmlns:sy="urn:switchyard-config:switchyard:1.1" xmlns="urn:switchyard-config:switchyard:1.1" xmlns:transform="urn:switchyard-config:transform:1.1" name="order-service" targetNamespace="urn:fr.simplex_software.soa:order-service:1.0">
<sca:composite name="order-service" targetNamespace="urn:fr.simplex_software.soa:order-service:1.0">
<sca:service name="OrderWebService" promote="OrderComponent/OrderService">
<sca:interface.wsdl interface="wsdl/OrderProcessing.wsdl#wsdl.porttype(OrderProcessing)"/>
<soap:binding.soap name="OrderWebServiceBinding">
<soap:contextMapper soapHeadersType="VALUE"/>
<soap:wsdl>wsdl/OrderProcessing.wsdl</soap:wsdl>
<soap:socketAddr>:18001</soap:socketAddr>
<soap:contextPath>orders</soap:contextPath>
</soap:binding.soap>
</sca:service>
<sca:reference name="OrderRepositoryReference" multiplicity="0..1" promote="OrderComponent/OrderRepositoryReference">
<sca:interface.java interface="fr.simplex_software.soa.order_service.OrderRepository"/>
<jpa:binding.jpa name="order-jpa-binding">
<jpa:entityClassName>fr.simplex_software.soa.order_service.PurchaseOrderEntity</jpa:entityClassName>
<jpa:persistenceUnit>order-pu</jpa:persistenceUnit>
<jpa:transactionManager>#jtaTransactionManager</jpa:transactionManager>
<jpa:produce>
<jpa:usePersist>true</jpa:usePersist>
</jpa:produce>
</jpa:binding.jpa>
</sca:reference>
<sca:component name="OrderComponent">
<bean:implementation.bean class="fr.simplex_software.soa.order_service.OrderServiceBean"/>
<sca:service name="OrderService">
<sca:interface.java interface="fr.simplex_software.soa.order_service.OrderService"/>
</sca:service>
<sca:reference name="OrderRepositoryReference">
<sca:interface.java interface="fr.simplex_software.soa.order_service.OrderRepository"/>
</sca:reference>
</sca:component>
</sca:composite>
<sy:transforms>
<transform:transform.jaxb from="java:fr.simplex_software.soa.order_service.jaxb.PurchaseOrderResponse" to="{http://simplex_software.fr/soa/order}PurchaseOrderResponse" contextPath="fr.simplex_software.soa.order_service.jaxb"/>
<transform:transform.jaxb from="{http://simplex_software.fr/soa/order}PurchaseOrder" to="java:fr.simplex_software.soa.order_service.jaxb.PurchaseOrder" contextPath="fr.simplex_software.soa.order_service.jaxb"/>
</sy:transforms>
</sy:switchyard>
We’re almost done ! The only missing part are the transformations. Looking at our OrderProcessing.wsdl, which is our service’s contract, we can see that it accepts an input of the type PurchaseOrder, defined as a complex element in the po.xsd. This complex element, passed to our service as an XML document, needs to be transformed into a PurchaseOrderEntity class instance, such that to be persisted. So we need a transformer able to transform a PurchaseOrder complex element into a Java class, in occurrence a JPA entity. These conversions between XSD elements and Java classes are known under the name of unmarshalling/marshalling process and the part of the Java specifications dealing with them is named JAX-B (Java Architecture for XML Binding).
Accordingly, we need to build the Java classes equivalent to our XSD complex elements defined in the schema imported by our service’s contract such that to be able to unmarshall into them the content of the XML payload and to marshall them into XML payloads. We didi it as a separate maven project using the jaxb2-maven-plugin to compile the our schema grammar po.xsd into Java classes. The result of this compilation, done by XJC, the JAX-B compiler, is represented by the classes PurchaseOrder and PurchaseOrderResponse, which are the input and output types of our service.
So, by the XJC compilation process, we generated Java classes that may be marshalled into XSD complex elements of type PurchaseOrder and PurchaseOrderResponse and which could store the data resulted from their unmarshalling process. The only thing that we have to do now is to provide the mecanich associated to this marshalling/unmarshalling process and this is simply done by the following sequence of our switchyard.xml file :
<sy:transforms>
<transform:transform.jaxb from="java:fr.simplex_software.soa.order_service.jaxb.PurchaseOrderResponse" to="{http://simplex_software.fr/soa/order}PurchaseOrderResponse" contextPath="fr.simplex_software.soa.order_service.jaxb"/>
<transform:transform.jaxb from="{http://simplex_software.fr/soa/order}PurchaseOrder" to="java:fr.simplex_software.soa.order_service.jaxb.PurchaseOrder" contextPath="fr.simplex_software.soa.order_service.jaxb"/>
</sy:transforms>
What this sequence is saying is that we are using theout-of-the-box switchyard JAX-B transformers to transform :
- from the XSD complex element {http://simplex_software.fr/soa/order}PurchaseOrder to the JAX-B generated class simplex_software.soa.order_service.jaxb.PurchaseOrder
- and from the JAXB-generated class simplex_software.soa.order_service.jaxb.PurchaseOrderResponse into the XSD complex element {http://simplex_software.fr/soa/order}PurchaseOrderResponse
Now, back to the implementation of our service, the OrderServiceBean CDI class, the only thing we have to do is to add our business logic consisting in :
- Injecting the Switchyard JPA Binding OrderRepository implementation
- Using the JPA service to store the input PurchaseOrder into the database
- Insatiate an PurchaseOrderResponse and return it to the caller
All this as shown by the code below :
@Service(OrderService.class)
public class OrderServiceBean implements OrderService
{
private static Logger logger = Logger.getLogger(OrderServiceBean.class);
@Inject
@Reference("OrderRepositoryReference")
private OrderRepository store;
@Override
public PurchaseOrderResponse processOrder(PurchaseOrder order)
{
logger.info("*** processOrder: " + order.getOrderId());
store.persistOrder(new PurchaseOrderEntity(order));
PurchaseOrderResponse resp = new ObjectFactory().createPurchaseOrderResponse();
resp.setAccepted(true);
resp.setOrderId("1234");
resp.setStatus("Accepted");
return resp;
}
}
Last but not least, our SCA composite may be unit tested using the Switchyard unit test frameworks based on the notion of MixIn, as explained by the Switchyard documentation. Here is a simple test :
@RunWith(SwitchYardRunner.class)
@SwitchYardTestCaseConfig(config = SwitchYardTestCaseConfig.SWITCHYARD_XML, mixins = {CDIMixIn.class, HTTPMixIn.class, TransactionMixIn.class})
public class OrderWebServiceTest
{
private HTTPMixIn httpMixIn;
private static Logger logger = Logger.getLogger(OrderWebServiceTest.class);
@Test
public void testProcessOrder() throws Exception
{
logger.info("*** Entered in testProcessOrder");
httpMixIn.setContentType("application/soap+xml");
httpMixIn.postResourceAndTestXML("http://localhost:18001/orders/OrderProcessingService", "/xml/soap-request.xml", "/xml/soap-response.xml");
}
}
Of course, for more realistic tests in a integration context we use as usual SoapUI, on the behalf of which we generated the soap-request.xml and soap-response.xml files used above.
Conclusions
In this exercice we tried to implement the same business case using two SOA stacks, The Oracle one and the RedHat one. It’s difficult to make a real comparation of these two SOA stacks based on such simple business case, however my conclusions are as follows :
- Oracle SOA Suite is a more mature product, providing all the required tools and wizzards. While it is easier to be used by a non-developer population, like business analysts, thanks to its quite impressive powership and its coverage of a big diversity of cases that typically appear in the design process, it is less opened to Java standards and specifications. Many functionalities, such as persistence, are achieved on the behalf of sprcific adapters. Its support of XML based transformations, like XSLT, XQuery, XPath, is very complete, however its support for JAX-B is limited to the one provided by JDeveloper.
- JBoss Fuse Service Works is a more recent product which doesn’t beneficiate yet of all the tools and wizzards. However, its support of the Java standards and specifications is more consistent. Its integration with maven is absolutelly natural, and it directly supports JPA, JAX-B, CDI, EJB, Camel, and other Java or open standards. While its utilisation by business analysts might become a nightmare, it is really designed on the purpose to be a software developer tool. It dramatically lacks tools like XQuery/XSLT/XPath wizzard and editors.
To resume everything in one sentence, Oracle SOA Suite is more an XML set of tools dedicated to the business analysts, even if JDeveloper and WebLogic represent a very powerfull development pkatform, while JBoss Fuse Service Works is more adapted to developers who may use graphical design, component and palettes in order to build a basic layout or schelet but systematically need to go the hard way of the manually code writing, adapting and completing. In this respect, JBoss Fuse Service Works require a much more important learning curve with no obvious gains in productivity once this curved is absorbed. Oracle SOA Suite aims at being used by more or less any business analysts with few or none Java development skills.