write own Spring PayloadTransformer and load it - spring-integration

I'm currently working with citrus-framework to test an application.
One of my interfaces uses Protobuf and I would like to implement a protobuf-to-json-transformer which is compatible with spring-integration to use it similarly like the following but with my transformer instead of the object-to-string-transformer:
<int:channel id="configRawReplies" />
<int:object-to-string-transformer id="configtransformer" input-channel="configRawReplies" output-channel="configResponse" />
<int:channel id="configResponse">
<int:queue />
</int:channel>
for now I have a prototyp exactly like object-to-string-transformer and I'm loading it with:
<bean id="Proto2Json" class="com.nobody.citrus.transformer.ProtoToJSONString">
<property name="input-channel" value="none"/>
<property name="output-channel" value="none"/>
</bean>
but it fails.
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'Proto2Json' defined in URL [file:/Users/nobody/DevOops/test/citrus-scala/target/test-classes/citrus-context.xml]:
Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException:
Invalid property 'input-channel' of bean class [com.pme.citrus.transformer.ProtoToJSONString]:
Bean property 'input-channel' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
Does somebody have an idea or an hint where to look on the web?
BR

That's correct. You really need to follow a design in the ObjectToStringTransformer to implement your own AbstractPayloadTransformer. And that one has to be as a plain <bean> definition in your application context.
Only the problem that you don't understand why we really have all those custom tags to utilize input-channel and output-channel attributes as well. The point is that this
<int:object-to-string-transformer>, for example, provides for the application context several beans, including the mentioned ObjectToStringTransformer instance, a MessageTransformingHandler and, finally, ConsumerEndpointFactoryBean to connect a MessageHandler with an inputChannel.
So, what you are missing here is a generic <int:transformer> definition for your custom AbstractPayloadTransformer implementation:
<bean id="Proto2Json" class="com.nobody.citrus.transformer.ProtoToJSONString"/>
<int:tranformer ref="Proto2Json" input-channel="configRawReplies" output-channel="configResponse"/>
Please, read more Reference Manual to avoid similar discussions in the future:
https://docs.spring.io/spring-integration/reference/html/overview.html#programming-tips
https://docs.spring.io/spring-integration/reference/html/messaging-transformation-chapter.html

Related

Spring JMS using MarshallingMessageConverter while supporting String, etc

Using XML configuration - I have MarshallingMessageConverter working; however, I still want to send some messages as TextMessage with simple String values.
It seems that my configuration is forcing me to go from one ditch (No automatic JAXB marshalling) into the other (JAXB marshalling only):
Here's my relevant XML configuration:
<bean id="jaxbConverter" class="org.springframework.jms.support.converter.MarshallingMessageConverter">
<property name="marshaller" ref="jaxb2Marshaller" />
<property name="unmarshaller" ref="jaxb2Marshaller" />
</bean>
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="errorHandler" ref="jmsErrorHandler" />
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destinationResolver" ref="jmsDestinationResolver"/>
<property name="messageConverter" ref="jaxbConverter" />
</bean>
The JAXB marshalling works fine, but I sometimes want to send an empty body (header properties only) message; which causes an error like so:
.UnmarshallingFailureException: JAXB unmarshalling exception; nested exception is javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
Which makes sense, because I'm sending a non-JAXB string (or nothing) in the body.
Is it possible to have the best of both worlds; the String, byte[], Map conversion behavior of org.springframework.jms.support.converter.SimpleMessageConverter -and- org.springframework.jms.support.converter.MarshallingMessageConverter ?
Is the only way to accomplish this by making a second container factory with the other converter and explicitly using it in my #JmsListener annotation?
Create a simple DelegatingMessageConverter(implement MessageConverter) and have it delegate to the SimpleMessageConverter, MarshallingMessageConverter (or even a MappingJackson2MessageConverter) based on a message property, e.g. message.getStringProperty("contentType") - text/plain, application/xml, application/json.

using gateway for starting transaction

I have been following previous answers that talk about inserting gateway / service-activator to start a new transaction mid way in SI processing. I have tried below code but for some reason it does not work, if you could point to error in this configuration please.
All the threads seems to be waiting for something to happen and the sp outbound gateway is not invoked, but i cant figure out what.
The idea here is to process each task produced by splitter in a thread pool under a new transaction.
<int:splitter...output-channel="taskChannel"/>
<int:channel id="taskChannel">
<int:dispatcher task-executor="taskExecutor"/>
</int:channel>
<int:gateway id="txGw" service-interface="com.some.test.StartTransactionalGateway"
default-request-channel="taskChannel" default-reply-channel="individualTask">
</int:gateway>
<int:service-activator ref="txGw"
input-channel="taskChannel"
output-channel="individualTask"
method="startTx"
auto-startup="true">
</int:service-activator>
<int-jdbc:stored-proc-outbound-gateway ...request-channel="individualTask" .....
interface StartTransactionalGateway {
#Transactional
Message<?> startTx(Message<?> m);
}
default-request-channel="taskChannel" the gateway is sending messages to itself.
You can't mix channels in the subflow with the main channels. You need something like...
<int:splitter...output-channel="taskChannel"/>
<int:channel id="taskChannel">
<int:dispatcher task-executor="taskExecutor"/>
</int:channel>
<int:service-activator ref="txGw"
input-channel="taskChannel"
output-channel="whereWeWantTheResultsToGo"
method="startTx"
reply-timeout="0"
auto-startup="true" />
<int:gateway id="txGw" service-interface="com.some.test.StartTransactionalGateway"
default-request-channel="toStoredProc" />
<int-jdbc:stored-proc-outbound-gateway ...request-channel="toStoredProd" .....
You don't need a default-reply-channel; simply omit the reply-channel on the stored proc gateway and the reply will automatically go back.
Well, Don't forget that you can mark something transactional not only using #Transactional annotation. There is <tx:advice> for the XML declaration.
If you need to wrap to the TX just only that <int-jdbc:stored-proc-outbound-gateway>, there is <request-handler-advice> sub-element to wrap the handleRequestMessage of the underlying component to any Advice:
<int-jdbc:request-handler-advice-chain>
<tx:advice>
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
</int-jdbc:request-handler-advice-chain>
From other side your code can not work, because <int-jdbc:stored-proc-outbound-gateway> may not return result. The procedure is one-way. But the <gateway> interface is request-reply.
UPDATE
See my answer here Keep transaction within Spring Integration flow. It is another trick how to make down-stream flow transactional.
With <gateway> you should be sure that your transactional subflow returns to the replyChannel in the end. Or... Make your gateway's method as void to achieve one-way behaviour.

modifying output directory in outbound-channel-adapter spring integration

The following is my configuration xml for a file polling functionality. I have to change the output directory sometimes.
<int-file:inbound-channel-adapter id="filesIn" directory="file:${paths.root}" channel="abc" >
<int:poller id="poller" fixed-delay="5000"/>
</int-file:inbound-channel-adapter>
<int:channel id="abc"/>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="#aPath.getPath()"
delete-source-files="true"
filename-generator ="filenameGenerator"/>
<bean id="filenameGenerator" class="com.dms.util.FileNameGenerator"/>
In the
#Override
public String generateFileName(Message<?> message)
{
I have tried setting the value of a configured bean property.
This is the additional configuration for that
<bean name="aPath" class="com.dms.util.GetOutPath">
<property name="path" value="${paths.destination}"/>
</bean>
paths.destination is from a property file.
In the generateFileName method I have added the code for changing the property value as follows
#Autowired
private GetOutPath outPathBean;
For the bean:
#Component("outPathBean")
and in my code
outPathBean.setPath(newFolder);
My debugging shows that the value of the property does not change. My question is, How do I modify the directory either in the generateFileName method or by any other way.
Please help!
The general mechanism you are trying to use will work because the file name generator is calle before evaluating the directory expression.
However, you have two instances of you really have the #Component defined (and you are using component scanning, you will have two instances of GetOutPath - aPath and outPathBean.
The expression is using the instance that you are not changing.
You need to inject the same bean instance that you are using in your expression.

Getting a reference to the current ActivePivotManager

How do you get a reference to the current ActivePivotManger? I've found code that uses it but no way to actually get ahold of it.
If you look at the class SandboxConfig in last v4.4.x you'll see that this class is annotated as following:
#PropertySource(value="classpath:sandbox.properties")
#Configuration
#Import(value={
ActivePivotConfig.class,
ActivePivotServicesConfig.class,
WebServicesConfig.class,
RemotingConfig.class,
SecurityConfig.class
})
public class SandboxConfig {
The ActivePivotConfig.class in the annotation is the one in which we define the activePivotManager which is defined as a member of the SandboxConfig class:
/** ActivePivot Manager, automatically wired */
#Autowired
protected IActivePivotManager activePivotManager;
The #Autowired here is important as it means that this is provided already.
in the previous versions of AP we were defining this as following in our project:
<!-- ActivePivot Manager -->
<bean id="ActivePivotManager" class="com.quartetfs.biz.pivot.impl.ActivePivotManagerFactory">
<property name="resourceName" value="DESC-INF/ActivePivotManager.xml" />
<property name="autoStart" value="false" />
<property name="healthCheckPeriod" value="120"/>
</bean>
If you want to use the ActivePivotManager instance stick then to what is in the SandboxConfig and add your logic there, use the ActivePivotManager instance defined there.
If you're not happy with that move to full XML wiring which is still supported as I can understand that some stuff is hidden and you expect to see the instance of ActivePivotManager instantiated clearly somewhere (which is done actually in ActivePivotConfig.class).

Mule ESB JAXB XML To Object Transformer Better Way?

Mule 3.3 can automatically unmarshall an XML string to an object using JAXB given that:
1. you first register your jaxb annotated classes with spring.
2. there is a component that requires such type as input
So I have managed to do the transformation, but I had to create a "DumbTransformer" that does nothing. It has a method that returns the same object it receives. I need it in order to trigger the XML to Object conversion so that I can further process the message.
Flow Example:
<spring:beans>
<spring:bean id="dumbTransformer" class="foo.bar.DumbTransformer"/>
</spring:beans>
<flow name="main" doc:name="main">
<vm:inbound-endpoint path="in" doc:name="VM" />
<component doc:name="Java">
<spring-object bean="dumbTransformer"/>
</component>
<splitter expression="#[payload.items]" doc:name="Split Items"/>
<logger message="#[payload]" level="INFO" doc:name="Log Item"/>
<vm:outbound-endpoint path="out" doc:name="VM" />
</flow>
DumbTransformer.java
package foo.bar;
#ContainsTransformerMethods
public class InvoiceUnmarshaller extends AbstractTransformer {
#Transformer
public MyJaxbAnnotatedClass foo(#Payload MyJaxbAnnotatedClass i) {
return i;
}
}
Is there a way to acomplish this without having to create such DumbTransformers?
Thanks.
As you guessed it, the JAXB deserialization doesn't occur because there is no component to satisfy:
there is a component that requires such type as input
So what if you had an auto-transformer to do just that:
<auto-transformer returnClass="foo.bar.MyJaxbAnnotatedClass" />
The Mule XML Module provides OOTB a JAXB Transformer. I would rather leverage mule capabilities whenever possible rather than writing custom code

Resources