diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java index 117251e..242cfd3 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java @@ -1,5 +1,6 @@ package it.fabioformosa.quartzmanager.aspects; +import org.quartz.JobExecutionContext; import org.quartz.SchedulerException; /** @@ -11,6 +12,6 @@ import org.quartz.SchedulerException; */ public interface ProgressNotifier { - void send() throws SchedulerException; + void send(JobExecutionContext jobExecutionContext) throws SchedulerException; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java index b0ac383..b60fab1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java @@ -1,17 +1,11 @@ package it.fabioformosa.quartzmanager.aspects; import it.fabioformosa.quartzmanager.dto.TriggerStatus; -import it.fabioformosa.quartzmanager.services.SchedulerService; -import org.quartz.DailyTimeIntervalTrigger; -import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; +import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.stereotype.Component; -import javax.annotation.Resource; - /** * * Notify the progress of the trigger through websocket @@ -29,8 +23,8 @@ public class WebSocketProgressNotifier implements ProgressNotifier { // @Resource // private Scheduler scheduler; - @Resource - private SchedulerService schedulerService; +// @Resource +// private LegacySchedulerService schedulerService; // @Resource // private TriggerMonitor triggerMonitor; @@ -42,34 +36,26 @@ public class WebSocketProgressNotifier implements ProgressNotifier { // } @Override - public void send() throws SchedulerException { + public void send(JobExecutionContext jobExecutionContext) throws SchedulerException { TriggerStatus currTriggerStatus = new TriggerStatus(); - Trigger trigger = schedulerService.getOneSimpleTrigger().get(); + Trigger trigger = jobExecutionContext.getTrigger(); currTriggerStatus.setFinalFireTime(trigger.getFinalFireTime()); currTriggerStatus.setNextFireTime(trigger.getNextFireTime()); currTriggerStatus.setPreviousFireTime(trigger.getPreviousFireTime()); - int timesTriggered = 0; - int repeatCount = 0; - if (trigger instanceof SimpleTrigger) { SimpleTrigger simpleTrigger = (SimpleTrigger) trigger; - timesTriggered = simpleTrigger.getTimesTriggered(); - repeatCount = simpleTrigger.getRepeatCount(); + currTriggerStatus.setRepeatCount(simpleTrigger.getRepeatCount() + 1); + currTriggerStatus.setTimesTriggered(simpleTrigger.getTimesTriggered()); } else if (trigger instanceof DailyTimeIntervalTrigger) { DailyTimeIntervalTrigger dailyTrigger = (DailyTimeIntervalTrigger) trigger; - timesTriggered = dailyTrigger.getTimesTriggered(); - repeatCount = dailyTrigger.getRepeatCount(); + currTriggerStatus.setRepeatCount(dailyTrigger.getRepeatCount() + 1); } - Trigger jobTrigger = schedulerService.getOneSimpleTrigger().get(); - if (jobTrigger != null && jobTrigger.getJobKey() != null) { - currTriggerStatus.setJobKey(jobTrigger.getJobKey().getName()); - currTriggerStatus.setJobClass(jobTrigger.getClass().getSimpleName()); - currTriggerStatus.setTimesTriggered(timesTriggered); - currTriggerStatus.setRepeatCount(repeatCount + 1); - } + JobDetail jobDetail = jobExecutionContext.getJobDetail(); + currTriggerStatus.setJobKey(jobDetail.getKey().getName()); + currTriggerStatus.setJobClass(trigger.getClass().getSimpleName()); messagingTemplate.convertAndSend("/topic/progress", currTriggerStatus); } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java new file mode 100644 index 0000000..ba76182 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java @@ -0,0 +1,10 @@ +package it.fabioformosa.quartzmanager.controllers; + +import org.springframework.beans.factory.annotation.Value; + +public class AbstractTriggerController { + + @Value("${quartz-manager.jobClass}") + protected String jobClassname; + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index 5798662..065a601 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -10,7 +10,7 @@ import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.dto.TriggerStatus; import it.fabioformosa.quartzmanager.enums.SchedulerStates; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.impl.triggers.SimpleTriggerImpl; @@ -40,10 +40,10 @@ public class SchedulerController { private final Logger log = LoggerFactory.getLogger(SchedulerController.class); - private SchedulerService schedulerService; + private LegacySchedulerService legacySchedulerService; - public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) { - this.schedulerService = schedulerService; + public SchedulerController(LegacySchedulerService legacySchedulerService, ConversionService conversionService) { + this.legacySchedulerService = legacySchedulerService; this.conversionService = conversionService; } @@ -60,7 +60,7 @@ public class SchedulerController { }) public SchedulerConfigParam getConfig() throws SchedulerException { log.debug("SCHEDULER - GET CONFIG params"); - SchedulerConfigParam schedulerConfigParam = schedulerService.getOneSimpleTrigger() + SchedulerConfigParam schedulerConfigParam = legacySchedulerService.getOneSimpleTrigger() .map(SchedulerController::fromSimpleTriggerToSchedulerConfigParam) .orElse(new SchedulerConfigParam(0L, 0, 0)); return schedulerConfigParam; @@ -69,7 +69,7 @@ public class SchedulerController { public static SchedulerConfigParam fromSimpleTriggerToSchedulerConfigParam(SimpleTrigger simpleTrigger){ int timesTriggered = simpleTrigger.getTimesTriggered(); int maxCount = simpleTrigger.getRepeatCount() + 1; - long triggersPerDay = SchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval()); + long triggersPerDay = LegacySchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval()); return new SchedulerConfigParam(triggersPerDay, maxCount, timesTriggered); } @@ -82,7 +82,7 @@ public class SchedulerController { }) public SchedulerDTO getScheduler() { log.debug("SCHEDULER - GET Scheduler..."); - SchedulerDTO schedulerDTO = conversionService.convert(schedulerService.getScheduler(), SchedulerDTO.class); + SchedulerDTO schedulerDTO = conversionService.convert(legacySchedulerService.getScheduler(), SchedulerDTO.class); return schedulerDTO; } @@ -98,7 +98,7 @@ public class SchedulerController { log.trace("SCHEDULER - GET PROGRESS INFO"); TriggerStatus progress = new TriggerStatus(); - SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) schedulerService.getOneSimpleTrigger().get(); + SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) legacySchedulerService.getOneSimpleTrigger().get(); if (jobTrigger != null && jobTrigger.getJobKey() != null) { progress.setJobKey(jobTrigger.getJobKey().getName()); progress.setJobClass(jobTrigger.getClass().getSimpleName()); @@ -122,9 +122,9 @@ public class SchedulerController { public Map getStatus() throws SchedulerException { log.trace("SCHEDULER - GET STATUS"); String schedulerState = ""; - if (schedulerService.getScheduler().isShutdown() || !schedulerService.getScheduler().isStarted()) + if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted()) schedulerState = SchedulerStates.STOPPED.toString(); - else if (schedulerService.getScheduler().isStarted() && schedulerService.getScheduler().isInStandbyMode()) + else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode()) schedulerState = SchedulerStates.PAUSED.toString(); else schedulerState = SchedulerStates.RUNNING.toString(); @@ -139,7 +139,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void pause() throws SchedulerException { log.info("SCHEDULER - PAUSE COMMAND"); - schedulerService.getScheduler().standby(); + legacySchedulerService.getScheduler().standby(); } @GetMapping("/resume") @@ -150,7 +150,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void resume() throws SchedulerException { log.info("SCHEDULER - RESUME COMMAND"); - schedulerService.getScheduler().start(); + legacySchedulerService.getScheduler().start(); } @GetMapping("/run") @@ -161,7 +161,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void run() throws SchedulerException { log.info("SCHEDULER - START COMMAND"); - schedulerService.getScheduler().start(); + legacySchedulerService.getScheduler().start(); } @GetMapping("/stop") @@ -172,7 +172,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void stop() throws SchedulerException { log.info("SCHEDULER - STOP COMMAND"); - schedulerService.getScheduler().shutdown(true); + legacySchedulerService.getScheduler().shutdown(true); } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java new file mode 100644 index 0000000..e8c5909 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java @@ -0,0 +1,91 @@ +package it.fabioformosa.quartzmanager.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.SchedulerException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Slf4j +@RequestMapping(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL) +@SecurityRequirement(name = "basic-auth") +@RestController +public class SimpleTriggerController extends AbstractTriggerController { + + static public final String SIMPLE_TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/simple-triggers"; + + private SimpleTriggerSchedulerService simpleSchedulerService; + + public SimpleTriggerController(SimpleTriggerSchedulerService simpleSchedulerService) { + this.simpleSchedulerService = simpleSchedulerService; + } + + @GetMapping("/{name}") + @Operation(summary = "Get a simple trigger by name") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Got the trigger by its name", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = SimpleTriggerDTO.class)) }), + @ApiResponse(responseCode = "404", description = "Trigger not found", + content = @Content) + }) + public SimpleTriggerDTO getSimpleTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { + return simpleSchedulerService.getSimpleTriggerByName(name); + } + + @PostMapping("/{name}") + @ResponseStatus(HttpStatus.CREATED) + @Operation(summary = "Create a new simple trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Created a new simple trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) + public SimpleTriggerDTO postSimpleTrigger(@PathVariable String name, @Valid @RequestBody SimpleTriggerInputDTO simpleTriggerInputDTO) throws SchedulerException, ClassNotFoundException { + log.info("SIMPLE TRIGGER - CREATING a SimpleTrigger {} {}", name, simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName(name) + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + SimpleTriggerDTO newTriggerDTO = simpleSchedulerService.scheduleSimpleTrigger(jobClassname, simpleTriggerCommandDTO); + log.info("SIMPLE TRIGGER - CREATED a SimpleTrigger {}", newTriggerDTO); + return newTriggerDTO; + } + + @PutMapping("/{name}") + @Operation(summary = "Reschedule a simple trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Rescheduled a simple trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) + public TriggerDTO rescheduleSimpleTrigger(@PathVariable String name, @Valid @RequestBody SimpleTriggerInputDTO simpleTriggerInputDTO) throws SchedulerException { + log.info("SIMPLE TRIGGER - RESCHEDULING the trigger {} {}", name, simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName(name) + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + TriggerDTO triggerDTO = simpleSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO); + log.info("SIMPLE TRIGGER - RESCHEDULED the trigger {}", triggerDTO); + return triggerDTO; + } + + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 0c93ccd..47e60c5 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -8,10 +8,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -21,24 +21,23 @@ import javax.validation.Valid; @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @SecurityRequirement(name = "basic-auth") @RestController -public class TriggerController { +public class TriggerController extends AbstractTriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; + static public final String SIMPLE_TRIGGER_BASE_URL = "/simple-triggers"; - @Value("${quartz-manager.jobClass}") - private String jobClassname; + private LegacySchedulerService schedulerService; - private SchedulerService schedulerService; - - public TriggerController(SchedulerService schedulerService) { + public TriggerController(LegacySchedulerService schedulerService) { this.schedulerService = schedulerService; } @GetMapping("/{name}") - public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException { - return schedulerService.getTriggerByName(name); + public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { + return schedulerService.getLegacyTriggerByName(name); } + @Deprecated @PostMapping("/{name}") @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Create a new trigger") @@ -56,6 +55,8 @@ public class TriggerController { return newTriggerDTO; } + + @PutMapping("/{name}") @Operation(summary = "Reschedule the trigger") @ApiResponses(value = { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java new file mode 100644 index 0000000..4f386a0 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java @@ -0,0 +1,31 @@ +package it.fabioformosa.quartzmanager.controllers.advices; + +import it.fabioformosa.quartzmanager.exceptions.ExceptionResponse; +import it.fabioformosa.quartzmanager.exceptions.ResourceConflictException; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public class ExceptionHandlingController { + + @ExceptionHandler(ResourceConflictException.class) + public ResponseEntity resourceConflict(ResourceConflictException ex) { + ExceptionResponse response = new ExceptionResponse(); + response.setErrorCode("Conflict"); + response.setErrorMessage(ex.getMessage()); + return new ResponseEntity(response, HttpStatus.CONFLICT); + } + + @ExceptionHandler(TriggerNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + @ResponseBody + public ExceptionResponse triggerNotFound(TriggerNotFoundException ex){ + return ExceptionResponse.builder().errorCode(HttpStatus.NOT_FOUND.toString()).errorMessage(ex.getMessage()).build(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java new file mode 100644 index 0000000..ba2d0c8 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java @@ -0,0 +1,32 @@ +package it.fabioformosa.quartzmanager.converters; + + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class SimpleTriggerCommandDTOToSimpleTrigger implements Converter { + @Override + public SimpleTrigger convert(SimpleTriggerCommandDTO triggerCommandDTO) { + TriggerBuilder triggerTriggerBuilder = TriggerBuilder.newTrigger(); + if(triggerCommandDTO.getSimpleTriggerInputDTO().getStartDate() != null) + triggerTriggerBuilder.startAt(triggerCommandDTO.getSimpleTriggerInputDTO().getStartDate()); + if(triggerCommandDTO.getSimpleTriggerInputDTO().getEndDate() != null) + triggerTriggerBuilder.endAt(triggerCommandDTO.getSimpleTriggerInputDTO().getEndDate()); + + SimpleTrigger newSimpleTrigger = triggerTriggerBuilder.withSchedule( + SimpleScheduleBuilder.simpleSchedule() + .withIntervalInMilliseconds(triggerCommandDTO.getSimpleTriggerInputDTO().getRepeatInterval()) + .withRepeatCount(triggerCommandDTO.getSimpleTriggerInputDTO().getRepeatCount()) + .withMisfireHandlingInstructionNextWithRemainingCount() + ) + .withIdentity(triggerCommandDTO.getTriggerName()) + .build(); + return newSimpleTrigger; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java new file mode 100644 index 0000000..c691b7a --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java @@ -0,0 +1,22 @@ +package it.fabioformosa.quartzmanager.converters; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import org.quartz.SimpleTrigger; +import org.springframework.stereotype.Component; + +@Component +public class SimpleTriggerToSimpleTriggerDTO extends TriggerToTriggerDTO { + + @Override + protected void convert(SimpleTrigger source, SimpleTriggerDTO target) { + super.convert(source, target); + target.setTimesTriggered(source.getTimesTriggered()); + target.setRepeatCount(source.getRepeatCount()); + target.setRepeatInterval(source.getRepeatInterval()); + } + + @Override + protected SimpleTriggerDTO createOrRetrieveTarget(SimpleTrigger source) { + return new SimpleTriggerDTO(); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java index 479be88..3c35a2e 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java @@ -1,6 +1,6 @@ package it.fabioformosa.quartzmanager.converters; -import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO; +import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverter; import it.fabioformosa.quartzmanager.dto.JobKeyDTO; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; @@ -10,10 +10,10 @@ import org.quartz.TriggerKey; import org.springframework.stereotype.Component; @Component -public class TriggerToTriggerDTO extends AbstractBaseConverterToDTO { +public class TriggerToTriggerDTO extends AbstractBaseConverter { @Override - protected void convert(Trigger source, TriggerDTO target) { + protected void convert(S source, T target) { TriggerKey triggerKey = source.getKey(); TriggerKeyDTO triggerKeyDTO = conversionService.convert(triggerKey, TriggerKeyDTO.class); target.setTriggerKeyDTO(triggerKeyDTO); @@ -30,7 +30,11 @@ public class TriggerToTriggerDTO extends AbstractBaseConverterToDTO resourceConflict(ResourceConflictException ex) { - ExceptionResponse response = new ExceptionResponse(); - response.setErrorCode("Conflict"); - response.setErrorMessage(ex.getMessage()); - return new ResponseEntity(response, HttpStatus.CONFLICT); - } -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java index e642282..cc591c1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java @@ -1,25 +1,15 @@ package it.fabioformosa.quartzmanager.exceptions; -public class ExceptionResponse { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class ExceptionResponse { private String errorCode; private String errorMessage; - - public ExceptionResponse() {} - - public String getErrorCode() { - return errorCode; - } - - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMessage() { - return errorMessage; - } - - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java index a5af1fe..d3ed391 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java @@ -1,5 +1,9 @@ package it.fabioformosa.quartzmanager.exceptions; +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter public class ResourceConflictException extends RuntimeException { private static final long serialVersionUID = 1791564636123821405L; @@ -8,14 +12,7 @@ public class ResourceConflictException extends RuntimeException { public ResourceConflictException(Long resourceId, String message) { super(message); - setResourceId(resourceId); - } - - public Long getResourceId() { - return resourceId; - } - - public void setResourceId(Long resourceId) { this.resourceId = resourceId; } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java new file mode 100644 index 0000000..26d3933 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java @@ -0,0 +1,16 @@ +package it.fabioformosa.quartzmanager.exceptions; + +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +public class TriggerNotFoundException extends Exception { + + private String name; + + public TriggerNotFoundException(String name) { + super("Trigger with name " + name + " not found!"); + this.name = name; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java index a55378b..b2bf316 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java @@ -1,7 +1,7 @@ package it.fabioformosa.quartzmanager.jobs; -import javax.annotation.Resource; - +import it.fabioformosa.quartzmanager.aspects.ProgressNotifier; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.SchedulerException; @@ -10,13 +10,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessageSendingOperations; -import it.fabioformosa.quartzmanager.aspects.ProgressNotifier; -import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; +import javax.annotation.Resource; /** * Extends this class to create a job that produces LogRecord to be displayed * into the GUI panel - * + * * @author Fabio.Formosa * */ @@ -42,7 +41,7 @@ public abstract class AbstractLoggingJob implements Job { try { LogRecord logMsg = doIt(jobExecutionContext); logAndSend(logMsg); - progressNotifier.send(); + progressNotifier.send(jobExecutionContext); } catch (SchedulerException e) { log.error("Error updating progress " + e.getMessage()); } @@ -54,4 +53,4 @@ public abstract class AbstractLoggingJob implements Job { messagingTemplate.convertAndSend("/topic/logs", logRecord); } -} \ No newline at end of file +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java new file mode 100644 index 0000000..26c2ed2 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java @@ -0,0 +1,26 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.springframework.core.convert.ConversionService; + +public class AbstractSchedulerService { + + protected Scheduler scheduler; + protected ConversionService conversionService; + + public AbstractSchedulerService(Scheduler scheduler, ConversionService conversionService) { + this.scheduler = scheduler; + this.conversionService = conversionService; + } + + protected Trigger getTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = scheduler.getTrigger(new TriggerKey(name)); + if(trigger == null) + throw new TriggerNotFoundException(name); + return trigger; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java similarity index 75% rename from quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java rename to quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java index 7900aee..bea3ef0 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java @@ -3,6 +3,7 @@ package it.fabioformosa.quartzmanager.services; import it.fabioformosa.quartzmanager.common.utils.Try; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; import org.quartz.*; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.core.convert.ConversionService; @@ -10,26 +11,24 @@ import org.springframework.stereotype.Service; import java.util.Optional; +import static org.quartz.TriggerBuilder.newTrigger; + @Service -public class SchedulerService { +public class LegacySchedulerService extends AbstractSchedulerService { public static final int MILLS_IN_A_DAY = 1000 * 60 * 60 * 24; public static final int SEC_IN_A_DAY = 60 * 60 * 24; - private Scheduler scheduler; - private ConversionService conversionService; - - public SchedulerService(Scheduler scheduler, ConversionService conversionService) { - this.scheduler = scheduler; - this.conversionService = conversionService; + public LegacySchedulerService(Scheduler scheduler, ConversionService conversionService) { + super(scheduler, conversionService); } public static int fromTriggerPerDayToMillsInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(SchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value + return (int) Math.ceil(Long.valueOf(LegacySchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value } public static int fromTriggerPerDayToSecInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(SchedulerService.SEC_IN_A_DAY) / triggerPerDay); + return (int) Math.ceil(Long.valueOf(LegacySchedulerService.SEC_IN_A_DAY) / triggerPerDay); } public static long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) { @@ -59,10 +58,12 @@ public class SchedulerService { .findFirst(); } - public TriggerDTO getTriggerByName(String name) throws SchedulerException { - Trigger trigger = scheduler.getTrigger(new TriggerKey(name)); - return conversionService.convert(trigger, TriggerDTO.class); - } + public TriggerDTO getLegacyTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = getTriggerByName(name); + return conversionService.convert(trigger, TriggerDTO.class); + } + + public TriggerDTO scheduleNewTrigger(String name, String jobClassname, SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { Class jobClass = (Class) Class.forName(jobClassname); @@ -71,9 +72,9 @@ public class SchedulerService { .storeDurably(false) .build(); - int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); + int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); - Trigger newTrigger = TriggerBuilder.newTrigger() + Trigger newTrigger = newTrigger() .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(intervalInMills) @@ -88,14 +89,16 @@ public class SchedulerService { return conversionService.convert(newTrigger, TriggerDTO.class); } + + public TriggerDTO rescheduleTrigger(String name, SchedulerConfigParam config) throws SchedulerException { - int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); + int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); Optional optionalTriggerKey = getTriggerByKey(name); TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name)); Trigger trigger = scheduler.getTrigger(triggerKey); - Trigger newTrigger = TriggerBuilder.newTrigger() + Trigger newTrigger = newTrigger() .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(intervalInMills) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java new file mode 100644 index 0000000..95ec237 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java @@ -0,0 +1,48 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.quartz.*; +import org.springframework.core.convert.ConversionService; +import org.springframework.stereotype.Service; + +@Service +public class SimpleTriggerSchedulerService extends AbstractSchedulerService { + + public SimpleTriggerSchedulerService(Scheduler scheduler, ConversionService conversionService) { + super(scheduler, conversionService); + } + + public SimpleTriggerDTO getSimpleTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = getTriggerByName(name); + return conversionService.convert(trigger, SimpleTriggerDTO.class); + } + + public SimpleTriggerDTO scheduleSimpleTrigger(String jobClassname, SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException, ClassNotFoundException { + Class jobClass = (Class) Class.forName(jobClassname); + JobDetail jobDetail = JobBuilder.newJob() + .ofType(jobClass) + .storeDurably(false) + .build(); + + SimpleTrigger newSimpleTrigger = conversionService.convert(triggerCommandDTO, SimpleTrigger.class); + scheduler.scheduleJob(jobDetail, newSimpleTrigger); + + return conversionService.convert(newSimpleTrigger, SimpleTriggerDTO.class); + } + + public TriggerDTO rescheduleSimpleTrigger(SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException { + //Optional optionalTriggerKey = getTriggerByKey(name); +// TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name)); + + SimpleTrigger newSimpleTrigger = conversionService.convert(triggerCommandDTO, SimpleTrigger.class); + + TriggerKey triggerKey = TriggerKey.triggerKey(triggerCommandDTO.getTriggerName()); + scheduler.rescheduleJob(triggerKey, newSimpleTrigger); + + return conversionService.convert(newSimpleTrigger, SimpleTriggerDTO.class); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java new file mode 100644 index 0000000..b0e9a54 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java @@ -0,0 +1,123 @@ +package it.fabioformosa.quartzmanager.controllers; + +import it.fabioformosa.quartzmanager.QuartManagerApplicationTests; +import it.fabioformosa.quartzmanager.controllers.utils.InvalidSimpleTriggerCommandDTOProvider; +import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; +import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import java.util.Date; + +import static org.mockito.ArgumentMatchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + +@ContextConfiguration(classes = {QuartManagerApplicationTests.class}) +@WebMvcTest(controllers = SimpleTriggerController.class, properties = { + "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" +}) +class SimpleTriggerControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private SimpleTriggerSchedulerService simpleTriggerSchedulerService; + + @AfterEach + void cleanUp(){ + Mockito.reset(simpleTriggerSchedulerService); + } + + @Test + void whenGetIsCalled_thenASimpleTriggerIsReturned() throws Exception { + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger"); + Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("mytrigger")).thenReturn(expectedSimpleTriggerDTO); + + mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))); + } + + @Test + void givenAnExistingTrigger_whenGetIsCalled_then404IsReturned() throws Exception { + Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name")); + + mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/not_existing_trigger_name") + .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isNotFound()); + } + + @Test + void givenASimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception { + SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); + Mockito.when(simpleTriggerSchedulerService.scheduleSimpleTrigger(any(), any())).thenReturn(expectedSimpleTriggerDTO); + mockMvc.perform( + post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(simpleTriggerInputDTO)) + ) + .andExpect(MockMvcResultMatchers.status().isCreated()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))) + ; + } + + private SimpleTriggerInputDTO buildSimpleTriggerCommandDTO() { + return SimpleTriggerInputDTO.builder() + .startDate(new Date()) + .repeatCount(20) + .repeatInterval(20000L) + .build(); + } + + @ParameterizedTest + @ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class) + void givenAnInvalidSimpleTriggerCommandDTO_whenPostedANewTrigger_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerComandDTO) throws Exception { + mockMvc.perform(post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSimpleTriggerComandDTO))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + + @Test + void givenATriggerName_whenPutSimpleTriggerCommandDTO_thenTheSimpleTriggerIsRescheduled() throws Exception { + SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName("mytrigger") + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + Mockito.when(simpleTriggerSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO)).thenReturn(expectedSimpleTriggerDTO); + + mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(simpleTriggerInputDTO))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))); + } + + @ParameterizedTest + @ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class) + void givenAnInvalidSimpleTriggerCommandDTO_whenATriggerIsRescheduled_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerCommandTO) throws Exception { + mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSimpleTriggerCommandTO))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index 85ec109..6f741ba 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -6,7 +6,7 @@ import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -25,7 +25,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { - "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob" + "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" }) class TriggerControllerTest { @@ -33,7 +33,7 @@ class TriggerControllerTest { private MockMvc mockMvc; @MockBean - private SchedulerService schedulerService; + private LegacySchedulerService schedulerService; @AfterEach void cleanUp(){ @@ -67,7 +67,7 @@ class TriggerControllerTest { @Test void whenGetIsCalled_thenATriggerIsReturned() throws Exception { TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); - Mockito.when(schedulerService.getTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); + Mockito.when(schedulerService.getLegacyTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); mockMvc.perform(get(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java new file mode 100644 index 0000000..9cdd068 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java @@ -0,0 +1,19 @@ +package it.fabioformosa.quartzmanager.controllers.utils; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.util.stream.Stream; + +public class InvalidSimpleTriggerCommandDTOProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return Stream.of( + Arguments.of(SimpleTriggerInputDTO.builder().build()), + Arguments.of(SimpleTriggerInputDTO.builder().repeatCount(1).build()), + Arguments.of(SimpleTriggerInputDTO.builder().repeatInterval(1L).build()) + ); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java index 2fb9080..7ee9335 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java @@ -1,9 +1,7 @@ package it.fabioformosa.quartzmanager.controllers.utils; import it.fabioformosa.quartzmanager.common.utils.DateUtils; -import it.fabioformosa.quartzmanager.dto.JobKeyDTO; -import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; +import it.fabioformosa.quartzmanager.dto.*; import java.time.LocalDateTime; @@ -30,4 +28,50 @@ public class TriggerUtils { .build(); } + static public SimpleTriggerDTO getSimpleTriggerInstance(String triggerName, SimpleTriggerInputDTO simpleTriggerInputDTO){ + return SimpleTriggerDTO.builder() + .description("simple trigger") + .repeatCount(simpleTriggerInputDTO.getRepeatCount()) + .repeatInterval(simpleTriggerInputDTO.getRepeatInterval()) + .endTime(DateUtils.getHoursFromNow(2L)) + .finalFireTime(DateUtils.getHoursFromNow(2L)) + .jobKeyDTO(JobKeyDTO.builder() + .group("defaultJobGroup") + .name("sampleJob") + .build()) + .mayFireAgain(true) + .triggerKeyDTO(TriggerKeyDTO.builder() + .group("defaultTriggerGroup") + .name(triggerName) + .build()) + .misfireInstruction(1) + .nextFireTime(DateUtils.getHoursFromNow(1L)) + .priority(1) + .startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now())) + .build(); + } + + static public SimpleTriggerDTO getSimpleTriggerInstance(String triggerName){ + return SimpleTriggerDTO.builder() + .description("simple trigger") + .repeatCount(2) + .repeatInterval(1000L) + .endTime(DateUtils.getHoursFromNow(2L)) + .finalFireTime(DateUtils.getHoursFromNow(2L)) + .jobKeyDTO(JobKeyDTO.builder() + .group("defaultJobGroup") + .name("sampleJob") + .build()) + .mayFireAgain(true) + .triggerKeyDTO(TriggerKeyDTO.builder() + .group("defaultTriggerGroup") + .name(triggerName) + .build()) + .misfireInstruction(1) + .nextFireTime(DateUtils.getHoursFromNow(1L)) + .priority(1) + .startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now())) + .build(); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java new file mode 100644 index 0000000..f8fb8d3 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java @@ -0,0 +1,14 @@ +package it.fabioformosa.quartzmanager.jobs; + +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord.LogType; +import org.quartz.JobExecutionContext; + +public class SampleJob extends AbstractLoggingJob { + + @Override + public LogRecord doIt(JobExecutionContext jobExecutionContext) { + return new LogRecord(LogType.INFO, "Hello!"); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java new file mode 100644 index 0000000..ecf8bb9 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java @@ -0,0 +1,81 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.common.utils.DateUtils; +import it.fabioformosa.quartzmanager.dto.*; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; +import org.springframework.core.convert.ConversionService; + +import java.util.Date; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.MockitoAnnotations.openMocks; + +class SimpleTriggerSchedulerServiceTest { + + @InjectMocks + private SimpleTriggerSchedulerService simpleSchedulerService; + + @Mock + private Scheduler scheduler; + + @Mock + private ConversionService conversionService; + + @BeforeEach + void setUp() { + openMocks(this); + } + + @Test + void givenANotExistingTrigger_whenGetSimplerTriggerByNameIsCalled_thenThrowException() throws SchedulerException { + String not_existing_trigger = "not_existing_trigger"; + Mockito.when(scheduler.getTrigger(any())).thenReturn(null); + + Throwable throwable = Assertions.catchThrowable(() -> simpleSchedulerService.getSimpleTriggerByName(not_existing_trigger)); + Assertions.assertThat(throwable).isInstanceOf(TriggerNotFoundException.class); + } + + @Test + void givenASimpleTriggerCommandDTO_whenASimpleTriggerIsScheduled_thenATriggerDTOIsReturned() throws SchedulerException, ClassNotFoundException { + SimpleTriggerInputDTO triggerInputDTO = SimpleTriggerInputDTO.builder() + .startDate(new Date()) + .repeatInterval(5000L).repeatCount(5) + .endDate(DateUtils.getHoursFromNow(1)) + .build(); + + String simpleTriggerName = "simpleTrigger"; + + SimpleTriggerDTO expectedTriggerDTO = SimpleTriggerDTO.builder() + .startTime(triggerInputDTO.getStartDate()) + .repeatInterval(1000) + .repeatCount(10) + .mayFireAgain(true) + .finalFireTime(triggerInputDTO.getEndDate()) + .jobKeyDTO(JobKeyDTO.builder().name("MyJob").build()) + .misfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW) + .triggerKeyDTO(TriggerKeyDTO.builder().name(simpleTriggerName).build()) + .build(); + + Mockito.when(scheduler.scheduleJob(any(), any())).thenReturn(new Date()); + Mockito.when(conversionService.convert(any(), eq(SimpleTriggerDTO.class))).thenReturn(expectedTriggerDTO); + + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName(simpleTriggerName) + .simpleTriggerInputDTO(triggerInputDTO) + .build(); + SimpleTriggerDTO simpleTrigger = simpleSchedulerService.scheduleSimpleTrigger("it.fabioformosa.quartzmanager.jobs.SampleJob", simpleTriggerCommandDTO); + + Assertions.assertThat(simpleTrigger).isEqualTo(expectedTriggerDTO); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties deleted file mode 100644 index 4dfe84c..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -name: Phil \ No newline at end of file diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml new file mode 100644 index 0000000..88a3041 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml @@ -0,0 +1,18 @@ +app: + name: quartz-manager + +quartz: + enabled: true + +job: + frequency: 4000 + repeatCount: 19 + +logging: + level: + org.springframework.boot.autoconfigure.security: INFO + it.fabioformosa: DEBUG + org.quartz: INFO + +quartz-manager: + jobClass: it.fabioformosa.quartzmanager.jobs.SampleJob