Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/81.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 基于Spring Boot AMQP的JmsListener在TextMessage上失败_Java_Spring_Spring Boot_Activemq_Spring Jms - Fatal编程技术网

Java 基于Spring Boot AMQP的JmsListener在TextMessage上失败

Java 基于Spring Boot AMQP的JmsListener在TextMessage上失败,java,spring,spring-boot,activemq,spring-jms,Java,Spring,Spring Boot,Activemq,Spring Jms,我有一个Spring引导应用程序,它在从ActiveMQ代理检索TextMessage类型的JMS消息时遇到问题 如果消费者试图从代理检索消息,它无法自动将消息转换为TextMessage,而是将其视为ByteMessage。有一个JmsListener,它应该将队列中的消息作为TextMessage读取: ... @JmsListener(destination = "foo") public void jmsConsumer(TextMessage message) { ... JmsLi

我有一个Spring引导应用程序,它在从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]
我提取了一个小样本应用程序 要调试此问题,请执行以下操作:

现实生活中的producer是一个利用apachecamel的商业应用程序。因此,我几乎无法更改/定制制作人。我试图建立一个样本生产者,它显示了同样的行为

我是否可以调整消费者,使其将消息视为文本消息


此外:是否有任何方法可以直接在Spring中以编程方式从消息中检索其他AMQP属性?当然,我仍然可以将消息作为ByteMessage读取,并尝试解析掉属性。但我正在寻找一种更干净的方式,它由任何SpringAPI支持。Spring@Headers注释到目前为止没有帮助。

如果您在从字节[]转换为字符串时遇到问题,请使用:

.convertBodyTo(String.class)
路线示例:

from(QUEUE_URL)
                .routeId("consumer")
                .convertBodyTo(String.class)
                .log("${body}")
                .to("mock:mockRoute");

如果从字节[]到字符串的转换有问题,请使用:

.convertBodyTo(String.class)
路线示例:

from(QUEUE_URL)
                .routeId("consumer")
                .convertBodyTo(String.class)
                .log("${body}")
                .to("mock:mockRoute");

我曾经与问题所有者遇到过同样的问题,在我跟随@AndyWilkinson的评论,在activemq.xml中的transportConnector上添加transport.transformer选项后,问题得到了解决

<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600&amp;transport.transformer=jms"/>

我曾经与问题所有者遇到过同样的问题,在我跟随@AndyWilkinson的评论,在activemq.xml中的transportConnector上添加transport.transformer选项后,问题得到了解决

<transportConnector name="amqp" uri="amqp://0.0.0.0:5672?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600&amp;transport.transformer=jms"/>

我也有同样的错误,这是因为LazyResolutionMessage是从MessagingMessageConverter调用的,MessagingMessageConverter是MessageConverter的默认实现,实际上它没有,因为它是默认的:

return ((org.springframework.messaging.Message) payload).getPayload();
我已经完成了您想要的,最终我的消费者的工作方式是:

@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,则在Bean注释中不需要name属性

请注意,在尝试转换/强制转换消息类型时,您还可以实现一个ErrorHandler来处理异常

ConnectionFactory是Amazon的SQSConnectionFactory的Spring管理bean,因为我想从SQS队列中消费。请正确提供您的等价物。我的看法是:

@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()
    );
}

我也有同样的错误,这是因为LazyResolutionMessage是从MessagingMessageConverter调用的,MessagingMessageConverter是MessageConverter的默认实现,实际上它没有,因为它是默认的:

return ((org.springframework.messaging.Message) payload).getPayload();
我已经完成了您想要的,最终我的消费者的工作方式是:

@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,则在Bean注释中不需要name属性

请注意,在尝试转换/强制转换消息类型时,您还可以实现一个ErrorHandler来处理异常

ConnectionFactory是Amazon的SQSConnectionFactory的Spring管理bean,因为我想从SQS队列中消费。请正确提供您的等价物。我的看法是:

@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()
    );
}

您正在接收一个字节[],默认情况下,该字节被转换为ByteMessage。我建议要么只接收字节[],要么不接收带有特定JMS类型消息的消息。尝试字符串而不是文本消息。属性不是AFAIK头,因此不确定在此场景中它们映射到何处。如果您真的想使用文本消息,请编写自定义消息转换器,将字节[]转换为文本消息。谢谢,但问题不在于更改类型:我可以获取字节[]内容以及字符串。然而,在这种情况下,消息还包含一些额外的数据-所谓的属性查看提到的AMQP规范以了解详细信息。我对制作人最初发送的真实短信感兴趣。我不想自己从byte[]或String对象解析它。如果使用的传输与JMS兼容,我希望得到正确的JMS对象,而不是我自己需要处理的字节数。但是您没有使用JMS作为传输,这是使用AMQP时的主要问题。您基本上是通过AMQP隧道JMS,这与JMS不同。因此,消息被修改,文本消息将被转换为AMQP理解的内容,如果您查看Java API,它是一个字节[]。事实上,您并没有使用JMS,而是使用了您试图硬塞进JMS中的AMQP。为什么不直接使用AMQP呢?这真的是AMQP还是ActiveMQ AMQ?您可以接收消息,这是通过多个传输的spring消息抽象
包括JMS。它有一个有效负载和从JMS消息映射的头文件。为了避免人们浪费时间,这个问题也被看作是Spring引导问题。我怀疑这是一个代理配置问题,使用的是默认的本机映射,而不是所需的jms映射。有关各种映射选项的详细信息,请参阅。您将收到一个字节[],默认情况下该字节将转换为BytemMessage。我建议要么只接收字节[],要么不接收带有特定JMS类型消息的消息。尝试字符串而不是文本消息。属性不是AFAIK头,因此不确定在此场景中它们映射到何处。如果您真的想使用文本消息,请编写自定义消息转换器,将字节[]转换为文本消息。谢谢,但问题不在于更改类型:我可以获取字节[]内容以及字符串。然而,在这种情况下,消息还包含一些额外的数据-所谓的属性查看提到的AMQP规范以了解详细信息。我对制作人最初发送的真实短信感兴趣。我不想自己从byte[]或String对象解析它。如果使用的传输与JMS兼容,我希望得到正确的JMS对象,而不是我自己需要处理的字节数。但是您没有使用JMS作为传输,这是使用AMQP时的主要问题。您基本上是通过AMQP隧道JMS,这与JMS不同。因此,消息被修改,文本消息将被转换为AMQP理解的内容,如果您查看Java API,它是一个字节[]。事实上,您并没有使用JMS,而是使用了您试图硬塞进JMS中的AMQP。为什么不直接使用AMQP呢?这真的是AMQP还是ActiveMQ AMQ?您可以改为接收消息,这是通过包括JMS在内的多个传输的spring消息传递抽象。它有一个有效负载和从JMS消息映射的头文件。为了避免人们浪费时间,这个问题也被看作是Spring引导问题。我怀疑这是一个代理配置问题,使用的是默认的本机映射,而不是所需的jms映射。有关各种映射选项的详细信息,请参阅。我不在消费者端使用Camel,也不愿意在消费者端使用Camel。