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;
}
Related
I have a simple consumer in Spring working. I have a config class defined with a bunch of factories, etc. When I remove the config class, the consumer still works. I'm wondering the benefit of having the factory, ie:
#Bean
public ConcurrentKafkaListenerContainerFactory<String,
GenericRecord> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, GenericRecord> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setBatchListener(true);
return factory;
}
public ConsumerFactory<String, GenericRecord> consumerFactory() {
return new DefaultKafkaConsumerFactory<>(retrieveConsumerConfigs());
}
and now just passing vals in via application properties and calling it a day. I have explicit control over the config in the class-based approach, but was also thinking I could drop the class and have the vals be available through the spring env variables like spring.kafka.bootstrapservers, for example.
The container factory is required for #KafkaListener methods.
Spring Boot will auto-configure one (from application.properties/yml) if you don't provide your own bean. See KafkaAutoConfiguration.
Boot will also configure the consumer factory (if you don't).
An application, typically, does not need to declare any infrastructure beans.
EDIT
I prefer to never declare my own infrastructure beans. If I need some feature that is not exposed as a Boot property, or where I want to override some property for just one container, I simply add a customizer bean.
#Component
class Customizer {
public Customizer(ConcurrentKafkaListenerContainerFactory<?, ?> factory) {
factory.setContainerCustomizer(container -> {
if (container.getContainerProperties().getGroupId().equals("slowGroup")) {
container.getContainerProperties().setIdleBetweenPolls(60_000);
}
});
}
}
or
#Component
class Customizer {
Customizer(AbstractKafkaListenerContainerFactory<?, ?, ?> containerFactory,
ThreadPoolTaskExecutor exec) {
containerFactory.getContainerProperties().setConsumerTaskExecutor(exec);
}
}
etc.
the simple consumer in Spring works because spring-boot auto-configuration under the hoods creates an object of ConcurrentKafkaListenerContainerFactory and registers it with the spring container.
You can validate it by injecting the implementation of KafkaListenerContainerFactory as done below:
#RestController
public class EmployeeController {
private final KafkaListenerContainerFactory kafkaListenerContainerFactory;
#Autowired
public EmployeeController(KafkaListenerContainerFactory kafkaListenerContainerFactory) {
System.out.println(kafkaListenerContainerFactory instanceof ConcurrentKafkaListenerContainerFactory);
this.kafkaListenerContainerFactory = kafkaListenerContainerFactory;
}
}
But if you are not happy with spring boot's auto-generated bean, you can create your own bean and register it with the spring container by using #Bean annotation
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 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";
I am trying to convert a XML based configuration to JAVA based configuration. Can someone please let me know the java annotation based configuration for the following
<jms:outbound-channel-adapter channel="requestChannel"
connection-factory="testConnectionFactory"
destination-name="${jms.queueName}"
message-converter="messageConverter"/>
I tried having a look at this Reference doc. But i am not able to understand how do I map the above xml to the annotation config.
#ServiceActivator(inputChannel="requestChannel")
#Bean
public MessageHandler outbound(JmsTemplate jmsTemplate) {
JmsSendingMessageHandler handler = new JmsSendingMessageHandler(jmsTemplate);
handler.setDestinationName(...);
...
return handler;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory jmsConnectionFactory) {
...
template.setMessageConverter(converter());
return template;
}
Then add the connection factory and converter beans.
EDIT
Also pay attention to Spring Integration Java DSL project, which provides the org.springframework.integration.dsl.jms.Jms Factory on the matter. You can find its usage in the JmsTests: https://github.com/spring-projects/spring-integration-java-dsl/blob/master/src/test/java/org/springframework/integration/dsl/test/jms/JmsTests.java
In my Spring Boot application I'm listening message queue. When a message appears I need to execute it synchronously(one by one) in some task-executor.
I'm using Amazon SQS, this is my config:
/**
* AWS Credentials Bean
*/
#Bean
public AWSCredentials awsCredentials() {
return new BasicAWSCredentials(accessKey, secretAccessKey);
}
/**
* AWS Client Bean
*/
#Bean
public AmazonSQS amazonSQSAsyncClient() {
AmazonSQS sqsClient = new AmazonSQSClient(awsCredentials());
sqsClient.setRegion(Region.getRegion(Regions.US_EAST_1));
return sqsClient;
}
/**
* AWS Connection Factory
*/
#Bean
public SQSConnectionFactory connectionFactory() {
SQSConnectionFactory.Builder factoryBuilder = new SQSConnectionFactory.Builder(
Region.getRegion(Regions.US_EAST_1));
factoryBuilder.setAwsCredentialsProvider(new AWSCredentialsProvider() {
#Override
public AWSCredentials getCredentials() {
return awsCredentials();
}
#Override
public void refresh() {
}
});
return factoryBuilder.build();
}
/**
* Registering QueueListener for queueName
*/
#Bean
public DefaultMessageListenerContainer defaultMessageListenerContainer() {
DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer();
messageListenerContainer.setConnectionFactory(connectionFactory());
messageListenerContainer.setMessageListener(new MessageListenerAdapter(new QueueListener()));
messageListenerContainer.setDestinationName(queueName);
return messageListenerContainer;
}
Also I need to have possibility to check the status of this task-executor, for example - number of scheduled tasks.
Is it a good idea to use Spring SyncTaskExecutor for this purpose ? If so, could you please show an example how it can be used with Spring Boot.
EDIT:
After revealing your messaging technology and Spring configuration for it, simplest way for you is to configure SyncTaskExecutor (or Executors.newFixedThreadPool(1) would do the job also) as executor for your DefaultMessageListenerContainer. Use this method.
You can register Task executor as separate bean (via #Bean annotation) and autowire it to defaultMessageListenerContainer() method (just add TaskExectuor as parameter).
Below answer is relevant for JMS messaging. It was created before AWS SQS usage was revealed in question:
You didn't mention which messaging technology are you using, therefore I assume JMS.
If synchronous execution is requirement, I believe you can't use native JMS listeners (need to avoid SimpleJmsListenerContainerFactory or SimleMessageListenerContainer).
Instead I would suggest to use #JmsListener annotation with DefaultJmsListenerContainerFactory (this uses long polling instead of native JMS listeners) and configure SyncTaskExecutor (or Executors.newFixedThreadPool(1) would do the job also) as executor for mentioned container factory: DefaultJmsListenerContainerFactory.setTaskExecutor().
This is simple Spring Boot JMS example with DefaultJmsListenerContainerFactory configured. You just need to plug in suitable task executor.