Overview
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 (interface or class) used to access the underlying content. Any Java object will do - java.lang.String, java.io.InputStream, and com.example.types.PurchaseOrder are all examples of a representation. The format of a message refers to the actual data type of the content.
For example, the format of the following content would be "XML":
<MyBook> <Chapter1> <Chapter2> </MyBook>
The representation of the content could be a java.lang.String:
String content = "<MyBook>...";
Of course, the representation could also be a org.w3c.dom.Document, java.io.InputStream, etc.
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 component may expose a service offered by a Bean component. 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 Use Cases
1) Different representations of the same data. A change in representation is just a change to the Java object used to access the data. Generally speaking, no changes are made to the data or it's underlying format. This type of transformation is often referred to as a "converter", as it converts from one Java type to another. JBoss ESB provided a number of basic converters, such as ByteArrayAsString and LongToDateConverter. Frameworks such as Camel offer a wide variety of converters such as File=>InputStream, String=>Document, etc. These are all examples of representation changes and can be used directly within the SwitchYard transformation framework.
2) Different format for the same representation. A change in format represents a change to the underlying data or data type. A common example for SOA would be transforming one XML format to another, while keeping the same representation (e.g. org.w3.dom.Document). Another example would be taking input of an XML string and generating output of a CSV string. In both cases, an understanding of the underlying data model and type system is required to perform the transformation (unlike simple conversion).
3) Different reprsentation and format. It is not uncommon to see a combination of #1 and #2 when connecting services. This is listed as a distinct use case to simply acknowledge that #1 and #2 are not mutually exclusive and to make sure that the transformation framework can handle it.
4) Transformation as a service. Sometimes the main job of the service is just to translate the message. Creating a transformation service must be simple and straightforward.
API
A transformation is represented in SwitchYard as an instance of org.switchyard.transform.Transformer:
interface Transformer<F,T> { Class<F> getFromType(); Class<T> getToType(); String getFrom(); String getTo(); T transform(F from); }
Each instance of Transformer transforms from a Java type to a Java type, represented by <F> and <T> in the interface. A transformer can also declare the message names that correspond to the source and target data formats. The Java type and message names can be used to resolve a transformer instance when one is not explicitly set in a message exchange.
The TransformerRegistry provides a mechanism for registering transformer instances that can be queried by the SwitchYard runtime to dynamically match service consumer and provider message representation and format. There is one instance of TransformerRegistry per domain and it can be accessed via the ServiceDomain interface.
interface TransformerRegistry { void addTransformer(Transformer<?,?> transformer); boolean removeTransformer(Transformer<?,?> transformer); Transformer<?,?> getTransformer(String from, String to); }
While developers can implement Transformer directly and use the TransformerRegistry to make it available to the runtime, this is not required. SwitchYard also provides a configuration based approach for declaring transformations and associating them with a service
Applying Transformations
Transformation is performed by invoking a Transformer instance within an exchange handler (TransformHandler). This handler executes as part of the service consumer's handler chain, which means that the transformation takes place before the message hits the bus on the request path. The transform handler kicks in again on the reply path if the provider returns a response. TransformHandler is automatically added to each exchange's handler chain by the runtime.
The TransformHandler executes a transform in the following two situations:
- The exchange has one or more transformer instances set on the exchange context.
- The transformation registry contains a transform which applies to the messages being exchanged.
An example of the first option:
// Define the transformation Transformer<Date, String> dateToString = new BaseTransformer<Date, String>() { public String transform(Date from) { // transform the input date to the desired output string return new SimpleDateFormat().format(from); } } // Create the exchange, add the transformer, and invoke the service Exchange exchange = _domain.createExchange(serviceName, ExchangePattern.IN_ONLY); exchange.getContext().setProperty(Transformer.class.getName(), dateToString); Message msg = MessageBuilder.newInstance().buildMessage(); msg.setContent(input); exchange.send(msg);
An example of a configuration-driven approach to follow soon ....
Comments