I'm using Camel to consume and produce messages in RabbitMQ. Also, I'm working with Spring boot so I have created a ConnectionFactory bean with all the configuration I want.
That works great but I have to declare the name of the bean in every Endpoint string I create.
Is there a way to setup camel to use this specific bean by default?
According to these source lines I don't think it is achievable.
If you name your bean rabbitConnectionFactory then you don't have to specify this in every endpoint.
For example:
#Bean
public ConnectionFactory rabbitConnectionFactory() {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5672);
factory.setUsername("guest");
factory.setPassword("guest");
return factory;
}
And after that your rabbitmq URI is as simple as: public static final String RABBIT_URI = "rabbitmq:%s?queue=%s&routingKey=%s&autoDelete=false";
Related
The spring documentation says:
Destinations, as ConnectionFactory instances, are JMS administered
objects that you can store and retrieved in JNDI. When configuring a
Spring application context, you can use the JNDI JndiObjectFactoryBean
factory class or to perform dependency injection on
your object’s references to JMS destinations.
However, this strategy
is often cumbersome if there are a large number of destinations in the
application or if there are advanced destination management features
unique to the JMS provider.
The question is:
How to proceed when I have a large number of destinations in my application?
Using the strategy mentioned above I have to define:
JndiTemplate
JndiDestinationResolver
JndiObjectFactoryBean
CachingConnectionFactory
JmsTemplate
For EACH destination.
So If I have 20 queues, I'll have to define 100 such beans...
The comment in the Spring documentation makes a note on 'using JNDI for destination endpoints' versus 'not using JNDI for destination endpoints'. So in your case - are your destinations stored in JNDI ? If you don't have to use that, forget about it. Only load your ConnectionFactory (one object) from JNDI or simply create it from scratch.
And then you don't have to assign one Spring bean to each destination. You could have just one Java 'consumer bean' which then uses JmsTemplate. I guess your connection factory is the same, so that's only one new JmsTemplate(connectionFactory). Then do createSession/createConsumer, etc. as needed.
You can just use a single JmsTemplate, CachingConnectionFactory and JndiDestinationResolver...
The whole point of using a DestinationResolver is to lazily resolve the destinations for you. Use the specific send or [convertAndSend][5]. ThedestininationNamewill be passed on to theDestinationResolver` to get the destination.
The only drawback is that you need to use the jndi-name as the destinationName.
#Bean
public JndiDestinationResolver jndiDestinationResolver() {
return new JndiDestinationResolver();
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDestinationResolver(jndiDestinationResolver());
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
With this you can use the following to dynamically resolve the destination from JNDI.
jmsTemplate.send("jms/queue1", "This is a message");
jmsTemplate.send("jms/queue3", "This is another message");
In question How to set the consumer-tag value in spring-amqp it is being asked how to change the consumer tag when using Spring Amqp and the answer suggests to provide an implementation of ConsumerTagStrategy.
I'm using Spring Boot 2.0.5 and I'm trying to figure out if I can do the same customization, though I can't find any configuration property about that nor providing a bean of type ConsumerTagStrategy seems to work.
How should I go about this?
Override boot's container factory bean declaration and add it there.
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setConsumerTagStrategy(q -> "myConsumerFor." + q);
return factory;
}
I'm making an application that uses Spring Boot, MySQL and Redis on the Back End and Angular on the Front End. I want to deploy it to Heroku so I could use my front end with it but I just can't seem to configure the remote URL for Redis. I have the Redis To Go Add-on on Heroku for this with the remote URL ready. I just don't know how to configure the environment variables to access that instead of localhost and the default port 6379.
I added the following lines to my application.properties but it still did not work :
spring.redis.url= #URL
spring.redis.host= #HOSTNAME
spring.redis.password= #PASSWORD
spring.redis.port = #PORT
I keep getting the following error :
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis on localhost:6379; nested exception is com.lambdaworks.redis.RedisConnectionException: Unable to connect to localhost/127.0.0.1:6379
Is there any way I could configure this to access the remote url instead of localhost?
I'm using Lettuce and not Jedis and my HttpSessionConfig file is :
#EnableRedisHttpSession
public class HttpSessionConfig {
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
I was having a similar issue, this is how I solved it:
If you define the bean this way:
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
You are not allowing Spring to take the Redis values from the application.properties.
To make it work, please do the following:
Remove this bean definition:
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
Define the RedisTemplate bean this way:
#Bean
public RedisTemplate<String, Object> deliveryRedisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
Define the following values in application.properties:
spring.redis.database=<replace-me>
spring.redis.host=<replace-me>
spring.redis.port=<replace-me>
spring.redis.timeout=<replace-me>
I was frustrated with the exact same issue you are facing and the spring boot documentation or examples do nothing to address what we are facing. The reason why your config entries are not being used is because you are creating a new instance of LettuceConnectionFactory with a parameter-less constructor. Digging into the source/byte code you can see that constructor is completely ignoring the spring.redis.host and spring.redis.port values and hardcoding them to localhost:6379.
What you should be doing is either:
Use the LettuceConnectionFactory(hostname, port) constructor.
Don't define the connection factory. Get rid of the entire #Bean entry for connectionFactory() and Spring will automatically wire everything up and use your config entries.
Also as a side note; to get this working with AWS Elasticache (locally via VPN) I had to add to that config class:
#Bean
public static ConfigureRedisAction configureRedisAction() {
return ConfigureRedisAction.NO_OP;
}
I found the Heroku documentation for connecting to their Redis add-on (https://devcenter.heroku.com/articles/heroku-redis#connecting-in-java) contains an example using Jedis and therefore needed a little adaption. The content of the REDIS_URL added by Heroku to the environment of your running app resembles
redis://h:credentials#host:port
I parsed this using RedisURI.create and then set the host, port and password parameters of RedisStandaloneConfiguration
val uri = RedisURI.create(configuration.redisUrl)
val config = RedisStandaloneConfiguration(uri.host, uri.port)
config.setPassword(uri.password)
return LettuceConnectionFactory(config)
The code above is Kotlin rather than Java but perhaps it will help? You can find the full code at https://github.com/DangerousDarlow/springboot-redis
You need to choose between depending on autoconfiguration or defining your custom connection template.
First way is to remove HttpSessionConfig and then your redis properties from application.properties file will be applied. And as you have spring-redis-data-session dependency on your classpath your lettuce connection will be created implicitly.
Second solution is defining your connection properties as host, port, password inside LettuceConnectionFactory.
However it is recommended to use autoconfiguration.
Set the configuration in application.properties and RedisStandaloneConfiguration.
#Configuration
#PropertySource("application.properties")
#EnableRedisHttpSession
public class SpringRedisConfig {
#Autowired
private Environment env;
#Bean
public LettuceConnectionFactory connectionFactory() {
RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration();
redisConf.setHostName(env.getProperty("spring.redis.host"));
redisConf.setPort(Integer.parseInt(env.getProperty("spring.redis.port")));
redisConf.setPassword(RedisPassword.of(env.getProperty("spring.redis.password")));
return new LettuceConnectionFactory(redisConf);
}
}
How would I configure Spring Cloud AWS XML config with annotations?
I am especially interested in changing default taskExecutor.
I found that there is SimpleMessageListenerContainerFactory used to configure for AWS messaging via Java .
So changing default taskExecutor is just matter for overriding this default container factory bean. Something like this:
#Bean
public SimpleMessageListenerContainerFactory simpleMessageListenerContainerFactory(AmazonSQSAsync amazonSqs) {
SimpleMessageListenerContainerFactory factory = new SimpleMessageListenerContainerFactory();
factory.setAmazonSqs(amazonSqs);
Executor executor = Executors.newFixedThreadPool(1);
ConcurrentTaskExecutor taskExecutor = new ConcurrentTaskExecutor(executor);
factory.setTaskExecutor(taskExecutor);
return factory;
}
I'm using the new Spring Boot 2.0M7 and I am trying to define some conditional logic to load different beans depending on the active profile.
I have this (working) bean configuration. That defines an sqs based connection factory for all environments except test and activemq for test.
#Configuration
#EnableJms
public class QueueConfig {
private static Logger LOG = LoggerFactory.getLogger(QueueConfig.class);
#Profile({"!test"})
#Bean
public ConnectionFactory sqsConnectionFactory() {
LOG.info("using sqs");
return new SQSConnectionFactory(new ProviderConfiguration(), AmazonSQSClientBuilder.standard()
.withRegion(Regions.EU_WEST_1)
.withCredentials(new DefaultAWSCredentialsProviderChain())
);
}
#Profile({"test"})
#Bean
public ConnectionFactory activeMqConnectionFactory() {
LOG.info("using activemq");
return new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
}
#Bean
public JmsTemplate defaultJmsTemplate(ConnectionFactory connectionFactory) {
return new JmsTemplate(connectionFactory);
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setConcurrency("3-10");
return factory;
}
}
This works with a single profile. I can see in my test (annotated with #ActiveProfiles("test") that the test profile is active and the correct bean loads (log message).
However, changing #Profile({"!test"}) to #Profile({"!test","!dev}) on the sqsConnectionFactory and #Profile({"test"}) to #Profile({"test","dev}) on the activeMqConnectionFactory breaks things.
I get an unresolved bean exception because it now has two instances instead of 1. I can see in my logs that the test profile is still active and despite this it happily loads both the sqs and activemq implementations even though it shouldn't.
Did something change with the logic for #Profile in spring boot 2.x? If so,
how can I define that the activemq implementation is used when dev or test profile is active and sqs otherwise?
If not, what am I doing wrong here?
There are many ways you can approach that problem. Here is one:
Create another profile sqs. Use it to enable or disable beans.
#Profile({"sqs"})
#Bean
public ConnectionFactory sqsConnectionFactory() { ... }
#Profile({"!sqs"})
#Bean
public ConnectionFactory activeMqConnectionFactory() { ... }
Then declare your profiles in configuration files as using this one, or not:
---
spring.profiles: dev
...
---
spring.profiles: test
...
---
spring.profiles: prod
spring.profiles.include:
- sqs
#Profile({"!test","!dev}) - here you are missing one " after !dev, however, if it is just a typo in here post, try following (that works for me)
#Profile(value={"!test", "!dev"})
and btw - I personally prefer to have one configuration #Bean per class, in that case you are basically annotating your whole class with #Profile, for me it is much readable