Skip to content

Commit 2d56a89

Browse files
committed
Spring Web Error Handling with and without zalando problem
1 parent 953b50f commit 2d56a89

File tree

10 files changed

+266
-73
lines changed

10 files changed

+266
-73
lines changed

pom.xml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<dse-driver.version>1.1.2</dse-driver.version>
2727
<reactor.kafka.version>1.0.0.RELEASE</reactor.kafka.version>
2828
<springfox.version>2.8.0</springfox.version>
29+
<zalando.problem.version>0.22.0</zalando.problem.version>
2930
</properties>
3031

3132
<dependencies>
@@ -141,11 +142,33 @@
141142
<groupId>io.kubernetes</groupId>
142143
<artifactId>client-java</artifactId>
143144
</dependency>
144-
145+
<!-- https://mvnrepository.com/artifact/org.zalando/problem -->
146+
<dependency>
147+
<groupId>org.zalando</groupId>
148+
<artifactId>problem-spring-web</artifactId>
149+
<version>${zalando.problem.version}</version>
150+
</dependency>
145151
</dependencies>
146152

147153
<dependencyManagement>
148154
<dependencies>
155+
<!-- https://mvnrepository.com/artifact/org.zalando/problem -->
156+
<dependency>
157+
<groupId>org.zalando</groupId>
158+
<artifactId>problem</artifactId>
159+
<version>${zalando.problem.version}</version>
160+
</dependency>
161+
<dependency>
162+
<groupId>org.zalando</groupId>
163+
<artifactId>jackson-datatype-problem</artifactId>
164+
<version>${zalando.problem.version}</version>
165+
</dependency>
166+
<!-- https://mvnrepository.com/artifact/org.zalando/problem-spring-web -->
167+
<dependency>
168+
<groupId>org.zalando</groupId>
169+
<artifactId>problem-spring-web</artifactId>
170+
<version>${zalando.problem.version}</version>
171+
</dependency>
149172
<dependency>
150173
<groupId>de.codecentric</groupId>
151174
<artifactId>spring-boot-admin-dependencies</artifactId>
Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
package com.aardizio;
22

3-
import java.util.Collection;
3+
import com.aardizio.errors.ApiError;
4+
import com.aardizio.errors.ApiErrorHandler;
5+
import com.aardizio.errors.CustomException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
47

5-
import org.apache.kafka.streams.KafkaStreams;
68
import org.springframework.beans.factory.annotation.Autowired;
79
import org.springframework.beans.factory.annotation.Qualifier;
810
import org.springframework.boot.SpringApplication;
911
import org.springframework.boot.autoconfigure.SpringBootApplication;
1012
import org.springframework.boot.context.event.ApplicationReadyEvent;
1113
import org.springframework.context.ApplicationListener;
14+
import org.springframework.context.annotation.Bean;
15+
import org.springframework.http.HttpStatus;
1216
import org.springframework.web.reactive.config.EnableWebFlux;
17+
import org.zalando.problem.ProblemModule;
18+
import org.zalando.problem.validation.ConstraintViolationProblemModule;
1319

1420
import lombok.extern.slf4j.Slf4j;
1521
import reactor.kafka.receiver.KafkaReceiver;
@@ -19,27 +25,30 @@
1925
@EnableSwagger2
2026
@EnableWebFlux
2127
@Slf4j
22-
public class SpringReactiveApplication implements ApplicationListener<ApplicationReadyEvent>{
28+
public class SpringReactiveApplication implements ApplicationListener<ApplicationReadyEvent> {
2329

2430
@Qualifier("simpleConsumer")
2531
@Autowired
26-
private KafkaReceiver<String,String> receiver;
27-
28-
@Autowired
29-
private Collection<KafkaStreams> streams;
32+
private KafkaReceiver<String, String> receiver;
3033

34+
@Autowired
35+
private ApiErrorHandler apiErrorHandler;
3136

3237
public static void main(String[] args) {
3338
SpringApplication.run(SpringReactiveApplication.class, args);
3439
}
3540

36-
public void onApplicationEvent(ApplicationReadyEvent event){
41+
public void onApplicationEvent(ApplicationReadyEvent event) {
42+
apiErrorHandler.register(CustomException.class, new ApiError(HttpStatus.BAD_REQUEST, "002", "3"));
3743
receiver.receive().subscribe(r -> {
38-
log.info("Received message: %s\n", r);
44+
log.info("Received message: %s\n", r);
3945
r.receiverOffset().acknowledge();
4046
});
41-
streams.forEach(KafkaStreams::start);
4247
}
43-
48+
49+
@Bean
50+
public ObjectMapper objectMapper() {
51+
return new ObjectMapper().registerModules(new ProblemModule().withStackTraces(false), new ConstraintViolationProblemModule());
52+
}
4453

4554
}
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
package com.aardizio.config;
22

3-
import java.util.Properties;
4-
5-
import org.apache.kafka.common.serialization.Serde;
6-
import org.apache.kafka.common.serialization.Serdes;
7-
import org.apache.kafka.streams.Consumed;
8-
import org.apache.kafka.streams.KafkaStreams;
9-
import org.apache.kafka.streams.StreamsBuilder;
10-
import org.apache.kafka.streams.StreamsConfig;
11-
import org.apache.kafka.streams.kstream.KStream;
12-
import org.apache.kafka.streams.kstream.Printed;
13-
import org.apache.kafka.streams.kstream.Produced;
3+
import lombok.extern.slf4j.Slf4j;
144
import org.springframework.beans.factory.annotation.Autowired;
155
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
16-
import org.springframework.context.annotation.Bean;
176
import org.springframework.context.annotation.Configuration;
187

19-
import lombok.extern.slf4j.Slf4j;
20-
218
/**
229
* Config class to build kafka streams.
2310
* @author alessandropioardizio
@@ -31,7 +18,7 @@ public class KafkaStreamsConfig {
3118
private KafkaProperties kafkaProperties;
3219

3320

34-
@Bean("simpleStream")
21+
/* @Bean("simpleStream")
3522
public KafkaStreams simpleStream() {
3623
3724
Properties props = new Properties();
@@ -47,6 +34,6 @@ public KafkaStreams simpleStream() {
4734
upperCasedStream.print(Printed.<String, String>toSysOut().withLabel("Yelling App"));
4835
log.info("Requesting a simple kafka stream");
4936
return new KafkaStreams(builder.build(),streamsConfig);
50-
}
37+
} */
5138

5239
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.aardizio.errors;
2+
3+
import org.zalando.problem.AbstractThrowableProblem;
4+
5+
import javax.annotation.concurrent.Immutable;
6+
import java.net.URI;
7+
8+
import static java.lang.String.format;
9+
import static org.zalando.problem.Status.BAD_REQUEST;
10+
11+
@Immutable
12+
public abstract class AbstractBusinessProblem extends AbstractThrowableProblem {
13+
14+
static final URI TYPE = URI.create("https://example.org/out-of-stock");
15+
16+
private String message;
17+
private String errorCode;
18+
private String level;
19+
20+
public AbstractBusinessProblem(final String message, final String errorCode, final String level) {
21+
super(TYPE, "Business Problem", BAD_REQUEST, format("Item %s is no longer available", message));
22+
this.message = message;
23+
this.errorCode = errorCode;
24+
this.level = level;
25+
}
26+
27+
public String getMessage() {
28+
return message;
29+
}
30+
31+
public String getErrorCode() {
32+
return errorCode;
33+
}
34+
35+
public String getLevel() {
36+
return level;
37+
}
38+
39+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.aardizio.errors;
2+
3+
import org.springframework.http.HttpStatus;
4+
5+
public class ApiError {
6+
7+
private HttpStatus status;
8+
private String message;
9+
private String errorCode;
10+
private String level;
11+
12+
public ApiError(HttpStatus status, String errorCode , String level , String message) {
13+
super();
14+
this.status = status;
15+
this.message = message;
16+
this.errorCode = errorCode;
17+
this.level = level;
18+
}
19+
20+
public ApiError(HttpStatus status, String errorCode , String level) {
21+
super();
22+
this.status = status;
23+
this.errorCode = errorCode;
24+
this.level = level;
25+
}
26+
27+
public HttpStatus getStatus() {
28+
return status;
29+
}
30+
31+
public void setStatus(HttpStatus status) {
32+
this.status = status;
33+
}
34+
35+
public String getMessage() {
36+
return message;
37+
}
38+
39+
public void setMessage(String message) {
40+
this.message = message;
41+
}
42+
43+
public String getErrorCode() {
44+
return errorCode;
45+
}
46+
47+
public void setErrorCode(String errorCode) {
48+
this.errorCode = errorCode;
49+
}
50+
51+
public String getLevel() {
52+
return level;
53+
}
54+
55+
public void setLevel(String level) {
56+
this.level = level;
57+
}
58+
59+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.aardizio.errors;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.http.HttpStatus;
5+
import org.springframework.http.MediaType;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.stereotype.Component;
8+
import org.springframework.web.bind.annotation.ControllerAdvice;
9+
import org.springframework.web.context.request.WebRequest;
10+
import org.zalando.problem.spring.web.advice.ProblemHandling;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
/**
16+
* https://www.baeldung.com/problem-spring-web
17+
* https://github.com/zalando/problem
18+
* https://github.com/zalando/problem-spring-web
19+
*/
20+
@Component
21+
@ControllerAdvice
22+
@Slf4j
23+
public class ApiErrorHandler implements ProblemHandling {
24+
25+
private Map<Class, ApiError> errors = new HashMap<>();
26+
27+
{
28+
register(Exception.class, new ApiError(HttpStatus.BAD_REQUEST, "001", "2"));
29+
}
30+
31+
public ResponseEntity<Object> handleAll(Exception ex, WebRequest request) {
32+
33+
log.error("Handling exception of type {} , for request path {}", ex.getClass(), request.getContextPath());
34+
if (errors.containsKey(ex.getClass())) {
35+
ApiError ae = errors.get(ex.getClass());
36+
ae.setMessage(ex.getMessage());
37+
return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_PROBLEM_JSON).body(ae);
38+
} else {
39+
// returning a default response
40+
return ResponseEntity.badRequest().contentType(MediaType.APPLICATION_PROBLEM_JSON)
41+
.body(errors.get(ex.getClass()));
42+
}
43+
}
44+
45+
public void register(Class key, ApiError value) {
46+
errors.put(key, value);
47+
}
48+
49+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.aardizio.errors;
2+
3+
public class CustomException extends Exception {
4+
5+
public CustomException(String message){
6+
super(message);
7+
}
8+
}
9+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.aardizio.errors;
2+
3+
public class DefaultBusinessProblem extends AbstractBusinessProblem {
4+
public DefaultBusinessProblem(final String message, final String errorCode, final String level){
5+
super(message, errorCode, level);
6+
}
7+
}

0 commit comments

Comments
 (0)