I am currently creating a RestApi project in SpringBoot.
We would like to add a program to this project that can be run from the command line.
Specifically, the image is that a monitoring batch on the server executes this program periodically.
I did some research on my own and thought it might be possible to implement this using CommandLineRunner.
I did some research on my own and thought it might be possible to implement this using CommandLineRunner.
#Component
public class DemoComponent implements CommandLineRunner{
#Override
public void run(String... args) throws Exception {
System.out.println("Hello");
}
}
When I start springBoot, "Hello" is output to the console.
enter image description here
What I would like is to run this process from the command line.
Please let me know.
What are you trying to achieve here?
Scheduler?
If you want to create a scheduled operation, you can utilize spring scheduler:
#Configuration
#EnableScheduling
public class SchedulerConfig {
...
}
#Scheduled(cron = "0 15 10 15 * ?")
public void scheduleHello() {
long now = System.currentTimeMillis() / 1000;
System.out.println("Hello - " + now);
}
https://www.baeldung.com/spring-scheduled-tasks
Monitoring??
If your purpose is to monitor your springboot service health, you might want to try spring-actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
you can monitor your service health in:
/actuator/health or /health
https://www.baeldung.com/spring-boot-actuators
Related
I would like to create an application in Java to automate trades in my Binance account. Thankfully, joaopsilva has made it easy through an open source API which fetches candlesticks through REST Client or WebSocket. I would like to use WebSocket since it is lighter.
I searched in several sources and still I could not find an example project which uses the Spring Boot framework to build an event-driven application which interacts with a connected WebSocket.
If my line of reason is correct, I should define a bean for Spring to instantiate the WebSocket client:
#Configuration
public class WebSocketConfig {
#Bean
public BinanceApiWebSocketClient binanceApiWebSocketClient() {
return BinanceApiClientFactory.newInstance().newWebSocketClient();
}
}
To interact with the socket event, I created a #Scheduled response, in which I used an arbitrary rate of 1 per second just for testing. I did it like this:
#Configuration
#EnableScheduling
public class SocketListener {
#Autowired BinanceApiWebSocketClient client;
#Scheduled(fixedRate = 1000)
public void scheduleFixedDelayTask() {
client.onCandlestickEvent("ethbtc", CandlestickInterval.ONE_MINUTE, response ->
System.out.println(response));
}
}
It works. If I launch the Spring application, it successfuly configures the client Bean and it prints the candlestick events. However, every 1 second, what I receive is an enormous chunk of events. It looks like this:
So, I'm wondering. Am I doing this correctly? Would there be a way to Schedule the listener not to receive chunks of events, but to listen the socket exactly when an event happens (not setting delay = 1, which of course causes unnecessary performance issues).
If your question is about the correct place to start the event streaming through the websocket client defined as a bean, one of the options is an ApplicationRunner bean whose run() method will be executed once on application start:
#SpringBootApplication
public class BinanceClientApplication {
public static void main(String[] args) {
SpringApplication.run(BinanceClientApplication.class, args);
}
#Bean
public ApplicationRunner applicationRunner(BinanceApiWebSocketClient binanceApiWebSocketClient) {
return args -> {
binanceApiWebSocketClient.onCandlestickEvent("ethbtc",
CandlestickInterval.ONE_MINUTE,
response ->
System.out.println(response));
};
}
}
#Configuration
public class WebSocketConfig {
#Bean
public BinanceApiWebSocketClient binanceApiWebSocketClient() {
return BinanceApiClientFactory.newInstance().newWebSocketClient();
}
}
With the scheduled task you have a new event stream is started with every task execution and you end up having multiple streams of the same events.
I am a spring newbie and a spring batch newbie -- so, please bear with me.
I understand that spring batch is the framework that will help run steps and tasks.
I tried using spring batch by creating steps and task using but these steps and tasks are hardcoded at program build/compile time. However, I could not figure out how to dynamically create Tasks and Steps.
What I want to do is to have a user create a script of how tasks are assembled from a list of steps. Each step will invoke a remote call to an existing REST endpoint. A task will have multiple such steps. the user will create multiple such tasks.
Is it possible to dynamically create such a task with such steps ? If yes, could you point me to some sample code how to do this with the required API ?
UPDATE :
I understand that a HelloWorld Job which calls a REST application using callRestApplication() looks like this.
#SpringBootApplication
#EnableBatchProcessing
public class HelloApplication {
#Autowired
private JobBuilderFactory jobBuilderFactory;
#Autowired
private StepBuilderFactory stepBuilderFactory;
#Bean
public Step step() {
return this.stepBuilderFactory.get("step1")
.tasklet((stepContribution, chunkContext) -> {
callRestApplication();
return RepeatStatus.FINISHED;
}).build();
}
#Bean
public Job job() {
return this.jobBuilderFactory.get("HelloWorldJob")
.start(step())
.build();
}
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
However, this is static. I was looking for a way where I can do something like this
public static void main(String[] args) {
SpringApplication.run(HelloagainApplication.class, args);
List<JobDefinition> jobDefinitions = parseScript();
for (JobDefinition jobDefinition : jobDefinitions) {
Job job = new Job();
job.setName(jobDefinition.getName());
for (StepDefinition stepDefinition : jobDefinition.getStepDefinitions()) {
Step step = new Step();
step.setEndPoint(stepDefinition.getEndPoint());
step.setName(stepDefinition.getName());
step.setProperty1(stepDefinition.getProp1());
job.addStep(step);
}
job.startJob();
}
}
If the script has 10 job definitions, then 10 jobs will be started, each with x number of steps.
In spring batch, how can I do the following
job.addStep(step);
job.startJob();
Thanks
There is no such feature in Spring Batch. You need to write custom code that parses your user's script and create job/step definitions accordingly.
That said, Spring Cloud Dataflow might help you. You can pre-define your tasks and let the user compose them using the GUI or the text-based scriptable DSL. Another feature that could be interesting to you is the single-step job starter from Spring Cloud Task, which allows you to dynamically create a single-step job by providing some properties (no coding needed).
all,
I am using spring boot in my project. It is great.
My project has a part of it that operates on database periodically(with a timer), and not in response to a http request.
It's periodically queries a sensor(a lot of sensors) and collects temperature readouts, and stores the readouts into database.
Before storing it into database, the readout number is compared to a warning threshold to test if a warning should be generated.
The threshold number is to be queried(complicated) out from database, too.
I have a ThresholdRepository extending JPAResository for this query, so I want to use it in this scenario.
My question is: Could I use #Autowire to make spring boot generate ThresholdRepository instance for me? If not, how to instantiate ThresholdRepository in this timer thread?
I find some code at :http://www.yjs001.cn/java/spring/33161827667841434606.html
unfortunately, the code is outdated and RepositoryMetadata has no getDomainClass and I don't know which alternative should be used.
Please someone help me out.
Any recommendation is appreciated.
The repository I mentioned is as following:
public interface ThresholdInfoRepository extends JpaRepository<ThresholdInfo, Long> {
ThresholdInfo findOneByGatewayIdAndNodeAddrAndChannel(Long gatewayId, Byte nodeAddr, Byte channel);
List<ThresholdInfo> findByGatewayId(Long gatewayId);
}
It's short, but does a lot of work.
Yes, you can,
You have to #EnableJpaRepositories for your repositories to become a bean.
Then, to be able to autowire it, your TimerTask needs to be a Spring Bean as well. You could use spring-tasks https://spring.io/guides/gs/scheduling-tasks/
#SpringBootApplication
#EnableScheduling
#EnableJpaRepositories
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}
}
#Component
public class UpdateTask {
#Autowired
ThresholdInfoRepository thresholdInfoRepository;
#Scheduled(fixedRate = ...)
public void updateSensor() {
thresholdInfoRepository.find(...)
readoutRepository.save(...);
}
}
Spring boot will start a timer thread to execute your scheduled method.
I have a #Scheduled task in my application which is setup using CRON and run every 4 hours. The problem I face is that the CRON job does not start immediately after application startup but it starts only 4 hours after the application startup.
I tried to use a #PostConstruct method inside the task to invoke it, but that results in an error due to an uninitialized Spring context.
Please tell me how I can make the Scheduled task run immediately on application deployment and then on every 4 hours after the deployment.
EDIT:
I would not use a #PostConstruct since my scheduled method depends on other Beans , which are not initialized when this PostConstruct method runs for some reason.
By 0 */4 * * * you specify "At minute 0 past every 4th hour (0:00, 4:00, 8:00 etc.)", which is not at startup time and then every 4 hours as I think you want.
You can specify initial delay and rate by:
#Scheduled(initialDelay=0, fixedRate=4*60*60*1000)
If you are worried about hard-coded values, you can still provide config value:
#Scheduled(initialDelay=0, fixedRateString = "${some.config.string}")
I had #EnableScheduling in my app config. I had
#Scheduled(fixedDelay=5000) in my scheduled task class. Even then it didn't work.
I added #Service to my scheduled task class and everything worked fine.
I was having the same situation. I need to run cron scheduler every 1st day of the month and also when the application starts.
This is how I achieved using ApplicationListener.
import org.springframework.context.ApplicationListener;
import org.springframework.boot.context.event.ApplicationReadyEvent;
#Component
public class ApplicationReadyListner implements ApplicationListener<ApplicationReadyEvent> {
#Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// callYourTask();
}
}
I'm not sure if you have tried this but you can use #Scheduled with an initialDelay
For fixed-delay and fixed-rate tasks, an initial delay may be specified indicating the number of milliseconds to wait before the first execution of the method.
https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html#scheduling-annotation-support-scheduled
#Scheduled(initialDelay=0, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
I assume this will execute your scheduled method when the application is run.
If is not possible to use initialDelay attribute with the cron rule and your intention is execute this Job after every restart, you can implement a CommandLineRunner on your application class:
#SpringBootApplication
public class MyApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Autowired
private TaskService taskService;
#Override
public void run(final String... args) throws Exception {
taskService.run();
}
}
It's not the best strategy, but works.
Just specify the method you want to run in the init_method attribute of the bean.
Java config: #Bean(init_method="methodWhichStartsTask")
XML config: <bean id="someId" class="com.example.Clazz" init-method="methodWhichStartsTask"/>
This will invoke the method just after the bean is properly initialized and if the method is scheduled, then it will be called afterwards every 4 hours.
Instead of cron inside #Scheduled, use fixedRate like below. By default initialDelay is -1, which will start immediately or you can set 0.
#Scheduled(fixedRate=4*60*60*1000)
public void test() {
System.out.println("helloworld");
}
user #EnableScheduling annotation on your class which contains you1 scheduled method:
see spring document:
To enable support for #Scheduled and #Async annotations add
#EnableScheduling and #EnableAsync to one of your #Configuration
classes:
#Configuration
#EnableAsync
#EnableScheduling
public class AppConfig {
}
link : https://docs.spring.io/spring/docs/current/spring-framework-reference/html/scheduling.html
#EnableScheduling annotation should be added in the main class for the Spring application.
Example :
#SpringBootApplication
#EnableScheduling
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
and above the method where the actual scheduling needed we need to add
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
Example:
#Scheduled(initialDelay = 1000, fixedDelay = 4*60*60*1000)
public void schleduleWork() {
/* Code Here */
}
For the above scheduler the first hit will be at 1000 milli second and subsequest hits will be at the interval of 4 hrs
Got a pretty standard Spring Boot (1.3.5) application.
Enabled scheduling with #EnableScheduling (tried on main application entry point and a #Configuration annotated class.
Created a simple class with a #Scheduled method (simple fixedDelay schedule).
Scheduled task executes twice (always).
From what I have gathered so far, it is probably because two contexts are being loaded, and thusly picking up my beans twice.
Ok.
So how do I fix/prevent this double execution, since all the config is basically hidden Spring Boot magic?
Framework versions:
Spring Boot 1.3.5
Spring Cloud Brixton SR1
Main application:
#SpringBootApplication
#EnableDiscoveryClient
#EnableAsync
#EnableCircuitBreaker
public class AlertsApplication {
public static void main(final String[] args) {
SpringApplication.run(AlertsApplication.class, args);
}
}
My task class (HookCreateRequest list is pulled in from application.yml - I do not believe that to be relevant currently, but if required, can be provided):
#ConditionalOnProperty(name = "init.runner", havingValue = "InitRunner")
#ConfigurationProperties(prefix = "webhook")
public class InitRunner /*implements CommandLineRunner*/ {
private final List<HookCreateRequest> receivers = new ArrayList<>();
#Autowired
private WebHookService hookService;
#Scheduled (fixedRate = 300000)
public void run() throws Exception {
getReceivers().stream().forEach(item -> {
log.debug("Request : {}", item);
hookService.create(item);
});
}
public List<HookCreateRequest> getReceivers() {
return receivers;
}
}
There is zero xml configuration.
Not sure what else might be relevant?
EDIT 2016/07/04
I have modified to output the scheduled instance when it runs (I suspected that two different instances were being created). However, the logs seem to indicate it is the SAME instance of the task object.
logs:
15:01:16.170 DEBUG - scheduled.ScheduleHookRecreation - Schedule task running: scheduled.ScheduleHookRecreation#705a651b
...task stuff happening
...first run completes, then:
15:01:39.050 DEBUG - scheduled.ScheduleHookRecreation - Schedule task running: scheduled.ScheduleHookRecreation#705a651b
So it would seem it is the same task instance (#705a651b). Now why would in the name of sweet things would it be executed twice?
EDIT 2016/07/05
I added a #PostConstruct method to the class that carries the scheduled method, with just some logging output in. By doing that I could verify that the #PostConstruct method is being called twice - which seems to confirm that the bean is being picked up twice, which which presumably means it is fed to the scheduler twice. So how to prevent this?
Had the same problem, in my case the reason was in #Scheduled annotation's initialDelay parameter absence - method was called on application start.