Lokomotif: Use Case (4: Implementing Spring Boot: Scheduler Report)
Published: 2023-11-22 01:08:51

Prerequisites:

1. Java version of 17
2. MongoDB URI connection
3. PostgreSQL database connection
4. Telegram bot username and token
4. Visual Studio Code [Spring Intializr](https://code.visualstudio.com/docs/java/java-spring-boot#:~:text=The%20Spring%20Initializr%20extension%20allows,for%20vscode%2Dspring%2Dinitializr%20.)

And now, let's go into the cooking step:

## Initialize Spring Boot Application

1. Using Visual Studio Code, I clicked `ctrl` + `shift` + `p` altogether, choose Spring Initializr: Create Maven Project
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xwrxnb2a7d485ytxac49.png)

2. Choose Spring Boot version of **3.0.12**, I actually migrating my project from version 2.7.17 to 3.0.0.

3. Choose **Java **as project language.

4. Type your project's `group id`. For me, I choose `com.tujuhsembilan`.
5. Type your `artifact id`, I choose `scheduler`.
6. Choose `Jar` as the `packaging type`.
7. Choose 17 for Java version.
8. Add these dependency by searching them. (If you cannot find them in the search tab, you can add them manually later at `pom.xml`).
    ```
    <dependencies>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-data-jpa</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-hateoas</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-quartz</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>com.h2database</groupId>
    		<artifactId>h2</artifactId>
    		<scope>runtime</scope>
    	</dependency>
    	<dependency>
    		<groupId>org.projectlombok</groupId>
    		<artifactId>lombok</artifactId>
    		<optional>true</optional>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-test</artifactId>
    		<scope>test</scope>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-data-mongodb</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.modelmapper</groupId>
    		<artifactId>modelmapper</artifactId>
    		<version>3.0.0</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-web</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-data-rest</artifactId>
    	</dependency>
    	<dependency>
    		<groupId>org.postgresql</groupId>
    		<artifactId>postgresql</artifactId>
    		<scope>runtime</scope>
    	</dependency>
    	<dependency>
    		<groupId>org.telegram</groupId>
    		<artifactId>telegrambots-spring-boot-starter</artifactId>
    		<version>6.1.0</version>
    	</dependency>
    	<dependency>
    		<groupId>org.telegram</groupId>
    		<artifactId>telegrambots-abilities</artifactId>
    		<version>6.7.0</version>
    	</dependency>
    	<dependency>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-security</artifactId>
    	</dependency>
    </dependencies>
    ```
9. After the project initialized, I create few packages. I made these packages at the same level of `Application.java` (`SchedulerApplication.java` for me)
    ```
    * configuration
    * controller
    * model
      - Dto
    * repository
    * service
    ```

## Setting aplication.properties
First thing first, we have to make sure that our application knows all connection that we need and put it inside `application.properties`
```
spring.data.mongodb.uri=mongodb+srv://<>username:<password>@cluster0.cluster.mongodb.net/<dbname> ##Make sure the URI is including the database name

spring.datasource.url=jdbc:postgresql://localhost:5432/summary
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver

# This will create table automatically in your database
spring.jpa.hibernate.ddl-auto=create

telegram.bot.username=bot_username
telegram.bot.token=bot_token
```

## Create configuration for application
Create a file inside `configuration` package called `ApplicationConfig.java`
```
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

@Configuration
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@EnableJpaAuditing
@EnableWebSecurity
public class ApplicationConfig {
    
    @Bean
    public ModelMapper modelMapper() {
        return new ModelMapper();
    }

    @Bean
    protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
                .cors().and()
                .authorizeHttpRequests(config -> config
                        .anyRequest().permitAll())
                .csrf(AbstractHttpConfigurer::disable);

        return http.build();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        final CorsConfiguration config = new CorsConfiguration();

        config.setAllowedOrigins(Arrays.asList("http://127.0.0.1:5173"));
        config.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT", "PATCH"));
        config.setAllowCredentials(true);
        config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));

        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return source;
    }

}
```
1. `@EnableWebSecurity` annotation will let the application knows this application needs some configuration related to the security 
2. Security related configuration is needed to prevent some security problem like CORS policy error which I had in client-side before I have the right configuration

## Create configuration for web security
As I said before, few things need to be declared to avoid CORS policy error. In the `configuration` package you can add another class called `WebSecurityConfig.java` to define cors mappings.
```
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class WebSecurityConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry cors) {
        cors
                .addMapping("/**")
                .allowedOrigins("http://localhost:8080")
                .allowedMethods("*")
                .allowedHeaders("*");
    }

}
```

## Defining models and Dtos
1. First model that we will create inside `model` package is `LokomotifDocument.java`. This model is presenting response that we will get from MongoDB, hence the content of the model is the same as Spring Boot: Info Scheduler service
    ```
    import jakarta.persistence.Column;
    import jakarta.persistence.Entity;
    import jakarta.persistence.EntityListeners;
    import jakarta.persistence.Id;
    
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    import org.springframework.data.mongodb.core.mapping.Document;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    @Getter
    @Setter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Document(collection = "lokomotifs")
    @EntityListeners(AuditingEntityListener.class)
    public class LokomotifDocument {
        
        @Id
        private String _id;
    
        @Column
        private String kodeLoko;
    
        @Column
        private String namaLoko;
    
        @Column
        private String dimensiLoko;
    
        @Column
        private String status;
    
        @Column
        private String createdDate;
    
    }
    ```
2. The second model that we are going to create in `model` package is `Lokomotif.java`. Different thing about this model and the previous one is that this model will be use to communicate with PostgreSQL database and also the type of the `createdDate` field is `LocalDateTime` instead of `String`
    ```
    import java.time.LocalDateTime;
    
    import jakarta.persistence.Column;
    import jakarta.persistence.Entity;
    import jakarta.persistence.EntityListeners;
    import jakarta.persistence.Id;
    import jakarta.persistence.JoinColumn;
    import jakarta.persistence.ManyToOne;
    import jakarta.persistence.Table;
    
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    @Getter
    @Setter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity
    @Table(name = "lokomotif")
    @EntityListeners(AuditingEntityListener.class)
    public class Lokomotif {
        
        @Id
        private String _id;
    
        @Column
        private String kodeLoko;
    
        @Column
        private String namaLoko;
    
        @Column
        private String dimensiLoko;
    
        @Column
        private String status;
    
        @Column(columnDefinition = "TIMESTAMP")
        private LocalDateTime createdDate;
    
        @ManyToOne
        @JoinColumn(name = "summary_id", nullable = true)
        private Summary summary;
    
    }
    ```
3. The last model that will be created in the `model` package is `Summary.java` which represents the data structure of summary that will be use to provide summary report
    ```
    import java.time.LocalDateTime;
    import java.util.List;
    
    import jakarta.persistence.Column;
    import jakarta.persistence.Entity;
    import jakarta.persistence.EntityListeners;
    import jakarta.persistence.GeneratedValue;
    import jakarta.persistence.GenerationType;
    import jakarta.persistence.Id;
    import jakarta.persistence.OneToMany;
    import jakarta.persistence.Table;
    
    import org.springframework.data.annotation.CreatedDate;
    import org.springframework.data.jpa.domain.support.AuditingEntityListener;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
    import lombok.Setter;
    
    @Getter
    @Setter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Entity
    @Table(name = "summary", schema = "public")
    @EntityListeners(AuditingEntityListener.class)
    public class Summary {
        
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE)
        private Integer id;
    
        @Column
        private Integer totalLokomotif;
    
        @Column
        @CreatedDate
        private LocalDateTime createdDate;
    
        @Column
        @OneToMany(mappedBy = "summary")
        private List<Lokomotif> lokomotifs;
    }
    ```
4. Moving on to the `dto` package, we will create a dto of `LokomotifDto.java`
    ```
    import java.time.LocalDateTime;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class LokomotifDto {
        
        private String _id;
        private String kodeLoko;
        private String namaLoko;
        private String dimensiLoko;
        private String status;
        private LocalDateTime createdDate;
    
    }
    ```
5. And last for `dto` package we will create a `SummaryDto.java`
    ```
    import java.time.LocalDateTime;
    import java.util.List;
    
    import com.tujuhsembilan.scheduler.model.Lokomotif;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class SummaryDto {
        
        private Integer id;
        private Integer totalLokomotif;
        private LocalDateTime createdDate;
        private List<Lokomotif> lokomotifs;
    
        @Override
        public String toString() {
            return "Report created at " + createdDate.toLocalDate().toString() + "\r\n" + "Total of lokomotifs generated into database are: " + totalLokomotif.toString();
        }
    
    }
    ```

## Defining repositories
We will create about 3 repositories inside `repository` package
1. `LokomotifMongoRepository.java`
    ```
    import org.springframework.data.mongodb.repository.MongoRepository;
    import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
    
    import com.tujuhsembilan.scheduler.model.LokomotifDocument;
    import java.util.List;
    
    
    @EnableMongoRepositories
    public interface LokomotifMongoRepository extends MongoRepository<LokomotifDocument, String> {
        List<LokomotifDocument> findByCreatedDateBetween(String createdDate, String today);
    }
    ```
2. `LokomotifJpaRepository.java`
    ```
    @Repository
    public interface LokomotifJpaRepository extends JpaRepository<Lokomotif, String> {
        List<Lokomotif> findAllByCreatedDateBetween(LocalDateTime yesterday, LocalDateTime today, Pageable page);
        
    }
    ```
3. `SummaryRepository.java`
    ```
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import com.tujuhsembilan.scheduler.model.Summary;
    
    @Repository
    public interface SummaryRepository extends JpaRepository<Summary, Integer> {
        
    }
    ```

## Defining services
We are going to make three services in a `service` package
1. `LokomotifService.java` in this service we define a method to get lokomotif data from database that was created for the last 24 hours
    ```
    import java.time.LocalDateTime;
    import java.util.List;
    import java.util.stream.Collectors;
    
    import org.modelmapper.ModelMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.tujuhsembilan.scheduler.model.Lokomotif;
    import com.tujuhsembilan.scheduler.model.LokomotifDocument;
    import com.tujuhsembilan.scheduler.repository.LokomotifJpaRepository;
    import com.tujuhsembilan.scheduler.repository.LokomotifMongoRepository;
    import com.tujuhsembilan.scheduler.utils.LocalDateTimeToStringConverter;
    import com.tujuhsembilan.scheduler.utils.StringToLocalDateTimeConverter;
    
    import lombok.RequiredArgsConstructor;
    
    @Service
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class LokomotifService {
        
        private final ModelMapper modelMapper;
        
        private final LokomotifMongoRepository lokomotifMongoRepository;
    
        private final LokomotifJpaRepository lokomotifJpaRepository;
    
        public List<Lokomotif> getAllLokomotif() {
    
            modelMapper.addConverter(new StringToLocalDateTimeConverter());
    
            List<LokomotifDocument> data = lokomotifMongoRepository.findAll();
    
            List<Lokomotif> lokomotifs = data
                .stream()
                .map(element -> modelMapper.map(element, Lokomotif.class))
                .collect(Collectors.toList());
            
            return lokomotifs;
    
        }
    
        public List<Lokomotif> getLokomotifCreatedYesterday() {
    
            LocalDateTimeToStringConverter converter = new LocalDateTimeToStringConverter();
    
            modelMapper.addConverter(new StringToLocalDateTimeConverter());
    
            LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
            LocalDateTime today = LocalDateTime.now();
            String yesterdayDate = converter.convert(yesterday);
            String todayDate = converter.convert(today);
    
            List<LokomotifDocument> data = lokomotifMongoRepository.findByCreatedDateBetween(yesterdayDate, todayDate);
    
            List<Lokomotif> lokomotifs = data
                .stream()
                .map(element -> modelMapper.map(element, Lokomotif.class))
                .collect(Collectors.toList());
            
            return lokomotifs;
    
        }
    
    }
    ```
2. `SummaryService.java` in this service we will create an instance of Summary model
    ```
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.tujuhsembilan.scheduler.model.Lokomotif;
    import com.tujuhsembilan.scheduler.model.Summary;
    import com.tujuhsembilan.scheduler.repository.SummaryRepository;
    
    import lombok.RequiredArgsConstructor;
    
    @Service
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class SummaryService {
    
        private final SummaryRepository summaryRepository;
    
        public Summary createSummary(List<Lokomotif> lokomotifs) {
            var dailySummary = Summary
                .builder()
                .totalLokomotif(lokomotifs.size())
                .lokomotifs(lokomotifs)
                .build();
            
                return summaryRepository.save(dailySummary);
        }
    }
    ```
3. `BotSummaryService.java` in this service we define methods to communicate with telegram bot API
    ```
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.telegram.telegrambots.bots.TelegramLongPollingBot;
    import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
    import org.telegram.telegrambots.meta.api.objects.Update;
    import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
    
    import com.tujuhsembilan.scheduler.model.dto.SummaryDto;
    
    @Service
    public class BotSummaryService extends TelegramLongPollingBot {
        
        @Value("${telegram.bot.username}")
        private String botUsername;
    
        @Value("${telegram.bot.token}")
        private String botToken;
    
        @Value("${telegram.bot.chatId}")
        private String chatId;
    
        @Override
        public String getBotUsername() {
            return this.botUsername;
        }
    
        @Override
        public String getBotToken() {
            return this.botToken;
        }
    
        public String getChatId() {
            return this.chatId;
        }
    
        @Override
        public void onUpdateReceived(Update update) {
            if (update.hasMessage()) {
                var message = update.getMessage();
                var chatId = message.getChatId();
                try {
                    var reply = "hi";
                    sendNotification(String.valueOf(chatId), reply);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        public void startScheduledReporting(SummaryDto summaryDto) {
            try {
                String reply = summaryDto.toString();
                sendNotification(this.chatId, reply);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private void sendNotification(String chatId, String message) throws TelegramApiException {
            var response = new SendMessage(chatId, message);
            execute(response);
        }
    
    }
    ```

## Working with controllers
We are going to make two controllers inside `controller` package
1. `LokomotifController.java` where we have method for API endpoint call from client side and also a method to save random locomotive data for the last 24 hours into PostgreSQL for every one hour (scheduled)
    ```
    import java.time.LocalDateTime;
    import java.util.List;
    import java.util.stream.Collectors;
    
    import org.modelmapper.ModelMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.PageRequest;
    import org.springframework.data.domain.Pageable;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.tujuhsembilan.scheduler.model.Lokomotif;
    import com.tujuhsembilan.scheduler.model.dto.LokomotifDto;
    import com.tujuhsembilan.scheduler.repository.LokomotifJpaRepository;
    import com.tujuhsembilan.scheduler.service.LokomotifService;
    
    import lombok.RequiredArgsConstructor;
    
    @RestController
    @RequestMapping("/lokomotifs")
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class LokomotifController {
        
        private final ModelMapper modelMapper;
    
        private final LokomotifService lokomotifService;
    
        private final LokomotifJpaRepository lokomotifJpaRepository;
    
        @GetMapping("/")
        public ResponseEntity<List<LokomotifDto>> getAllLokomotif(@RequestParam int page) {
    
            LocalDateTime yesterday = LocalDateTime.now().minusDays(3);
            LocalDateTime today = LocalDateTime.now();
    
            Pageable pageRequest = PageRequest.of(page, Integer.MAX_VALUE);
            
            List<Lokomotif> data = lokomotifJpaRepository.findAllByCreatedDateBetween(yesterday, today, pageRequest);
    
            List<LokomotifDto> dtos = data
                .stream()
                .map(element -> modelMapper.map(element, LokomotifDto.class))
                .collect(Collectors.toList());
            
            return ResponseEntity.status(HttpStatus.OK).body(dtos);
        }
    
        @PostMapping("/")
        @Scheduled(fixedRate = 3600000)
        public ResponseEntity<List<LokomotifDto>> saveLokomotifCreatedYesterday() {
    
            List<Lokomotif> data = lokomotifService.getLokomotifCreatedYesterday();
    
            List<Lokomotif> savedData = lokomotifJpaRepository.saveAll(data);
    
            List<LokomotifDto> dtos = savedData
                .stream()
                .map(element -> modelMapper.map(element, LokomotifDto.class))
                .collect(Collectors.toList());
            
            return ResponseEntity.status(HttpStatus.CREATED).body(dtos);
    
        }
    
    }
    ```
2. `SummaryController.java` in this controller we have method to save summary into PostgreSQL for every one hour (scheduled)
    ```
    import java.time.LocalDateTime;
    import java.util.List;
    
    import org.modelmapper.ModelMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Pageable;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.tujuhsembilan.scheduler.model.Lokomotif;
    import com.tujuhsembilan.scheduler.model.dto.SummaryDto;
    import com.tujuhsembilan.scheduler.repository.LokomotifJpaRepository;
    import com.tujuhsembilan.scheduler.service.BotSummaryService;
    import com.tujuhsembilan.scheduler.service.SummaryService;
    
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    @RestController
    @RequestMapping("/summary")
    @RequiredArgsConstructor(onConstructor = @__(@Autowired))
    public class SummaryController {
        
        private final ModelMapper modelMapper;
    
        private final LokomotifJpaRepository lokomotifJpaRepository;
    
        private final SummaryService summaryService;
    
        private final BotSummaryService botSummaryService;
    
        @ResponseBody
        @PostMapping("/")
        @Scheduled(fixedRate = 3600000)
        public ResponseEntity<SummaryDto> createSummary() {
            
            LocalDateTime yesterday = LocalDateTime.now().minusDays(3);
            LocalDateTime today = LocalDateTime.now();
            
            List<Lokomotif> lokomotifs = lokomotifJpaRepository.findAllByCreatedDateBetween(yesterday, today, Pageable.unpaged());
    
            var savedSummary = summaryService.createSummary(lokomotifs);
    
            log.info("Summary is saved to database");
    
            var dto = modelMapper.map(savedSummary, SummaryDto.class);
    
            botSummaryService.startScheduledReporting(dto);
    
            log.info("Summary is sent to telegram");
    
            return ResponseEntity.status(HttpStatus.CREATED).body(dto);
        }
    
    }
    ```

## Updating Application.java (SchedulerApplication.java for me)
To make sure that this application is implementing scheduling feature we need to add an annotation of `@Enablescheduling` so the class will look like this:
```
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {

	public static void main(String[] args) {
		SpringApplication.run(SchedulerApplication.class, args);
	}

}
```

## Running the application
Since I already have extension for springboot in my visual studio code, I could just press run button and the correct log when the code works properly will look like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x7hng2atvoswfi27n7ns.png)

And you will have a telegram summary coming in after running the application

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmnbjswxbfjasmqjhhhy.jpeg)