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}