/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.rest.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.brooklyn.util.text.Strings;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
public class ApiError implements Serializable {
private static final long serialVersionUID = -8244515572813244686L;
public static Builder builder() {
return new Builder();
}
public static ApiError of(Throwable t) {
return builderFromThrowable(t).build();
}
public static ApiError of(String message) {
return builder().message(message).build();
}
/** @deprecated since 0.7.0; use {@link #builderFromThrowable(Throwable)} */
@Deprecated
public static Builder fromThrowable(Throwable t) {
return builderFromThrowable(t);
}
/**
* @return An {@link ApiError.Builder} whose message is initialised to either the throwable's
* message or the throwable's class name if the message is null and whose details are
* initialised to the throwable's stack trace.
*/
public static Builder builderFromThrowable(Throwable t) {
checkNotNull(t, "throwable");
String message = Optional.fromNullable(t.getMessage())
.or(t.getClass().getName());
return builder()
.message(message)
.details(Throwables.getStackTraceAsString(t));
}
public static class Builder {
private String message;
private String details;
private Integer errorCode;
public Builder message(String message) {
this.message = checkNotNull(message, "message");
return this;
}
public Builder details(String details) {
this.details = checkNotNull(details, "details");
return this;
}
public Builder errorCode(Status errorCode) {
return errorCode(errorCode.getStatusCode());
}
public Builder errorCode(Integer errorCode) {
this.errorCode = errorCode;
return this;
}
/** as {@link #prefixMessage(String, String)} with default separator of `: ` */
public Builder prefixMessage(String prefix) {
return prefixMessage(prefix, ": ");
}
/** puts a prefix in front of the message, with the given separator if there is already a message;
* if there is no message, it simply sets the prefix as the message
*/
public Builder prefixMessage(String prefix, String separatorIfMessageNotBlank) {
if (Strings.isBlank(message)) message(prefix);
else message(prefix+separatorIfMessageNotBlank+message);
return this;
}
public ApiError build() {
return new ApiError(message, details, errorCode);
}
/** @deprecated since 0.7.0; use {@link #copy(ApiError)} */
@Deprecated
public Builder fromApiError(ApiError error) {
return copy(error);
}
public Builder copy(ApiError error) {
return this
.message(error.message)
.details(error.details)
.errorCode(error.error);
}
public String getMessage() {
return message;
}
}
private final String message;
@JsonSerialize(include=Inclusion.NON_EMPTY)
private final String details;
@JsonSerialize(include=Inclusion.NON_NULL)
private final Integer error;
public ApiError(String message) { this(message, null); }
public ApiError(String message, String details) { this(message, details, null); }
public ApiError(
@JsonProperty("message") String message,
@JsonProperty("details") String details,
@JsonProperty("error") Integer error) {
this.message = checkNotNull(message, "message");
this.details = details != null ? details : "";
this.error = error;
}
public String getMessage() {
return message;
}
public String getDetails() {
return details;
}
public Integer getError() {
return error;
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (other == null || getClass() != other.getClass()) return false;
ApiError that = ApiError.class.cast(other);
return Objects.equal(this.message, that.message) &&
Objects.equal(this.details, that.details) &&
Objects.equal(this.error, that.error);
}
@Override
public int hashCode() {
return Objects.hashCode(message, details, error);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("message", message)
.add("details", details)
.add("error", error)
.toString();
}
public Response asBadRequestResponseJson() {
return asResponse(Status.BAD_REQUEST, MediaType.APPLICATION_JSON_TYPE);
}
public Response asResponse(Status defaultStatus, MediaType type) {
return Response.status(error!=null ? error : defaultStatus!=null ? defaultStatus.getStatusCode() : Status.INTERNAL_SERVER_ERROR.getStatusCode())
.type(type)
.entity(this)
.build();
}
public Response asResponse(MediaType type) {
return asResponse(null, type);
}
public Response asJsonResponse() {
return asResponse(MediaType.APPLICATION_JSON_TYPE);
}
}