1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.frontend.jersey.resources;
20
21 import java.util.Optional;
22 import java.util.stream.Stream;
23
24 import org.apache.logging.log4j.LogManager;
25 import org.apache.logging.log4j.Logger;
26 import org.jdom2.Document;
27 import org.mycore.common.MCRSessionMgr;
28 import org.mycore.common.MCRTransactionHelper;
29 import org.mycore.common.content.MCRContent;
30 import org.mycore.frontend.jersey.MCRJerseyUtil;
31 import org.mycore.frontend.servlets.MCRErrorServlet;
32
33 import jakarta.servlet.http.HttpServletRequest;
34 import jakarta.ws.rs.InternalServerErrorException;
35 import jakarta.ws.rs.WebApplicationException;
36 import jakarta.ws.rs.core.Context;
37 import jakarta.ws.rs.core.HttpHeaders;
38 import jakarta.ws.rs.core.MediaType;
39 import jakarta.ws.rs.core.Response;
40 import jakarta.ws.rs.core.UriInfo;
41 import jakarta.ws.rs.core.Response.Status;
42 import jakarta.ws.rs.ext.ExceptionMapper;
43 import jakarta.ws.rs.ext.Provider;
44 import jakarta.xml.bind.annotation.XmlAttribute;
45 import jakarta.xml.bind.annotation.XmlElement;
46 import jakarta.xml.bind.annotation.XmlRootElement;
47 import jakarta.xml.bind.annotation.adapters.XmlAdapter;
48 import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
49
50
51
52
53
54
55
56
57 @Provider
58 public class MCRJerseyExceptionMapper implements ExceptionMapper<Exception> {
59
60 private static Logger LOGGER = LogManager.getLogger();
61
62 @Context
63 HttpHeaders headers;
64
65 @Context
66 HttpServletRequest request;
67
68 @Context
69 UriInfo uriInfo;
70
71 @Override
72 public Response toResponse(Exception exc) {
73 Response response = getResponse(exc);
74 if (exc instanceof WebApplicationException && !(exc instanceof InternalServerErrorException)) {
75 LOGGER.warn("{}: {}, {}", uriInfo.getRequestUri(), response.getStatus(), exc.getMessage());
76 LOGGER.debug("Exception: ", exc);
77 } else {
78 LOGGER.warn(() -> "Error while processing request " + uriInfo.getRequestUri(), exc);
79 }
80 if (headers.getAcceptableMediaTypes().contains(MediaType.TEXT_HTML_TYPE)) {
81
82 if (!MCRSessionMgr.isLocked() && !MCRTransactionHelper.isTransactionActive()) {
83 MCRSessionMgr.getCurrentSession();
84 MCRTransactionHelper.beginTransaction();
85 }
86 try {
87 int status = response.getStatus();
88 String source = exc.getStackTrace().length > 0 ? exc.getStackTrace()[0].toString() : null;
89 Document errorPageDocument = MCRErrorServlet.buildErrorPage(exc.getMessage(), status,
90 uriInfo.getRequestUri().toASCIIString(), null, source, exc);
91 MCRContent result = MCRJerseyUtil.transform(errorPageDocument, request);
92 return Response.serverError()
93 .entity(result.getInputStream())
94 .type(result.getMimeType())
95 .status(status)
96 .build();
97 } catch (Exception transformException) {
98 return Response.status(Status.INTERNAL_SERVER_ERROR).entity(transformException).build();
99 } finally {
100 if (!MCRSessionMgr.isLocked()) {
101 MCRTransactionHelper.commitTransaction();
102 }
103 }
104 }
105 return response;
106 }
107
108 private Response getResponse(Exception exc) {
109 if (exc instanceof WebApplicationException) {
110 Response response = ((WebApplicationException) exc).getResponse();
111 if (response.hasEntity()) {
112 return response;
113 }
114 return Response.fromResponse(response)
115 .entity(exc.getMessage())
116 .type(MediaType.TEXT_PLAIN_TYPE)
117 .build();
118 }
119 Object entity = Optional.ofNullable(request.getContentType())
120 .map(MediaType::valueOf)
121 .filter(t -> t.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE) || t
122 .isCompatible(MediaType.MULTIPART_FORM_DATA_TYPE))
123 .map(t -> (Object) exc.getMessage())
124 .orElseGet(() -> new MCRExceptionContainer(exc));
125 return Response.status(Status.INTERNAL_SERVER_ERROR).entity(entity).build();
126 }
127
128 @XmlRootElement(name = "error")
129 private static class MCRExceptionContainer {
130 private Exception exception;
131
132 private MCRExceptionContainer() {
133
134 }
135
136 MCRExceptionContainer(Exception e) {
137 this.exception = e;
138 }
139
140 @XmlElement
141 @XmlJavaTypeAdapter(ExceptionTypeAdapter.class)
142 public Exception getException() {
143 return exception;
144 }
145
146 @XmlAttribute
147 public Class<? extends Exception> getType() {
148 return exception.getClass();
149 }
150
151 }
152
153 private static class ExceptionTypeAdapter extends XmlAdapter<RException, Exception> {
154 @Override
155 public Exception unmarshal(RException v) throws Exception {
156 throw new UnsupportedOperationException();
157 }
158
159 @Override
160 public RException marshal(Exception v) {
161 RException ep = new RException();
162 ep.message = v.getMessage();
163 ep.stackTrace = Stream.of(v.getStackTrace()).map(RStackTraceElement::getInstance)
164 .toArray(RStackTraceElement[]::new);
165 Optional.ofNullable(v.getCause())
166 .filter(Exception.class::isInstance)
167 .map(Exception.class::cast)
168 .map(this::marshal)
169 .ifPresent(c -> ep.cause = c);
170 return ep;
171 }
172 }
173
174 private static class RException {
175 @XmlElement
176 private String message;
177
178 @XmlElement
179 private RStackTraceElement[] stackTrace;
180
181 @XmlElement
182 private RException cause;
183
184 }
185
186 private static class RStackTraceElement {
187 @XmlAttribute
188 private String className;
189
190 @XmlAttribute
191 private String method;
192
193 @XmlAttribute
194 private String file;
195
196 @XmlAttribute
197 private int line;
198
199 private static RStackTraceElement getInstance(StackTraceElement ste) {
200 RStackTraceElement rste = new RStackTraceElement();
201 rste.className = ste.getClassName();
202 rste.method = ste.getMethodName();
203 rste.file = ste.getFileName();
204 rste.line = ste.getLineNumber();
205 return rste;
206 }
207 }
208
209 }