import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.Response.Status;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.restconf.common.ErrorTags;
import org.opendaylight.yangtools.yang.common.ErrorType;
import org.opendaylight.yangtools.yang.common.OperationFailedException;
import org.opendaylight.yangtools.yang.common.RpcError;
-import org.opendaylight.yangtools.yang.common.YangError;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangNetconfError;
+import org.opendaylight.yangtools.yang.data.api.YangNetconfErrorAware;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
/**
- * Unchecked exception to communicate error information, as defined in the ietf restcong draft, to be sent to the
- * client.
- *
- * <p>
- * See also <a href="https://tools.ietf.org/html/draft-bierman-netconf-restconf-02">RESTCONF</a>
+ * Unchecked exception to communicate error information, as defined
+ * <a href="https://www.rfc-editor.org/rfc/rfc8040#section-3.9">"errors" YANG Data Template</a>.
*
* @author Devin Avery
* @author Thomas Pantelis
*/
-public class RestconfDocumentedException extends WebApplicationException {
- private static final long serialVersionUID = 1L;
+public class RestconfDocumentedException extends RuntimeException {
+ @java.io.Serial
+ private static final long serialVersionUID = 3L;
+
+ private final List<RestconfError> errors;
- private final ImmutableList<RestconfError> errors;
- private final Status status;
+ // FIXME: this field should be non-null
+ private final transient @Nullable EffectiveModelContext modelContext;
/**
* Constructs an instance with an error message. The error type defaults to APPLICATION and the error tag defaults
// FIXME: We override getMessage so supplied message is lost for any public access
// this was lost also in original code.
super(cause);
- if (!errors.isEmpty()) {
- this.errors = ImmutableList.copyOf(errors);
+ if (errors.isEmpty()) {
+ this.errors = List.of(new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, message));
} else {
- this.errors = ImmutableList.of(
- new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, message));
+ this.errors = List.copyOf(errors);
}
- status = null;
+ modelContext = null;
}
/**
this(message, cause, convertToRestconfErrors(rpcErrors));
}
- /**
- * Constructs an instance with an HTTP status and no error information.
- *
- * @param status
- * the HTTP status.
- */
- public RestconfDocumentedException(final Status status) {
- errors = ImmutableList.of();
- this.status = requireNonNull(status, "Status can't be null");
+ public RestconfDocumentedException(final Throwable cause, final RestconfError error) {
+ super(cause);
+ errors = List.of(error);
+ modelContext = null;
}
- public RestconfDocumentedException(final Throwable cause, final RestconfError error) {
- super(cause, ErrorTags.statusOf(error.getErrorTag()));
- errors = ImmutableList.of(error);
- status = null;
+ public RestconfDocumentedException(final Throwable cause, final RestconfError error,
+ final EffectiveModelContext modelContext) {
+ super(cause);
+ errors = List.of(error);
+ this.modelContext = requireNonNull(modelContext);
+ }
+
+ public RestconfDocumentedException(final Throwable cause, final List<RestconfError> errors) {
+ super(cause);
+ if (errors.isEmpty()) {
+ throw new IllegalArgumentException("At least one error is required");
+ }
+ this.errors = List.copyOf(errors);
+ modelContext = null;
}
public static RestconfDocumentedException decodeAndThrow(final String message,
final OperationFailedException cause) {
for (final RpcError error : cause.getErrorList()) {
- if (error.getErrorType() == RpcError.ErrorType.TRANSPORT
- && error.getTag().equals(ErrorTag.RESOURCE_DENIED.elementBody())) {
+ if (error.getErrorType() == ErrorType.TRANSPORT && error.getTag().equals(ErrorTag.RESOURCE_DENIED)) {
throw new RestconfDocumentedException(error.getMessage(), ErrorType.TRANSPORT,
ErrorTags.RESOURCE_DENIED_TRANSPORT, cause);
}
}
/**
- * Throw an instance of this exception if the specified exception has a {@link YangError} attachment.
+ * Throw an instance of this exception if the specified exception has a {@link YangNetconfError} attachment.
*
* @param cause Proposed cause of a RestconfDocumented exception
*/
public static void throwIfYangError(final Throwable cause) {
- if (cause instanceof YangError) {
- final YangError error = (YangError) cause;
- throw new RestconfDocumentedException(cause, new RestconfError(error.getErrorType().toNetconf(),
- new ErrorTag(error.getErrorTag()), error.getErrorMessage().orElse(null),
- error.getErrorAppTag().orElse(null)));
+ if (cause instanceof YangNetconfErrorAware infoAware) {
+ throw new RestconfDocumentedException(cause, infoAware.getNetconfErrors().stream()
+ .map(error -> new RestconfError(error.type(), error.tag(), error.message(), error.appTag(),
+ // FIXME: pass down error info
+ null, error.path()))
+ .toList());
}
}
private static List<RestconfError> convertToRestconfErrors(final Collection<? extends RpcError> rpcErrors) {
- final List<RestconfError> errorList = new ArrayList<>();
- if (rpcErrors != null) {
- for (RpcError rpcError : rpcErrors) {
- errorList.add(new RestconfError(rpcError));
- }
+ if (rpcErrors == null || rpcErrors.isEmpty()) {
+ return List.of();
}
+ final var errorList = new ArrayList<RestconfError>();
+ for (var rpcError : rpcErrors) {
+ errorList.add(new RestconfError(rpcError));
+ }
return errorList;
}
- public List<RestconfError> getErrors() {
- return errors;
+ @Override
+ public String getMessage() {
+ return "errors: " + errors;
}
- public Status getStatus() {
- return status;
+ /**
+ * Reference to {@link EffectiveModelContext} in which this exception was generated. This method will return
+ * {@code null} if this exception was serialized or if the context is not available.
+ *
+ * @return Reference model context
+ */
+ public @Nullable EffectiveModelContext modelContext() {
+ return modelContext;
}
- @Override
- public String getMessage() {
- return "errors: " + errors + (status != null ? ", status: " + status : "");
+ public List<RestconfError> getErrors() {
+ return errors;
}
}