Call a method after Spring Boot app starts - java

I have a Java Spring Boot application which has a Scheduler which calls a async task from a Service The task takes a few minutes (usually 3-5mins) to complete.
The same async method from the Service can also be called trough a UI Application, by calling the API from the Spring Boot Controller.
Code:
Scheduler
#Component
public class ScheduledTasks {
#Autowired
private MyService myService;
#Scheduled(cron = "0 0 */1 * * ?")
public void scheduleAsyncTask() {
myService.doAsync();
}
}
Service
#Service
public class MyService {
#Async("threadTaskExecutor")
public void doAsync() {
//Do Stuff
}
}
Controller
#CrossOrigin
#RestController
#RequestMapping("/mysrv")
public class MyController {
#Autowired
private MyService myService;
#CrossOrigin
#RequestMapping(value = "/", method = RequestMethod.POST)
public void postAsyncUpdate() {
myService.doAsync();
}
}
The scheduler runs the async task every hour, but a user can also run it manually from the UI.
But, I do not want the async method to run again if it is already in the middle of execution.
In order to do that, I have created a table in DB which contains a flag which goes on when the method is running and then it is turned off after the method completes.
Something like this in my service class:
#Autowired
private MyDbRepo myDbRepo;
#Async("threadTaskExecutor")
public void doAsync() {
if (!myDbRepo.isRunning()) {
myDbRepo.setIsRunning(true);
//Do Stuff
myDbRepo.setIsRunning(false);
} else {
LOG.info("The Async task is already running");
}
}
Now, the problem is that the flag sometimes gets stuck due to various reasons (app restarting, some other application error etc.)
So, I want to reset the flag in DB each time the spring boot application is deployed and whenever is restarts.
How can I do that? Is there some way to run a method just after the Spring Boot Application starts, from where I can call a method from my Repo to un set the flags in the database?

Check for the #PostConstruct for e.g here https://www.baeldung.com/running-setup-logic-on-startup-in-spring

If you want to do some stuff after whole application booted and ready use below sample from
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
} // class
If it is enough to hook after a single bean creating use #PostConstruct as suggested by #loan M

In your particular case , you need to reset the database after application deployment, so the best way for you to do that is to use the Spring CommandLineRunner .
Spring boot provides a CommanLineRunner interface with a callback run() method which can be invoked at application startup
after the Spring application context is instantiated.
CommandLineRunner beans can be defined within the same application context and can be ordered using the #Ordered interface or #Order
annotation.
#Component
public class CommandLineAppStartupRunnerSample implements CommandLineRunner {
private static final Logger LOG =
LoggerFactory.getLogger(CommandLineAppStartupRunnerSample .class);
#Override
public void run(String...args) throws Exception {
LOG.info("Run method is executed");
//Do something here
}
}
Answer referred from site : https://www.baeldung.com/running-setup-logic-on-startup-in-spring

Related

Best way to call java interface methods after springboot application start up? [duplicate]

I want to run code after my spring-boot app starts to monitor a directory for changes.
I have tried running a new thread but the #Autowired services have not been set at that point.
I have been able to find ApplicationPreparedEvent, which fires before the #Autowired annotations are set. Ideally I would like the event to fire once the application is ready to process http requests.
Is there a better event to use, or a better way of running code after the application is live in spring-boot?
It is as simple as this:
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() {
System.out.println("hello world, I have just started up");
}
Tested on version 1.5.1.RELEASE
Try:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#SuppressWarnings("resource")
public static void main(final String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
context.getBean(Table.class).fillWithTestdata(); // <-- here
}
}
Have you tried ApplicationReadyEvent?
#Component
public class ApplicationStartup
implements ApplicationListener<ApplicationReadyEvent> {
/**
* This event is executed as late as conceivably possible to indicate that
* the application is ready to service requests.
*/
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
// here your code ...
return;
}
}
Code from: http://blog.netgloo.com/2014/11/13/run-code-at-spring-boot-startup/
This is what the documentation mentions about the startup events:
...
Application events are sent in the following order, as your application runs:
An ApplicationStartedEvent is sent at the start of a run, but before
any processing except the registration of listeners and initializers.
An ApplicationEnvironmentPreparedEvent is sent when the Environment to be used in the context is known, but before the context
is created.
An ApplicationPreparedEvent is sent just before the refresh is started, but after bean definitions have been loaded.
An ApplicationReadyEvent is sent after the refresh and any related callbacks have been processed to indicate the application is ready to
service requests.
An ApplicationFailedEvent is sent if there is an exception on startup.
...
Why not just create a bean that starts your monitor on initialization, something like:
#Component
public class Monitor {
#Autowired private SomeService service
#PostConstruct
public void init(){
// start your monitoring in here
}
}
the init method will not be called until any autowiring is done for the bean.
The "Spring Boot" way is to use a CommandLineRunner. Just add beans of that type and you are good to go. In Spring 4.1 (Boot 1.2) there is also a SmartInitializingBean which gets a callback after everything has initialized. And there is SmartLifecycle (from Spring 3).
ApplicationReadyEvent is really only useful if the task you want to perform is not a requirement for correct server operation. Starting an async task to monitor something for changes is a good example.
If, however your server is in a 'not ready' state until the task is completed then it's better to implement SmartInitializingSingleton because you'll get the callback before your REST port has been opened and your server is open for business.
Don't be tempted to use #PostConstruct for tasks that should only happen once ever. You'll get a rude surprise when you notice it being called multiple times...
You can extend a class using ApplicationRunner , override the run() method and add the code there.
import org.springframework.boot.ApplicationRunner;
#Component
public class ServerInitializer implements ApplicationRunner {
#Override
public void run(ApplicationArguments applicationArguments) throws Exception {
//code goes here
}
}
Use a SmartInitializingSingleton bean in spring > 4.1
#Bean
public SmartInitializingSingleton importProcessor() {
return () -> {
doStuff();
};
}
As alternative a CommandLineRunner bean can be implemented or annotating a bean method with #PostConstruct.
Best way to execute block of code after Spring Boot application started is using PostConstruct annotation.Or also you can use command line runner for the same.
1. Using PostConstruct annotation
#Configuration
public class InitialDataConfiguration {
#PostConstruct
public void postConstruct() {
System.out.println("Started after Spring boot application !");
}
}
2. Using command line runner bean
#Configuration
public class InitialDataConfiguration {
#Bean
CommandLineRunner runner() {
return args -> {
System.out.println("CommandLineRunner running in the UnsplashApplication class...");
};
}
}
Providing an example for Dave Syer answer, which worked like a charm:
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(CommandLineAppStartupRunner.class);
#Override
public void run(String...args) throws Exception {
logger.info("Application started with command-line arguments: {} . \n To kill this application, press Ctrl + C.", Arrays.toString(args));
}
}
I really like the suggestion for usage of the EventListener annotation by #cahen (https://stackoverflow.com/a/44923402/9122660) since it is very clean. Unfortunately I could not get this to work in a Spring + Kotlin setup. What does work for Kotlin is adding the class as a method parameter:
#EventListener
fun doSomethingAfterStartup(event: ApplicationReadyEvent) {
System.out.println("hello world, I have just started up");
}
You have several choices:
Using CommandLineRunner or ApplicationRunner as a Bean definition:
Spring Boot executes these towards the end of the application startup process. In most circumstances, the CommandLineRunner will do the job. Following is an example of a CommandLineRunner implementation with Java 8:
#Bean
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World");
}
Note that the args is the String array of arguments. You can also provide an implementation of this interface and define it as a Spring Component:
#Component
public class MyCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Hello World");
}
}
You can use the ApplicationRunner if you need better argument management. ApplicationRunner takes an ApplicationArguments instance that has enhanced argument management options.
You can also order the CommandLineRunner and ApplicationRunner beans using Spring's #Order annotation:
#Bean
#Order(1)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 1");
}
#Bean
#Order(2)
public CommandLineRunner commandLineRunner() {
return (args) -> System.out.println("Hello World, Order 2");
}
Using Spring Boot's ContextRefreshedEvent:
Spring Boot publishes several events at startup. These events indicate the completion of a phase in the application startup process. You can listen to the ContextRefreshedEvent and execute custom code:
#EventListener(ContextRefreshedEvent.class)
public void execute() {
if(alreadyDone) {
return;
}
System.out.println("hello world");
}
ContextRefreshedEvent is published several times. Thus, ensure to put a check whether the code execution is already finished.
Try this one and it will run your code when the application context has fully started.
#Component
public class OnStartServer implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
// EXECUTE YOUR CODE HERE
}
}
Spring boot provides an ApplicationRunner interface with a run() method to be invoked at application startup.
However, instead of raw String arguments passed to the callback method, we have an instance of the ApplicationArguments class.
#Component
public class AppStartupRunner implements ApplicationRunner {
#Override
public void run(ApplicationArguments args) throws Exception {
//some logic here
}
}
just implement CommandLineRunner for spring boot application.
You need to implement run method,
public classs SpringBootApplication implements CommandLineRunner{
#Override
public void run(String... arg0) throws Exception {
// write your logic here
}
}
you can use #Component
#RequiredArgsConstructor
#Component
#Slf4j
public class BeerLoader implements CommandLineRunner {
//declare
#Override
public void run(String... args) throws Exception {
//some code here
}
If you mean running peace of code once after the application started, you can use the CommandLineRunner as below:
#SpringBootApplication
public class SpringBootApplication
implements CommandLineRunner {
private static Logger LOG = LoggerFactory
.getLogger(SpringBootConsoleApplication.class);
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(SpringBootConsoleApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
#Override
public void run(String... args) {
// enter code you want to run after app loaded here
LOG.info("EXECUTING : command line runner");
for (int i = 0; i < args.length; ++i) {
LOG.info("args[{}]: {}", i, args[i]);
}
}
}
Otherwise, you can use the DevTools dependency, which help you to run new codes without manually restarting the application.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
don't forget to add these codes to your pom.xml to avoid version warnings:
<properties>
<java.version>17</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
give it a thump up if this was helpful to you!
Best way you use CommandLineRunner or ApplicationRunner
The only difference between is run() method
CommandLineRunner accepts array of string and ApplicationRunner accepts ApplicationArugument.

How to add sample data to database with springboot?

I've a Spring Boot application with the Spring Data JPA and two entities mapped to a database. My application is set to recreate the database on every startup. Now i want to create some instances of these POJOs, persist them in the database so I've some data to test my application with in the ongoing development process.
What is the best way to do that?
What i tried so far:
My class to add the sample data
public class DBSampleAdditor {
#PersistenceContext
private EntityManager em;
#Transactional
public void addSampleData() {
// creating POJO instances and persist them with em.perists()
}
}
I tried to call these functions in the main of my ApplicationClass
#SpringBootApplication()
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
That didnt work at all, as the EntityManager never got an Instance from the persistence unit.
I also tried to create a CommandLineRunner:
#Component
public class PTCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Adding sample data...");
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
But that one seemed to never been called in the startup process.
You can use method with #PostConstruct with #Component to insert data at startup.
#PostConstruct will be invoked after the bean has been created, dependencies have been injected, all managed properties are set, and before the bean is actually set into scope.
Just inject your database repository in the class marked with #Component and call your injected database repository inside the method marked with #PostConstruct
Example:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
In order to use spring managed components like the EntityManager the objects need to spring Beans, which basically means they are created inside the spring context and can utilise the IoC container.
In the example above, running the DbSampleAdditor class from the main method is outside the spring context, so the beans like PersistenceContext wont get injected.
One fairly simple way to do this inside the spring context is to add an ApplicationListener for the ApplicationReadyEvent
#Component
class Initialise implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private final DbSampleAdditor db;
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
db.addSampleData();
}
}
When the application ready event fires all the necessary spring bean wiring is set up so when it runs the addSampleData method, things like the EM are good to go.
Other approaches for setting up a DB for spring boot are documented here
Instead of creating an object DBSampleAdditor, try to autowire it so that it is available on application startup. #Autowired DBSampleAdditor DBSampleAdditor Then you try to add sample data within the run method

Cucumber + spring: How to trigger SmartLifecycle events through tests?

I have a spring boot application with cucumber. I would like to perform certain events within the test BEFORE the start-SmartLifecycle event of the beans is called.
Given Something that needs to happen before beans are started
And Beans start up now
Then Life is good
Is there any way to achieve this?
By default, it looks like Spring initializes and starts all the beans before any Cucumber statements are executed.
Example:
class Context {
#Bean
SomeBean someBean() { return new SomeBean(); }
}
class SomeBean implements SmartLifecycle {
#Override
void start() {
// some meaningful work that depends on setup that needs to be done beforehand
}
// rest of interface implementation
}
Cucumber definitions file:
#ContextConfiguration(classes = Context.class)
class CucumberFeatures {
#Autowired
private SomeBean someBean;
#Given("Something that needs to happen before beans are started")
public void something() {
// ...
}
#Given("Beans start up now")
public void beansStarted() {
// This should start beans in their defined order now
}
#Then("Life is good")
public void lifeIsGood() { ... }
}
You can't test a dependency injection container while using that same container to inject dependencies into your test. Injecting dependencies requires the application context to have been refreshed already.
So you have to create an instance of the ApplicationContext manually and manage it's life-cycle yourself.

How can I declare a startup-hook in a Java Web App

I want to do some initializing (timers and logging) when the web app is started. I cannot use a ContextListener since I dont get any of my Spring-Components within it. I need a simple hook like "afterWebAppHasLoadedAndEverythingyIsSetUp()".
Does it exist?
The ApplicationContext publishes certain types of events when loading the beans.
Event handling in the ApplicationContext is provided through the ApplicationEvent class and ApplicationListener interface. So if a bean implements the ApplicationListener, then every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified.
Following events are provided by spring
ContextRefreshedEvent
ContextStartedEvent
ContextStoppedEvent
ContextClosedEvent
Firstly create a implementation of ApplicationListener as follows
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class CStartEventHandler
implements ApplicationListener<ContextStartedEvent>{
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
Then you just have to Register the class as a bean with the Spring.
If you are using Spring Boot (which i recommend), you can use the following CommandLineRunner bean to run your initialization stuff after the application started up. You will be able to get all the spring beans ready, below is the code snippet.
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
// here you can take any number of object which is anutowired automatically
#Bean
public CommandLineRunner init(MyService service, MyRepository repository) {
// do your stuff with the beans
return null;
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

#Async in Spring doesn't work in Service class?

#Async method in #Service annotated class in standalone Spring Boot application doesn't run asynchronously. What am I doing wrong?
When I run the same method directly from main class (#SpringBootApplication annotated), it works. Example:
Main class
#SpringBootApplication
#EnableAsync
public class Application implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
// here when I call downloadAnSave() it runs asynchronously...
// but when I call downloadAnSave() via downloadAllImages() it does not run asynchronously...
}
}
and my service class (and here asynchronous behavior doesn't work):
#EnableAsync
#Service
public class ImageProcessorService implements IIMageProcessorService {
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}
Calling async method from within the same class would trigger the original method and not the intercepted one.
You need to create another service with the async method, and call it from your service.
Spring creates a proxy for each service and component you create using the common annotations. Only those proxies contain the wanted behavior defined by the method annotations such as the Async. So, calling those method not via the proxy but by the original naked class would not trigger those behaviors.
Workaround is:
#EnableAsync
#Service("ip-service")
public class ImageProcessorService implements IIMageProcessorService {
#Autowired
#Qualifier("ip-service")
ImageProcessorService ipService;
public void downloadAllImages(Run lastRun) {
// this method calls downloadAnSave() in loop and should run asynchronously....
ipService.downloadAnSave(productId, imageUrl);
}
#Async
#Override
public boolean downloadAnSave(String productId, String imageUrl) {
//
}
}
With such approach you call method of proxy, not the class instance.
The same approach can be used with other tools working with proxies, e.g. #Transactional etc.

Categories

Resources