2 * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.common.errors;
10 import static java.util.Objects.requireNonNull;
12 import java.io.Serializable;
13 import java.util.Locale;
14 import org.eclipse.jdt.annotation.NonNull;
15 import org.opendaylight.yangtools.yang.common.RpcError;
16 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
21 * Encapsulates a restconf error as defined in the ietf restconf draft.
25 * <b>Note:</b> Enumerations defined within are provided by the ietf restconf draft.
28 * See also <a href="https://tools.ietf.org/html/draft-bierman-netconf-restconf-02">RESTCONF</a>.
30 public class RestconfError implements Serializable {
31 private static final Logger LOG = LoggerFactory.getLogger(RestconfError.class);
32 private static final long serialVersionUID = 1L;
34 // FIXME: remove this enum in favor of RpcError.ErrorType (or its equivalent)
35 public enum ErrorType {
37 * Errors relating to the transport layer.
41 * Errors relating to the RPC or notification layer.
45 * Errors relating to the protocol operation layer.
49 * Errors relating to the server application layer.
53 public String getErrorTypeTag() {
54 return name().toLowerCase(Locale.ROOT);
57 public static ErrorType valueOfCaseInsensitive(final String value) {
59 return ErrorType.valueOf(ErrorType.class, value.toUpperCase(Locale.ROOT));
60 } catch (IllegalArgumentException e) {
65 public static @NonNull ErrorType valueOf(final RpcError.ErrorType errorType) {
80 public enum ErrorTag {
81 IN_USE("in-use", 409 /* Conflict */),
82 INVALID_VALUE("invalid-value", 400 /* Bad Request */),
83 TOO_BIG("too-big", 413 /* Request Entity Too Large */),
84 MISSING_ATTRIBUTE("missing-attribute", 400 /* Bad Request */),
85 BAD_ATTRIBUTE("bad-attribute", 400 /* Bad Request */),
86 UNKNOWN_ATTRIBUTE("unknown-attribute", 400 /* Bad Request */),
87 MISSING_ELEMENT("missing-element", 400 /* Bad Request */),
88 BAD_ELEMENT("bad-element", 400 /* Bad Request */),
89 UNKNOWN_ELEMENT("unknown-element", 400 /* Bad Request */),
90 UNKNOWN_NAMESPACE("unknown-namespace", 400 /* Bad Request */),
91 ACCESS_DENIED("access-denied", 403 /* Forbidden */),
92 LOCK_DENIED("lock-denied", 409 /* Conflict */),
93 RESOURCE_DENIED("resource-denied", 409 /* Conflict */),
94 ROLLBACK_FAILED("rollback-failed", 500 /* INTERNAL_SERVER_ERROR */),
95 DATA_EXISTS("data-exists", 409 /* Conflict */),
96 DATA_MISSING("data-missing", dataMissingHttpStatus()),
97 OPERATION_NOT_SUPPORTED("operation-not-supported", 501 /* Not Implemented */),
98 OPERATION_FAILED("operation-failed", 500 /* INTERNAL_SERVER_ERROR */),
99 PARTIAL_OPERATION("partial-operation", 500 /* INTERNAL_SERVER_ERROR */),
100 MALFORMED_MESSAGE("malformed-message", 400 /* Bad Request */),
101 RESOURCE_DENIED_TRANSPORT("resource-denied-transport", 503 /* Service Unavailable */);
103 private final String tagValue;
104 private final int statusCode;
106 ErrorTag(final String tagValue, final int statusCode) {
107 this.tagValue = tagValue;
108 this.statusCode = statusCode;
111 public String getTagValue() {
112 return this.tagValue.toLowerCase(Locale.ROOT);
115 public static ErrorTag valueOfCaseInsensitive(final String value) {
117 return ErrorTag.valueOf(ErrorTag.class, value.toUpperCase(Locale.ROOT).replaceAll("-", "_"));
118 } catch (IllegalArgumentException e) {
119 return OPERATION_FAILED;
123 public int getStatusCode() {
127 private static int dataMissingHttpStatus() {
128 // Control over the HTTP status reported on "data-missing" conditions. This defaults to disabled,
129 // HTTP status 409 as specified by RFC8040 (and all previous drafts). See the discussion in:
130 // https://www.rfc-editor.org/errata/eid5565
131 // https://mailarchive.ietf.org/arch/msg/netconf/hkVDdHK4xA74NgvXzWP0zObMiyY/
132 final String propName = "org.opendaylight.restconf.eid5565";
133 final String propValue = System.getProperty(propName, "disabled");
136 // RFC7231 interpretation: 404 Not Found
137 LOG.info("RESTCONF data-missing condition is reported as HTTP status 404 (Errata 5565)");
142 LOG.warn("Unhandled {} value \"{}\", assuming disabled", propName, propValue);
145 // RFC8040 specification: 409 Conflict
150 private final ErrorType errorType;
151 private final ErrorTag errorTag;
152 private final String errorInfo;
153 private final String errorAppTag;
154 private final String errorMessage;
155 private final YangInstanceIdentifier errorPath;
158 * Constructs a RestConfError.
161 * The enumerated type indicating the layer where the error occurred.
163 * The enumerated tag representing a more specific error cause.
164 * @param errorMessage
165 * A string which provides a plain text string describing the error.
167 public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage) {
168 this(errorType, errorTag, errorMessage, null, null, null);
172 * Constructs a RestConfError object.
175 * The enumerated type indicating the layer where the error occurred.
177 * The enumerated tag representing a more specific error cause.
178 * @param errorMessage
179 * A string which provides a plain text string describing the error.
181 * A string which represents an application-specific error tag that further specifies the error cause.
183 public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
184 final String errorAppTag) {
185 this(errorType, errorTag, errorMessage, errorAppTag, null, null);
189 * Constructs a RestConfError object.
192 * The enumerated type indicating the layer where the error occurred.
194 * The enumerated tag representing a more specific error cause.
195 * @param errorMessage
196 * A string which provides a plain text string describing the error.
198 * An instance identifier which contains error path
200 public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
201 final YangInstanceIdentifier errorPath) {
202 this(errorType, errorTag, errorMessage, null, null, errorPath);
206 * Constructs a RestConfError object.
209 * The enumerated type indicating the layer where the error occurred.
211 * The enumerated tag representing a more specific error cause.
212 * @param errorMessage
213 * A string which provides a plain text string describing the error.
215 * A string which represents an application-specific error tag that further specifies the error cause.
217 * A string, <b>formatted as XML</b>, which contains additional error information.
219 public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
220 final String errorAppTag, final String errorInfo) {
221 this(errorType, errorTag, errorMessage, errorAppTag, errorInfo, null);
225 * Constructs a RestConfError object.
228 * The enumerated type indicating the layer where the error occurred.
230 * The enumerated tag representing a more specific error cause.
231 * @param errorMessage
232 * A string which provides a plain text string describing the error.
234 * A string which represents an application-specific error tag that further specifies the error cause.
236 * A string, <b>formatted as XML</b>, which contains additional error information.
238 * An instance identifier which contains error path
240 public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
241 final String errorAppTag, final String errorInfo, final YangInstanceIdentifier errorPath) {
242 this.errorType = requireNonNull(errorType, "Error type is required for RestConfError");
243 this.errorTag = requireNonNull(errorTag, "Error tag is required for RestConfError");
244 this.errorMessage = errorMessage;
245 this.errorAppTag = errorAppTag;
246 this.errorInfo = errorInfo;
247 this.errorPath = errorPath;
251 * Constructs a RestConfError object from an RpcError.
253 public RestconfError(final RpcError rpcError) {
255 this.errorType = rpcError.getErrorType() == null ? ErrorType.APPLICATION : ErrorType
256 .valueOfCaseInsensitive(rpcError.getErrorType().name());
258 this.errorTag = rpcError.getTag() == null ? ErrorTag.OPERATION_FAILED : ErrorTag
259 .valueOfCaseInsensitive(rpcError.getTag());
261 this.errorMessage = rpcError.getMessage();
262 this.errorAppTag = rpcError.getApplicationTag();
264 String localErrorInfo = null;
265 if (rpcError.getInfo() == null) {
266 if (rpcError.getCause() != null) {
267 localErrorInfo = rpcError.getCause().getMessage();
268 } else if (rpcError.getSeverity() != null) {
269 localErrorInfo = "<severity>" + rpcError.getSeverity().toString().toLowerCase(Locale.ROOT)
273 localErrorInfo = rpcError.getInfo();
276 this.errorInfo = localErrorInfo;
277 this.errorPath = null;
280 public ErrorType getErrorType() {
284 public ErrorTag getErrorTag() {
288 public String getErrorInfo() {
292 public String getErrorAppTag() {
296 public String getErrorMessage() {
300 public YangInstanceIdentifier getErrorPath() {
305 public String toString() {
306 return "RestconfError ["
307 + "error-type: " + errorType.getErrorTypeTag() + ", error-tag: " + errorTag.getTagValue()
308 + (errorAppTag != null ? ", error-app-tag: " + errorAppTag : "")
309 + (errorMessage != null ? ", error-message: " + errorMessage : "")
310 + (errorInfo != null ? ", error-info: " + errorInfo : "")
311 + (errorPath != null ? ", error-path: " + errorPath.toString() : "")