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.controller.config.util.xml;
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;
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;
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 (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, RPC, PROTOCOL, APPLICATION;
68 public String getTypeValue() {
73 * @deprecated Use {@link #getTypeValue()} instead.
76 public String getTagValue() {
80 public static ErrorType from( String text ) {
82 return valueOf( text.toUpperCase() );
84 catch( Exception e ) {
90 public enum ErrorTag {
91 ACCESS_DENIED("access-denied"),
92 BAD_ATTRIBUTE("bad-attribute"),
93 BAD_ELEMENT("bad-element"),
94 DATA_EXISTS("data-exists"),
95 DATA_MISSING("data-missing"),
97 INVALID_VALUE("invalid-value"),
98 LOCK_DENIED("lock-denied"),
99 MALFORMED_MESSAGE("malformed-message"),
100 MISSING_ATTRIBUTE("missing-attribute"),
101 MISSING_ELEMENT("missing-element"),
102 OPERATION_FAILED("operation-failed"),
103 OPERATION_NOT_SUPPORTED("operation-not-supported"),
104 RESOURCE_DENIED("resource-denied"),
105 ROLLBCK_FAILED("rollback-failed"),
107 UNKNOWN_ATTRIBUTE("unknown-attribute"),
108 UNKNOWN_ELEMENT("unknown-element"),
109 UNKNOWN_NAMESPACE("unknown-namespace");
111 private final String tagValue;
113 ErrorTag(final String tagValue) {
114 this.tagValue = tagValue;
117 public String getTagValue() {
118 return this.tagValue;
121 public static ErrorTag from( String text ) {
122 for( ErrorTag e: values() )
124 if( e.getTagValue().equals( text ) ) {
129 return OPERATION_FAILED;
133 public enum ErrorSeverity {
136 public String getSeverityValue() {
141 * @deprecated Use {@link #getSeverityValue()} instead.
144 public String getTagValue() {
148 public static ErrorSeverity from( String text ) {
150 return valueOf( text.toUpperCase() );
152 catch( Exception e ) {
158 private final ErrorType errorType;
159 private final ErrorTag errorTag;
160 private final ErrorSeverity errorSeverity;
161 private final Map<String, String> errorInfo;
163 public DocumentedException(String message) {
165 DocumentedException.ErrorType.APPLICATION,
166 DocumentedException.ErrorTag.INVALID_VALUE,
167 DocumentedException.ErrorSeverity.ERROR
171 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
172 final ErrorSeverity errorSeverity) {
173 this(message, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
176 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
177 final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
179 this.errorType = errorType;
180 this.errorTag = errorTag;
181 this.errorSeverity = errorSeverity;
182 this.errorInfo = errorInfo;
185 public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
186 final ErrorTag errorTag, final ErrorSeverity errorSeverity) {
187 this(message, cause, errorType, errorTag, errorSeverity, Collections.<String, String> emptyMap());
190 public DocumentedException(final String message, final Exception cause, final ErrorType errorType,
191 final ErrorTag errorTag, final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
192 super(message, cause);
193 this.errorType = errorType;
194 this.errorTag = errorTag;
195 this.errorSeverity = errorSeverity;
196 this.errorInfo = errorInfo;
199 public static <E extends Exception> DocumentedException wrap(E exception) throws DocumentedException {
200 final Map<String, String> errorInfo = new HashMap<>();
201 errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
202 throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
203 ErrorSeverity.ERROR, errorInfo);
205 public static DocumentedException wrap(ValidationException e) throws DocumentedException {
206 final Map<String, String> errorInfo = new HashMap<>();
207 errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Validation failed");
208 throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
209 ErrorSeverity.ERROR, errorInfo);
212 public static DocumentedException wrap(ConflictingVersionException e) throws DocumentedException {
213 final Map<String, String> errorInfo = new HashMap<>();
214 errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Optimistic lock failed");
215 throw new DocumentedException(e.getMessage(), e, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
216 ErrorSeverity.ERROR, errorInfo);
219 public static DocumentedException fromXMLDocument( Document fromDoc ) {
221 ErrorType errorType = ErrorType.APPLICATION;
222 ErrorTag errorTag = ErrorTag.OPERATION_FAILED;
223 ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
224 Map<String, String> errorInfo = null;
225 String errorMessage = "";
227 Node rpcReply = fromDoc.getDocumentElement();
229 // FIXME: BUG? - we only handle one rpc-error.
231 NodeList replyChildren = rpcReply.getChildNodes();
232 for( int i = 0; i < replyChildren.getLength(); i++ ) {
233 Node replyChild = replyChildren.item( i );
234 if( RPC_ERROR.equals( replyChild.getNodeName() ) )
236 NodeList rpcErrorChildren = replyChild.getChildNodes();
237 for( int j = 0; j < rpcErrorChildren.getLength(); j++ )
239 Node rpcErrorChild = rpcErrorChildren.item( j );
240 if( ERROR_TYPE.equals( rpcErrorChild.getNodeName() ) ) {
241 errorType = ErrorType.from(rpcErrorChild.getTextContent());
243 else if( ERROR_TAG.equals( rpcErrorChild.getNodeName() ) ) {
244 errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
246 else if( ERROR_SEVERITY.equals( rpcErrorChild.getNodeName() ) ) {
247 errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
249 else if( ERROR_MESSAGE.equals( rpcErrorChild.getNodeName() ) ) {
250 errorMessage = rpcErrorChild.getTextContent();
252 else if( ERROR_INFO.equals( rpcErrorChild.getNodeName() ) ) {
253 errorInfo = parseErrorInfo( rpcErrorChild );
261 return new DocumentedException( errorMessage, errorType, errorTag, errorSeverity, errorInfo );
264 private static Map<String, String> parseErrorInfo( Node node ) {
265 Map<String, String> infoMap = new HashMap<>();
266 NodeList children = node.getChildNodes();
267 for( int i = 0; i < children.getLength(); i++ ) {
268 Node child = children.item( i );
269 if( child.getNodeType() == Node.ELEMENT_NODE ) {
270 infoMap.put( child.getNodeName(), child.getTextContent() );
277 public ErrorType getErrorType() {
278 return this.errorType;
281 public ErrorTag getErrorTag() {
282 return this.errorTag;
285 public ErrorSeverity getErrorSeverity() {
286 return this.errorSeverity;
289 public Map<String, String> getErrorInfo() {
290 return this.errorInfo;
293 public Document toXMLDocument() {
296 doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
298 Node rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
299 doc.appendChild( rpcReply );
301 Node rpcError = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR );
302 rpcReply.appendChild( rpcError );
304 rpcError.appendChild( createTextNode( doc, ERROR_TYPE, getErrorType().getTypeValue() ) );
305 rpcError.appendChild( createTextNode( doc, ERROR_TAG, getErrorTag().getTagValue() ) );
306 rpcError.appendChild( createTextNode( doc, ERROR_SEVERITY, getErrorSeverity().getSeverityValue() ) );
307 rpcError.appendChild( createTextNode( doc, ERROR_MESSAGE, getLocalizedMessage() ) );
309 Map<String, String> errorInfoMap = getErrorInfo();
310 if( errorInfoMap != null && !errorInfoMap.isEmpty() ) {
313 * <bad-attribute>message-id</bad-attribute>
314 * <bad-element>rpc</bad-element>
318 Node errorInfoNode = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, ERROR_INFO );
319 errorInfoNode.setPrefix( rpcReply.getPrefix() );
320 rpcError.appendChild( errorInfoNode );
322 for ( Entry<String, String> entry : errorInfoMap.entrySet() ) {
323 errorInfoNode.appendChild( createTextNode( doc, entry.getKey(), entry.getValue() ) );
327 catch( ParserConfigurationException e ) {
328 // this shouldn't happen
329 LOG.error("Error outputting to XML document", e);
335 private Node createTextNode( Document doc, String tag, String textContent ) {
336 Node node = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag );
337 node.setTextContent( textContent );
342 public String toString() {
343 return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
344 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
345 + this.errorInfo + '}';