Runtime resolution of destinations within Spring and JMS - spring

I am trying to dynamically resolve;
#JmsListener(destinations = "${some.key}")
Now I know we can resolve this using properties using such thing as PropertySourcesPlaceholderConfigurer.
I have found the following spring ticket, https://jira.spring.io/browse/SPR-12289. Which gives me some indication that this can be done without the use of properties files using a DestinationResolver. I am hoping that this is a resolver that is not property based but I am not 100% sure. Could some give me an example of this without using xml based spring.

See the documentation which shows how to configure a custom destination resolver in the listener container factory that generates the listener for the annotation:
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory =
new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setDestinationResolver(destinationResolver());
factory.setConcurrency("3-10");
return factory;
}

Related

Mutil Consumer means my server using multi thread?

I am working on a simple project in Java.
I want to know that if I write this code, SimpleRabbitListenerContainerFactory.setConcurrentConsumers(2) then my program use multi thread?
Simple answer is yes.
Assuming that you are configuring it inside #Bean annotated method that configures your listener.
According to Spring AMPQ docs it should look somehow similar to that:
#Configuration
#EnableRabbit
public class AppConfig {
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrentConsumers(3);
factory.setMaxConcurrentConsumers(10);
return factory;
}
}
As you can see, there is also max number of concurrent consumers set up which means that you will be using something between min=3 and max=10 consumers.
If you set only .setConcurrentConsumers(3) it will mean that you are using fixed number. No more, no less.
Recommended reads are:
Listener Concurrency
Annotation driven async config

How to configure request timeouts in Spring Cloud Gateway via code

I need to configure request timeouts in code for all routes. I know global timeouts can be configured via following properties in application.properties, but how can they be configured in code?
spring.cloud.gateway.httpclient.connect-timeout=1000
spring.cloud.gateway.httpclient.response-timeout=5s
I have looked at GatewayAutoConfiguration how timeouts are configured by default. HttpClientProperties holds both properties, however it cannot be overwritten.
#Bean
public HttpClientProperties httpClientProperties() {
return new HttpClientProperties();
}
Can this be done in code?
I solved my problem. I created my own bean and used annotation #Primary to be able to create a separate bean with the same type. GatewayAutoConfiguration now uses my bean instead of the default bean.
#Bean
#Primary
public HttpClientProperties overwrittenHttpClientProperties() {
HttpClientProperties p = new HttpClientProperties();
p.setConnectTimeout(3000);
p.setResponseTimeout(Duration.ofMillis(10000));
return p;
}

Spring Data MongoDB Configure Database bean

How to I configure the Mongo database bean? The bean is initialized by the MongoDB driver somehow with somethings pre-configured. I can't get the bean using mongoClient.getDatabase() because the database name either comes from the connection uri or just the standard property. And I can't define a new bean to update the old one like this public MongoDatabase mongoDatabase(MongoDatabase database) because that causes a ban definition cycle loop. What do I do?
Not entirely relevant to the question, but here's my intention:
What I trying to do is add custom Conventions introduced in MongoDB Driver version 3.6.
http://mongodb.github.io/mongo-java-driver/3.6/bson/pojos/
I know that spring data mongodb uses an older version, so I just replaced it with the newer one.
You need to take care of couple of things if you wish to configure MongoTemplate Or MongoDatabase yourself.
You need to disable spring boot's Mongo Auto Configuration. You can achieve this as show below.
#SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class})
You have to create MongoTemplate or MongoDatabase bean and register it with Spring application context(using #Bean).
#Bean
public MongoDbFactory mongoDbFactory() throws Exception {
ServerAddress server = new ServerAddress(host,port);
MongoClientOptions.Builder builder = MongoClientOptions.builder();
CodecRegistry codecRegistry = CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new XYZCodec(..)),
MongoClient.getDefaultCodecRegistry());
builder.codecRegistry(codecRegistry).build();
MongoClientOptions options = builder.build();
MongoClient mongoClient = new MongoClient(server,options);
return new SimpleMongoDbFactory(mongoClient, mongoDataBase);
}
Replace XYZCodec with the codec you want.

Spring Integration - kafka Outbound adapter not taking topic value exposed as spring bean

I have successfully integrated kafka outbound channle adapter with fixed topic name. Now, i want to make the topic name configurable and hence, want to expose it via application properties.
application.properties contain one of the following entry:
kafkaTopic:testNewTopic
My configuration class looks like below:
#Configuration
#Component
public class KafkaConfig {
#Value("${kafkaTopic}")
private String kafkaTopicName;
#Bean
public String getTopic(){
return kafkaTopicName;
}
#Bean
public KafkaTemplate<String, String> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
#Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");//this.brokerAddress);
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
// set more properties
return new DefaultKafkaProducerFactory<>(props);
}
}
and in my si-config.xml, i have used the following (ex: topic="getTopic") :
<int-kafka:outbound-channel-adapter
id="kafkaOutboundChannelAdapter" kafka-template="kafkaTemplate"
auto-startup="true" sync="true" channel="inputToKafka" topic="getTopic">
</int-kafka:outbound-channel-adapter>
However, the configuration is unable to pick up the topic name when exposed via bean. But it works fine when i hard code the value of the topic name.
Can someone please suggest what i am doing wrong here?
Does topic within kafka outbound channel accept the value referred as bean?
How do i externalize it as every application using my utility will supply different kafka topic names
The topic attribute is for string value.
However it supports property placeholder resolution:
topic="${kafkaTopic}"
and also SpEL evaluation for aforementioned bean:
topic="#{getTopic}"
Just because this is allowed by the XML parser configuration.
However you may pay attention that KafkaTemplate, which you inject into the <int-kafka:outbound-channel-adapter> has defaultTopic property. Therefore you won't need to worry about that XML.
And one more option available for you is Spring Integration Annotations configuration. Where you can define a #ServiceActivator for the KafkaProducerMessageHandler #Bean:
#ServiceActivator(inputChannel = "inputToKafka")
#Bean
KafkaProducerMessageHandler kafkaOutboundChannelAdapter() {
kafkaOutboundChannelAdapter adapter = new kafkaOutboundChannelAdapter( kafkaTemplate());
adapter.setSync(true);
adapter.setTopicExpression(new LiteralExpression(this.kafkaTopicName));
return adapter;
}

Is "jmsListenerContainerFactory" the default factory used by SimpleJmsListenerEndpoint

I am working with Spring JMS 4.1 to register messages listeners
In my xml configuration file, I have defined a bean named "jmsListenerContainerFactory":
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory"...p:concurrency="3-5".../>
First question : with Spring 4.x version, isn't it better to declare this factory this way : <jms:listener-container ... />
Second and main question : as stated in official doc (24.6.1) : by default, the infrastructure looks for a bean named jmsListenerContainerFactory as the source for the factory to use to create message listener containers. Is it also the case when programmatically registering endpoints this way:
.
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination("...");
endpoint.setMessageListener(message -> {...});
registrar.registerEndpoint(endpoint);
or do we have to set it explicitly this way for example : registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
Thanks
I don't understand your first question; in the first case, you are defining a DefaultJmsListenerContainerFactory (a factory that creates listener containers) whereas the XML is creating a listener container directly.
The factory is useful if you need to create lots of containers with similar properties.
For simple container configuration, when you are not using annotated listeners, it certainly might be simpler to use traditional XML, or #Bean definitions for the container.
For the second question, the default registrar is already populated with the container factory bean name when it is passed into the configureListeners method; you don't have to set it.
SimpleJmsListenerEndpoint always looks for named bean "jmsListenerContainerFactory". so even there is no explicitly setting:
registrar.setContainerFactoryBeanName("jmsListenerContainerFactory");
JmsListenerEndpoint still can find the JmsListenerContainerFactory if there exists bean "jmsListenerContainerFactory".
Mean that in case you need to apply JmsListenerContainerFactory with different bean name, then setting on method registrar.setContainerFactoryBeanName("") doesn't effect at all.
Code below for the work and not working cases:
// config factory class
#Bean(name = "customJMSListenerContainerFactory")
public DefaultJmsListenerContainerFactory
listenerQueueFactory() {
DefaultJmsListenerContainerFactory factory = new
DefaultJmsListenerContainerFactory();
//more configs...
return factory;
}
// on consumer class
#Configuration
#EnableJms
public class MyConsumer implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
registrar.registerEndpoint(endpoint);
// this setting will not work => Spring JMS bug
registrar.setContainerFactoryBeanName("customJMSListenerContainerFactory");
// but this setting works
registrar.setContainerFactory(listenerQueueFactory());
}
}
This is bug of spring jms.

Resources