spring-projects / spring-amqp

Spring AMQP - support for Spring programming model with AMQP, especially but not limited to RabbitMQ
https://spring.io/projects/spring-amqp
Apache License 2.0
806 stars 624 forks source link

Make it easier to expose message headers and also benefit from converters in MessageListener POJO [AMQP-180] #1741

Closed spring-operator closed 9 years ago

spring-operator commented 13 years ago

Dave Syer opened AMQP-180 and commented

Useful forum discussion: http://forum.springsource.org/showthread.php?111630-Get-message-info-in-handleMessage-class


Affects: 1.0.0.RC2

Issue Links:

spring-operator commented 13 years ago

Dave Syer commented

It seems like overriding handleResult() gives a lot of flexibility (but possibly not enough?), so I'm tempted to resolve as Won't Fix. Any other opinions?

spring-operator commented 13 years ago

Albert-Jan de Vries commented

I've got the following MessageHandler class:

public Result handleMessage(Input input) { // TODO how should this work? if (messageContainsReplyTo(messageProperties)) {

        return processInput(input);
    }
    else
    {
        LOG.error("Message doesn't contain a replyTo address, so nothing happend!");
        return null;
    }
}

And changed it this way:

public Result handleMessage(Input input, MessageProperties messageProperties)

By changing the MessageListenerAdapter onMessage methode:

line: 343

Object[] listenerArguments = new Object[] { buildListenerArguments(convertedMessage)[0], message.getMessageProperties() };

spring-operator commented 13 years ago

Dave Syer commented

I think we should plan to tackle this more comprehensively in 1.1, since it is not impossible with 1.0. Note to readers: the approach above is not thread safe (so stick to just overriding existing methods).

spring-operator commented 11 years ago

Daniel commented

I have a very similar issue in the sense that I was also wondering how to access the Message headers: http://forum.spring.io/forum/spring-projects/integration/amqp/725218-demultiplexing-messages-from-topic-exchange?_=1383385438137&_=1383387113713

Perhaps a simple resolution could be the following: ////// org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter:

This allows us to override the adapter (specifically, buildListenerArguments), take whatever we need from the message, and send it on as additional parameter(s).

N.B. Gary (Russell) suggested an even nicer solution along the way of supporting the following (but I don't know what code changes to propose to build that): public void handleMessage(@ConvertedPayload Foo foo, @Property("bar") String bar) { ... }

spring-operator commented 11 years ago

Gary Russell commented

Another (quick) user work-around is to create a custom message converter; subclass SimpleMessageConverter, in fromMessage(), call super.fromMessage() then populate a POJO with the converted result plus any additional message properties the listener might be interested in.

spring-operator commented 11 years ago

Daniel commented

Gary: you suggested this workaround on the forum as well, but as I replied it creates new problems for the serverside handler.

Suppose the client has 10 different POJOs it can send to the server, e.g. HelloRequest, ServiceRequest, etc.. The server has a handler POJO with handleMessage(HelloRequest request), handleMessage(ServiceRequest) etc.. If you do as you suggested, you will have to either: A) define a new POJO for each of the client requests that wraps the original client object and has the necessary additional field(s) (and then change the various handleMessage methods to accept the wrapped types B) define 1 generic wrapper that wraps all the possible client request

A) results in a bunch of duplication and B) breaks the serverside handler b/c you can then only have 1 handleMessage(Wrapper w) that needs to unwrap and reroute (using instanceof) to 10 different methods. Either option is far from elegant, right? (my 2 line suggestion specifically avoids these problems).

spring-operator commented 11 years ago

Gary Russell commented

I don't disagree with the basic premise; and I agree we can enhance the adapter; I am just trying to provide a quick work-around.

For B), you can use a simple Map<String, Object> as the argument, where the map has 'convertedMessage' and other keys.

But, I agree, you'd need to do some routing based on the type. If you bring the abstraction up a level (as I also suggested in the forum) using Spring integration would do exactly what you want.

amqp-inbound-adapter->channel->service-activator

Where the service activator is defined as a POJO that implements something like...


public interface MyService {

    public fooHandler(Foo foo, @Header("bar") String bar, @Header("baz") String baz);

    public barHandler(Bar bar, @Header("baz") String baz, @Header("qux") String qux);

...
}

And Spring Integration will take care of invoking the appropriate method, based on the payload type.

spring-operator commented 9 years ago

Artem Bilan commented

The is fixed with the POJO method-level @RabbitListener which is based on the Spring Messaging, where @Payload and @Header annotation come to the rescue.

Also see the linked issue #2001, how the same @RabbitListener can be used to route message to the appropriate method with @RabbitHandler for specific payload type.