001/* 002 * Copyright 2020 Global Biodiversity Information Facility (GBIF) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.gbif.api.model.common.messaging; 017 018import java.util.StringJoiner; 019 020public class Response<T> { 021 022 private String messageKey; 023 private String message; 024 private Status status; 025 private Integer statusCode; 026 private T content; 027 028 private Throwable rootException; 029 030 public Response() { 031 } 032 033 public Response(Response<T> response) { 034 message = response.message; 035 messageKey = response.messageKey; 036 status = response.status; 037 statusCode = response.statusCode; 038 } 039 040 /** 041 * Constructor using status only. 042 */ 043 public Response(Status status) { 044 this.status = status; 045 } 046 047 public Response(Status status, String messageKey) { 048 this.status = status; 049 this.messageKey = messageKey; 050 } 051 052 public Response(Status status, T content, String messageKey) { 053 this.status = status; 054 this.content = content; 055 this.messageKey = messageKey; 056 } 057 058 /** 059 * @return the content 060 */ 061 public T getContent() { 062 return content; 063 } 064 065 /** 066 * @param content the content to set 067 */ 068 public void setContent(T content) { 069 this.content = content; 070 } 071 072 /** 073 * @return the message 074 */ 075 public String getMessage() { 076 return message; 077 } 078 079 /** 080 * @param message the message to set 081 */ 082 public void setMessage(String message) { 083 this.message = message; 084 } 085 086 /** 087 * @return the messageKey 088 */ 089 public String getMessageKey() { 090 return messageKey; 091 } 092 093 /** 094 * @param messageKey the messageKey to set 095 */ 096 public void setMessageKey(String messageKey) { 097 this.messageKey = messageKey; 098 } 099 100 /** 101 * @return rootException when the response contains an error 102 */ 103 public Throwable getRootException() { 104 return rootException; 105 } 106 107 /** 108 * Sets the root exception when is error response. 109 */ 110 public void setRootException(Throwable rootException) { 111 this.rootException = rootException; 112 } 113 114 /** 115 * @return the status 116 */ 117 public Status getStatus() { 118 return status; 119 } 120 121 /** 122 * @param status the status to set 123 */ 124 public void setStatus(Status status) { 125 this.status = status; 126 } 127 128 /** 129 * @return the statusCode 130 */ 131 public Integer getStatusCode() { 132 return statusCode; 133 } 134 135 /** 136 * @param statusCode the statusCode to set 137 */ 138 public void setStatusCode(Integer statusCode) { 139 this.statusCode = statusCode; 140 } 141 142 public void copyTo(Response<?> response) { 143 response.setStatus(status); 144 response.setStatusCode(statusCode); 145 response.setMessage(message); 146 response.setMessageKey(messageKey); 147 response.setRootException(response.getRootException()); 148 } 149 150 public String statusReason() { 151 if (statusCode != null) { 152 return StatusCode.valueOf(statusCode).getReason(); 153 } 154 return null; 155 } 156 157 @Override 158 public String toString() { 159 return new StringJoiner(", ", Response.class.getSimpleName() + "[", "]") 160 .add("messageKey='" + messageKey + "'") 161 .add("message='" + message + "'") 162 .add("status=" + status) 163 .add("statusCode=" + statusCode) 164 .add("content=" + content) 165 .add("rootException=" + rootException) 166 .toString(); 167 } 168 169 public enum Status { 170 171 OK, ERROR 172 173 } 174 175 public enum StatusCode { 176 177 OK(200, "The request was accepted and the response entity-body contains the resource representation, " 178 + "if any is applicable"), CREATED(201, 179 "The request was accepted and resulted in the creation of a new resource. The response Location header contains" 180 + " the created resource URI"), ACCEPTED(202, 181 "The client request has been accepted but is not handled in real time. The response Location header contains " 182 + "the URI of a resource which can be used to check for progress"), NO_CONTENT(204, 183 "The client request was accepted but no resource representation is sent in response. A GET, POST, " 184 + "PUT or DELETE might typically warrant this response."), MOVED_PERMANENTLY(301, 185 "Sent when a client triggers an action that causes the URI of a resource change, " 186 + "or if an old URI was requested. An example could be an update to a resource that causes a version " 187 + "increment"), SEE_OTHER(303, 188 "The request has been processed, and the client is recommended to see another resource, " 189 + "which is likely to be the resource they are seeking. This is used only for POST, " 190 + "PUT or DELETE when the result might be a resource of interest. For the PortalAPI a GET will always return " 191 + "a 307 instead of a 303."), NOT_MODIFIED(304, 192 "Issued when the client provided the If-Modified-Since header and the representation has not changed. No " 193 + "response entity-body will be given."), TEMPORARY_REDIRECT(307, 194 "The request has not been processed, and the client is recommended to see another resource, " 195 + "which is likely to be the resource they are seeking. This is returned should a client request a resource " 196 + "in the canonical form (e.g. without a version number), which has a default representation as another " 197 + "resource (e.g. the latest version)."), BAD_REQUEST(400, 198 "There was a problem on the client side, and further information, if any, is available will be given in the " 199 + "response entity-body"), UNAUTHORIZED(401, 200 "The proper authentication credentials were not supplied to operate on the protected resource. Note that 403 " 201 + "will be used when credentials are supplied and correct, but still disallowed"), FORBIDDEN(403, 202 "The authentication credentials supplied were correct, but do not allow operation on the protected resource"), 203 NOT_FOUND(404, "The server does not know the resource being requested"), CONFLICT(409, 204 "Indicates that the operation was not accepted since it would leave one or more resources in an inconsistent " 205 + "state. An example could be the deletion of a user account, which is referenced by annotations made by the " 206 + "user."), INTERNAL_SERVER_ERROR(500, 207 "There was a problem on the server side, and diagnostic information, if any, " 208 + "is available in the response entity-body"), SERVICE_UNAVAILABLE(503, 209 "The server is responding, but the underlying systems are not operating correctly, " 210 + "such as through resource starvation while under heavy load. A Retry-After header will be provided to " 211 + "indicate the period at which the server suggest the client try again "), SERVER_UNAVAILABLE(999, 212 "The server is not responding or is too slow"); 213 214 private Integer code; 215 216 private String reason; 217 218 public static StatusCode valueOf(int statusCode) { 219 for (StatusCode value : StatusCode.values()) { 220 if (value.getCode() == statusCode) { 221 return value; 222 } 223 } 224 return null; 225 } 226 227 StatusCode(int code, String reason) { 228 this.code = code; 229 this.reason = reason; 230 } 231 232 /** 233 * @return the code 234 */ 235 public Integer getCode() { 236 return code; 237 } 238 239 /** 240 * @param code the code to set 241 */ 242 public void setCode(Integer code) { 243 this.code = code; 244 } 245 246 /** 247 * @return the reason 248 */ 249 public String getReason() { 250 return reason; 251 } 252 253 /** 254 * @param reason the reason to set 255 */ 256 public void setReason(String reason) { 257 this.reason = reason; 258 } 259 260 } 261 262 /** 263 * Builder for Response objects. 264 * Source code generated using: http://code.google.com/p/bpep/. 265 * 266 * @param <T> type of the content field 267 */ 268 public static class Builder<T> { 269 270 private String messageKey; 271 private String message; 272 private Status status; 273 private Integer statusCode; 274 private T content; 275 private Throwable rootException; 276 277 public Response<T> build() { 278 Response<T> response = new Response<T>(); 279 response.messageKey = messageKey; 280 response.message = message; 281 response.status = status; 282 response.statusCode = statusCode; 283 response.content = content; 284 response.rootException = rootException; 285 return response; 286 } 287 288 public Builder<T> content(T content) { 289 this.content = content; 290 return this; 291 } 292 293 /** 294 * Creates an builder using/copying a response object. 295 */ 296 public Builder<T> fromResponse(Response<?> otherResponse) { 297 return status(otherResponse.getStatus()).statusCode(otherResponse.getStatusCode()) 298 .message(otherResponse.getMessage()) 299 .messageKey(otherResponse.getMessageKey()) 300 .rootException(otherResponse.getRootException()); 301 } 302 303 public Builder<T> message(String message) { 304 this.message = message; 305 return this; 306 } 307 308 public Builder<T> messageKey(String messageKey) { 309 this.messageKey = messageKey; 310 return this; 311 } 312 313 public Builder<T> rootException(Throwable rootException) { 314 this.rootException = rootException; 315 return this; 316 } 317 318 public Builder<T> status(Status status) { 319 this.status = status; 320 return this; 321 } 322 323 public Builder<T> statusCode(Integer statusCode) { 324 this.statusCode = statusCode; 325 return this; 326 } 327 } 328}