<MyBook> <Chapter1> <Chapter2> </MyBook>
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.
Take the following message content:
<MyBook> <Chapter1> <Chapter2> </MyBook>
The format of this content is XML. One representation of XML in Java is as a String. Of course, the representation could also be a org.w3c.dom.Document, java.io.InputStream, etc.
String content = "<MyBook>...";
Transformation plays an important role in connecting service consumers and providers, since the format and representation of message content can be quite different between the two. For example, a SOAP gateway binding will likely use a different representation and format for messages than a service offered by a Java Bean. In order to route services from the SOAP gateway to the Bean providing the service, the format and representation of the SOAP message will need to change. Implementing the transformation logic directly in the consumer or provider pollutes the service logic and can lead to tight coupling. SwitchYard allows for the transformation logic to declared outside the service logic and injected into the mediation layer at runtime.
Transformation of message content is specified in the descriptor of your SwitchYard application (switchyard.xml). The qualified name of the type being transformed from as well as the type being transformed to are defined along with the transformer implementation. This allows transformation to be a declarative aspect of a SwitchYard application, as the runtime will automatically register and execute transfomers in the course of a message exchange.
<transforms> <transform.java bean="MyTransformerBean" from="{urn:switchyard-quickstart-demo:orders:1.0}submitOrder" to="java:org.switchyard.quickstarts.demos.orders.Order"/> </transforms>
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.
For WSDL interfaces, the message name is determined based on the fully-qualified element name of a WSDL message. Take the following WSDL definition:
<definitions xmlns:tns="urn:switchyard-quickstart:bean-service:1.0"> <message name="submitOrder"> <part name="parameters" element="tns:submitOrder"/> </message> <portType name="OrderService"> <operation name="submitOrder"> <input message="tns:submitOrder"/> </operation> </portType> </definitions>
This would yield the following message type name based on the message element name defined in the WSDL:
{urn:switchyard-quickstart:bean-service:1.0}submitOrder
When Java interfaces are used for the service contract, the message name consists of the full package name + the class name, prefixed with "java:".
package org.switchyard.example; public interface OrderService { void submitOrder(Order order); }
The message type name for the submitOrder method in this Java interface would be "java:org.switchyard.example.Order". Occasionally, it can be useful to override the default operation name generated for a Java interface. The @OperationTypes annotation provides this capability by allowing the user to specify the input, output, and/or fault type names used for a Java service interface. For example, if we wanted to accept XML input content without any need for transformation to a Java object model, the OrderService interface could be changed to look like this:
package org.switchyard.example; @OperationTypes(in = "{urn:switchyard-quickstart:bean-service:1.0}submitOrder") public interface OrderService { void submitOrder(String orderXML); }
Aside from short-circuiting the requirement for transformation, this annotation can be useful if you want to maintain tight control over the names used for message content.
There are two methods available for creating a Java-based transformer in SwitchYard:
Implement the org.switchyard.transform.Transfomer interface and add a <transform.java> definition to your switchyard.xml.
Annotate one or more methods on your Java class with @Transformer.
When using the @Transformer annotation, the SwitchYard maven plugin will automatically generate the <transform.java> definition(s) for you and add them to the switchyard.xml packaged in your application. The following Java class would produce the <transform.java> definition provided above:
@Named("MyTransformerBean") public class MyTransformer { @Transformer(from = "{urn:switchyard-quickstart-demo:orders:1.0}submitOrder") public Order transform(Element from) { // handle transformation here } }
The optional from and to elements of the @Transformer annotation can be used to specify the qualified type name used during transformer registration. If not supplied, the full class name of the method parameter will be used as the from type and the full class name of the return type will be used as the to type.
The CDI bean name specified by @Named annotation is used to resolve transformer class. If you don't specify, then class name of the transformer is used instead like following:
<transforms> <transform.java class="org.switchyard.quickstarts.demos.orders.MyTransformer" from="{urn:switchyard-quickstart-demo:orders:1.0}submitOrder" to="java:org.switchyard.quickstarts.demos.orders.Order"/> </transforms>
Note that both of above <transform.java> definition has a bean attribute or a class attribute. bean attribute and class attribute are mutually exclusive.
There are three distinct transformation models available with Smooks in SwitchYard:
XML to Java : Based on a standard Smooks Java Binding configuration.
Java to XML: Based on a standard Smooks Java Binding configuration.
Smooks : This is a "normal" Smooks transformation in which the developer must define which Smooks filtering Result is to be exported back to the SwitchYard Message as the transformation result.
Smooks transformations are declared by including a <transform.smooks> definition in switchyard.xml.
<transform.smooks config="/smooks/OrderAck_XML.xml" from="java:org.switchyard.quickstarts.transform.smooks.OrderAck" to="{urn:switchyard-quickstart:transform-smooks:1.0}submitOrderResponse" type="JAVA2XML"/>
The config attribute points to a Smooks resource containing the mapping definition. The type attribute can be one of SMOOKS, XML2JAVA, or JAVA2XML.
The JSON transformer provides a basic mapping facility between POJOs and JSON (JSON marshalling and unmarshalling). Just like the JAXB Transformer, specification of the transformer requires a to and from specification with one Java type and one QNamed JSON type, depending on whether you're performing a Java to JSON or JSON to Java transformation.
The following configuration illustrates a JSON to Java transformation.
<trfm:transform.json from="{urn:switchyard-quickstart:transform-json:1.0}order" to="java:org.switchyard.quickstarts.transform.json.Order"/>
The following configuration illustrates a Java to JSON transformation of the same types as above.
<trfm:transform.json from="java:org.switchyard.quickstarts.transform.json.Order" to="{urn:switchyard-quickstart:transform-json:1.0}order"/>
The XSLT transformer allows you to perform a transformation between 2 types, using an XSLT. It is configured simply by specifying the to and from QNames, as well as the path to the XSLT to be applied.
<transform.xslt from="{http://acme/}A" to="{http://acme/}B" xsltFile="com/acme/xslt/A2B.xslt"/>
The JAXB transformer allows you to perform Java to XML (and XML to Java) transformations using JAXB (XML marshalling and unmarshalling). It is exactly like the JSON Transformer in terms of how it is configured i.e. a to and from configuration with one Java type and one QNamed XML type.
JAXB Java models can be generated from an XML Schema using XJC, or from a WSDL using tools like wsconsume (.sh/.bat), which is shipped with both SwitchYard AS6 and AS7 distros (in the bin directory).
Working out the available transformations is quite simple. Just look in the ObjectFactory class source file (ObjectFactory.java). In this source file you will see factory methods representing the available marshallings/unmarshallings e.g.
@XmlElementDecl(namespace = "http://com.acme/orders", name = "create") public JAXBElement<CreateOrder> createOrder(CreateOrder value) { return new JAXBElement<Order>(_CreateOrder_QNAME, CreateOrder.class, null, value); }
In the above example, the @XmlElementDecl annotation tells us that the XML QName associated with the com.acme.orders.CreateOrder type is "{http://com.acme/orders}create". From this, we can deduce that we have the following possible SwitchYard JAXB Transformer configurations:
<transform.jaxb from="{http://com.acme/orders}create" to="java:com.acme.orders.CreateOrder" /> <transform.jaxb from="java:com.acme.orders.CreateOrder" to="{http://com.acme/orders}create" />
One of the nice things about the JAXB Transformer integrations in SwitchYard is the fact that JAXB Transformations are automatically detected and automatically added, for your application deployment. So, if for example, you develop a CDI Bean Service and use JAXB generated types in the Service Interface, you will not need to configure any of the transformations. SwitchYard will automatically detect their availability for your application and will automatically apply them at the appropriate time during service invocation.