Spring Boot application run method periodically - java

I am playing with a simple Spring Boot application and RabbitMQ.
However I cannot figure out how to run a method periodically.
Here is my Application class
#SpringBootApplication
public class SampleApp {
#Autowired
Sender sender;
public static void main(String[] args) {
SpringApplication.run(SampleApp.class, args);
}
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
sender.sendMessage();
}
}
And the sendMessage method is defined as below
#Scheduled(fixedRate = 3000L)
public void sendMessage() {
log.info("Sending message...");
rabbitTemplate.convertAndSend("my-exchange", "my-routing-key", "TEST MESSAGE");
}
However this method is called only once, I can see only a single line in the console.
What I missed in my code?
Thanks.

Looks like you are missing #EnableScheduling:
#EnableScheduling
#SpringBootApplication
public class SampleApp {
...
}
Quoting the documentation:
Enables Spring's scheduled task execution capability, similar to functionality found in Spring's <task:*> XML namespace. To be used on #Configuration classes as follows:
#Configuration
#EnableScheduling
public class AppConfig {
// various #Bean definitions
}
This enables detection of #Scheduled annotations on any Spring-managed bean in the container.

I am usually using the Spring ThreadPoolTaskScheduler. You define it, as Bean for example then you wrap your method into a Runnable and you call it at intervals defined by a CronTrigger. The result can be retrieved using a ScheduledFuture
Check https://www.baeldung.com/spring-task-scheduler for a complete beginner tutorial.

Here's an alternative way if you don't want to rely on Spring's scheduler.
It's using rxjava2, here's an example:
#Component
public class MessagePublisher {
Sender sender;
Disposable d;
#Autowired
public Config(Sender sender) {
this.sender = sender;
this.d = doSomethingAfterStartup();
}
public Disposable doSomethingAfterStartup() {
return Observable.interval(3000, TimeUnit.MILLISECONDS).subscribe(tick -> {
sender.sendMessage();
});
}
}

Related

#Async not working for method having return type void

I wanted to make a method writing to DB as async using #Async annotation.
I marked the class with the annotation #EnableAsync:
#EnableAsync
public class FacialRecognitionAsyncImpl {
#Async
public void populateDataInPushQueue(int mediaId, int studentId) {
//myCode
}
}
while calling the populateDataInPushQueue method, the write operation should be executed in another thread and the flow should continue from the class I am calling this method. But this is not happening and the program execution is waiting for this method to complete.
The #Async annotation has few limitations - check whether those are respected:
it must be applied to public methods only
it cannot be called from the same class as defined
the return type must be either void or Future
The following can be found at the documentation of #EnableAsync:
Please note that proxy mode allows for the interception of calls through the proxy only; local calls within the same class cannot get intercepted that way.
Another fact is that the class annotated with #EnableAsync must be a #Configuration as well. Therefore start with an empty class:
#EnableAsync
#Configuration
public class AsyncConfiguration { }
In my opinion, you are missing #Configuration annotation and your async service is not component scanned. Here is an example code fragment that should do the trick:
#Configuration
#EnableAsync //should be placed together.
public class FacialRecognitionAsyncService {
#Async
public void populateDataInPushQueue(int mediaId, int studentId) {
//myCode
}
}
#Configuration
#EnableAsync
public class FacialServiceConfig {
// this will make your service to be scanned.
#Bean
public FacialRecognitionAsyncService createFacialRecognitionService() {
return new FacialRecognitionAsyncService();
}
}
Now the service bean that is invoking the async method. Notice that it has been dependency injected. This way spring AOP proxies will be invoked on each invocation fo the facialService. Spring uses AOP in the back scenes in order to implement #Async.
#Service
public class MyBusinessService {
#Autowire
FacialRecognitionAsyncService facialService;
public myBusinessMethod() {
facialService.populateDataInPushQueue()
}
Notice that FacialService is injected in MyService through dependency injection.

Sharing an instance of a class across a spring boot application

I have a particular class used to interface with a service that requires initialization. In the application lifecycle, the only place this makes sense is in the start of the application because the rest of the spring application cannot run without it. I had the idea to do this:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
try {
MyRequiredService mrs = new MyRequiredService();
mrs.connect(); // This will throw if it fails
run(MyApplication.class, args);
} catch(MyException e) {
System.out.println("Failed to connect to MyRequiredService!");
}
}
}
This will launch the service and attempt to connect but I have one big problem. How do I pass this class around the application? I need it's functions in the service endpoints I am writing.
I didn't see anything obvious and searching "passing class instance in spring boot application" turns up a bunch of unrelated topics.
Is there a smart, clean way to do this in spring boot? I apologize for a contrived example. The names of the service are unique enough I didn't want to violate any agreements.
You can make Spring do this for you. First, you need to annotate your class with #Service, so Spring will pick it up when scanning for classes.
Then, define an init() method and annotate it with #PostConstruct. Spring will instantiate your MyRequiredService class and call init()
#Service
public class MyRequiredService {
#PostConstruct
public void init() {
connect();
}
public void connect() {
// ...
}
}
You could call connect() from the constructor, but I don't like to define objects that may throw exceptions out of the constructor.
And then, you can use MyRequiredService in some other class by injecting it via the #Autowired annotation:
#Component
public class MyOtherClass {
private final MyRequiredService service;
public MyOtherClass(final MyRequiredService service) {
this.service = service;
}
// Other methods here.
}
This has the same overall effect as what you're trying to do above. If MyRequiredService fails, the application will not start up.
Make it a bean. Then it will be in the ApplicationContext which then you can pass to your desired other classes through the constructor
#Configuration
public class ApplicationConfiguration
{
#Bean
public MyRequiredService myRequiredService()
{
MyRequiredService mrs = new MyRequiredService();
try {
mrs.connect(); // This will throw if it fails
return mrs;
} catch(MyException e) {
log.error("Failed to connect to MyRequiredService!");
throw new IllegalStateException("MyRequiredService failed connection. Stopping startup");
}
}
#Bean
public SomeOtherService someOtherService(MyRequiredService mrs) {
return new SomeOtherService(mrs);
}
}
IMHO Instead of catching the error and logging it. I would throw it and stop the application from starting, but to keep with your example I added the throw IllegalStateException after the log.
Doing it this way Spring will create your MyRequiredService bean in the ApplicationContext then you can see I added as a parameter needed by the bean below that. Spring will grab that bean out of the ApplicationContext and supply it to the bean. If Spring doesn't find the bean in the ApplicationContext it will throw an error and stop the application from startup.
a class implements BeanFactoryPostProcessor which is init before normal bean
#Configuration
public class MyRequiredService implements BeanFactoryPostProcessor,
PriorityOrdered, InitializingBean {
#Override
public int getOrder() {
return Integer.MIN_VALUE;
}
public void connect() {
// ...
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
#Override
public void afterPropertiesSet() throws Exception {
connect();
}
}

SpringBoot - Register a bean before #Component's get scanned

I have a component Login that depends on ValidatorService. ValidatorService is being injected/autowired in the Login constructor. ValidationServiceImpl is provided by an external API, so I can't just annotate it as #Service.
#Component
class Login {
#Autowire
public Login (ValidatorService validator) {
}
}
#SpringBootApplication
public class Starter {
public static void main(String[] args)
{
SpringApplication.run(Starter.class, args);
}
}
I'm looking for a way to register ValidatorService as a bean before #Components get scanned. Is there a way to get ApplicationContext instance before starting the application?
SpringBoot 2.0.4.RELEASE
UPDATE
I need to pass a validationId that I'll get from main(args) to this external API.
public static void main(String[] args) {
String validationId = args[0];
ValidatorService service = ExternalValidationAPI.getValidationServiceImp(validationId);
}
You should be able to declare it as a bean as such in one of your configuration classes:
#Bean
public ValidatorService validatorService(){
return new ValidatorServiceImpl();
}
This will then autowire in the ValidatorService implementation class at the point it is needed. This method needs to go in an #Configuration class (your Starter class is one).
There's a good example of how to do this here.
I believe you can solve your problem with the help of the #Configurable annotation.
Annotate your Login class with #Configurable instead of #Componenet, and when the ValidatorService object becomes available, you can initiate the Login object with it.
You need to define a ValidationService bean :
#Configuration
public class ValidationServiceConfig {
#Bean
public ValidationService validationService(#Value("${validationId}") String validationId) {
return new ValidationServiceImpl(validationId);
}
}
and run the program this way : java -jar program.jar --validationId=xxx
I solved my problem creating a Configuration class and declaring a Bean to handle the instantiation of the external service that will be injected later (as some people have suggested). In order to retrieve the program arguments I autowired DefaultApplicationArguments to retrieve program arguments with getSourceArgs():
#Configuration
public class ValidatorConfig {
#Autowired
DefaultApplicationArguments applicationArguments;
#Bean
public ValidatorService validatorService()
{
String validationId = applicationArguments.getSourceArgs()[0];
return ExternalValidationAPI.getValidationServiceImp(validationId);
}

Adding scheduler for #Scheduled annotation in spring without using xml annotations

I have several methods with the annotation #Scheduled. For each annotation or a group of them I want a different scheduler to be used. For example:
Group A has 3 methods with #Scheduled annotation which need to use Scheduler X.
Group B has 5 methods with #Scheduled annotation which need to use Scheduler Y.
From what I have read in Does spring #Scheduled annotated methods runs on different threads?, if the scheduler is not specified then only one of those methods will run at a time.
I know how this connection can be done using xml-based annotation. But is there a way that this can be done using Java-based annotation only?
It can be done using java config. But not using an annotation attributes.
You could have a look at the Spring API doc for some extended example.
For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler());
}
#Bean(destroyMethod="shutdown")
public Executor taskScheduler() {
return Executors.newScheduledThreadPool(42);
}
}
#Scheduled group is not yet supported. See this open issue.
If you want use more than one scheduler you have to create and configure them programmatically. For example:
#Configuration
#EnableScheduling
public class AppConfig implements SchedulingConfigurer {
[...]
#Bean(destroyMethod="shutdown", name = "taskSchedulerA")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
#Bean(destroyMethod="shutdown", name = "taskSchedulerB")
public Executor taskSchedulerA() {
return Executors.newScheduledThreadPool(42);
}
}
#Service
public class MyService {
#Autowired #Qualifier("taskSchedulerA")
private Executor taskSchedulerA;
#Autowired #Qualifier("taskSchedulerB")
private Executor taskSchedulerB;
#PostConstruct
public void schedule(){
Executors.newScheduledThreadPool(42).schedule(new Runnable() {
#Override
public void run() {
functionOfGroupA();
}
} , ..);
}
}

Spring Boot conditional compilation/configuration

I have a Spring project that runs locally during the development/debugging phase,
while on production it will be loaded on a PaaS.
My problem is that there are certain instruction that must be executed depending on the platform!
Currently I check a boolean (using #ConfigurationProperties) that I read from the application.properties, but I'm wondering if there's a smarter way because I have also to change the boolean when I push in production.
You should use Spring profiles and implement your check a little bit mor object oriented:
I assume your code looks something like this, and Logic is a spring managed bean:
#Component
public class Logic {
public void doIt() {
doMoreLogic();
if (yourProperty == true) {
your();
certain();
instructions();
}
doWhateverYouWant();
}
}
If you extract the certain logic to a class, then you can do it more the object oriented way:
public interface PlatformDependentLogic {
void platformInstructions();
}
#Component #Profile("dev")
public class DevLogic implements PlatformDependentLogic {
public void platformInstructions() {
your();
certain();
instructions();
}
}
#Component #Profile("!dev")
public class NoopLogic implements PlatformDependentLogic {
public void platformInstructions() {
// noop
}
}
Now you can reference the logic by doing this in your Logic bean:
#Component
public class Logic {
private #Autowired PlatformDependentLogic platformLogic;
public void doIt() {
doMoreLogic();
platformLogic.platformInstructions();
doWhateverYouWant();
}
}
Of course you can utilize the spring boot specific #ConditionalOnProperty instead of the #Profile annotation like this:
#ConditionalOnProperty(name="your.property", hasValue="dev")
To get a better understanding of this annotation and how it workds you should read the official documentation of #ConditionalOnProperty
May I suggest using Gradle product flavors for your local/Paas environments, something similar to this:
https://code.tutsplus.com/tutorials/using-gradle-build-variants--cms-25005

Categories