In my application i have a router that dinamically send XML tag to the appropiate channel:
<!-- Route each tag to the appropiate channel -->
<int-xml:xpath-router id="router" input-channel="routerChannel" evaluate-as-string="true">
<int-xml:xpath-expression expression="concat(name(./node()), 'Channel')" />
</int-xml:xpath-router>
Using this XML this route each tag to serviceChannel and activityChannel
<root>
<service attr1="x" attr2="y" />
<activity anotherAttr="W" />
</root>
My service activator(S) POJO are something like this:
class Service {
private String attr1;
private String attr2;
/* Setter and getter omitted */
}
And this is the applicationContext.xml configuration:
<!-- Service activators -->
<int:service-activator input-channel="serviceChannel" method="schedule">
<bean class="it.mypkg.Service" />
</int:service-activator>
The router send the entire Node to bean and i will need to extract the attributes "by hand".
There is a way to "unmarshall" the Node so in attr1 and attr2 i will get the value provided in XML ?
Usally i do this with a simple Unmarshaller (using JAXB) and adding the annotations #XmlRootElement, #XmlAttribute and so on.
I think before pass to POJO i will need to use SI-XML UnmarshallingTransformer but i really dont know how to do this... moreover this should be "enough general" to handle all tags (sure, all POJO classes like Service and Activity will have #XmlAttribute etc)
Use an Unmarshalling Transformer and then route on the unmarshalled objects.
Related
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
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.
I have requirement wherein I need to expose a restful service with XML payload. After that I need to get hold of xml payload and transform it to different xml using xslt transformer.
I am struggling how to get hold of xml payload that can act as a input to the xslt transformer.
I want to avoid marshelling and unmarshlling overhead.
Can someone please help me on same.
Regards
Lalit
When asking questions like this, it's better if you show what you have tried and what didn't work.
In this case, all you need is an HTTP Inbound Gateway.
If the incoming content-type contains text; the payload will be String, by default. If not (e.g. application/xml) then you'll need to configure the gateway with the type you want...
request-payload-type="java.lang.String"
otherwise, the payload will be a byte[].
Based on Gary's suggestion... used following code and it worked...
<int:channel id="inputChannel"></int:channel>
<int-http:inbound-gateway request-channel="inputChannel" path="/test" supported-methods="POST">
<int-http:request-mapping consumes="application/xml" produces="application/xml" />
</int-http:inbound-gateway>
<int:chain input-channel="inputChannel">
<int:service-activator ref="sa1"></int:service-activator>
<int-xml:xslt-transformer
xsl-resource="classpath:/testTransformer.xsl"/>
<int:service-activator ref="sa2"></int:service-activator>
</int:chain>
<!-- input type is byte[] -->
<bean id="sa1" class="com.fidintl.integration.ServiceActivator1">
</bean>
<!-- input type is String -->
<bean id="sa2" class="com.fidintl.integration.ServiceActivator2"></bean>
It looks like in post (as Gary mentioned) output is byte[] which I converted to string in ServiceActivator1
Regards,
Lalit Kumar
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.
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