1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.restapi.v2;
20
21 import java.beans.Transient;
22 import java.time.Instant;
23 import java.util.Date;
24 import java.util.Optional;
25 import java.util.UUID;
26
27 import org.apache.logging.log4j.LogManager;
28 import org.mycore.restapi.converter.MCRInstantXMLAdapter;
29
30 import com.fasterxml.jackson.annotation.JsonAutoDetect;
31
32 import jakarta.ws.rs.BadRequestException;
33 import jakarta.ws.rs.ClientErrorException;
34 import jakarta.ws.rs.ForbiddenException;
35 import jakarta.ws.rs.InternalServerErrorException;
36 import jakarta.ws.rs.NotAcceptableException;
37 import jakarta.ws.rs.NotAllowedException;
38 import jakarta.ws.rs.NotAuthorizedException;
39 import jakarta.ws.rs.NotFoundException;
40 import jakarta.ws.rs.NotSupportedException;
41 import jakarta.ws.rs.ServerErrorException;
42 import jakarta.ws.rs.WebApplicationException;
43 import jakarta.ws.rs.core.Response;
44 import jakarta.xml.bind.annotation.XmlAccessType;
45 import jakarta.xml.bind.annotation.XmlAccessorType;
46 import jakarta.xml.bind.annotation.XmlAttribute;
47 import jakarta.xml.bind.annotation.XmlElement;
48 import jakarta.xml.bind.annotation.XmlRootElement;
49 import jakarta.xml.bind.annotation.XmlTransient;
50 import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
51
52 @XmlRootElement(name = "error")
53 @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
54 @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
55 public class MCRErrorResponse {
56
57 UUID uuid;
58
59 Instant timestamp;
60
61 String errorCode;
62
63 Throwable cause;
64
65 String message;
66
67 String detail;
68
69 int status;
70
71 public static MCRErrorResponse fromStatus(int status) {
72 final MCRErrorResponse response = new MCRErrorResponse();
73 response.setStatus(status);
74 return response;
75 }
76
77 private MCRErrorResponse() {
78 uuid = UUID.randomUUID();
79 timestamp = Instant.now();
80 }
81
82 public WebApplicationException toException() {
83 WebApplicationException e;
84 Response.Status s = Response.Status.fromStatusCode(status);
85 final Response response = Response.status(s)
86 .entity(this)
87 .build();
88
89 switch (Response.Status.Family.familyOf(status)) {
90 case CLIENT_ERROR:
91
92 switch (s != null ? s : Response.Status.OK) {
93 case BAD_REQUEST:
94 e = new BadRequestException(getMessage(), response, getCause());
95 break;
96 case FORBIDDEN:
97 e = new ForbiddenException(getMessage(), response, getCause());
98 break;
99 case NOT_ACCEPTABLE:
100 e = new NotAcceptableException(getMessage(), response, getCause());
101 break;
102 case METHOD_NOT_ALLOWED:
103 e = new NotAllowedException(getMessage(), response, getCause());
104 break;
105 case UNAUTHORIZED:
106 e = new NotAuthorizedException(getMessage(), response, getCause());
107 break;
108 case NOT_FOUND:
109 e = new NotFoundException(getMessage(), response, getCause());
110 break;
111 case UNSUPPORTED_MEDIA_TYPE:
112 e = new NotSupportedException(getMessage(), response, getCause());
113 break;
114 default:
115 e = new ClientErrorException(getMessage(), response, getCause());
116 }
117 break;
118 case SERVER_ERROR:
119
120 switch (s != null ? s : Response.Status.OK) {
121 case INTERNAL_SERVER_ERROR:
122 e = new InternalServerErrorException(getMessage(), response, getCause());
123 break;
124 default:
125 e = new ServerErrorException(getMessage(), response, getCause());
126 }
127 break;
128 default:
129 e = new WebApplicationException(getMessage(), getCause(), response);
130 }
131 LogManager.getLogger().error(this::getLogMessage, e);
132 return e;
133 }
134
135 String getLogMessage() {
136 return getUuid() + " - " + getErrorCode() + ": " + getMessage();
137 }
138
139 @XmlAttribute
140 public UUID getUuid() {
141 return uuid;
142 }
143
144 public void setUuid(UUID uuid) {
145 this.uuid = uuid;
146 }
147
148 @XmlAttribute
149 public String getErrorCode() {
150 return Optional.ofNullable(errorCode).filter(s -> !s.isBlank()).orElse("UNKNOWN");
151 }
152
153 public void setErrorCode(String errorCode) {
154 this.errorCode = errorCode;
155 }
156
157 @Transient
158 @XmlTransient
159 public Throwable getCause() {
160 return cause;
161 }
162
163 public void setCause(Throwable cause) {
164 this.cause = cause;
165 }
166
167 @XmlElement
168 public String getMessage() {
169 return message;
170 }
171
172 public void setMessage(String message) {
173 this.message = message;
174 }
175
176 @XmlElement
177 public String getDetail() {
178 return detail;
179 }
180
181 public void setDetail(String detail) {
182 this.detail = detail;
183 }
184
185 @XmlAttribute
186 @XmlJavaTypeAdapter(MCRInstantXMLAdapter.class)
187 public Instant getTimestamp() {
188 return timestamp;
189 }
190
191 public void setTimestamp(Date timestamp) {
192 this.timestamp = timestamp.toInstant();
193 }
194
195 @XmlTransient
196 @Transient
197 public int getStatus() {
198 return status;
199 }
200
201 public void setStatus(int status) {
202 this.status = status;
203 }
204
205 public MCRErrorResponse withCause(Throwable cause) {
206 setCause(cause);
207 return this;
208 }
209
210 public MCRErrorResponse withMessage(String message) {
211 setMessage(message);
212 return this;
213 }
214
215 public MCRErrorResponse withDetail(String detail) {
216 setDetail(detail);
217 return this;
218 }
219
220 public MCRErrorResponse withErrorCode(String errorCode) {
221 setErrorCode(errorCode);
222 return this;
223 }
224
225 @Override
226 public String toString() {
227 return "MCRErrorResponse{" +
228 "uuid=" + uuid +
229 ", status=" + status +
230 ", timestamp=" + timestamp +
231 ", errorCode='" + errorCode + '\'' +
232 ", message='" + message + '\'' +
233 ", detail='" + detail + '\'' +
234 ", cause=" + cause +
235 '}';
236 }
237 }