All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.ditto.base.model.exceptions.DittoRuntimeException Maven / Gradle / Ivy

/*
 * Copyright (c) 2017 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.eclipse.ditto.base.model.exceptions;

import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Predicate;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.atteo.classindex.IndexSubclasses;
import org.eclipse.ditto.base.model.common.HttpStatus;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.DittoHeadersSettable;
import org.eclipse.ditto.base.model.headers.WithManifest;
import org.eclipse.ditto.base.model.json.FieldType;
import org.eclipse.ditto.base.model.json.JsonSchemaVersion;
import org.eclipse.ditto.base.model.json.Jsonifiable;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonField;
import org.eclipse.ditto.json.JsonFieldDefinition;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonObjectBuilder;

/**
 * Parent RuntimeException for all RuntimeExceptions of Ditto.
 */
@IndexSubclasses
public abstract class DittoRuntimeException extends RuntimeException
        implements Jsonifiable.WithPredicate, DittoHeadersSettable,
        WithManifest {

    private final String errorCode;
    private final HttpStatus httpStatus;
    private final String description;
    private final URI href;
    private final transient DittoHeaders dittoHeaders; // not serializable!

    /**
     * Constructs a new {@code DittoRuntimeException} object.
     *
     * @param errorCode a code which uniquely identifies the exception.
     * @param httpStatus the HTTP status.
     * @param dittoHeaders the headers with which this Exception should be reported back to the user.
     * @param message the detail message for later retrieval with {@link #getMessage()}.
     * @param description a description with further information about the exception.
     * @param cause the cause of the exception for later retrieval with {@link #getCause()}.
     * @param href a link to a resource which provides further information about the exception.
     * @throws NullPointerException if {@code errorCode}, {@code httpStatus} or {@code dittoHeaders} is {@code null}.
     * @since 2.0.0
     */
    protected DittoRuntimeException(final String errorCode,
            final HttpStatus httpStatus,
            final DittoHeaders dittoHeaders,
            @Nullable final String message,
            @Nullable final String description,
            @Nullable final Throwable cause,
            @Nullable final URI href) {

        super(message, cause);
        this.errorCode = checkNotNull(errorCode, "error code");
        this.httpStatus = checkNotNull(httpStatus, "httpStatus");
        this.dittoHeaders = checkNotNull(dittoHeaders, "Ditto headers");
        this.description = description;
        this.href = href;
    }

    /**
     * Takes the throwable and tries to map it to a DittoRuntimeException.
     * 

* If the throwable is a {@link java.util.concurrent.CompletionException} or a {@link java.util.concurrent.ExecutionException}, * this method tries to map the cause of this exception to a DittoRuntimeException. *

* * @param throwable the throwable to map. * @param alternativeExceptionBuilder used to build an alternative DittoRuntimeException if the throwable could not * be mapped. * @return either the mapped exception or the exception built by {@code alternativeExceptionBuilder}. */ public static DittoRuntimeException asDittoRuntimeException(final Throwable throwable, final Function alternativeExceptionBuilder) { final Throwable cause = getRootCause(throwable); if (cause instanceof DittoRuntimeException) { return (DittoRuntimeException) cause; } return alternativeExceptionBuilder.apply(cause); } private static Throwable getRootCause(final Throwable throwable) { if (throwable instanceof CompletionException || throwable instanceof ExecutionException) { @Nullable final Throwable cause = throwable.getCause(); if (null != cause) { return getRootCause(cause); } } return throwable; } /** * Returns the error code to uniquely identify this exception. * * @return the error code. */ public String getErrorCode() { return errorCode; } /** * Retrieves the required HttpStatus with which this Exception should be reported back to the user. * * @return the HttpStatus. * @since 2.0.0 */ public HttpStatus getHttpStatus() { return httpStatus; } @Override public DittoHeaders getDittoHeaders() { return dittoHeaders; } @Override public JsonSchemaVersion getImplementedSchemaVersion() { return dittoHeaders.getSchemaVersion().orElse(getLatestSchemaVersion()); } /** * Returns the description which should be reported to the user. * * @return the description. */ public Optional getDescription() { return Optional.ofNullable(description); } /** * Returns a link with which the user can find further information regarding this exception. * * @return a link to provide the user with further information about this exception. */ public Optional getHref() { return Optional.ofNullable(href); } @Override public String getManifest() { return getErrorCode(); } @SuppressWarnings({"squid:MethodCyclomaticComplexity", "squid:S1067"}) @Override public boolean equals(@Nullable final Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } final DittoRuntimeException that = (DittoRuntimeException) o; return Objects.equals(errorCode, that.errorCode) && Objects.equals(httpStatus, that.httpStatus) && Objects.equals(description, that.description) && Objects.equals(getMessage(), that.getMessage()) && Objects.equals(href, that.href); } @Override public int hashCode() { return Objects.hash(errorCode, httpStatus, description, getMessage(), href); } /** * Returns all non-hidden marked fields of this exception. * * @return a JSON object representation of this exception including only non-hidden marked fields. */ @Override public JsonObject toJson() { return toJson(FieldType.notHidden()); } @Override public JsonObject toJson(final JsonSchemaVersion schemaVersion, final Predicate thePredicate) { final Predicate predicate = schemaVersion.and(thePredicate); final Predicate nonNullAndCustomDefined = predicate.and(JsonField.isValueNonNull()); final JsonObjectBuilder jsonObjectBuilder = JsonFactory.newObjectBuilder() .set(JsonFields.STATUS, httpStatus.getCode(), nonNullAndCustomDefined) .set(JsonFields.ERROR_CODE, errorCode, nonNullAndCustomDefined) .set(JsonFields.MESSAGE, getMessage(), nonNullAndCustomDefined) .set(JsonFields.DESCRIPTION, description, nonNullAndCustomDefined) .set(JsonFields.HREF, href != null ? href.toString() : null, nonNullAndCustomDefined); appendToJson(jsonObjectBuilder, nonNullAndCustomDefined); return jsonObjectBuilder.build(); } /** * Creates a new {@code DittoRuntimeException} from a JSON object. * * @param jsonObject the JSON object of which the exception is to be created. * @param dittoHeaders the headers of the exception. * @param builder the builder for the exception. * @param the type of the DittoRuntimeException. * @return the exception. * @throws NullPointerException if any argument is {@code null}. * @throws org.eclipse.ditto.json.JsonMissingFieldException if this JsonObject did not contain an error message. * @throws org.eclipse.ditto.json.JsonParseException if the passed in {@code jsonObject} was not in the * expected format. * @since 1.3.0 */ public static T fromJson(final JsonObject jsonObject, final DittoHeaders dittoHeaders, final DittoRuntimeExceptionBuilder builder) { checkNotNull(jsonObject, "jsonObject"); checkNotNull(builder, "builder"); jsonObject.getValue(JsonFields.DESCRIPTION).ifPresent(builder::description); jsonObject.getValue(JsonFields.HREF).map(URI::create).ifPresent(builder::href); return builder.dittoHeaders(dittoHeaders) .message(jsonObject.getValueOrThrow(JsonFields.MESSAGE)) .build(); } /** * Creates a new {@code DittoRuntimeException} from a message. * * @param message detail message. This message can be later retrieved by the {@link #getMessage()} method. * @param dittoHeaders dittoHeaders the headers of the command which resulted in this exception. * @param builder the builder for the exception. * @param the type of the DittoRuntimeException. * @return the exception. * @throws NullPointerException if {@code dittoHeaders} or {@code builder} argument is {@code null}. * @since 1.3.0 */ public static T fromMessage(@Nullable final String message, final DittoHeaders dittoHeaders, final DittoRuntimeExceptionBuilder builder) { checkNotNull(builder, "builder"); return builder .dittoHeaders(dittoHeaders) .message(message) .build(); } /** * Allows to append exception-specific fields to the passed {@code jsonObjectBuilder}. * * @param jsonObjectBuilder the JsonObjectBuilder to add the fields to. * @param predicate the predicate to evaluate when adding the payload (already contains the schema version and that * the field is not null). */ protected void appendToJson(final JsonObjectBuilder jsonObjectBuilder, final Predicate predicate) { // empty per default } protected DittoRuntimeExceptionBuilder toBuilder( final DittoRuntimeExceptionBuilder builder) { builder.message(getMessage()); builder.dittoHeaders(getDittoHeaders()); builder.cause(getCause()); getHref().ifPresent(builder::href); getDescription().ifPresent(builder::description); return builder; } protected static Optional getHttpStatus(final JsonObject jsonObject) { return jsonObject.getValue(JsonFields.STATUS).flatMap(HttpStatus::tryGetInstance); } protected static Optional getHref(final JsonObject jsonObject) { final Function uriForStringOrNull = uriString -> { try { return new URI(uriString); } catch (final URISyntaxException e) { return null; } }; return jsonObject.getValue(JsonFields.HREF).map(uriForStringOrNull); } @Override public String toString() { return getClass().getSimpleName() + " [" + "message='" + getMessage() + '\'' + ", errorCode=" + errorCode + ", httpStatus=" + httpStatus + ", description='" + description + '\'' + ", href=" + href + ", dittoHeaders=" + dittoHeaders + ']'; } /** * An enumeration of the known {@link JsonField}s of a {@code DittoRuntimeException}. */ @Immutable public static final class JsonFields { /** * JSON field containing the HTTP status code of the exception. */ public static final JsonFieldDefinition STATUS = JsonFactory.newIntFieldDefinition("status", FieldType.REGULAR, JsonSchemaVersion.V_2); /** * JSON field containing the error code of the exception. */ public static final JsonFieldDefinition ERROR_CODE = JsonFactory.newStringFieldDefinition("error", FieldType.REGULAR, JsonSchemaVersion.V_2); /** * JSON field containing the message of the exception. */ public static final JsonFieldDefinition MESSAGE = JsonFactory.newStringFieldDefinition("message", FieldType.REGULAR, JsonSchemaVersion.V_2); /** * JSON field containing the description of the message. */ public static final JsonFieldDefinition DESCRIPTION = JsonFactory.newStringFieldDefinition("description", FieldType.REGULAR, JsonSchemaVersion.V_2); /** * JSON field containing the link to further information about the exception. */ public static final JsonFieldDefinition HREF = JsonFactory.newStringFieldDefinition("href", FieldType.REGULAR, JsonSchemaVersion.V_2); private JsonFields() { throw new AssertionError(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy