1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.client.lib;
20
21 import jakarta.ws.rs.BadRequestException;
22 import jakarta.ws.rs.ForbiddenException;
23 import jakarta.ws.rs.NotAuthorizedException;
24 import jakarta.ws.rs.NotFoundException;
25 import jakarta.ws.rs.WebApplicationException;
26 import jakarta.ws.rs.core.GenericType;
27 import jakarta.ws.rs.core.Response;
28 import jakarta.ws.rs.ext.Provider;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Set;
32 import org.apache.commons.lang3.StringUtils;
33 import org.apache.cxf.jaxrs.client.ResponseExceptionMapper;
34 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
35 import org.apache.syncope.common.lib.SyncopeClientException;
36 import org.apache.syncope.common.lib.to.ErrorTO;
37 import org.apache.syncope.common.lib.types.ClientExceptionType;
38 import org.apache.syncope.common.rest.api.RESTHeaders;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 @Provider
43 public class RestClientExceptionMapper implements ResponseExceptionMapper<Exception> {
44
45 private static final Logger LOG = LoggerFactory.getLogger(RestClientExceptionMapper.class);
46
47 @Override
48 public Exception fromResponse(final Response response) {
49 int statusCode = response.getStatus();
50 String message = response.getHeaderString(RESTHeaders.ERROR_INFO);
51
52 Exception ex;
53 SyncopeClientCompositeException scce = checkSyncopeClientCompositeException(response);
54 if (scce != null) {
55
56 ex = scce.getExceptions().size() == 1
57 ? scce.getExceptions().iterator().next()
58 : scce;
59 } else if (statusCode == Response.Status.UNAUTHORIZED.getStatusCode()) {
60
61 ex = new NotAuthorizedException(StringUtils.isBlank(message)
62 ? "Remote unauthorized exception"
63 : message,
64 response);
65 } else if (statusCode == Response.Status.FORBIDDEN.getStatusCode()) {
66
67 ex = new ForbiddenException(StringUtils.isBlank(message)
68 ? "Remote forbidden exception"
69 : message,
70 response);
71 } else if (statusCode == Response.Status.BAD_REQUEST.getStatusCode()) {
72
73 ex = StringUtils.isBlank(message)
74 ? new BadRequestException(response)
75 : new BadRequestException(message, response);
76 } else {
77
78 ex = new WebApplicationException("Remote exception with status code: %s".
79 formatted(Response.Status.fromStatusCode(statusCode).name()),
80 response);
81 }
82
83 if (ex instanceof NotFoundException
84 || (ex instanceof SyncopeClientException sce && sce.getType() == ClientExceptionType.NotFound)) {
85
86 if (LOG.isDebugEnabled()) {
87 LOG.debug("Exception thrown", ex);
88 } else {
89 LOG.warn("{} thrown: {}", NotFoundException.class.getSimpleName(), ex.getMessage());
90 }
91 } else {
92 LOG.error("Exception thrown", ex);
93 }
94
95 return ex;
96 }
97
98 private static SyncopeClientCompositeException checkSyncopeClientCompositeException(final Response response) {
99 SyncopeClientCompositeException compException = SyncopeClientException.buildComposite();
100
101
102 List<ErrorTO> errors = null;
103 try {
104 ErrorTO error = response.readEntity(ErrorTO.class);
105 if (error != null) {
106 errors = List.of(error);
107 }
108 } catch (Exception e) {
109 LOG.debug("Could not read {}, attempting to read composite...", ErrorTO.class.getName(), e);
110 }
111 if (errors == null) {
112 try {
113 errors = response.readEntity(new GenericType<>() {
114 });
115 } catch (Exception e) {
116 LOG.debug("Could not read {} list, attempting to read headers...", ErrorTO.class.getName(), e);
117 }
118 }
119
120
121 if (errors == null) {
122 List<String> exTypesInHeaders = response.getStringHeaders().get(RESTHeaders.ERROR_CODE);
123 if (exTypesInHeaders == null) {
124 LOG.debug("No " + RESTHeaders.ERROR_CODE + " provided");
125 return null;
126 }
127 List<String> exInfos = response.getStringHeaders().get(RESTHeaders.ERROR_INFO);
128
129 Set<String> handledExceptions = new HashSet<>();
130 exTypesInHeaders.forEach(exTypeAsString -> {
131 ClientExceptionType exceptionType = null;
132 try {
133 exceptionType = ClientExceptionType.fromHeaderValue(exTypeAsString);
134 } catch (IllegalArgumentException e) {
135 LOG.error("Unexpected value of " + RESTHeaders.ERROR_CODE + ": {}", exTypeAsString, e);
136 }
137 if (exceptionType != null) {
138 handledExceptions.add(exTypeAsString);
139
140 SyncopeClientException clientException = SyncopeClientException.build(exceptionType);
141 if (exInfos != null && !exInfos.isEmpty()) {
142 for (String element : exInfos) {
143 if (element.startsWith(exceptionType.name())) {
144 clientException.getElements().add(StringUtils.substringAfter(element, ":"));
145 }
146 }
147 }
148 compException.addException(clientException);
149 }
150 });
151
152 exTypesInHeaders.removeAll(handledExceptions);
153 if (!exTypesInHeaders.isEmpty()) {
154 LOG.error("Unmanaged exceptions: {}", exTypesInHeaders);
155 }
156 } else {
157 for (ErrorTO error : errors) {
158 SyncopeClientException clientException = SyncopeClientException.build(error.getType());
159 clientException.getElements().addAll(error.getElements());
160 compException.addException(clientException);
161 }
162 }
163
164 if (compException.hasExceptions()) {
165 return compException;
166 }
167
168 return null;
169 }
170 }