SwitchYard tutorial

switchyard tutorialIn this tutorial we will describe how to create your first SwitchYard Service. We will then show how to consume it through a JSF interface or a SOAP based client.

 

SwitchYard is a lightweight service delivery framework which provides support for developing, deploying and managing service oriented applications. You can download SwitchYard from JBoss site

Once unzipped, you will see that the distribution contains a bundled JBoss AS 7 where you can deploy your services.

There are several service implementations available in SwitchYard which allow routing services between SwitchYard and other frameworks. You might for example connect your SwitchYard services to Apache Camel services or Drools decision rules.

 

In this tutorial we will show how to use Bean services which leverages the power of Java EE6 and CDI allowing to turn Java objects into services by means of a simple @Service annotation added to your bean. Then, your beans are automatically registered as Services and can be injected as CDI beans using @Inject.
In order to do that, you need to use a pluggable container named the Bean Component which allows Java classes (or beans) to provide and consume services. The bean component has been implemented as a Weld extension, thus you don’t need to learn a new programming model: Bean Services are standard CDI beans with a few extra annotations.

In this example, the MathService interface represents the Service Interface, defining the service operations that are implemented by the MathServiceBean class: this can be done through the org.switchyard.component.bean.Service annotation:

package com.sample;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import org.switchyard.component.bean.Reference;
import org.switchyard.component.bean.Service;

@Service(MathService.class)
@ApplicationScoped
public class MathServiceBean implements MathService {
    
        
    @Override
    public Result addition(Calculator order) {
         
        Result result = new Result();
        result.setTotal(order.getNumber1() + order.getNumber2());
        
        
        return result;
    }

}

package com.sample;

public interface MathService {
    
    Result addition(Calculator order);
    
}

And this is the bean class that wraps the result of the addition (we are using a class instead of a primitive in case you plan to add more complex operations like divisions):

package com.sample;

public class Result {

    private int total;

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}

Finally, this is the switchyard.xml configuration file where your Service and its implementation class are described:

<?xml version="1.0" encoding="UTF-8"?>
<switchyard xmlns="urn:switchyard-config:switchyard:1.0" name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0">
    <composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" name="orders" targetNamespace="urn:switchyard-quickstart-demo:orders:0.1.0">
        <service name="MathService" promote="MathService" />

        <component name="MathService">
            <implementation.bean xmlns="urn:switchyard-component-bean:config:1.0" class="com.sample.MathServiceBean"/>
            <service name="MathService">
                <interface.java interface="com.sample.MathService"/>
            </service>
            
        </component>
    </composite>

</switchyard>

The last thing you need adding is an empty META-INF/beans.xml file in your deployed application. When the application is deployed, the Weld runtime scans the application for beans and picks up @Service beans and make them available to the application deployer (will depend on the container). At this point, the service can be invoked from other services within SwitchYard or bound to a wire protocol via SwitchYard gateways.

Testing Services with JSF

The JavaServer Faces (JSF) technology can be used to quickly design your UI of Java EE applications. JSF is very tightly integrated with CDI to provide the Object Model behind the JSF user interface components.

It’s possible to annotate your SwitchYard Service CDI beans with the @Named annotation and inject them directly into your application’s JSF components; however invoking directly the Service from JSF would result in a tight coupling between the JSF page and the Service.

Thus, you should invoke your SwitchYard CDI Bean Service through the SwitchYard Exchange mechanism by means of the @Reference injected Service reference. This mechanism provides a client side proxy bean that handles all the SwitchYard Exchange invocation.

package com.sample;

import org.switchyard.component.bean.Reference;

import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;

@Named
@RequestScoped
public class Calculator implements Serializable {

    @Inject
    @Reference   
    private MathService orderService;

    private int number1;
    private int number2;
    private int total;

    // Getters - Setters omitted for brevity

    public void sum() {
        total = orderService.addition(this).getTotal();

    }
}

And this is the JSF page which invokes the Calculator Named Bean:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
    <!-- CSS omitted for brevity -->
</h:head>
<h:body>
    <h2>Switchyard Simple Calculator</h2>
    <h:form id="jsfexample">
        <h:panelGrid columns="2" styleClass="default">

            <h:outputText value="Enter number 1:" />
            <h:inputText value="#{calculator.number1}" />

            <h:outputText value="Enter number 2:" />
            <h:inputText value="#{calculator.number2}" />

            <h:commandButton actionListener="#{calculator.sum}"
                styleClass="buttons" value="Save key/value" />
            <h:outputText value="Total #{calculator.total}" />                

            <h:messages />

        </h:panelGrid>

    </h:form>
</h:body>
</html>

So here is how your Web application structure should look like:

SwitchYardExample.war

?   home.xhtml
?
????META-INF
?      |  switchyard.xml
?  
?
????WEB-INF
    ?   beans.xml
    ?   faces-config.xml
    ?   web.xml
    ?
    ????classes
        ????com
        ?   ????sample
        ?           Calculator.class
        ?           MathService.class
        ?           MathServiceBean.class
        ?           Result.class

You can deploy your application to built-in JBoss AS 7 server (C:\switchyard-as7-0.4\standalone\deployments) and invoke it (supposing that your Web application name was SwitchYardExample) :

http://localhost:8080/SwitchYardExample/home.xhtml

By filling up the input text, your Calculator bean should proxy your call to your @Service which returns the result:

jboss soa switchyard tutorial web services


 

So far we have consumed Services from a JSF page. It is however possible to consume them by means of other protocols. Transformation represents a change to the format and/or representation of a message’s content.  The representation of a message is simply the Java contract (e.g. java.lang.String, org.example.MyFancyObject) used to access the underlying content.  The format of a message refers to the actual structure of the data itself.  Examples of data formats include XML, JSON, CSV, and EDI.

Transformers can be applied to a Java class using the org.switchyard.annotations.Transformer annotation which defines an incoming channel (from parameter) and an outgoing channel (to parameter).

 

package com.sample;

import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;

import org.switchyard.annotations.Transformer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.StringReader;

public class Transformers {
    // Transforms the incoming XML message to Calculator
    @Transformer(from = "{urn:switchyard-quickstart-demo:math:1.0}addition")
    public Calculator transform(Element from) {
        Calculator calc = new Calculator();

        calc.setNumber1(Integer.parseInt(getElementValue(from, "number1")));
        calc.setNumber2(Integer.parseInt(getElementValue(from, "number2")));
       
        return calc;
    }
    // Transforms the Result class into an XML response
    @Transformer(to = "{urn:switchyard-quickstart-demo:math:1.0}additionResponse")
    public Element transform(Result result) {
        StringBuffer ackXml = new StringBuffer()
            .append("<math:additionResponse xmlns:orders=\"urn:switchyard-quickstart-demo:math:1.0\">")
            .append("<Result>")
            .append("<total>" + result.getTotal() + "</total>")
            .append("</Result>")
            .append("</math:additionResponse>");

        return toElement(ackXml.toString());
    }


    private String getElementValue(Element parent, String elementName) {
        String value = null;
        NodeList nodes = parent.getElementsByTagName(elementName);
        if (nodes.getLength() > 0) {
            value = nodes.item(0).getChildNodes().item(0).getNodeValue();
        }
        return value;
    }

    private Element toElement(String xml) {
        DOMResult dom = new DOMResult();
        try {
            TransformerFactory.newInstance().newTransformer().transform(
                    new StreamSource(new StringReader(xml)), dom);
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return ((Document)dom.getNode()).getDocumentElement();
    }
}

The “to” parameter can be omitted is the outgoing channel is a concrete java type. Conversely, you can omit the “from” if the incoming channel is a concrete Java type

The public Calculator transform(Element from) method will transform from a DOM element to a Calculator instance.

jboss soa switchyard tutorial esb
The public Element transform(Result result) converts from a Result java class to an XML element.
jboss soa esb switchyard
Since transformations occur between named types (i.e. from type A, to type B), it’s important to understand how the type names are derived.  The type of the message is determined based on the service contract, which can be WSDL or Java.

In this example, the message name is determined based on the fully-qualified element name of a WSDL message.  Take the following WSDL definition (we have used wsprovide tool in JBOSS_HOME/bin to generate the WSDL see this link):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions 
    targetNamespace="urn:switchyard-quickstart-demo:math:1.0" 
    xmlns="http://schemas.xmlsoap.org/wsdl/" 
    xmlns:tns="urn:switchyard-quickstart-demo:math:1.0"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    
  <types>
    <xsd:schema 
        targetNamespace="urn:switchyard-quickstart-demo:math:1.0" 
        xmlns:tns="urn:switchyard-quickstart-demo:math:1.0" 
        xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <xs:element name="addition" type="tns:additionType"/>
        <xs:element name="additionResponse" type="tns:additionResponseType"/>
        <xs:element name="calculator" type="tns:calculatorType"/>
        <xs:element name="result" type="tns:resultType"/>
        <xs:complexType name="additionType">
            <xs:sequence>
                <xs:element name="calculator" type="tns:calculatorType"/>
            </xs:sequence>
        </xs:complexType>
        <xs:complexType name="additionResponseType">
            <xs:sequence>
                <xs:element name="result" type="tns:resultType"/>
            </xs:sequence>
        </xs:complexType>
        <xs:complexType name="calculatorType">
            <xs:sequence>
                <xs:element name="number1" type="xs:int"/>
                <xs:element name="number2" type="xs:int"/>
               
            </xs:sequence>
        </xs:complexType>
        <xs:complexType name="resultType">
            <xs:sequence>
                <xs:element name="total" type="xs:int"/>
                 
            </xs:sequence>
        </xs:complexType>
    </xsd:schema>
  </types>
  
  <message name="addition">
    <part name="parameters" element="tns:addition"/>
  </message>
  <message name="additionResponse">
    <part name="parameters" element="tns:additionResponse"/>
  </message>
  
  <portType name="MathService">
    <operation name="addition">
      <input message="tns:addition"/>
      <output message="tns:additionResponse"/>
    </operation>
  </portType>
  
  <binding name="MathServiceBinding" type="tns:MathService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
    <operation name="addition">
      <soap:operation soapAction="urn:switchyard-quickstart-demo:math:1.0"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  
  <service name="MathService">
    <port name="MathServicePort" binding="tns:MathServiceBinding">
      <soap:address location="http://localhost:18001/test"/>
    </port>
  </service>
</definitions>

Finally, you need to declare your Web service SOAP binding in your switchyard.xml file and the Transformers which will be used to convert the messages from one channel to another:

<?xml version="1.0" encoding="UTF-8"?>
<switchyard xmlns="urn:switchyard-config:switchyard:1.0" name="math" targetNamespace="urn:switchyard-quickstart-demo:math:0.1.0">
    <composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" name="math" targetNamespace="urn:switchyard-quickstart-demo:math:0.1.0">
        <service name="MathService" promote="MathService">
            <binding.soap xmlns="urn:switchyard-component-soap:config:1.0">
                <wsdl>wsdl/MathService.wsdl</wsdl>
                <socketAddr>:18001</socketAddr>
                <contextPath>calculator</contextPath>
            </binding.soap>  
        </service>
         
        <component name="MathService">
            <implementation.bean xmlns="urn:switchyard-component-bean:config:1.0" class="com.sample.MathServiceBean"/>
            <service name="MathService">
                <interface.java interface="com.sample.MathService"/>
            </service>
            
        </component>
    </composite>
    <transforms>
        <transform.java xmlns="urn:switchyard-config:transform:1.0" class="com.sample.Transformers" from="{urn:switchyard-quickstart-demo:math:1.0}addition" to="java:com.sample.Calculator"/>
        <transform.java xmlns="urn:switchyard-config:transform:1.0" class="com.sample.Transformers" from="java:com.sample.Result" to="{urn:switchyard-quickstart-demo:math:1.0}additionResponse"/>
    </transforms>
</switchyard>

Fine, now deploy your Web service. From the server logs, you will see that your Web service has been published at the socket address defined in your switchyard.xml file:

 

21:59:21,727 INFO  [org.switchyard.component.soap.InboundHandler] (MSC service thread 1-7) Publishing WebService at http://127.0.0.1:18001/calculator/MathService

 

Now you can use any SOAP Web service client to test your Web service against the WSDL which is reachable at http://127.0.0.1:18001/calculator/MathService?wsdl
Here’s the SOAP outgoing message:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:switchyard-quickstart-demo:math:1.0">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:addition>
         <calculator>
            <number1>5</number1>
            <number2>4</number2>
         </calculator>
      </urn:addition>
   </soapenv:Body>
</soapenv:Envelope>

And this is the returning SOAP message:

   <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <math:additionResponse xmlns:math="urn:switchyard-quickstart-demo:math:1.0">
         <Result>
            <total>9</total>
         </Result>
      </math:additionResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

In the next tutorial we will show how to use an Eclipse plugin (SOAP Ui) to invoke and test Web services. Stay tuned !

Found the article helpful? if so please follow us on Socials