简体   繁体   中英

AWS SqsListener deserialize Custom Object with Jackson

I can only listen to strings once I try to receive a custom object it throws the following error. It seems that I need to teach Spring to handle my custom object (B2BOrder)

org.springframework.messaging.converter.MessageConversionException: Cannot convert from [java.lang.String] to [br.com.b2breservas.api.model.B2BOrder] for GenericMessage [payload={"comments":"95d29059-8552-42fa-8fd9-a1d776416269"},

My SQSConfig

@Configuration
@EnableSqs
public class SqsConfig {

    private static final String DEFAULT_THREAD_NAME_PREFIX = ClassUtils.getShortName(SimpleMessageListenerContainer.class) + "-";

    @Bean
    public QueueMessagingTemplate myMessagingTemplate(AmazonSQSAsync amazonSqs, ResourceIdResolver resolver) {
        ObjectMapper mapper = new ObjectMapper()
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JodaModule())
                .registerModule(new JavaTimeModule());
        // configure the Jackson mapper as needed
        // maybe I need to do something here!

        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setSerializedPayloadClass(String.class);
        converter.setStrictContentTypeMatch(false);
        converter.setObjectMapper(mapper);

        return new QueueMessagingTemplate(amazonSqs, resolver, converter);
    }

    @Bean
    public ClientConfiguration sqsClientConfiguration() {
        return new ClientConfiguration()
                .withConnectionTimeout(30000)
                .withRequestTimeout(30000)
                .withClientExecutionTimeout(30000);
    }

    @Bean
    public ExecutorFactory sqsExecutorFactory() {
        return new ExecutorFactory() {
            @Override
            public ExecutorService newExecutor() {
                return new ThreadPoolExecutor(2, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
            }
        };
    }

    @Value("${b2b.b2b.accesstoken}")
    public String accesstoken;

    @Value("${b2b.b2b.secretkey}")
    public String secretkey;

    @Bean
    public AmazonSQSAsync amazonSqs(ClientConfiguration sqsClientConfiguration, ExecutorFactory sqsExecutorFactory) {
        BasicAWSCredentials credential = new BasicAWSCredentials(accesstoken, secretkey);
        return AmazonSQSAsyncClientBuilder.standard()
                .withClientConfiguration(sqsClientConfiguration)
                .withExecutorFactory(sqsExecutorFactory)
//                .withEndpointConfiguration(sqsEndpointConfiguration)
//                .withCredentials(credentialsProvider)
                .withCredentials(new AWSStaticCredentialsProvider(credential))
                .build();
    }


    @Bean
    public AsyncTaskExecutor queueContainerTaskEecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setThreadNamePrefix(DEFAULT_THREAD_NAME_PREFIX);
        threadPoolTaskExecutor.setCorePoolSize(2);
        threadPoolTaskExecutor.setMaxPoolSize(2);
        // No use of a thread pool executor queue to avoid retaining message to long in memory
        threadPoolTaskExecutor.setQueueCapacity(0);
        threadPoolTaskExecutor.afterPropertiesSet();
        return threadPoolTaskExecutor;
    }

    @Bean
    public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs, AsyncTaskExecutor queueContainerTaskEecutor) {
        SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
        factory.setAmazonSqs(amazonSqs);
        factory.setAutoStartup(true);
//        factory.setQueueMessageHandler();
        factory.setMaxNumberOfMessages(1);
        factory.setWaitTimeOut(20);
        factory.setTaskExecutor(queueContainerTaskEecutor);
        return factory;
    }

}

Listener

@Component
public class SqsHub {


    @SqsListener(
            "https://sqs.us-west-2.amazonaws.com/3234/32443-checkout.fifo"
    )
    public void listen(B2BOrder message) {
//  public void listen(String message) { THIS WORKS!!   
        System.out.println("!!!! received message {} {}" + message.toString());
    }
}

Sending

    ....
    @Autowired
    AmazonSQSAsync amazonSqs;

    @GetMapping("/yay")
    public String yay() {
        try {
            B2BOrder pendingOrder = new B2BOrder();
            pendingOrder.setComments(UUID.randomUUID().toString());
            String pendingOrderJson = objectMapper.writeValueAsString(pendingOrder);
            QueueMessagingTemplate queueMessagingTemplate = new QueueMessagingTemplate(amazonSqs);
            Map<String, Object> headers = new HashMap<>();
            headers.put(SqsMessageHeaders.SQS_GROUP_ID_HEADER, "my-application");
            headers.put(SqsMessageHeaders.SQS_DEDUPLICATION_ID_HEADER, UUID.randomUUID().toString());
            queueMessagingTemplate.convertAndSend("booking-checkout.fifo", pendingOrderJson, headers);
        } catch (final AmazonClientException | JsonProcessingException ase) {
            System.out.println("Error Message: " + ase.getMessage());
        }
        return "sdkjfn";
    }
    ....

Simple Custom Object


public class B2BOrder implements Serializable {

    @JsonProperty
    private String comments;

}

UPDATE

@Michiel answer took me here, but still got the same error.

    @Autowired
    public ObjectMapper objectMapper;


    @Bean
    public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs, AsyncTaskExecutor queueContainerTaskEecutor) {
        SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
        factory.setAmazonSqs(amazonSqs);
        factory.setAutoStartup(true);
        QueueMessageHandlerFactory queueMessageHandlerFactory = new QueueMessageHandlerFactory();
        queueMessageHandlerFactory.setAmazonSqs(amazonSqs);
        MappingJackson2MessageConverter jsonMessageConverter = new MappingJackson2MessageConverter();
        jsonMessageConverter.setObjectMapper(objectMapper);
        queueMessageHandlerFactory.setMessageConverters(Collections.singletonList(jsonMessageConverter));
        factory.setQueueMessageHandler(queueMessageHandlerFactory.createQueueMessageHandler());
//        factory.setMaxNumberOfMessages(1);
        factory.setWaitTimeOut(20);
        factory.setTaskExecutor(queueContainerTaskEecutor);
        return factory;
    }
    ```

Although you have registered a MessageConverter , it is only configured to be used in the outgoing request (using the QueueMessagingTemplate ). Your MessageListener does not have a MessageConverter configuration. Therefore, incoming messages can only be retrieved as a 'raw' type such as String.

In your snippet you have commented the following line of code:

//        factory.setQueueMessageHandler();

This is the location where you could set a QueueMessageHandler that itself has one or more MessageConverters attached.

[edit] Sure:

QueueMessageHandlerFactory handlerFactory = new QueueMessageHandlerFactory();
handlerFactory.setMessageConverters(yourJacksonConfig);
QueueMessageHandler messageHandler = handlerFactory.createQueueMessageHandler();
factory.setQueueMessageHandler(messageHandler);

This Spring documentation might be of help.

I'm using a SimpleMessageListenerContainer which is defined in a Bean as follows

@Bean
@Primary
fun simpleMessageListenerContainerFactory(amazonSQS: AmazonSQSAsync): SimpleMessageListenerContainerFactory =
    SimpleMessageListenerContainerFactory().apply {
        setAmazonSqs(amazonSQS)
        setMaxNumberOfMessages(10)
        setAutoStartup(false)
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM