Transformer
A Transformer changes a message's payload or structure. It is the tool of choice when one route step's output type differs from the previous step's input type. Examples: parse a JSON envelope, base64-decode a binary payload, map a FIX message to a Kafka-friendly record.
| Property | Value |
|---|---|
| Java interface | com.connamara.sdk.v1.adapter.components.Transformer |
| Method | Message<?> transform(Message<?> message) |
| Pipeline slot | After Conditions, before Processors. |
| Return contract | A new Message<?> (use DerivedMessage.derive(...)) or the input message reference unchanged. |
| Side effects | Discouraged. Transformers should be pure with respect to their input message; persistent side effects belong in a Processor. |
Complete example
package com.example.myplugin;
import com.connamara.sdk.v1.adapter.components.Transformer;
import com.connamara.sdk.v1.common.component.ManifestResource;
import com.connamara.sdk.v1.common.message.DerivedMessage;
import com.connamara.sdk.v1.common.message.Message;
import java.util.Map;
@ManifestResource("""
{
"id": "uppercase-transformer",
"pluginId": "my-plugin",
"functionalType": "uppercase-transformer",
"displayName": "Uppercase Transformer",
"category": "TRANSFORMER",
"configuration": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Uppercase Settings",
"properties": {}
}
}
""")
public class UpperCaseTransformer implements Transformer {
public UpperCaseTransformer(Map<String, Object> config) {
// No configuration needed for this transformer.
}
@Override
public Message<?> transform(Message<?> message) {
Object payload = message.getPayload();
if (payload instanceof String text) {
return DerivedMessage.derive(message, text.toUpperCase(), Map.of());
}
return message;
}
}
Key rules
- Headers are reused, not copied. The
DerivedMessage.derive(...)wrapper references the source message's headers. Never build a freshMessageHeadersmap; doing so allocates and breaks the platform's zero-copy contract. - Return the input reference for pass-through. When a Transformer cannot apply (wrong payload type, feature toggle off), return the original
messagereference unchanged. Do not return a derived copy with no changes; the wrapper is wasted work. - No
null. Transformers cannot drop messages. Returningnullis a Processor-only behavior. If a Transformer needs to drop, restructure the route so a Processor follows. - Constructor argument. Every Transformer constructor takes
Map<String, Object>populated from the Portal form, even when the configuration is empty.
Test pattern
@Test
@DisplayName("GIVEN a lowercase string payload WHEN transformed THEN payload is uppercase")
void transform_uppercasesString() {
UpperCaseTransformer transformer = new UpperCaseTransformer(Map.of());
Message<String> message = mock(Message.class);
when(message.getPayload()).thenReturn("hello world");
when(message.getHeaders()).thenReturn(new MessageHeaders(Collections.emptyMap()));
Message<?> result = transformer.transform(message);
assertEquals("HELLO WORLD", result.getPayload());
}
When to reach for a Transformer
| Need | Reach for |
|---|---|
| Convert payload type or wire format. | Transformer. |
| Add a derived header without modifying the payload. | Transformer if the header is a function of the payload; Processor if it depends on external state or has side effects. |
| Drop the message based on its content. | Processor (Transformers cannot return null). |
| Decide whether the route should run at all. | Condition, not Transformer. |
See also
- Components Overview for how Transformers fit in the pipeline.
- Processor for stepwise business logic that runs after transformation.
- Component Manifest Schema for the form that drives the Transformer's configuration.