Remove redundant code constructs
[netconf.git] / netconf / netconf-api / src / main / java / org / opendaylight / netconf / api / 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.netconf.api;
10
11 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
12 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.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.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 (final 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("transport"), RPC("rpc"), PROTOCOL("protocol"), APPLICATION("application");
67
68         private final String typeValue;
69
70         ErrorType(final String typeValue) {
71             this.typeValue = Preconditions.checkNotNull(typeValue);
72         }
73
74         public String getTypeValue() {
75             return this.typeValue;
76         }
77
78         public static ErrorType from(final String text) {
79             for (ErrorType e : values()) {
80                 if (e.getTypeValue().equalsIgnoreCase(text)) {
81                     return e;
82                 }
83             }
84
85             return APPLICATION;
86         }
87     }
88
89     public enum ErrorTag {
90         ACCESS_DENIED("access-denied"),
91         BAD_ATTRIBUTE("bad-attribute"),
92         BAD_ELEMENT("bad-element"),
93         DATA_EXISTS("data-exists"),
94         DATA_MISSING("data-missing"),
95         IN_USE("in-use"),
96         INVALID_VALUE("invalid-value"),
97         LOCK_DENIED("lock-denied"),
98         MALFORMED_MESSAGE("malformed-message"),
99         MISSING_ATTRIBUTE("missing-attribute"),
100         MISSING_ELEMENT("missing-element"),
101         OPERATION_FAILED("operation-failed"),
102         OPERATION_NOT_SUPPORTED("operation-not-supported"),
103         RESOURCE_DENIED("resource-denied"),
104         ROLLBCK_FAILED("rollback-failed"),
105         TOO_BIG("too-big"),
106         UNKNOWN_ATTRIBUTE("unknown-attribute"),
107         UNKNOWN_ELEMENT("unknown-element"),
108         UNKNOWN_NAMESPACE("unknown-namespace");
109
110         private final String tagValue;
111
112         ErrorTag(final String tagValue) {
113             this.tagValue = tagValue;
114         }
115
116         public String getTagValue() {
117             return this.tagValue;
118         }
119
120         public static ErrorTag from(final String text) {
121             for (ErrorTag e : values()) {
122                 if (e.getTagValue().equals(text)) {
123                     return e;
124                 }
125             }
126
127             return OPERATION_FAILED;
128         }
129     }
130
131     public enum ErrorSeverity {
132         ERROR("error"), WARNING("warning");
133
134         private final String severityValue;
135
136         ErrorSeverity(final String severityValue) {
137             this.severityValue = Preconditions.checkNotNull(severityValue);
138         }
139
140         public String getSeverityValue() {
141             return this.severityValue;
142         }
143
144         public static ErrorSeverity from(final String text) {
145             for (ErrorSeverity e : values()) {
146                 if (e.getSeverityValue().equalsIgnoreCase(text)) {
147                     return e;
148                 }
149             }
150
151             return ERROR;
152         }
153     }
154
155     private final ErrorType errorType;
156     private final ErrorTag errorTag;
157     private final ErrorSeverity errorSeverity;
158     private final Map<String, String> errorInfo;
159
160     public DocumentedException(final String message) {
161         this(message, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
162                 DocumentedException.ErrorSeverity.ERROR);
163     }
164
165     public DocumentedException(final String message, final Exception cause) {
166         this(message, cause, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
167                 DocumentedException.ErrorSeverity.ERROR);
168     }
169
170     public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
171             final ErrorSeverity errorSeverity) {
172         this(message, errorType, errorTag, errorSeverity, Collections.emptyMap());
173     }
174
175     public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
176             final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
177         super(message);
178         this.errorType = errorType;
179         this.errorTag = errorTag;
180         this.errorSeverity = errorSeverity;
181         this.errorInfo = errorInfo;
182     }
183
184     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
185             final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
186         this(message, cause, errorType, errorTag, errorSeverity, Collections.emptyMap());
187     }
188
189     public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
190             final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
191         super(message, cause);
192         this.errorType = errorType;
193         this.errorTag = errorTag;
194         this.errorSeverity = errorSeverity;
195         this.errorInfo = errorInfo;
196     }
197
198     public static <E extends Exception> DocumentedException wrap(final E exception) throws DocumentedException {
199         final Map<String, String> errorInfo = new HashMap<>();
200         errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
201         throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION,
202                 ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR, errorInfo);
203     }
204
205     public static DocumentedException fromXMLDocument(final Document fromDoc) {
206
207         ErrorType errorType = ErrorType.APPLICATION;
208         ErrorTag errorTag = ErrorTag.OPERATION_FAILED;
209         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
210         Map<String, String> errorInfo = null;
211         String errorMessage = "";
212
213         Node rpcReply = fromDoc.getDocumentElement();
214
215         // FIXME: BUG? - we only handle one rpc-error.
216
217         NodeList replyChildren = rpcReply.getChildNodes();
218         for (int i = 0; i < replyChildren.getLength(); i++) {
219             Node replyChild = replyChildren.item(i);
220             if (RPC_ERROR.equals(replyChild.getNodeName())) {
221                 NodeList rpcErrorChildren = replyChild.getChildNodes();
222                 for (int j = 0; j < rpcErrorChildren.getLength(); j++) {
223                     Node rpcErrorChild = rpcErrorChildren.item(j);
224                     if (ERROR_TYPE.equals(rpcErrorChild.getNodeName())) {
225                         errorType = ErrorType.from(rpcErrorChild.getTextContent());
226                     } else if (ERROR_TAG.equals(rpcErrorChild.getNodeName())) {
227                         errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
228                     } else if (ERROR_SEVERITY.equals(rpcErrorChild.getNodeName())) {
229                         errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
230                     } else if (ERROR_MESSAGE.equals(rpcErrorChild.getNodeName())) {
231                         errorMessage = rpcErrorChild.getTextContent();
232                     } else if (ERROR_INFO.equals(rpcErrorChild.getNodeName())) {
233                         errorInfo = parseErrorInfo(rpcErrorChild);
234                     }
235                 }
236
237                 break;
238             }
239         }
240
241         return new DocumentedException(errorMessage, errorType, errorTag, errorSeverity, errorInfo);
242     }
243
244     private static Map<String, String> parseErrorInfo(final Node node) {
245         Map<String, String> infoMap = new HashMap<>();
246         NodeList children = node.getChildNodes();
247         for (int i = 0; i < children.getLength(); i++) {
248             Node child = children.item(i);
249             if (child.getNodeType() == Node.ELEMENT_NODE) {
250                 infoMap.put(child.getNodeName(), child.getTextContent());
251             }
252         }
253
254         return infoMap;
255     }
256
257     public ErrorType getErrorType() {
258         return this.errorType;
259     }
260
261     public ErrorTag getErrorTag() {
262         return this.errorTag;
263     }
264
265     public ErrorSeverity getErrorSeverity() {
266         return this.errorSeverity;
267     }
268
269     public Map<String, String> getErrorInfo() {
270         return this.errorInfo;
271     }
272
273     public Document toXMLDocument() {
274         Document doc = null;
275         try {
276             doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
277
278             Node rpcReply = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
279             doc.appendChild(rpcReply);
280
281             Node rpcError = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR);
282             rpcReply.appendChild(rpcError);
283
284             rpcError.appendChild(createTextNode(doc, ERROR_TYPE, getErrorType().getTypeValue()));
285             rpcError.appendChild(createTextNode(doc, ERROR_TAG, getErrorTag().getTagValue()));
286             rpcError.appendChild(createTextNode(doc, ERROR_SEVERITY, getErrorSeverity().getSeverityValue()));
287             rpcError.appendChild(createTextNode(doc, ERROR_MESSAGE, getLocalizedMessage()));
288
289             Map<String, String> errorInfoMap = getErrorInfo();
290             if (errorInfoMap != null && !errorInfoMap.isEmpty()) {
291                 /*
292                  * <error-info> <bad-attribute>message-id</bad-attribute>
293                  * <bad-element>rpc</bad-element> </error-info>
294                  */
295
296                 Node errorInfoNode = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO);
297                 errorInfoNode.setPrefix(rpcReply.getPrefix());
298                 rpcError.appendChild(errorInfoNode);
299
300                 for (Entry<String, String> entry : errorInfoMap.entrySet()) {
301                     errorInfoNode.appendChild(createTextNode(doc, entry.getKey(), entry.getValue()));
302                 }
303             }
304         } catch (final ParserConfigurationException e) {
305             // this shouldn't happen
306             LOG.error("Error outputting to XML document", e);
307         }
308
309         return doc;
310     }
311
312     private Node createTextNode(final Document doc, final String tag, final String textContent) {
313         Node node = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag);
314         node.setTextContent(textContent);
315         return node;
316     }
317
318     @Override
319     public String toString() {
320         return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
321                 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
322                 + this.errorInfo + '}';
323     }
324 }