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;
* 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";
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 {
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;
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) {
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();
// 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
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());
}
}
return infoMap;
}
+ // FIXME: NETCONF-793: remove all of these in favor of YangNetconfErrorAware
public ErrorType getErrorType() {
return this.errorType;
}
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 {
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()));
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;