Skip to content

Commit

Permalink
Added exception handling
Browse files Browse the repository at this point in the history
  • Loading branch information
juliuskrah committed Oct 11, 2017
1 parent 0ab7da9 commit abf16e4
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 32 deletions.
28 changes: 12 additions & 16 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
<version>2.5.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down Expand Up @@ -132,23 +138,13 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!-- <configuration>
<jvmArguments>add-modules java.xml.bind</jvmArguments>
</configuration> -->
<!-- <configuration> <jvmArguments>add-modules java.xml.bind</jvmArguments>
</configuration> -->
</plugin>
<!-- <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<arg>add-modules</arg>
<arg>java.xml.bind,java.xml.ws.annotation</arg>
</compilerArgs>
<jdkToolchain>
<version>9</version>
</jdkToolchain>
</configuration>
</plugin> -->
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <compilerArgs> <arg>add-modules</arg> <arg>java.xml.bind,java.xml.ws.annotation</arg>
</compilerArgs> <jdkToolchain> <version>9</version> </jdkToolchain> </configuration>
</plugin> -->
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
Expand Down
20 changes: 12 additions & 8 deletions src/main/java/com/juliuskrah/quartz/model/JobDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import java.util.Map;
import java.util.Set;

import javax.validation.Valid;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;
import static org.quartz.JobBuilder.*;
Expand Down Expand Up @@ -56,6 +58,7 @@ public class JobDescriptor {
private List<String> cc;
private List<String> bcc;
private Map<String, Object> data = new LinkedHashMap<>();
@Valid
@JsonProperty("triggers")
private List<TriggerDescriptor> triggerDescriptors = new ArrayList<>();

Expand Down Expand Up @@ -124,16 +127,17 @@ public Set<Trigger> buildTriggers() {
*
* @return the JobDetail built from this descriptor
*/
@JsonIgnore
public JobDetail buildJobDetail() {
// @formatter:off
JobDataMap jobDataMap = new JobDataMap(getData());
JobDataMap jobDataMap = new JobDataMap(data);
jobDataMap.put("subject", subject);
jobDataMap.put("messageBody", messageBody);
jobDataMap.put("to", to);
jobDataMap.put("cc", cc);
jobDataMap.put("bcc", bcc);
return newJob(EmailJob.class)
.withIdentity(getName(), getGroup())
.withIdentity(name, group)
.usingJobData(jobDataMap)
.build();
// @formatter:on
Expand All @@ -160,12 +164,12 @@ public static JobDescriptor buildDescriptor(JobDetail jobDetail, List<? extends
return new JobDescriptor()
.setName(jobDetail.getKey().getName())
.setGroup(jobDetail.getKey().getGroup())
.setSubject(jobDetail.getJobDataMap().getString("subject"))
.setMessageBody(jobDetail.getJobDataMap().getString("messageBody"))
.setTo((List<String>)jobDetail.getJobDataMap().get("to"))
.setCc((List<String>)jobDetail.getJobDataMap().get("cc"))
.setBcc((List<String>)jobDetail.getJobDataMap().get("bcc"))
// .setData(jobDetail.getJobDataMap().getWrappedMap())
.setSubject((String) jobDetail.getJobDataMap().remove("subject"))
.setMessageBody((String) jobDetail.getJobDataMap().remove("messageBody"))
.setTo((List<String>)jobDetail.getJobDataMap().remove("to"))
.setCc((List<String>)jobDetail.getJobDataMap().remove("cc"))
.setBcc((List<String>)jobDetail.getJobDataMap().remove("bcc"))
.setData(jobDetail.getJobDataMap().getWrappedMap())
.setTriggerDescriptors(triggerDescriptors);
// @formatter:on
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
public class TriggerDescriptor {
@NotBlank
private String name;
@NotBlank
private String group;
private LocalDateTime fireTime;
private String cron;
Expand Down Expand Up @@ -80,7 +81,7 @@ public Trigger buildTrigger() {
// @formatter:off
if (!isEmpty(cron)) {
if (!isValidExpression(cron))
throw new IllegalArgumentException("Provided expression " + cron + " is not a valid cron expression");
throw new IllegalArgumentException("Provided expression '" + cron + "' is not a valid cron expression");
return newTrigger()
.withIdentity(buildName(), group)
.withSchedule(cronSchedule(cron)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public Optional<JobDescriptor> findJob(String group, String name) {
scheduler.getTriggersOfJob(jobKey(name, group))));
} catch (SchedulerException e) {
log.error("Could not find job with key - {}.{} due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
// @formatter:on
log.warn("Could not find job with key - {}.{}", group, name);
Expand All @@ -84,6 +85,7 @@ public void deleteJob(String group, String name) {
log.info("Deleted job with key - {}.{}", group, name);
} catch (SchedulerException e) {
log.error("Could not delete job with key - {}.{} due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
}

Expand All @@ -97,6 +99,7 @@ public void pauseJob(String group, String name) {
log.info("Paused job with key - {}.{}", group, name);
} catch (SchedulerException e) {
log.error("Could not pause job with key - {}.{} due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
}

Expand All @@ -110,6 +113,7 @@ public void resumeJob(String group, String name) {
log.info("Resumed job with key - {}.{}", group, name);
} catch (SchedulerException e) {
log.error("Could not resume job with key - {}.{} due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
}
}
17 changes: 11 additions & 6 deletions src/main/java/com/juliuskrah/quartz/service/EmailService.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -53,16 +54,19 @@ public EmailService(Scheduler scheduler) {
*/
@Override
public JobDescriptor createJob(String group, JobDescriptor descriptor) {
descriptor.setGroup(group);
JobDetail jobDetail = descriptor.buildJobDetail();
Set<Trigger> triggersForJob = descriptor.buildTriggers();
log.info("About to save job with key - {}", jobDetail.getKey());
String name = descriptor.getName();
try {
if (scheduler.checkExists(jobKey(name, group)))
throw new DataIntegrityViolationException("Job with Key '" + group + "." + name +"' already exists");
descriptor.setGroup(group);
JobDetail jobDetail = descriptor.buildJobDetail();
Set<Trigger> triggersForJob = descriptor.buildTriggers();
log.info("About to save job with key - {}", jobDetail.getKey());
scheduler.scheduleJob(jobDetail, triggersForJob, false);
log.info("Job with key - {} saved sucessfully", jobDetail.getKey());
} catch (SchedulerException e) {
log.error("Could not save job with key - {} due to error - {}", jobDetail.getKey(), e.getLocalizedMessage());
throw new IllegalArgumentException(e.getLocalizedMessage());
log.error("Could not save job with key - {}.{} due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
return descriptor;
}
Expand Down Expand Up @@ -90,6 +94,7 @@ public void updateJob(String group, String name, JobDescriptor descriptor) {
log.warn("Could not find job with key - {}.{} to update", group, name);
} catch (SchedulerException e) {
log.error("Could not find job with key - {}.{} to update due to error - {}", group, name, e.getLocalizedMessage());
throw new RuntimeException(e.getLocalizedMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public ResponseEntity<JobDescriptor> findJob(@PathVariable String group, @PathVa
* @return
*/
@PutMapping(path = "/groups/{group}/jobs/{name}")
public ResponseEntity<Void> updateJob(@PathVariable String group, @PathVariable String name, @RequestBody JobDescriptor descriptor) {
public ResponseEntity<Void> updateJob(@PathVariable String group, @PathVariable String name, @Valid @RequestBody JobDescriptor descriptor) {
jobService.updateJob(group, name, descriptor);
return ResponseEntity.noContent().build();
}
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/juliuskrah/quartz/web/rest/errors/ErrorVO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.juliuskrah.quartz.web.rest.errors;

import java.io.Serializable;
import java.util.List;

import org.immutables.value.Value;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@Value.Immutable
@JsonSerialize(as = ImmutableErrorVO.class)
@JsonDeserialize(as = ImmutableErrorVO.class)
public interface ErrorVO extends Serializable {
String message();

String description();

List<FieldErrorVO> fieldErrors();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.juliuskrah.quartz.web.rest.errors;

import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;

import java.util.List;

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestControllerAdvice
public class ExceptionTranslator {

@ExceptionHandler(IllegalStateException.class)
@ResponseStatus(BAD_REQUEST)
public ErrorVO processUnsupportedTriggerError(IllegalStateException ex) {
ErrorVO dto = ImmutableErrorVO.builder()
.message("400: Bad Request")
.description(ex.getMessage())
.build();
return dto;
}

@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(BAD_REQUEST)
public ErrorVO processInvalidCronExpressionError(IllegalArgumentException ex) {
ErrorVO dto = ImmutableErrorVO.builder()
.message("400: Bad Request")
.description(ex.getMessage())
.build();
return dto;
}

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(BAD_REQUEST)
public ErrorVO processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
ImmutableErrorVO.Builder builder = ImmutableErrorVO.builder()
.message("400: Bad Request")
.description("Validation errors exist");

for (FieldError fieldError : fieldErrors) {
builder.addFieldErrors(ImmutableFieldErrorVO.builder()
.objectName(fieldError.getObjectName())
.field(fieldError.getField())
.message(fieldError.getCode())
.build());
}
return builder.build();
}

@ExceptionHandler(DataIntegrityViolationException.class)
@ResponseStatus(CONFLICT)
public ErrorVO processDataIntegrityViolationError(DataIntegrityViolationException ex) {
ErrorVO dto = ImmutableErrorVO.builder()
.message("409: Conflict")
.description(ex.getMessage())
.build();
return dto;
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorVO> processException(Exception ex) throws Exception {
if (log.isDebugEnabled()) {
log.debug("An unexpected error occurred: {}", ex.getMessage(), ex);
} else {
log.error("An unexpected error occurred: {}", ex.getMessage());
}
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation
(ex.getClass(), ResponseStatus.class) != null)
throw ex;

ErrorVO dto = ImmutableErrorVO.builder()
.message("500: Internal server error")
.description("Internal server error has occurred")
.build();
return ResponseEntity.status(INTERNAL_SERVER_ERROR).body(dto);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.juliuskrah.quartz.web.rest.errors;

import java.io.Serializable;

import org.immutables.value.Value;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

@Value.Immutable
@JsonSerialize(as = ImmutableFieldErrorVO.class)
@JsonDeserialize(as = ImmutableFieldErrorVO.class)
public interface FieldErrorVO extends Serializable {
String objectName();

String field();

String message();
}

0 comments on commit abf16e4

Please sign in to comment.