Working with Transformers
Transformers in iBeans are used to convert messages from one data type to another. You can also use any of the Mule transformers, so it is possible to transform data using XSLT, XQuery, or any other Mule transformer.
Creating Transformers
In their simplest form, transformers convert between different Java types. You create a transformer by performing the conversion in a method that you annotate with the @Transformer annotation:
@Transformer public URL stringToURL(String string) throws MalformedURLException { return new java.net.URL(string); }
At runtime, this method will be discovered and registered with iBeans. Then, whenever an iBean needs to convert from a String to a URL, this transformer will be used.
Here we take an input of java.lang.String, this is referred to as the source type and convert it to a java.net.URL, this is referred to as the return type.
| If you create a transformer with the same source and return types as one of the standard transformers provided with Mule iBeans, your custom transformer will be given priority and used instead. Be careful not to create multiple transformers with the same source and return types, or an exception will be thrown. |
Note that the method throws MalformedURLException, which is an exception specific to creating URL objects. It's good practice to just re-throw any exceptions thrown in a transformer method and let the container handle them.
Working with Collections
The iBeans transformation system support generics with collections, this means that transforms can be matched against collections of a specific type of object. To extend the example above, we could have a transformer that created a List of URL objects from a comma-separated list of URL strings.
@Transformer public List<URL> stringsToURLs(String string) throws MalformedURLException { List<URL urls = new ArrayList<URL>(); for (StringTokenizer tokenizer = new StringTokenizer(string); tokenizer.hasMoreTokens();) { urls.add(new URL(tokenizer.nextToken())); } return urls; }
| It is good practice to always provide types for any transforms that deal with collections. Doing so will provide more accurate information to iBeans about how to match your transformers and it provides better type checking in your code. |
Multiple Source Types
Transformers can only have one return type but we can define multiple source types. For example, lets say we might receive the URL string as s java.lang.String or java.lang.StringBuffer we could add the additional source type to the @Transformer annotation. Note that you can add a comma-separated list of source type classes.
@Transformer(sourceTypes = {StringBuffer.class})
public URL transformStringToURL(String string) throws MalformedURLException
{
return new URL(string);
}
Now if a request is made to convert a a java.lang.StringBuffer to a java.net.URL, this transformer would be discovered and behind the scenes iBeans would attempt to convert StringBuffer to String before calling the method. Note there obviously needs to be a transformer registered that will convert a StringBuffer to String. iBeans and Mule provides a selection of default transformers for dealing with JDK types such as String, byte arrays, InputStreams, Xml Documents, etc. But if you need a transform that doesn't exist you can just create a new transform method.
Working with XML and JSON
Most services deal with data formatted using XML or JSON (JavaScript Object Notation). iBeans supports binding objects to XML and vice-versa using JAXB (Java API for XML binding) which is standard in JDK 6. JSON marshaling to objects and back again using a JSON parsing framework called Jackson. These frameworks are supported automatically by iBeans. For more information see Using Bindings.
Accessing Message Headers
Many channels have headers associated with the response. If you need to access the header, add a parameter to the transformer method signature annotated with the @ReceivedHeaders annotation:
@Transformer public Person xmlToPerson(InputStream data, @ReceivedHeaders("*") Map headers)
The '*' indicates that all headers should be returned. Alternatively, you can specify a single header name and just return the value of that header:
@Transformer public Person xmlToPerson(InputStream data, @ReceivedHeaders("Content-Type") String contentType)
To receive a subset of headers, you can list them as comma-separated values:
@Transformer public Person xmlToPerson(InputStream data, @ReceivedHeaders("Content-Type, Host, X-MyHeader") Map headers)
By default an error will be thrown if a listed header is not on the response. To avoid this error, mark the header as optional with '?'. For example, X-MyHeader is optional in the following code:
@Transformer public Person xmlToPerson(InputStream data, @ReceivedHeaders("Content-Type, Host, X-MyHeader?") Map headers)
If the return type for the @ReceivedHeaders param is a java.util.List, just the values will be returned.
Transformer Rules
- Transformers must be defined in their own class, e.g., MyTransformers. You can have one or more transformer methods in the class, and each will be discoverable inside iBeans.
- If a transformer has state, all transformers defined in that class will share that state.
- Primitive types must not be used for transformer method return types. Only objects can be used.
- For collections use Lists or Sets, not arrays. Generics are supported and should be used where ever possible since the generic types are also using when trying to match transformers.
Add Comment