1 package cn.home1.oss.lib.errorhandle.internal.rpc;
2
3 import static com.google.common.base.Charsets.UTF_8;
4 import static com.google.common.collect.Lists.newArrayList;
5 import static org.apache.commons.lang3.StringUtils.toEncodedString;
6
7 import cn.home1.oss.lib.errorhandle.api.ResolvedError;
8 import cn.home1.oss.lib.errorhandle.api.ResolvedErrorException;
9
10 import com.fasterxml.jackson.databind.ObjectMapper;
11 import cn.home1.oss.lib.common.JaxbUtils;
12
13 import feign.codec.DecodeException;
14
15 import lombok.Setter;
16 import lombok.extern.slf4j.Slf4j;
17
18 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.http.HttpHeaders;
20 import org.springframework.http.HttpStatus;
21 import org.springframework.oxm.jaxb.Jaxb2Marshaller;
22 import org.springframework.util.StreamUtils;
23 import org.springframework.web.client.HttpClientErrorException;
24 import org.springframework.web.client.HttpServerErrorException;
25
26 @Setter
27 @Slf4j
28 public class GenericFeignErrorDecoder implements feign.codec.ErrorDecoder {
29
30 private final feign.codec.ErrorDecoder delegate = new Default();
31
32 private final Jaxb2Marshaller jaxb2Marshaller = JaxbUtils.jaxb2Marshaller();
33 @Autowired
34 private ObjectMapper objectMapper;
35
36 private static HttpHeaders cloneHeaders(final feign.Response response) {
37 final HttpHeaders responseHeaders = new HttpHeaders();
38 response.headers().entrySet().stream()
39 .forEach(entry -> responseHeaders.put(entry.getKey(), newArrayList(entry.getValue())));
40 return responseHeaders;
41 }
42
43 private static byte[] responseBody(final feign.Response response) {
44 try {
45 return StreamUtils.copyToByteArray(response.body().asInputStream());
46 } catch (final Exception wrapped) {
47 log.info("Failed to process response body.", wrapped);
48 throw new DecodeException("Failed to process response body.", wrapped);
49 }
50 }
51
52 @Override
53 public Exception decode(final String methodKey, final feign.Response response) {
54 if (log.isDebugEnabled()) {
55 log.debug("decode : {}", methodKey);
56 }
57
58 final HttpHeaders responseHeaders = cloneHeaders(response);
59 final byte[] responseBody = responseBody(response);
60 final Exception result;
61 if (ResolvedErrorException.isResolvedError(responseHeaders)) {
62 final String info = toEncodedString(responseBody, UTF_8);
63 try {
64
65 final String json =
66 info.startsWith("<string>") ? JaxbUtils.unmarshal(this.jaxb2Marshaller, info, String.class) : info;
67 result = new ResolvedErrorException(this.objectMapper.readValue(json, ResolvedError.class));
68 } catch (final Exception wrapped) {
69 log.info("Failed to decode resolvedError. info: " + info, wrapped);
70 throw new DecodeException("Failed to decode resolvedError. info: " + info, wrapped);
71 }
72 } else {
73 final HttpStatus statusCode = HttpStatus.valueOf(response.status());
74 final String statusText = response.reason();
75 if (response.status() >= 400 && response.status() <= 499) {
76 result = new HttpClientErrorException(
77 statusCode,
78 statusText,
79 responseHeaders,
80 responseBody,
81 null);
82 } else if (response.status() >= 500 && response.status() <= 599) {
83 result = new HttpServerErrorException(
84 statusCode,
85 statusText,
86 responseHeaders,
87 responseBody,
88 null);
89 } else {
90 result = delegate.decode(methodKey, response);
91 }
92 }
93 return result;
94 }
95 }