View Javadoc
1   package cn.home1.oss.lib.errorhandle.api;
2   
3   import static com.google.common.collect.Maps.newLinkedHashMap;
4   import static lombok.AccessLevel.PRIVATE;
5   
6   import com.fasterxml.jackson.annotation.JsonIgnore;
7   import com.fasterxml.jackson.annotation.JsonInclude;
8   import com.fasterxml.jackson.annotation.JsonProperty;
9   
10  import lombok.AllArgsConstructor;
11  import lombok.Builder;
12  import lombok.EqualsAndHashCode;
13  import lombok.Getter;
14  import lombok.Setter;
15  import lombok.ToString;
16  import lombok.extern.slf4j.Slf4j;
17  
18  import org.apache.commons.lang3.ArrayUtils;
19  import org.codehaus.jackson.map.annotate.JsonSerialize;
20  import org.springframework.http.HttpHeaders;
21  import org.springframework.http.HttpStatus;
22  
23  import java.io.Serializable;
24  import java.util.Map;
25  
26  import javax.xml.bind.annotation.XmlAccessType;
27  import javax.xml.bind.annotation.XmlAccessorType;
28  import javax.xml.bind.annotation.XmlElement;
29  import javax.xml.bind.annotation.XmlElementWrapper;
30  import javax.xml.bind.annotation.XmlElements;
31  import javax.xml.bind.annotation.XmlRootElement;
32  import javax.xml.bind.annotation.XmlType;
33  
34  /**
35   * Resolved error.
36   *
37   * <p>
38   * Created by zhanghaolun on 16/7/1.
39   * </p>
40   */
41  @XmlRootElement(name = "error") // Jaxb2RootElementHttpMessageConverter
42  @XmlAccessorType(XmlAccessType.FIELD)
43  @XmlType(propOrder = { //
44    "error", "exception", "message", "path", "status", "timestamp", "trace", "validationErrors", //
45    "datetime", "headers", "localizedMessage", "tracks"})
46  @JsonInclude(JsonInclude.Include.NON_EMPTY) // for Jackson 2.x
47  @JsonSerialize(include = JsonSerialize.Inclusion.NON_EMPTY) // for Jackson 1.x
48  @Builder(builderMethodName = "resolvedErrorBuilder")
49  @AllArgsConstructor(access = PRIVATE)
50  @ToString
51  @EqualsAndHashCode(of = {"error", "exception", "path", "status", "timestamp", "localizedMessage"})
52  @Setter(PRIVATE)
53  @Getter
54  @Slf4j
55  public class ResolvedError implements Serializable {
56  
57    public static final String RESOLVED_ERROR_COOKIE = "resolvedErrorCookie";
58    public static final String RESOLVED_ERROR_OBJECT = "resolvedError";
59    static final String HEADER_RESOLVED_ERROR = "RESOLVED-ERROR";
60    private static final long serialVersionUID = 1L;
61  
62    // ------------------------------ basic ------------------------------
63    @JsonProperty("error")
64    private String error;
65    @XmlElement
66    private String exception;
67    private String message;
68    private String path;
69    private Integer status;
70    private Long timestamp;
71    private String trace;
72    /**
73     * Nested XmlElements see: https://github.com/FasterXML/jackson-module-jaxb-annotations/issues/42
74     */
75    @JsonProperty("validationErrors")
76    @XmlElementWrapper(name = "validationErrors")
77    @XmlElement(name = "validationError")
78    //@XmlElements(@XmlElement(name = "validationError"))
79    private ValidationError[] validationErrors;
80    // ------------------------------ extended ------------------------------
81    /**
82     * ISO8601 string.
83     */
84    private String datetime;
85    /**
86     * 解析得到的 异常响应头信息.
87     */
88    @JsonProperty("headers")
89    @XmlElementWrapper(name = "headers")
90    @XmlElement(name = "header")
91    private HttpHeader[] headers;
92    /**
93     * 解析得到的 错误信息.
94     */
95    private String localizedMessage;
96    /**
97     * 解析得到的 异常的调用路径 (RPC).
98     */
99    @JsonProperty("tracks")
100   @XmlElementWrapper(name = "tracks")
101   @XmlElement(name = "track")
102   private String[] tracks;
103 
104   private ResolvedError() {
105     this.headers = HttpHeader.fromHttpHeaders(newHttpHeaders());
106   }
107 
108   public static HttpHeaders newHttpHeaders() {
109     final HttpHeaders headers = new HttpHeaders();
110     headers.add(HEADER_RESOLVED_ERROR, HEADER_RESOLVED_ERROR);
111     return headers;
112   }
113 
114   public static ResolvedError fromErrorAttributes(final Map<String, Object> errorAttributes) {
115     return errorAttributes == null ? null : ResolvedError.resolvedErrorBuilder()
116       // basic
117       .error((String) errorAttributes.get("error")) //
118       .exception((String) errorAttributes.get("exception"))
119       .message((String) errorAttributes.get("message")) //
120       .path((String) errorAttributes.get("path")) //
121       .status((Integer) errorAttributes.get("status")) //
122       .timestamp((Long) errorAttributes.get("timestamp")) //
123       .trace((String) errorAttributes.get("trace")) //
124       .validationErrors((ValidationError[]) errorAttributes.get("validationErrors")) //
125       // extended
126       .datetime((String) errorAttributes.get("datetime"))
127       .headers((HttpHeader[]) errorAttributes.get("headers")) //
128       .localizedMessage((String) errorAttributes.get("localizedMessage")) //
129       .tracks((String[]) errorAttributes.get("tracks")).build();
130   }
131 
132   @JsonIgnore
133   @org.codehaus.jackson.annotate.JsonIgnore
134   public HttpStatus getHttpStatus() {
135     HttpStatus result;
136     try {
137       result = HttpStatus.valueOf(this.status);
138     } catch (final Exception ex) {
139       result = HttpStatus.INTERNAL_SERVER_ERROR;
140       log.debug("error parse http status {}", this.status, ex);
141     }
142     return result;
143   }
144 
145   /**
146    * before set into cookie, call this method to avoid header size exceed limit.
147    *
148    * @return this
149    */
150   public ResolvedError eraseTraces() {
151     this.setTracks(null);
152     this.setTrace(null);
153     return this;
154   }
155 
156   public ResolvedError trackPrepend(final String track) {
157     this.tracks = this.tracks != null ? //
158       ArrayUtils.add(this.tracks, 0, track) : //
159       new String[]{track};
160     return this;
161   }
162 
163   public Map<String, Object> toErrorAttributes() {
164     final Map<String, Object> map = newLinkedHashMap();
165     // basic
166     map.put("error", this.error);
167     map.put("exception", this.exception);
168     map.put("message", this.message);
169     map.put("path", this.path);
170     map.put("status", this.status);
171     map.put("timestamp", this.timestamp);
172     map.put("trace", this.trace);
173     map.put("validationErrors", this.validationErrors);
174     // extended
175     map.put("datetime", datetime);
176     map.put("headers", this.headers);
177     map.put("localizedMessage", this.localizedMessage);
178     map.put("tracks", this.tracks);
179 
180     map.entrySet().removeIf(e -> e.getValue() == null);
181     return map;
182   }
183 }