2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
9 package org.opendaylight.netconf.api;
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;
14 import com.google.common.base.Preconditions;
15 import java.util.Collections;
16 import java.util.HashMap;
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;
28 * Checked exception to communicate an error that needs to be sent to the
31 public class DocumentedException extends Exception {
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";
42 private static final long serialVersionUID = 1L;
44 private static final Logger LOG = LoggerFactory.getLogger(DocumentedException.class);
46 private static final DocumentBuilderFactory BUILDER_FACTORY;
49 BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
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);
59 BUILDER_FACTORY.setNamespaceAware(true);
60 BUILDER_FACTORY.setCoalescing(true);
61 BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
62 BUILDER_FACTORY.setIgnoringComments(true);
65 public enum ErrorType {
66 TRANSPORT("transport"), RPC("rpc"), PROTOCOL("protocol"), APPLICATION("application");
68 private final String typeValue;
70 ErrorType(final String typeValue) {
71 this.typeValue = Preconditions.checkNotNull(typeValue);
74 public String getTypeValue() {
75 return this.typeValue;
78 public static ErrorType from(final String text) {
79 for (ErrorType e : values()) {
80 if (e.getTypeValue().equalsIgnoreCase(text)) {
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"),
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"),
106 UNKNOWN_ATTRIBUTE("unknown-attribute"),
107 UNKNOWN_ELEMENT("unknown-element"),
108 UNKNOWN_NAMESPACE("unknown-namespace");
110 private final String tagValue;
112 ErrorTag(final String tagValue) {
113 this.tagValue = tagValue;
116 public String getTagValue() {
117 return this.tagValue;
120 public static ErrorTag from(final String text) {
121 for (ErrorTag e : values()) {
122 if (e.getTagValue().equals(text)) {
127 return OPERATION_FAILED;
131 public enum ErrorSeverity {
132 ERROR("error"), WARNING("warning");
134 private final String severityValue;
136 ErrorSeverity(final String severityValue) {
137 this.severityValue = Preconditions.checkNotNull(severityValue);
140 public String getSeverityValue() {
141 return this.severityValue;
144 public static ErrorSeverity from(final String text) {
145 for (ErrorSeverity e : values()) {
146 if (e.getSeverityValue().equalsIgnoreCase(text)) {
155 private final ErrorType errorType;
156 private final ErrorTag errorTag;
157 private final ErrorSeverity errorSeverity;
158 private final Map<String, String> errorInfo;
160 public DocumentedException(final String message) {
161 this(message, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
162 DocumentedException.ErrorSeverity.ERROR);
165 public DocumentedException(final String message, final Exception cause) {
166 this(message, cause, DocumentedException.ErrorType.APPLICATION, DocumentedException.ErrorTag.INVALID_VALUE,
167 DocumentedException.ErrorSeverity.ERROR);
170 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
171 final ErrorSeverity errorSeverity) {
172 this(message, errorType, errorTag, errorSeverity, Collections.emptyMap());
175 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
176 final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
178 this.errorType = errorType;
179 this.errorTag = errorTag;
180 this.errorSeverity = errorSeverity;
181 this.errorInfo = errorInfo;
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());
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;
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);
205 public static DocumentedException fromXMLDocument(final Document fromDoc) {
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 = "";
213 Node rpcReply = fromDoc.getDocumentElement();
215 // FIXME: BUG? - we only handle one rpc-error.
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);
241 return new DocumentedException(errorMessage, errorType, errorTag, errorSeverity, errorInfo);
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());
257 public ErrorType getErrorType() {
258 return this.errorType;
261 public ErrorTag getErrorTag() {
262 return this.errorTag;
265 public ErrorSeverity getErrorSeverity() {
266 return this.errorSeverity;
269 public Map<String, String> getErrorInfo() {
270 return this.errorInfo;
273 public Document toXMLDocument() {
276 doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
278 Node rpcReply = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
279 doc.appendChild(rpcReply);
281 Node rpcError = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR);
282 rpcReply.appendChild(rpcError);
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()));
289 Map<String, String> errorInfoMap = getErrorInfo();
290 if (errorInfoMap != null && !errorInfoMap.isEmpty()) {
292 * <error-info> <bad-attribute>message-id</bad-attribute>
293 * <bad-element>rpc</bad-element> </error-info>
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);
300 for (Entry<String, String> entry : errorInfoMap.entrySet()) {
301 errorInfoNode.appendChild(createTextNode(doc, entry.getKey(), entry.getValue()));
304 } catch (final ParserConfigurationException e) {
305 // this shouldn't happen
306 LOG.error("Error outputting to XML document", e);
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);
319 public String toString() {
320 return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
321 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
322 + this.errorInfo + '}';