我有一个Spring Boot应用程序,它在从ActiveMQ代理检索TextMessage
类型的JMS消息时遇到问题。
如果使用者尝试从代理检索消息,则它不能自动将消息转换为TextMessage,而是将其视为ByteMessage。有一个JmsListener可以将队列中的消息作为TextMessage读取:
...
@JmsListener(destination = "foo")
public void jmsConsumer(TextMessage message) {
...
JmsListener
会产生如下警告,并丢弃消息:
org.springframework.jms.listener.adapter.ListenerExecutionFailedException: Listener method could not be invoked with incoming message
Endpoint handler details:
Method [public void net.aschemann.demo.springboot.jmsconsumer.JmsConsumer.jmsConsumer(javax.jms.TextMessage)]
Bean [net.aschemann.demo.springboot.jmsconsumer.JmsConsumer@4715f07]; nested exception is org.springframework.messaging.converter.MessageConversionException: Cannot convert from [[B] to [javax.jms.TextMessage] for org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener$MessagingMessageConverterAdapter$LazyResolutionMessage@7c49d298, failedMessage=org.springframework.jms.listener.adapter.AbstractAdaptableMessageListener$MessagingMessageConverterAdapter$LazyResolutionMessage@7c49d298
at org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:118) ~[spring-jms-5.1.4.RELEASE.jar:5.1.4.RELEASE]
我已经提取了一个小样本应用程序 调试问题:https://github.com/ascheman/springboot-camel-jms
现实生活中的生产者是使用Apache Camel的商业应用程序。因此,我几乎无法更改/定制生产者。我试图建立一个显示相同行为的示例生产者。
我可以以某种方式调整消费者以将消息视为TextMessage
吗?
此外:有什么方法可以直接在Spring中以编程方式从消息中检索其他AMQP属性吗?当然,我仍然可以将消息读为ByteMessage
并尝试将属性解析掉。但是我正在寻找一种不受任何Spring API支持的更干净的方法。到目前为止,Spring @Headers
注释没有帮助。
答案 0 :(得分:0)
我遵循@AndyWilkinson的评论,在 activemq.xml 中的transportConnector上添加了 transport.transformer 选项后,我遇到了与问题所有者相同的问题,接下来,问题就解决了。
<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&wireFormat.maxFrameSize=104857600&transport.transformer=jms"/>
答案 1 :(得分:0)
我遇到了同样的错误,这是因为 LazyResolutionMessage
是从 MessagingMessageConverter
调用的,MessageConverter
是 return ((org.springframework.messaging.Message) payload).getPayload();
、which converts your message 的默认实现(实际上它没有,因为它是默认值):
@JmsListener(destination = "${someName}")
public void consumeSomeMessages(MyCustomEvent e) {
....
}
我已经完成了你想要的,最后我的消费者是这样工作的:
@Bean(name = "jmsListenerContainerFactory")
public DefaultJmsListenerContainerFactory whateverNameYouWant(final ConnectionFactory genericCF) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setErrorHandler(t -> log.error("bad consumer, bad", t));
factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
factory.setConnectionFactory(genericCF);
factory.setMessageConverter(
new MessageConverter() {
@Override
public Message toMessage(Object object, Session session) {
throw new UnsupportedOperationException("since it's only for consuming!");
}
@Override
public MyCustomEvent fromMessage(Message m) {
try {
// whatever transformation you want here...
// here you could print the message, try casting,
// building new objects with message's attributes, so on...
// example:
return (new ObjectMapper()).readValue(((TextMessage) m).getText(), MyCustomEvent.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
);
return factory;
}
我必须做的是:
DefaultJmsListenerContainerFactory
几个关键点:
如果您的 jmsListenerContainerFactory
方法也被称为 name
,则您不需要 Bean
注释处的 ErrorHandler
属性
请注意,在尝试转换/转换消息类型时,您还可以实现 ConnectionFactory
来处理异常!
SQSConnectionFactory
是带有 Amazon @Bean("connectionFactory")
public SQSConnectionFactory someOtherNome() {
return new SQSConnectionFactory(
new ProviderConfiguration(),
AmazonSQSClientBuilder.standard()
.withRegion(Regions.US_EAST_1)
.withCredentials(
new AWSStaticCredentialsProvider(
new BasicAWSCredentials(
"keyAccess",
"keySecret"
)
)
)
.build()
);
}
的 Spring 托管 bean,因为我想从 SQS 队列中消费。请正确提供您的等价物。我的是:
max-width
答案 2 :(得分:-1)
如果从byte []到String的转换有问题,请使用:
.convertBodyTo(String.class)
路线示例:
from(QUEUE_URL)
.routeId("consumer")
.convertBodyTo(String.class)
.log("${body}")
.to("mock:mockRoute");