Bug 1010: Implement restconf error responses
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / 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.controller.sal.restconf.impl;
9
10 import java.io.PrintWriter;
11 import java.io.StringWriter;
12
13 import javax.ws.rs.core.Response.Status;
14
15 import org.opendaylight.yangtools.yang.common.RpcError;
16
17 import com.google.common.base.Preconditions;
18
19 /**
20  * Encapsulates a restconf error as defined in the ietf restconf draft.
21  *
22  * <br><br><b>Note:</b> Enumerations defined within are provided by the ietf restconf draft.
23  *
24  * @author Devin Avery
25  * @see {@link https://tools.ietf.org/html/draft-bierman-netconf-restconf-02}
26  */
27 public class RestconfError {
28
29     public static enum ErrorType {
30         /** Errors relating to the transport layer */
31         TRANSPORT,
32         /** Errors relating to the RPC or notification layer */
33         RPC,
34         /** Errors relating to the protocol operation layer. */
35         PROTOCOL,
36         /** Errors relating to the server application layer. */
37         APPLICATION;
38
39         public String getErrorTypeTag() {
40             return name().toLowerCase();
41         }
42
43         public static ErrorType valueOfCaseInsensitive( String value )
44         {
45             try {
46                 return ErrorType.valueOf( ErrorType.class, value.toUpperCase() );
47             }
48             catch( IllegalArgumentException e ) {
49                 return APPLICATION;
50             }
51         }
52     }
53
54     public static enum ErrorTag {
55         IN_USE( "in-use", Status.fromStatusCode(409)),
56         INVALID_VALUE( "invalid-value", Status.fromStatusCode(400)),
57         TOO_BIG( "too-big", Status.fromStatusCode(413)),
58         MISSING_ATTRIBUTE( "missing-attribute", Status.fromStatusCode(400)),
59         BAD_ATTRIBUTE( "bad-attribute", Status.fromStatusCode(400)),
60         UNKNOWN_ATTRIBUTE( "unknown-attribute", Status.fromStatusCode(400)),
61         BAD_ELEMENT( "bad-element", Status.fromStatusCode(400)),
62         UNKNOWN_ELEMENT( "unknown-element", Status.fromStatusCode(400)),
63         UNKNOWN_NAMESPACE( "unknown-namespace", Status.fromStatusCode(400)),
64         ACCESS_DENIED( "access-denied", Status.fromStatusCode(403)),
65         LOCK_DENIED( "lock-denied", Status.fromStatusCode(409)),
66         RESOURCE_DENIED( "resource-denied", Status.fromStatusCode(409)),
67         ROLLBACK_FAILED( "rollback-failed", Status.fromStatusCode(500)),
68         DATA_EXISTS( "data-exists", Status.fromStatusCode(409)),
69         DATA_MISSING( "data-missing", Status.fromStatusCode(409)),
70         OPERATION_NOT_SUPPORTED( "operation-not-supported", Status.fromStatusCode(501)),
71         OPERATION_FAILED( "operation-failed", Status.fromStatusCode(500)),
72         PARTIAL_OPERATION( "partial-operation", Status.fromStatusCode(500)),
73         MALFORMED_MESSAGE( "malformed-message", Status.fromStatusCode(400));
74
75         private final String tagValue;
76         private final Status statusCode;
77
78         ErrorTag(final String tagValue, final Status statusCode) {
79             this.tagValue = tagValue;
80             this.statusCode = statusCode;
81         }
82
83         public String getTagValue() {
84             return this.tagValue.toLowerCase();
85         }
86
87         public static ErrorTag valueOfCaseInsensitive( String value )
88         {
89             try {
90                 return ErrorTag.valueOf( ErrorTag.class, value.toUpperCase().replaceAll( "-","_" ) );
91             }
92             catch( IllegalArgumentException e ) {
93                 return OPERATION_FAILED;
94             }
95         }
96
97         public Status getStatusCode() {
98             return statusCode;
99         }
100     }
101
102     private final ErrorType errorType;
103     private final ErrorTag errorTag;
104     private final String errorInfo;
105     private final String errorAppTag;
106     private final String errorMessage;
107     //TODO: Add in the error-path concept as defined in the ietf draft.
108
109     static String toErrorInfo( Throwable cause ) {
110         StringWriter writer = new StringWriter();
111         cause.printStackTrace( new PrintWriter( writer ) );
112         return writer.toString();
113     }
114
115     /**
116      * Constructs a RestConfError
117      *
118      * @param errorType The enumerated type indicating the layer where the error occurred.
119      * @param errorTag The enumerated tag representing a more specific error cause.
120      * @param errorMessage A string which provides a plain text string describing the error.
121      */
122     public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage) {
123         this( errorType, errorTag, errorMessage, null );
124     }
125
126     /**
127      * Constructs a RestConfError object.
128      *
129      * @param errorType The enumerated type indicating the layer where the error occurred.
130      * @param errorTag The enumerated tag representing a more specific error cause.
131      * @param errorMessage A string which provides a plain text string describing the error.
132      * @param errorAppTag A string which represents an application-specific error tag that further
133      *                    specifies the error cause.
134      */
135     public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage,
136                          String errorAppTag) {
137         this( errorType, errorTag, errorMessage, errorAppTag, null );
138     }
139
140     /**
141      * Constructs a RestConfError object.
142      *
143      * @param errorType The enumerated type indicating the layer where the error occurred.
144      * @param errorTag The enumerated tag representing a more specific error cause.
145      * @param errorMessage A string which provides a plain text string describing the error.
146      * @param errorAppTag A string which represents an application-specific error tag that further
147      *                    specifies the error cause.
148      * @param errorInfo A string, <b>formatted as XML</b>, which contains additional error information.
149      */
150     public RestconfError(ErrorType errorType, ErrorTag errorTag, String errorMessage,
151                          String errorAppTag, String errorInfo) {
152         Preconditions.checkNotNull( errorType, "Error type is required for RestConfError" );
153         Preconditions.checkNotNull( errorTag, "Error tag is required for RestConfError");
154         this.errorType = errorType;
155         this.errorTag = errorTag;
156         this.errorMessage = errorMessage;
157         this.errorAppTag = errorAppTag;
158         this.errorInfo = errorInfo;
159     }
160
161     /**
162      * Constructs a RestConfError object from an RpcError.
163      */
164     public RestconfError( RpcError rpcError ) {
165
166         this.errorType = rpcError.getErrorType() == null ? ErrorType.APPLICATION :
167                                ErrorType.valueOfCaseInsensitive( rpcError.getErrorType().name() );
168
169         this.errorTag = rpcError.getTag() == null ? ErrorTag.OPERATION_FAILED :
170                                     ErrorTag.valueOfCaseInsensitive( rpcError.getTag().toString() );
171
172         this.errorMessage = rpcError.getMessage();
173         this.errorAppTag = rpcError.getApplicationTag();
174
175         String errorInfo = null;
176         if( rpcError.getInfo() == null ) {
177             if( rpcError.getCause() != null ) {
178                 errorInfo = toErrorInfo( rpcError.getCause() );
179             }
180             else if( rpcError.getSeverity() != null ) {
181                 errorInfo = "<severity>" + rpcError.getSeverity().toString().toLowerCase() +
182                             "</severity>";
183             }
184         }
185         else {
186             errorInfo = rpcError.getInfo();
187         }
188
189         this.errorInfo = errorInfo;
190     }
191
192     public ErrorType getErrorType() {
193         return errorType;
194     }
195
196     public ErrorTag getErrorTag() {
197         return errorTag;
198     }
199
200     public String getErrorInfo() {
201         return errorInfo;
202     }
203
204     public String getErrorAppTag() {
205         return errorAppTag;
206     }
207
208     public String getErrorMessage() {
209         return errorMessage;
210     }
211
212     @Override
213     public String toString() {
214         return "error-type: " + errorType.getErrorTypeTag()
215                 + ", error-tag: " + errorTag.getTagValue() + ", "
216                 + (errorAppTag != null ? "error-app-tag: " + errorAppTag + ", " : "")
217                 + (errorMessage != null ? "error-message: " + errorMessage : "")
218                 + (errorInfo != null ? "error-info: " + errorInfo + ", " : "") + "]";
219     }
220
221 }