Bug 6918: Edit ErrorSeverity and ErrorType from() method to accept lowercase strings
[controller.git] / opendaylight / config / config-util / src / main / java / org / opendaylight / controller / config / util / xml / DocumentedException.java
1 /*
2  * Copyright (c) 2015 Cisco 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
9 package org.opendaylight.controller.config.util.xml;
10
11 import static org.opendaylight.controller.config.util.xml.XmlMappingConstants.RPC_REPLY_KEY;
12 import static org.opendaylight.controller.config.util.xml.XmlMappingConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import javax.xml.parsers.DocumentBuilderFactory;
18 import javax.xml.parsers.ParserConfigurationException;
19 import org.opendaylight.controller.config.api.ConflictingVersionException;
20 import org.opendaylight.controller.config.api.ValidationException;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23 import org.w3c.dom.Document;
24 import org.w3c.dom.Node;
25 import org.w3c.dom.NodeList;
26
27 /**
28  * Checked exception to communicate an error that needs to be sent to the
29  * netconf client.
30  */
31 public class DocumentedException extends Exception {
32
33     public static final String RPC_ERROR = "rpc-error";
34     public static final String ERROR_TYPE = "error-type";
35     public static final String ERROR_TAG = "error-tag";
36     public static final String ERROR_SEVERITY = "error-severity";
37     public static final String ERROR_APP_TAG = "error-app-tag";
38     public static final String ERROR_PATH = "error-path";
39     public static final String ERROR_MESSAGE = "error-message";
40     public static final String ERROR_INFO = "error-info";
41
42     private static final long serialVersionUID = 1L;
43
44     private static final Logger LOG = LoggerFactory.getLogger(DocumentedException.class);
45
46     private static final DocumentBuilderFactory BUILDER_FACTORY;
47
48     static {
49         BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
50         try {
51             BUILDER_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
52             BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-general-entities", false);
53             BUILDER_FACTORY.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
54             BUILDER_FACTORY.setXIncludeAware(false);
55             BUILDER_FACTORY.setExpandEntityReferences(false);
56         } catch (ParserConfigurationException e) {
57             throw new ExceptionInInitializerError(e);
58         }
59         BUILDER_FACTORY.setNamespaceAware(true);
60         BUILDER_FACTORY.setCoalescing(true);
61         BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
62         BUILDER_FACTORY.setIgnoringComments(true);
63     }
64
65     public enum ErrorType {
66         TRANSPORT, RPC, PROTOCOL, APPLICATION;
67
68         public String getTagValue() {
69             return name();
70         }
71
72         public static ErrorType from( String text ) {
73             try {
74                 return valueOf( text.toUpperCase() );
75             }
76             catch( Exception e ) {
77                 return APPLICATION;
78             }
79         }
80     }
81
82     public enum ErrorTag {
83         ACCESS_DENIED("access-denied"),
84         BAD_ATTRIBUTE("bad-attribute"),
85         BAD_ELEMENT("bad-element"),
86         DATA_EXISTS("data-exists"),
87         DATA_MISSING("data-missing"),
88         IN_USE("in-use"),
89         INVALID_VALUE("invalid-value"),
90         LOCK_DENIED("lock-denied"),
91         MALFORMED_MESSAGE("malformed-message"),
92         MISSING_ATTRIBUTE("missing-attribute"),
93         MISSING_ELEMENT("missing-element"),
94         OPERATION_FAILED("operation-failed"),
95         OPERATION_NOT_SUPPORTED("operation-not-supported"),
96         RESOURCE_DENIED("resource-denied"),
97         ROLLBCK_FAILED("rollback-failed"),
98         TOO_BIG("too-big"),
99         UNKNOWN_ATTRIBUTE("unknown-attribute"),
100         UNKNOWN_ELEMENT("unknown-element"),
101         UNKNOWN_NAMESPACE("unknown-namespace");
102
103         private final String tagValue;
104
105         ErrorTag(final String tagValue) {
106             this.tagValue = tagValue;
107         }
108
109         public String getTagValue() {
110             return this.tagValue;
111         }
112
113         public static ErrorTag from( String text ) {
114             for( ErrorTag e: values() )
115             {
116                 if( e.getTagValue().equals( text ) ) {
117                     return e;
118                 }
119             }
120
121             return OPERATION_FAILED;
122         }
123     }
124
125     public enum ErrorSeverity {
126         ERROR, WARNING;
127
128         public String getTagValue() {
129             return name();
130         }
131
132         public static ErrorSeverity from( String text ) {
133             try {
134                 return valueOf( text.toUpperCase() );
135             }
136             catch( Exception e ) {
137                 return ERROR;
138             }
139         }
140     }
141
142     private final ErrorType errorType;
143     private final ErrorTag errorTag;
144     private final ErrorSeverity errorSeverity;
145     private final Map<String, String> errorInfo;
146
147     public DocumentedException(String message) {
148         this(message,
149                 DocumentedException.ErrorType.APPLICATION,
150                 DocumentedException.ErrorTag.INVALID_VALUE,
151                 DocumentedException.ErrorSeverity.ERROR
152         );
153     }
154
155     public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
156                                final ErrorSeverity errorSeverity) {
157         this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
158     }
159
160     public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
161                                final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
162         super(message);
163         this.errorType = errorType;
164         this.errorTag = errorTag;
165         this.errorSeverity = errorSeverity;
166         this.errorInfo = errorInfo;
167     }
168
169     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
170                                final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
171         this(message, cause, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
172     }
173
174     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
175                                final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
176         super(message, cause);
177         this.errorType = errorType;
178         this.errorTag = errorTag;
179         this.errorSeverity = errorSeverity;
180         this.errorInfo = errorInfo;
181     }
182
183     public static <E extends Exception> DocumentedException wrap(E exception) throws DocumentedException {
184         final Map<String, String> errorInfo = new HashMap<>();
185         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
186         throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
187                 ErrorSeverity.ERROR, errorInfo);
188     }
189     public static DocumentedException wrap(ValidationException e) throws DocumentedException {
190         final Map<String, String> errorInfo = new HashMap<>();
191         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Validation failed");
192         throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
193                 ErrorSeverity.ERROR, errorInfo);
194     }
195
196     public static DocumentedException wrap(ConflictingVersionException e) throws DocumentedException {
197         final Map<String, String> errorInfo = new HashMap<>();
198         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Optimistic lock failed");
199         throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
200                 ErrorSeverity.ERROR, errorInfo);
201     }
202
203     public static DocumentedException fromXMLDocument( Document fromDoc ) {
204
205         ErrorType errorType = ErrorType.APPLICATION;
206         ErrorTag errorTag = ErrorTag.OPERATION_FAILED;
207         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
208         Map<String, String> errorInfo = null;
209         String errorMessage = "";
210
211         Node rpcReply = fromDoc.getDocumentElement();
212
213         // FIXME: BUG? - we only handle one rpc-error.
214
215         NodeList replyChildren = rpcReply.getChildNodes();
216         for( int i = 0; i < replyChildren.getLength(); i++ ) {
217             Node replyChild = replyChildren.item( i );
218             if( RPC_ERROR.equals( replyChild.getNodeName() ) )
219             {
220                 NodeList rpcErrorChildren = replyChild.getChildNodes();
221                 for( int j = 0; j < rpcErrorChildren.getLength(); j++ )
222                 {
223                     Node rpcErrorChild = rpcErrorChildren.item( j );
224                     if( ERROR_TYPE.equals( rpcErrorChild.getNodeName() ) ) {
225                         errorType = ErrorType.from(rpcErrorChild.getTextContent());
226                     }
227                     else if( ERROR_TAG.equals( rpcErrorChild.getNodeName() ) ) {
228                         errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
229                     }
230                     else if( ERROR_SEVERITY.equals( rpcErrorChild.getNodeName() ) ) {
231                         errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
232                     }
233                     else if( ERROR_MESSAGE.equals( rpcErrorChild.getNodeName() ) ) {
234                         errorMessage = rpcErrorChild.getTextContent();
235                     }
236                     else if( ERROR_INFO.equals( rpcErrorChild.getNodeName() ) ) {
237                         errorInfo = parseErrorInfo( rpcErrorChild );
238                     }
239                 }
240
241                 break;
242             }
243         }
244
245         return new DocumentedException( errorMessage, errorType, errorTag, errorSeverity, errorInfo );
246     }
247
248     private static Map<String, String> parseErrorInfo( Node node ) {
249         Map<String, String> infoMap = new HashMap<>();
250         NodeList children = node.getChildNodes();
251         for( int i = 0; i < children.getLength(); i++ ) {
252             Node child = children.item( i );
253             if( child.getNodeType() == Node.ELEMENT_NODE ) {
254                 infoMap.put( child.getNodeName(), child.getTextContent() );
255             }
256         }
257
258         return infoMap;
259     }
260
261     public ErrorType getErrorType() {
262         return this.errorType;
263     }
264
265     public ErrorTag getErrorTag() {
266         return this.errorTag;
267     }
268
269     public ErrorSeverity getErrorSeverity() {
270         return this.errorSeverity;
271     }
272
273     public Map<String, String> getErrorInfo() {
274         return this.errorInfo;
275     }
276
277     public Document toXMLDocument() {
278         Document doc = null;
279         try {
280             doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
281
282             Node rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
283             doc.appendChild( rpcReply );
284
285             Node rpcError = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR );
286             rpcReply.appendChild( rpcError );
287
288             rpcError.appendChild( createTextNode( doc, ERROR_TYPE, getErrorType().getTagValue() ) );
289             rpcError.appendChild( createTextNode( doc, ERROR_TAG, getErrorTag().getTagValue() ) );
290             rpcError.appendChild( createTextNode( doc, ERROR_SEVERITY, getErrorSeverity().getTagValue() ) );
291             rpcError.appendChild( createTextNode( doc, ERROR_MESSAGE, getLocalizedMessage() ) );
292
293             Map<String, String> errorInfoMap = getErrorInfo();
294             if( errorInfoMap != null && !errorInfoMap.isEmpty() ) {
295                 /*
296                  * <error-info>
297                  *   <bad-attribute>message-id</bad-attribute>
298                  *   <bad-element>rpc</bad-element>
299                  * </error-info>
300                  */
301
302                 Node errorInfoNode = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO );
303                 errorInfoNode.setPrefix( rpcReply.getPrefix() );
304                 rpcError.appendChild( errorInfoNode );
305
306                 for ( Entry<String, String> entry : errorInfoMap.entrySet() ) {
307                     errorInfoNode.appendChild( createTextNode( doc, entry.getKey(), entry.getValue() ) );
308                 }
309             }
310         }
311         catch( ParserConfigurationException e ) {
312             // this shouldn't happen
313             LOG.error("Error outputting to XML document", e);
314         }
315
316         return doc;
317     }
318
319     private Node createTextNode( Document doc, String tag, String textContent ) {
320         Node node = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag );
321         node.setTextContent( textContent );
322         return node;
323     }
324
325     @Override
326     public String toString() {
327         return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
328                 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
329                 + this.errorInfo + '}';
330     }
331 }