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