config-util: final parameters
[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 ErrorType errorType, final ErrorTag errorTag,
192                                final ErrorSeverity errorSeverity) {
193         this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
194     }
195
196     public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
197                                final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
198         super(message);
199         this.errorType = errorType;
200         this.errorTag = errorTag;
201         this.errorSeverity = errorSeverity;
202         this.errorInfo = errorInfo;
203     }
204
205     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
206                                final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
207         this(message, cause, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
208     }
209
210     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
211                                final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
212         super(message, cause);
213         this.errorType = errorType;
214         this.errorTag = errorTag;
215         this.errorSeverity = errorSeverity;
216         this.errorInfo = errorInfo;
217     }
218
219     public static <E extends Exception> DocumentedException wrap(final E exception) throws DocumentedException {
220         final Map<String, String> errorInfo = new HashMap<>();
221         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
222         throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
223                 ErrorSeverity.ERROR, errorInfo);
224     }
225     public static DocumentedException wrap(final ValidationException e) throws DocumentedException {
226         final Map<String, String> errorInfo = new HashMap<>();
227         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Validation failed");
228         throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
229                 ErrorSeverity.ERROR, errorInfo);
230     }
231
232     public static DocumentedException wrap(final ConflictingVersionException e) throws DocumentedException {
233         final Map<String, String> errorInfo = new HashMap<>();
234         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Optimistic lock failed");
235         throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
236                 ErrorSeverity.ERROR, errorInfo);
237     }
238
239     public static DocumentedException fromXMLDocument( final Document fromDoc ) {
240
241         ErrorType errorType = ErrorType.APPLICATION;
242         ErrorTag errorTag = ErrorTag.OPERATION_FAILED;
243         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
244         Map<String, String> errorInfo = null;
245         String errorMessage = "";
246
247         Node rpcReply = fromDoc.getDocumentElement();
248
249         // FIXME: BUG? - we only handle one rpc-error.
250
251         NodeList replyChildren = rpcReply.getChildNodes();
252         for( int i = 0; i < replyChildren.getLength(); i++ ) {
253             Node replyChild = replyChildren.item( i );
254             if( RPC_ERROR.equals( replyChild.getNodeName() ) )
255             {
256                 NodeList rpcErrorChildren = replyChild.getChildNodes();
257                 for( int j = 0; j < rpcErrorChildren.getLength(); j++ )
258                 {
259                     Node rpcErrorChild = rpcErrorChildren.item( j );
260                     if( ERROR_TYPE.equals( rpcErrorChild.getNodeName() ) ) {
261                         errorType = ErrorType.from(rpcErrorChild.getTextContent());
262                     }
263                     else if( ERROR_TAG.equals( rpcErrorChild.getNodeName() ) ) {
264                         errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
265                     }
266                     else if( ERROR_SEVERITY.equals( rpcErrorChild.getNodeName() ) ) {
267                         errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
268                     }
269                     else if( ERROR_MESSAGE.equals( rpcErrorChild.getNodeName() ) ) {
270                         errorMessage = rpcErrorChild.getTextContent();
271                     }
272                     else if( ERROR_INFO.equals( rpcErrorChild.getNodeName() ) ) {
273                         errorInfo = parseErrorInfo( rpcErrorChild );
274                     }
275                 }
276
277                 break;
278             }
279         }
280
281         return new DocumentedException( errorMessage, errorType, errorTag, errorSeverity, errorInfo );
282     }
283
284     private static Map<String, String> parseErrorInfo( final Node node ) {
285         Map<String, String> infoMap = new HashMap<>();
286         NodeList children = node.getChildNodes();
287         for( int i = 0; i < children.getLength(); i++ ) {
288             Node child = children.item( i );
289             if( child.getNodeType() == Node.ELEMENT_NODE ) {
290                 infoMap.put( child.getNodeName(), child.getTextContent() );
291             }
292         }
293
294         return infoMap;
295     }
296
297     public ErrorType getErrorType() {
298         return this.errorType;
299     }
300
301     public ErrorTag getErrorTag() {
302         return this.errorTag;
303     }
304
305     public ErrorSeverity getErrorSeverity() {
306         return this.errorSeverity;
307     }
308
309     public Map<String, String> getErrorInfo() {
310         return this.errorInfo;
311     }
312
313     public Document toXMLDocument() {
314         Document doc = null;
315         try {
316             doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
317
318             Node rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
319             doc.appendChild( rpcReply );
320
321             Node rpcError = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR );
322             rpcReply.appendChild( rpcError );
323
324             rpcError.appendChild( createTextNode( doc, ERROR_TYPE, getErrorType().getTypeValue() ) );
325             rpcError.appendChild( createTextNode( doc, ERROR_TAG, getErrorTag().getTagValue() ) );
326             rpcError.appendChild( createTextNode( doc, ERROR_SEVERITY, getErrorSeverity().getSeverityValue() ) );
327             rpcError.appendChild( createTextNode( doc, ERROR_MESSAGE, getLocalizedMessage() ) );
328
329             Map<String, String> errorInfoMap = getErrorInfo();
330             if( errorInfoMap != null && !errorInfoMap.isEmpty() ) {
331                 /*
332                  * <error-info>
333                  *   <bad-attribute>message-id</bad-attribute>
334                  *   <bad-element>rpc</bad-element>
335                  * </error-info>
336                  */
337
338                 Node errorInfoNode = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO );
339                 errorInfoNode.setPrefix( rpcReply.getPrefix() );
340                 rpcError.appendChild( errorInfoNode );
341
342                 for ( Entry<String, String> entry : errorInfoMap.entrySet() ) {
343                     errorInfoNode.appendChild( createTextNode( doc, entry.getKey(), entry.getValue() ) );
344                 }
345             }
346         }
347         catch( final ParserConfigurationException e ) {
348             // this shouldn't happen
349             LOG.error("Error outputting to XML document", e);
350         }
351
352         return doc;
353     }
354
355     private Node createTextNode( final Document doc, final String tag, final String textContent ) {
356         Node node = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag );
357         node.setTextContent( textContent );
358         return node;
359     }
360
361     @Override
362     public String toString() {
363         return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
364                 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
365                 + this.errorInfo + '}';
366     }
367 }