Initial code drop of netconf protocol implementation
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / NetconfServerSessionListener.java
diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListener.java
new file mode 100644 (file)
index 0000000..4f71ab9
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.impl;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl;
+import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.opendaylight.protocol.framework.SessionListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class NetconfServerSessionListener implements
+        SessionListener<NetconfMessage, NetconfServerSession, NetconfTerminationReason> {
+
+    static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
+    public static final String MESSAGE_ID = "message-id";
+
+    private NetconfOperationRouterImpl operationRouter;
+
+    public NetconfServerSessionListener(NetconfOperationRouterImpl operationRouter) {
+        this.operationRouter = operationRouter;
+    }
+
+    @Override
+    public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
+
+    }
+
+    @Override
+    public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception e) {
+        logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, e.getMessage());
+
+        operationRouter.close();
+    }
+
+    @Override
+    public void onSessionTerminated(NetconfServerSession netconfNetconfServerSession,
+            NetconfTerminationReason netconfTerminationReason) {
+        logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
+                netconfTerminationReason.getErrorMessage());
+
+        operationRouter.close();
+    }
+
+    @Override
+    public void onMessage(NetconfServerSession session, NetconfMessage netconfMessage) {
+        try {
+
+            Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
+            // FIXME: there is no validation since the document may contain yang
+            // schemas
+            final NetconfMessage message = processDocument(netconfMessage);
+            logger.debug("Respondign with message {}", XmlUtil.toString(message.getDocument()));
+            session.sendMessage(message);
+
+            if (isCloseSession(netconfMessage)) {
+                closeNetconfSession(session);
+            }
+
+        } catch (final RuntimeException e) {
+            logger.error("Unexpected exception", e);
+            // TODO: should send generic error or close session?
+            throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
+        } catch (NetconfDocumentedException e) {
+            SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
+        }
+    }
+
+    private void closeNetconfSession(NetconfServerSession session) {
+        // destroy NetconfOperationService
+        session.close();
+        logger.info("Session {} closed successfully", session.getSessionId());
+    }
+
+    private NetconfMessage processDocument(final NetconfMessage netconfMessage) throws NetconfDocumentedException {
+
+        final Document incommingDocument = netconfMessage.getDocument();
+        final Node rootNode = incommingDocument.getDocumentElement();
+
+        if (rootNode.getNodeName().equals(XmlNetconfConstants.RPC_KEY)) {
+            final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent();
+            checkState(messageId != null);
+            final Document responseDocument = XmlUtil.newDocument();
+            Document rpcReply = operationRouter.onNetconfMessage(incommingDocument);
+            responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
+            return new NetconfMessage(responseDocument);
+        } else {
+            // unknown command, send RFC 4741 p.70 unknown-element
+            /*
+             * Tag: unknown-element Error-type: rpc, protocol, application
+             * Severity: error Error-info: <bad-element> : name of the
+             * unexpected element Description: An unexpected element is present.
+             */
+            // TODO add message to error info
+            throw new NetconfDocumentedException("Unknown tag " + rootNode.getNodeName(),
+                    NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.unknown_element,
+                    NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element",
+                            rootNode.getNodeName()));
+        }
+    }
+
+    private static boolean isCloseSession(final NetconfMessage incommingDocument) {
+        final Document document = incommingDocument.getDocument();
+        XmlElement rpcElement = XmlElement.fromDomDocument(document);
+        if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent())
+            return true;
+
+        return false;
+    }
+}