0

I am using spring-boot and spring-rabbitmq package. I have some config class and class representing client. This client class has one listener annotated with @RabbitListener. Everything is fine with my config and client - it does work.

However, I need to know some details about internals of this client class. Is something special set in a context ? I would like to be able to inject one of three available datasources (three beans). I mean that I would like to be able based on the first symbol of message (thanks to custom converter I can assume that this symbol happens) that actual datasource bean will be injected/used.

Any ideas? Maybe spring-rabbitmq modifies in some way context ?

1 Answers1

0

The class with @RabbitListener must be declared as a bean in application context (either way - out of scope of the question). And it must be singleton - created only once object. Therefore all the injections must be done during its instantiation/initialization.

So, during that @RabbitListener method invocation you should just choose a proper DataSource, for example from the:

@Autowired
Map<String, DataSource> dataSources;

What you describe in your question isn't a behavior of Spring Dependency Injection Container.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • My problem is that I have configured DatasourceRoutuing object which should choose datasource. In case of requests with user context I can choose based on username (for example). However, in this case I should make a decision based on first symbol of message. However, message is not set in context, so I have no access to it in datasource router. I can;t understand from what you will be able to inject `Map dataSources;`, after all there must exists some bean with this name. –  Mar 29 '17 at 19:22
  • So it is more complicated than choosing proper datasource due to the fact `routingDataSource` is invoked. –  Mar 29 '17 at 19:28
  • 1
    Well, since your logic is based on the `ThreadLocal` just place it there before calling `routingDataSource` in the `@RabbitListener`. The AMQP and JDBC are absolutely separate protocols and nobody is going to place value to the `ThreadLocal` unconditionally. It is pretty expensive. – Artem Bilan Mar 29 '17 at 19:34
  • Does `ThreadLocal` mean for you that each Thread for handle message has own message, so it can invoke other datasource ? I know that I have to place my logic in listener. However, I can't control calling routingDataSource because it is invoked automatically. Do you mean modification of context and pass information ? –  Mar 29 '17 at 19:40
  • 1
    OK. I give up. What is `context` in your terms, please? – Artem Bilan Mar 29 '17 at 19:42
  • Thanks for patience :) Saying context I mean the same thing as in case of context where username and similar stuff is hold. Why ? Because in router I have access to context, no message. –  Mar 29 '17 at 19:44
  • 1
    Absolutely unclear what is that. Show the code how `routingDataSource` gets access to such a "context" – Artem Bilan Mar 29 '17 at 19:46
  • This one guy: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/context/request/RequestContextHolder.html, getAttributes. –  Mar 29 '17 at 19:48
  • I think that I should use http://stackoverflow.com/questions/32580188/dynamic-datasource-routing-datasource-router-not-initialized. Simply, I should create class representing `DataSourceContextHolder`. What do you think ? Pay your attention to `ThreadLocal` stuff, thats crucial as I think –  Mar 29 '17 at 19:50
  • 1
    Oh! `RequestContextHolder`... But that is about HTTP. We talk here about AMQP. I don't see parallel... You should be as clear as possible with your questions otherwise we all just waste our time here... – Artem Bilan Mar 29 '17 at 20:16
  • Ok, can you refer to second my comment ? –  Mar 29 '17 at 20:21
  • 1
    Why? You get a solution. – Artem Bilan Mar 29 '17 at 20:35
  • Wait, please. So I should use `DataSourceContextHolder` in your opinion ? (Of course I must take care of getting more info about it) –  Mar 29 '17 at 20:36
  • 1
    Yes, that is what I see as a solution for you: place a value to the `ThreadLocal` from your `@RabbitListener` and get access to that from the `routingDataSource`. At least that is how it parallels that your wishes with the `RequestContextHolder` – Artem Bilan Mar 29 '17 at 20:49
  • From what I investigated this approach is thread-safe. I mean threads from rabbitMQ client and from servicing REST API requests. Am I right ? –  Mar 31 '17 at 23:09
  • Yes, it is. Only don't forget to clean thread local in the end of operation – Artem Bilan Mar 31 '17 at 23:11
  • I read about it. However, from what I should know when it is a good moment for invoking clean ? (`contextHolder.remove()`); For example in rabbitmq threads, and rest api (spring) threads. I am aware of the fact that thread-safety without cleaning is ok, but consequently it is aimed at OutOfMemory, so incredibly dangerous error. –  Apr 01 '17 at 15:31