Remove RestconfError.ErrorType
[netconf.git] / restconf / restconf-common / src / main / java / org / opendaylight / restconf / common / errors / RestconfError.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.restconf.common.errors;
9
10 import static java.util.Objects.requireNonNull;
11
12 import java.io.Serializable;
13 import java.util.Locale;
14 import org.opendaylight.yangtools.yang.common.ErrorType;
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;
19
20 /**
21  * Encapsulates a restconf error as defined in the ietf restconf draft.
22  *
23  * <br>
24  * <br>
25  * <b>Note:</b> Enumerations defined within are provided by the ietf restconf draft.
26  *
27  * @author Devin Avery
28  *     See also <a href="https://tools.ietf.org/html/draft-bierman-netconf-restconf-02">RESTCONF</a>.
29  */
30 public class RestconfError implements Serializable {
31     private static final Logger LOG = LoggerFactory.getLogger(RestconfError.class);
32     private static final long serialVersionUID = 1L;
33
34     public enum ErrorTag {
35         IN_USE("in-use", 409 /* Conflict */),
36         INVALID_VALUE("invalid-value", 400 /* Bad Request */),
37         TOO_BIG("too-big", 413 /* Request Entity Too Large */),
38         MISSING_ATTRIBUTE("missing-attribute", 400 /* Bad Request */),
39         BAD_ATTRIBUTE("bad-attribute", 400 /* Bad Request */),
40         UNKNOWN_ATTRIBUTE("unknown-attribute", 400 /* Bad Request */),
41         MISSING_ELEMENT("missing-element", 400 /* Bad Request */),
42         BAD_ELEMENT("bad-element", 400 /* Bad Request */),
43         UNKNOWN_ELEMENT("unknown-element", 400 /* Bad Request */),
44         UNKNOWN_NAMESPACE("unknown-namespace", 400 /* Bad Request */),
45         ACCESS_DENIED("access-denied", 403 /* Forbidden */),
46         LOCK_DENIED("lock-denied", 409 /* Conflict */),
47         RESOURCE_DENIED("resource-denied", 409 /* Conflict */),
48         ROLLBACK_FAILED("rollback-failed", 500 /* INTERNAL_SERVER_ERROR */),
49         DATA_EXISTS("data-exists", 409 /* Conflict */),
50         DATA_MISSING("data-missing", dataMissingHttpStatus()),
51         OPERATION_NOT_SUPPORTED("operation-not-supported", 501 /* Not Implemented */),
52         OPERATION_FAILED("operation-failed", 500 /* INTERNAL_SERVER_ERROR */),
53         PARTIAL_OPERATION("partial-operation", 500 /* INTERNAL_SERVER_ERROR */),
54         MALFORMED_MESSAGE("malformed-message", 400 /* Bad Request */),
55         RESOURCE_DENIED_TRANSPORT("resource-denied-transport", 503 /* Service Unavailable */);
56
57         private final String tagValue;
58         private final int statusCode;
59
60         ErrorTag(final String tagValue, final int statusCode) {
61             this.tagValue = tagValue;
62             this.statusCode = statusCode;
63         }
64
65         public String getTagValue() {
66             return this.tagValue.toLowerCase(Locale.ROOT);
67         }
68
69         public static ErrorTag valueOfCaseInsensitive(final String value) {
70             try {
71                 return ErrorTag.valueOf(ErrorTag.class, value.toUpperCase(Locale.ROOT).replaceAll("-", "_"));
72             } catch (IllegalArgumentException e) {
73                 return OPERATION_FAILED;
74             }
75         }
76
77         public int getStatusCode() {
78             return statusCode;
79         }
80
81         private static int dataMissingHttpStatus() {
82             // Control over the HTTP status reported on "data-missing" conditions. This defaults to disabled,
83             // HTTP status 409 as specified by RFC8040 (and all previous drafts). See the discussion in:
84             // https://www.rfc-editor.org/errata/eid5565
85             // https://mailarchive.ietf.org/arch/msg/netconf/hkVDdHK4xA74NgvXzWP0zObMiyY/
86             final String propName = "org.opendaylight.restconf.eid5565";
87             final String propValue = System.getProperty(propName, "disabled");
88             switch (propValue) {
89                 case "enabled":
90                     // RFC7231 interpretation: 404 Not Found
91                     LOG.info("RESTCONF data-missing condition is reported as HTTP status 404 (Errata 5565)");
92                     return 404;
93                 case "disabled":
94                     break;
95                 default:
96                     LOG.warn("Unhandled {} value \"{}\", assuming disabled", propName, propValue);
97             }
98
99             // RFC8040 specification: 409 Conflict
100             return 409;
101         }
102     }
103
104     private final ErrorType errorType;
105     private final ErrorTag errorTag;
106     private final String errorInfo;
107     private final String errorAppTag;
108     private final String errorMessage;
109     private final YangInstanceIdentifier errorPath;
110
111     /**
112      * Constructs a RestConfError.
113      *
114      * @param errorType
115      *            The enumerated type indicating the layer where the error occurred.
116      * @param errorTag
117      *            The enumerated tag representing a more specific error cause.
118      * @param errorMessage
119      *            A string which provides a plain text string describing the error.
120      */
121     public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage) {
122         this(errorType, errorTag, errorMessage, null, null, null);
123     }
124
125     /**
126      * Constructs a RestConfError object.
127      *
128      * @param errorType
129      *            The enumerated type indicating the layer where the error occurred.
130      * @param errorTag
131      *            The enumerated tag representing a more specific error cause.
132      * @param errorMessage
133      *            A string which provides a plain text string describing the error.
134      * @param errorAppTag
135      *            A string which represents an application-specific error tag that further specifies the error cause.
136      */
137     public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
138                          final String errorAppTag) {
139         this(errorType, errorTag, errorMessage, errorAppTag, null, null);
140     }
141
142     /**
143      * Constructs a RestConfError object.
144      *
145      * @param errorType
146      *            The enumerated type indicating the layer where the error occurred.
147      * @param errorTag
148      *            The enumerated tag representing a more specific error cause.
149      * @param errorMessage
150      *            A string which provides a plain text string describing the error.
151      * @param errorPath
152      *            An instance identifier which contains error path
153      */
154     public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
155                          final YangInstanceIdentifier errorPath) {
156         this(errorType, errorTag, errorMessage, null, null, errorPath);
157     }
158
159     /**
160      * Constructs a RestConfError object.
161      *
162      * @param errorType
163      *            The enumerated type indicating the layer where the error occurred.
164      * @param errorTag
165      *            The enumerated tag representing a more specific error cause.
166      * @param errorMessage
167      *            A string which provides a plain text string describing the error.
168      * @param errorAppTag
169      *            A string which represents an application-specific error tag that further specifies the error cause.
170      * @param errorInfo
171      *            A string, <b>formatted as XML</b>, which contains additional error information.
172      */
173     public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
174                          final String errorAppTag, final String errorInfo) {
175         this(errorType, errorTag, errorMessage, errorAppTag, errorInfo, null);
176     }
177
178     /**
179      * Constructs a RestConfError object.
180      *
181      * @param errorType
182      *            The enumerated type indicating the layer where the error occurred.
183      * @param errorTag
184      *            The enumerated tag representing a more specific error cause.
185      * @param errorMessage
186      *            A string which provides a plain text string describing the error.
187      * @param errorAppTag
188      *            A string which represents an application-specific error tag that further specifies the error cause.
189      * @param errorInfo
190      *            A string, <b>formatted as XML</b>, which contains additional error information.
191      * @param errorPath
192      *            An instance identifier which contains error path
193      */
194     public RestconfError(final ErrorType errorType, final ErrorTag errorTag, final String errorMessage,
195                          final String errorAppTag, final String errorInfo, final YangInstanceIdentifier errorPath) {
196         this.errorType = requireNonNull(errorType, "Error type is required for RestConfError");
197         this.errorTag = requireNonNull(errorTag, "Error tag is required for RestConfError");
198         this.errorMessage = errorMessage;
199         this.errorAppTag = errorAppTag;
200         this.errorInfo = errorInfo;
201         this.errorPath = errorPath;
202     }
203
204     /**
205      * Constructs a RestConfError object from an RpcError.
206      */
207     public RestconfError(final RpcError rpcError) {
208
209         this.errorType = rpcError.getErrorType().toNetconf();
210
211         this.errorTag = rpcError.getTag() == null ? ErrorTag.OPERATION_FAILED : ErrorTag
212                 .valueOfCaseInsensitive(rpcError.getTag());
213
214         this.errorMessage = rpcError.getMessage();
215         this.errorAppTag = rpcError.getApplicationTag();
216
217         String localErrorInfo = null;
218         if (rpcError.getInfo() == null) {
219             if (rpcError.getCause() != null) {
220                 localErrorInfo = rpcError.getCause().getMessage();
221             } else if (rpcError.getSeverity() != null) {
222                 localErrorInfo = "<severity>" + rpcError.getSeverity().toString().toLowerCase(Locale.ROOT)
223                         + "</severity>";
224             }
225         } else {
226             localErrorInfo = rpcError.getInfo();
227         }
228
229         this.errorInfo = localErrorInfo;
230         this.errorPath = null;
231     }
232
233     public ErrorType getErrorType() {
234         return errorType;
235     }
236
237     public ErrorTag getErrorTag() {
238         return errorTag;
239     }
240
241     public String getErrorInfo() {
242         return errorInfo;
243     }
244
245     public String getErrorAppTag() {
246         return errorAppTag;
247     }
248
249     public String getErrorMessage() {
250         return errorMessage;
251     }
252
253     public YangInstanceIdentifier getErrorPath() {
254         return errorPath;
255     }
256
257     @Override
258     public String toString() {
259         return "RestconfError ["
260                 + "error-type: " + errorType.elementBody() + ", error-tag: " + errorTag.getTagValue()
261                 + (errorAppTag != null ? ", error-app-tag: " + errorAppTag : "")
262                 + (errorMessage != null ? ", error-message: " + errorMessage : "")
263                 + (errorInfo != null ? ", error-info: " + errorInfo : "")
264                 + (errorPath != null ? ", error-path: " + errorPath.toString() : "")
265                 + "]";
266     }
267 }