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;
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.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;
30 * Checked exception to communicate an error that needs to be sent to the
33 public class DocumentedException extends Exception {
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";
44 private static final long serialVersionUID = 1L;
46 private static final Logger LOG = LoggerFactory.getLogger(DocumentedException.class);
48 private static final DocumentBuilderFactory BUILDER_FACTORY;
51 BUILDER_FACTORY = DocumentBuilderFactory.newInstance();
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);
61 BUILDER_FACTORY.setNamespaceAware(true);
62 BUILDER_FACTORY.setCoalescing(true);
63 BUILDER_FACTORY.setIgnoringElementContentWhitespace(true);
64 BUILDER_FACTORY.setIgnoringComments(true);
67 public enum ErrorType {
68 TRANSPORT("transport"),
71 APPLICATION("application");
73 private final String typeValue;
75 ErrorType(final String typeValue) {
76 this.typeValue = Preconditions.checkNotNull(typeValue);
79 public String getTypeValue() {
80 return this.typeValue;
84 * @deprecated Use {@link #getTypeValue()} instead.
87 public String getTagValue() {
88 return this.typeValue;
91 public static ErrorType from(final String text) {
92 for (ErrorType e : values()) {
93 if (e.getTypeValue().equalsIgnoreCase(text)) {
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"),
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"),
119 UNKNOWN_ATTRIBUTE("unknown-attribute"),
120 UNKNOWN_ELEMENT("unknown-element"),
121 UNKNOWN_NAMESPACE("unknown-namespace");
123 private final String tagValue;
125 ErrorTag(final String tagValue) {
126 this.tagValue = tagValue;
129 public String getTagValue() {
130 return this.tagValue;
133 public static ErrorTag from( final String text ) {
134 for( ErrorTag e: values() )
136 if( e.getTagValue().equals( text ) ) {
141 return OPERATION_FAILED;
145 public enum ErrorSeverity {
149 private final String severityValue;
151 ErrorSeverity(final String severityValue) {
152 this.severityValue = Preconditions.checkNotNull(severityValue);
155 public String getSeverityValue() {
156 return this.severityValue;
160 * @deprecated Use {@link #getSeverityValue()} instead.
163 public String getTagValue() {
164 return this.severityValue;
167 public static ErrorSeverity from(final String text) {
168 for (ErrorSeverity e : values()) {
169 if (e.getSeverityValue().equalsIgnoreCase(text)) {
178 private final ErrorType errorType;
179 private final ErrorTag errorTag;
180 private final ErrorSeverity errorSeverity;
181 private final Map<String, String> errorInfo;
183 public DocumentedException(final String message) {
185 DocumentedException.ErrorType.APPLICATION,
186 DocumentedException.ErrorTag.INVALID_VALUE,
187 DocumentedException.ErrorSeverity.ERROR
191 public DocumentedException(final String message, final Exception cause) {
193 DocumentedException.ErrorType.APPLICATION,
194 DocumentedException.ErrorTag.INVALID_VALUE,
195 DocumentedException.ErrorSeverity.ERROR
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());
204 public DocumentedException(final String message, final ErrorType errorType, final ErrorTag errorTag,
205 final ErrorSeverity errorSeverity, final Map<String, String> errorInfo) {
207 this.errorType = errorType;
208 this.errorTag = errorTag;
209 this.errorSeverity = errorSeverity;
210 this.errorInfo = errorInfo;
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());
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;
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);
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);
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);
247 public static DocumentedException fromXMLDocument( final Document fromDoc ) {
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 = "";
255 Node rpcReply = fromDoc.getDocumentElement();
257 // FIXME: BUG? - we only handle one rpc-error.
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() ) )
264 NodeList rpcErrorChildren = replyChild.getChildNodes();
265 for( int j = 0; j < rpcErrorChildren.getLength(); j++ )
267 Node rpcErrorChild = rpcErrorChildren.item( j );
268 if( ERROR_TYPE.equals( rpcErrorChild.getNodeName() ) ) {
269 errorType = ErrorType.from(rpcErrorChild.getTextContent());
271 else if( ERROR_TAG.equals( rpcErrorChild.getNodeName() ) ) {
272 errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
274 else if( ERROR_SEVERITY.equals( rpcErrorChild.getNodeName() ) ) {
275 errorSeverity = ErrorSeverity.from(rpcErrorChild.getTextContent());
277 else if( ERROR_MESSAGE.equals( rpcErrorChild.getNodeName() ) ) {
278 errorMessage = rpcErrorChild.getTextContent();
280 else if( ERROR_INFO.equals( rpcErrorChild.getNodeName() ) ) {
281 errorInfo = parseErrorInfo( rpcErrorChild );
289 return new DocumentedException( errorMessage, errorType, errorTag, errorSeverity, errorInfo );
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() );
305 public ErrorType getErrorType() {
306 return this.errorType;
309 public ErrorTag getErrorTag() {
310 return this.errorTag;
313 public ErrorSeverity getErrorSeverity() {
314 return this.errorSeverity;
317 public Map<String, String> getErrorInfo() {
318 return this.errorInfo;
321 public Document toXMLDocument() {
324 doc = BUILDER_FACTORY.newDocumentBuilder().newDocument();
326 Node rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY);
327 doc.appendChild( rpcReply );
329 Node rpcError = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_ERROR );
330 rpcReply.appendChild( rpcError );
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() ) );
337 Map<String, String> errorInfoMap = getErrorInfo();
338 if( errorInfoMap != null && !errorInfoMap.isEmpty() ) {
341 * <bad-attribute>message-id</bad-attribute>
342 * <bad-element>rpc</bad-element>
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 );
350 for ( Entry<String, String> entry : errorInfoMap.entrySet() ) {
351 errorInfoNode.appendChild( createTextNode( doc, entry.getKey(), entry.getValue() ) );
355 catch( final ParserConfigurationException e ) {
356 // this shouldn't happen
357 LOG.error("Error outputting to XML document", e);
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 );
370 public String toString() {
371 return "NetconfDocumentedException{" + "message=" + getMessage() + ", errorType=" + this.errorType
372 + ", errorTag=" + this.errorTag + ", errorSeverity=" + this.errorSeverity + ", errorInfo="
373 + this.errorInfo + '}';