001 002package io.vrap.rmf.base.client; 003 004import java.nio.charset.StandardCharsets; 005import java.time.ZoneOffset; 006import java.time.ZonedDateTime; 007import java.time.format.DateTimeFormatter; 008import java.util.Optional; 009 010import javax.annotation.Nullable; 011 012import io.vrap.rmf.base.client.error.BaseException; 013import io.vrap.rmf.base.client.utils.json.JsonUtils; 014 015public class ApiHttpException extends BaseException { 016 017 private final int statusCode; 018 @Nullable 019 private final String body; 020 @Nullable 021 private final ApiHttpHeaders headers; 022 @Nullable 023 private final ApiHttpResponse<byte[]> response; 024 @Nullable 025 private final ApiHttpRequest request; 026 027 private final ResponseSerializer serializer; 028 029 private final String dateAsString = DateTimeFormatter.ISO_INSTANT 030 .format(ZonedDateTime.now().withZoneSameInstant(ZoneOffset.UTC)); 031 032 public ApiHttpException(final int statusCode, final String body, final ApiHttpHeaders headers) { 033 this(statusCode, body, headers, null, null, null, ResponseSerializer.of()); 034 } 035 036 public ApiHttpException(final int statusCode, final String body, final ApiHttpHeaders headers, 037 final ApiHttpResponse<byte[]> response) { 038 this(statusCode, body, headers, null, response, null, ResponseSerializer.of()); 039 } 040 041 public ApiHttpException(final int statusCode, final String body, final ApiHttpHeaders headers, final String message, 042 final ApiHttpResponse<byte[]> response) { 043 this(statusCode, body, headers, message, response, null, ResponseSerializer.of()); 044 } 045 046 public ApiHttpException(final int statusCode, final String body, final ApiHttpHeaders headers, final String message, 047 final ApiHttpResponse<byte[]> response, Throwable cause) { 048 this(statusCode, body, headers, message, response, null, ResponseSerializer.of(), cause); 049 } 050 051 public ApiHttpException(final int statusCode, final String body, final ApiHttpHeaders headers, final String message, 052 final ApiHttpResponse<byte[]> response, final ResponseSerializer serializer) { 053 this(statusCode, body, headers, message, response, null, serializer); 054 } 055 056 public ApiHttpException(final int statusCode, @Nullable final String body, @Nullable final ApiHttpHeaders headers, 057 @Nullable final String message, @Nullable final ApiHttpResponse<byte[]> response, 058 @Nullable final ApiHttpRequest request) { 059 this(statusCode, body, headers, message, response, request, ResponseSerializer.of()); 060 } 061 062 public ApiHttpException(final int statusCode, @Nullable final String body, @Nullable final ApiHttpHeaders headers, 063 @Nullable final String message, @Nullable final ApiHttpResponse<byte[]> response, 064 @Nullable final ApiHttpRequest request, final ResponseSerializer serializer, Throwable cause) { 065 super(message, cause); 066 this.statusCode = statusCode; 067 this.body = body; 068 this.headers = headers; 069 this.response = response; 070 this.request = request; 071 this.serializer = serializer; 072 } 073 074 public ApiHttpException(final int statusCode, @Nullable final String body, @Nullable final ApiHttpHeaders headers, 075 @Nullable final String message, @Nullable final ApiHttpResponse<byte[]> response, 076 @Nullable final ApiHttpRequest request, final ResponseSerializer serializer) { 077 super(message); 078 this.statusCode = statusCode; 079 this.body = body; 080 this.headers = headers; 081 this.response = response; 082 this.request = request; 083 this.serializer = serializer; 084 } 085 086 public <T> T getBodyAs(final Class<T> clazz) throws SerializationException { 087 try { 088 return serializer.convertResponse(response, clazz).getBody(); 089 } 090 catch (Exception e) { 091 throw new SerializationException(e.getMessage()); 092 } 093 } 094 095 public int getStatusCode() { 096 return statusCode; 097 } 098 099 public String getBody() { 100 return body; 101 } 102 103 public ApiHttpHeaders getHeaders() { 104 return headers; 105 } 106 107 public ApiHttpResponse<byte[]> getResponse() { 108 return response; 109 } 110 111 public ApiHttpRequest getRequest() { 112 return request; 113 } 114 115 @Override 116 public final String getMessage() { 117 return Optional.ofNullable(super.getMessage()).map(s -> "detailMessage: " + s + "\n").orElse("") + httpSummary() 118 + responseBodyFormatted() + "http response: " 119 + Optional.ofNullable(getResponse()).map(Object::toString).orElse("<unknown>") + "\n" + "SDK: " 120 + BuildInfo.VERSION + "\n" 121 + Optional.ofNullable(request) 122 .map(x -> "" + x.getMethod() + " " + x.getUri()) 123 .map(x -> "endpoint: " + x + "\n") 124 .orElse("") 125 + "Java: " + System.getProperty("java.version") + "\n" + "cwd: " + System.getProperty("user.dir") + "\n" 126 + "request: " + Optional.ofNullable(request).map(Object::toString).orElse("<unknown>") + "\n" 127 + httpRequestLine() + requestBodyFormatted(); 128 } 129 130 private String httpSummary() { 131 try { 132 final StringBuilder builder = new StringBuilder(); 133 if (this.request != null) { 134 builder.append("summary: "); 135 final String httpMethod = Optional.of(this.request) 136 .map(r -> r.getMethod().toString()) 137 .orElseGet(() -> this.request.getMethod().toString()); 138 139 final String path = Optional.of(this.request) 140 .map(ApiHttpRequest::getUri) 141 .orElseGet(this.request::getUri) 142 .toString(); 143 144 final String responseCode = " with " + Optional.ofNullable(this.response) 145 .map(ApiHttpResponse::getStatusCode) 146 .map(Object::toString) 147 .map(r -> "response code " + r) 148 .orElse("an unknown status code"); 149 150 final String correlationId = Optional.ofNullable(this.response) 151 .map(ApiHttpResponse::getHeaders) 152 .flatMap(headers -> headers.getHeaders(ApiHttpHeaders.X_CORRELATION_ID).stream().findFirst()) 153 .map(id -> " with " + ApiHttpHeaders.X_CORRELATION_ID + " `" + id + "`") 154 .orElse(""); 155 156 builder.append(httpMethod) 157 .append(" ") 158 .append(path) 159 .append(" failed ") 160 .append(responseCode) 161 .append(correlationId) 162 .append(" on ") 163 .append(dateAsString) 164 .append("\n"); 165 } 166 return builder.toString(); 167 } 168 catch (final Exception e) { 169 return ""; 170 } 171 } 172 173 private String httpRequestLine() { 174 if (request == null) { 175 return ""; 176 } 177 else { 178 return "http request: " + request.toString() + "\n"; 179 } 180 } 181 182 private String responseBodyFormatted() { 183 try { 184 return Optional.ofNullable(response) 185 .map(ApiHttpResponse::getBody) 186 .map(b -> JsonUtils.prettyPrint(new String(b, StandardCharsets.UTF_8))) 187 .map(s -> "http response formatted body: " + s + "\n") 188 .orElse(""); 189 } 190 catch (final Exception e) { 191 return ""; 192 } 193 } 194 195 private String requestBodyFormatted() { 196 try { 197 final Optional<String> stringBodyOfHttpRequest = stringBodyOfHttpRequest(); 198 final Optional<String> stringBodyOfHttpRequestIntentSupplier = Optional.ofNullable(request) 199 .map(ApiHttpRequest::getSecuredBody); 200 return Optional 201 .ofNullable(stringBodyOfHttpRequest.orElse(stringBodyOfHttpRequestIntentSupplier.orElse(null))) 202 .map(JsonUtils::prettyPrint) 203 .map(s -> "http request formatted body: " + s + "\n") 204 .orElse(""); 205 } 206 catch (final Exception e) { 207 return ""; 208 } 209 } 210 211 private Optional<String> stringBodyOfHttpRequest() { 212 return Optional.ofNullable(request).map(ApiHttpRequest::getSecuredBody); 213 } 214}