Remove DocumentedException.ErrorTag
[netconf.git] / netconf / netconf-api / src / main / java / org / opendaylight / netconf / api / DocumentedException.java
index 669218d5d0698f20b27dca2292026eff5268c29c..4007d0aa9f91a52d6643770bb908dd6d16cac0a3 100644 (file)
@@ -10,12 +10,14 @@ package org.opendaylight.netconf.api;
 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY;
 import static org.opendaylight.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
+import org.opendaylight.yangtools.yang.common.ErrorTag;
 import org.opendaylight.yangtools.yang.common.ErrorType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -27,6 +29,7 @@ import org.w3c.dom.NodeList;
  * Checked exception to communicate an error that needs to be sent to the
  * netconf client.
  */
+// FIXME: NETCONF-793: implement YangNetconfErrorAware
 public class DocumentedException extends Exception {
 
     public static final String RPC_ERROR = "rpc-error";
@@ -38,10 +41,12 @@ public class DocumentedException extends Exception {
     public static final String ERROR_MESSAGE = "error-message";
     public static final String ERROR_INFO = "error-info";
 
-    private static final long serialVersionUID = 1L;
+    // FIXME: This is an RFC6241 definition, remove it once we have yangtools-7.0.5
+    @Deprecated(forRemoval = true)
+    public static final ErrorTag MALFORMED_MESSAGE = new ErrorTag("malformed-message");
 
+    private static final long serialVersionUID = 1L;
     private static final Logger LOG = LoggerFactory.getLogger(DocumentedException.class);
-
     private static final DocumentBuilderFactory BUILDER_FACTORY;
 
     static {
@@ -61,49 +66,8 @@ public class DocumentedException extends Exception {
         BUILDER_FACTORY.setIgnoringComments(true);
     }
 
-    public enum ErrorTag {
-        ACCESS_DENIED("access-denied"),
-        BAD_ATTRIBUTE("bad-attribute"),
-        BAD_ELEMENT("bad-element"),
-        DATA_EXISTS("data-exists"),
-        DATA_MISSING("data-missing"),
-        IN_USE("in-use"),
-        INVALID_VALUE("invalid-value"),
-        LOCK_DENIED("lock-denied"),
-        MALFORMED_MESSAGE("malformed-message"),
-        MISSING_ATTRIBUTE("missing-attribute"),
-        MISSING_ELEMENT("missing-element"),
-        OPERATION_FAILED("operation-failed"),
-        OPERATION_NOT_SUPPORTED("operation-not-supported"),
-        RESOURCE_DENIED("resource-denied"),
-        ROLLBCK_FAILED("rollback-failed"),
-        TOO_BIG("too-big"),
-        UNKNOWN_ATTRIBUTE("unknown-attribute"),
-        UNKNOWN_ELEMENT("unknown-element"),
-        UNKNOWN_NAMESPACE("unknown-namespace");
-
-        private final String tagValue;
-
-        ErrorTag(final String tagValue) {
-            this.tagValue = tagValue;
-        }
-
-        public String getTagValue() {
-            return this.tagValue;
-        }
-
-        public static ErrorTag from(final String text) {
-            for (ErrorTag e : values()) {
-                if (e.getTagValue().equals(text)) {
-                    return e;
-                }
-            }
-
-            return OPERATION_FAILED;
-        }
-    }
-
     private final ErrorType errorType;
+    @SuppressFBWarnings(value = "SE_BAD_FIELD", justification = "FIXME: should not be necessary with yangtools-7.0.5")
     private final ErrorTag errorTag;
     private final ErrorSeverity errorSeverity;
     private final Map<String, String> errorInfo;
@@ -141,14 +105,16 @@ public class DocumentedException extends Exception {
         this.errorType = errorType;
         this.errorTag = errorTag;
         this.errorSeverity = errorSeverity;
+        // FIXME: this contract (based on what fromXMLDocument does) is quite wrong. It ignores the XML realities of
+        //        what constitutes a tag and especially tag value when faced with encoding XML-namespaced entities --
+        //        such as 'identity' arguments -- represented as QNames.
         this.errorInfo = errorInfo;
     }
 
-    public static <E extends Exception> DocumentedException wrap(final E exception) throws DocumentedException {
-        final Map<String, String> errorInfo = new HashMap<>();
-        errorInfo.put(ErrorTag.OPERATION_FAILED.name(), "Exception thrown");
+    public static DocumentedException wrap(final Exception exception) throws DocumentedException {
         throw new DocumentedException(exception.getMessage(), exception, ErrorType.APPLICATION,
-                ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR, errorInfo);
+                ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR,
+                Map.of(ErrorTag.OPERATION_FAILED.elementBody(), "Exception thrown"));
     }
 
     public static DocumentedException fromXMLDocument(final Document fromDoc) {
@@ -162,9 +128,10 @@ public class DocumentedException extends Exception {
 
         Node rpcReply = fromDoc.getDocumentElement();
 
-        // FIXME: BUG? - we only handle one rpc-error. For now, shove extra errorMessages
-        // found in multiple rpc-error in the errorInfo Map to at least let them propagate
-        // back to caller.
+        // FIXME: we only handle one rpc-error. For now, shove extra errorMessages found in multiple rpc-error in the
+        //        errorInfo Map to at least let them propagate back to caller.
+        //        this will be solved through migration to YangNetconfErrorAware, as that allows reporting multipl
+        //        error events
         int rpcErrorCount = 0;
 
         NodeList replyChildren = rpcReply.getChildNodes();
@@ -182,7 +149,7 @@ public class DocumentedException extends Exception {
                         // FIXME: this should be a hard error
                         errorType = type != null ? type : ErrorType.APPLICATION;
                     } else if (ERROR_TAG.equals(rpcErrorChild.getLocalName())) {
-                        errorTag = ErrorTag.from(rpcErrorChild.getTextContent());
+                        errorTag = new ErrorTag(rpcErrorChild.getTextContent());
                     } else if (ERROR_SEVERITY.equals(rpcErrorChild.getLocalName())) {
                         final ErrorSeverity sev = ErrorSeverity.forElementBody(rpcErrorChild.getTextContent());
                         // FIXME: this should be a hard error
@@ -213,6 +180,22 @@ public class DocumentedException extends Exception {
         for (int i = 0; i < children.getLength(); i++) {
             Node child = children.item(i);
             if (child.getNodeType() == Node.ELEMENT_NODE) {
+                // FIXME: Holy namespace ignorance, Batman!
+                //
+                // So this is just not enough to decode things in the general sense. getTextContenxt() may easily be a
+                // qualified QName, such as an identity name or an instance-identifier. What the entire 'infoMap' needs
+                // to contain is each child's XML context, so that the string literal can be interpreted as needed.
+                //
+                // yang.common.YangNamespaceContext represents the minimal API surface that needs to be exposed. That
+                // effectively means:
+                //
+                // final class ElementValue implements YangNamespaceContext {
+                //   public final String elementContenxt();
+                // }
+                //
+                // Map<QName, ElementValue> infoMap;
+                //
+                // except... what do we use for revision?
                 infoMap.put(child.getNodeName(), child.getTextContent());
             }
         }
@@ -220,6 +203,7 @@ public class DocumentedException extends Exception {
         return infoMap;
     }
 
+    // FIXME: NETCONF-793: remove all of these in favor of YangNetconfErrorAware
     public ErrorType getErrorType() {
         return this.errorType;
     }
@@ -236,6 +220,8 @@ public class DocumentedException extends Exception {
         return this.errorInfo;
     }
 
+    // FIXME: this really should be an spi/util method (or even netconf-util-w3c-dom?) as this certainly is not the
+    //        primary interface we want to expose -- it is inherently mutable and its API is a pure nightmare.
     public Document toXMLDocument() {
         Document doc = null;
         try {
@@ -248,7 +234,7 @@ public class DocumentedException extends Exception {
             rpcReply.appendChild(rpcError);
 
             rpcError.appendChild(createTextNode(doc, ERROR_TYPE, getErrorType().elementBody()));
-            rpcError.appendChild(createTextNode(doc, ERROR_TAG, getErrorTag().getTagValue()));
+            rpcError.appendChild(createTextNode(doc, ERROR_TAG, getErrorTag().elementBody()));
             rpcError.appendChild(createTextNode(doc, ERROR_SEVERITY, getErrorSeverity().elementBody()));
             rpcError.appendChild(createTextNode(doc, ERROR_MESSAGE, getLocalizedMessage()));
 
@@ -275,7 +261,7 @@ public class DocumentedException extends Exception {
         return doc;
     }
 
-    private Node createTextNode(final Document doc, final String tag, final String textContent) {
+    private static Node createTextNode(final Document doc, final String tag, final String textContent) {
         Node node = doc.createElementNS(URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, tag);
         node.setTextContent(textContent);
         return node;