Adding custom headers using AmqpProxyFactoryBean + AmqpInvokerServiceExporter - spring-amqp

I have two separate services communicating using AmqpProxyFactoryBean (the "client") and AmqpInvokerServiceExporter (the "server"). Now, I'd like to include some custom headers on every request made through the AMQP proxy and be able to access them on the "server". Is there any easy way I can achieve this?

Since AmqpClientInterceptor uses AmqpTemplate to send and receive AMQP messages, you can provide for that RabbitTemplate any custom MessageConverter. And populate any additional headers from your toMessage() implementation.
However I'm not sure that you will be able to access to those custom header on the server side. We end up there just with RemoteInvocation.invoke().
So, seems for me you finally come up to the solution with an additional RPC param.
From other side that custom header may be useful for other AQMP routing scenarios when you can route that RPC message not only to the RPC queue.

Consider using Spring Integration AMQP Gateways instead of remoting over rabbitmq; that way you have complete control over the headers passed back and forth.
If you don't want to use Spring Integration, you can use the RabbitTemplate sendAndReceive methods on the client and either the receiveAndSend or a listener container on the server.
Again, this gives you full control over the headers.

Related

Spring Cloud DataFlow Rabbit Source: how to intercept and enrich messages in a Source

I have been successfully evaluating Spring Cloud DataFlow with a typically simple flow: source | processor | sink.
For deployment there will be multiple sources feeding into this pipeline which I can do using data flow labels. All well and good.
Each source is a different rabbitmq instance and because the processor needs to know where the message came from (because it has to call back to the source system to get further information), the strategy I'd thought of was to enrich each message with header details about the source system which is then transparently passed along to the processor.
Now, I'm well-versed in Spring, Spring Boot and Spring Integration but I cannot find out how to enrich each message in a dataflow source component.
The source component is bound to an org.springframework.cloud.stream.app.rabbit.source.RabbitSourceConfiguration. The source uses the default Source.OUTPUT channel. How do I get hold of each message in the source to enrich it?
My processor component uses some Spring Integration DSL to do some of what it needs to do but then this processor component has both an INPUT and OUTPUT channel by definition. Not so with the RabbitSourceConfiguration source.
So, can this be done?
I think you need a custom MessageListener on the MessageListenerContainer in RabbitSourceConfiguration.
In the RabbitSourceConfiguration you can set a custom ChannelAwareMessageListener (You can possibly extend from MessagingMessageListenerAdapter as well) on the MessageListenerContainer that does what you incline to do.
In the end what worked was subclassing org.springframework.cloud.stream.app.rabbit.source.RabbitSourceConfiguration to:
override public SimpleMessageListenerContainer container() so that I could insert a custom health check before calling super.container(). My business logic enriches each message (see next bullet) with details of where the message came from (note, this is the publisher of the messages and not the rabbit queue). There's a health check needed to validate the additional enriching information (which is provided via configuration) to ensure that messages aren't consumed from the queue and enriched with the wrong information. If the validation fails, the source component fails to start and hence no messages are consumed.
override the creation of the AmqpInboundChannelAdapter bean so that a custom subclass of DefaultAmqpHeaderMapper can be set on the adapter. This custom mapper adds the enriched headers in public Map toHeadersFromRequest(final MessageProperties source).
For me, the inability of stream/dataflow to intercept and modify messages in Source components is problematic. I really shouldn't have to fiddle around with the underlying message broker API in the ways I did. I should be able to do it with e.g. Spring Integration. Indeed I can register a global message interceptor but I cannot change the headers of the message.
This ability would go on my WIBNI (wouldn't it be nice if) list. Perhaps I'll raise a request for this.

How can I send common information will all the AFNetworking Request?

I am using AFNetworking for my client and server communication. I want to make a wrapper on top of AFNetworking so that I can set common header and extra information for all the HTTP requests. Basically all my HTTP request will go through one layer to AFNetworking. It will make my client server communication easier and I will be able to include any kind of data with all the http request at any point of time. What will be the best way to do it?
As example I want to send token, network status, user info etc.
More specifically:
I want to include some common info with all the request like network info, user info, token. Now its really difficult to change in each and every request. So I want to design in such a way that all the http call will go through one path and I can send anything with AFNetworking HTTP Request without touching all the file.
You should create one separate class that manage all the network related calls. You should subclass NSObject and make a class with different required methods that you need. Import your AFNetwotking in this class and use this class in whole project when needed to make network call!

Spring-WS payload based endpoint mapping unsuccessful in case of WS-Security Encryption

When using PayloadRootAnnotationMethodEndpointMapping together with WS-Security Encryption, I get this error: [EndpointNotFound] No endpoint mapping found for [SaajSoapMessage {http://www.w3.org/2001/04/xmlenc#}EncryptedData]
Spring WS wants first finding the right Endpoint, and only later decrypts the SOAP body. This is obviously not the right thing to do.
Please dont tell me that the SoapActionAnnotationMethodEndpointMapping is the solution. Is not there a better way? I would not like routing based on SOAP Action or WS-A Action.
Unfortunately, using the SoapActionAnnotationMethodEndpointMapping or AnnotationActionEndpointMapping are the only provided mappings you can use. In Spring-WS, the EndpointMapping is queried before any of the interceptors are invoked - including the security interceptor, and such you have to find a mapping that does not depend on the encrypted message payload.
That said, since EndpointMapping is an interface, you can also write your own, and include any kind of routing logic you can think of. Extending from AbstractAnnotationMethodEndpointMapping is a good start for this approach. When your mapping is done, just wire it up in the application context and it should be detected automatically by Spring-WS's MessageDispatcher.

Spring-AMQP RPC container

Exist a container on the spring-amqp that support a reply-to feature?
I want make RPCs like https://www.rabbitmq.com/tutorials/tutorial-six-java.html, but using spring-amqp.
Yes. Documentation here.
On the server side, the message listener container, when used with a message listener adapter will automatically handle the replies. You can also use the template's ...receiveAndReply methods on the server side.
EDIT
Note that we now have Spring Boot implementations of all 6 tutorials.

Capture outgoing HTTP request from Controller / Service

So I have the following scenario (it's a Grails 2.1 app):
I have a Controller that can be accessed via //localhost:8080/myController
This controller in turn executes a call to another URL opening a connection using new URL("https://my.other.url").openConnection()
I want to capture the request so I can log the information
I have a Filter present in my web.xml already which does the job well for controllers mapped in my app. But as soon as a request is fired to an external URL, I don't get anything.
I understand that my filter will only be invoked to URLs inside my app, and that depends on my filter mapping which is fine.
I'm struggling to see how a solution inside the app is actually viable. I'm thinking of using a mixed approach with the DevOps team to capture such outgoing calls from the container and then log them into a separate file.
I guess my questions are:
Is there a way to do it inside the app itself?
Is the approach I'm planning a sensible one?
Cheers!
Any reason why you don't want to use http-builder? There a Grails plugin for it, and it makes remote XML calls much easier than handling the plumbing yourself. At the bottom of the linked page they describe how you can enable request logging via log4j configuration.

Resources