a44b7a799d063bf4ba6e9c3f202176050aa07030
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / NetconfServerSessionListener.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.netconf.impl;
10
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableMap;
13 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
14 import org.opendaylight.controller.netconf.api.NetconfMessage;
15 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
16 import org.opendaylight.controller.netconf.api.NetconfSessionListener;
17 import org.opendaylight.controller.netconf.api.NetconfTerminationReason;
18 import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
19 import org.opendaylight.controller.netconf.util.messages.SendErrorExceptionUtil;
20 import org.opendaylight.controller.netconf.util.xml.XmlElement;
21 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
22 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25 import org.w3c.dom.Document;
26 import org.w3c.dom.Node;
27
28 import static com.google.common.base.Preconditions.checkState;
29
30 public class NetconfServerSessionListener implements NetconfSessionListener<NetconfServerSession> {
31     public static final String MESSAGE_ID = "message-id";
32
33     static final Logger logger = LoggerFactory.getLogger(NetconfServerSessionListener.class);
34     private final SessionMonitoringService monitoringService;
35     private final NetconfOperationRouter operationRouter;
36
37     public NetconfServerSessionListener(NetconfOperationRouter operationRouter, SessionMonitoringService monitoringService) {
38         this.operationRouter = operationRouter;
39         this.monitoringService = monitoringService;
40     }
41
42     @Override
43     public void onSessionUp(NetconfServerSession netconfNetconfServerSession) {
44         monitoringService.onSessionUp(netconfNetconfServerSession);
45     }
46
47     @Override
48     public void onSessionDown(NetconfServerSession netconfNetconfServerSession, Exception cause) {
49         logger.debug("Session {} down, reason: {}", netconfNetconfServerSession, cause.getMessage());
50         monitoringService.onSessionDown(netconfNetconfServerSession);
51
52         try {
53             operationRouter.close();
54         } catch (Exception closingEx) {
55             logger.debug("Ignoring exception while closing operationRouter", closingEx);
56         }
57     }
58
59     @Override
60     public void onSessionTerminated(NetconfServerSession netconfNetconfServerSession,
61             NetconfTerminationReason netconfTerminationReason) {
62         logger.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
63                 netconfTerminationReason.getErrorMessage());
64         monitoringService.onSessionDown(netconfNetconfServerSession);
65
66         try {
67             operationRouter.close();
68         } catch (Exception closingEx) {
69             logger.debug("Ignoring exception while closing operationRouter", closingEx);
70         }
71     }
72
73     @Override
74     public void onMessage(NetconfServerSession session, NetconfMessage netconfMessage) {
75         try {
76
77             Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
78             // FIXME: there is no validation since the document may contain yang
79             // schemas
80             final NetconfMessage message = processDocument(netconfMessage,
81                     session);
82             logger.debug("Responding with message {}", XmlUtil.toString(message.getDocument()));
83             session.sendMessage(message);
84
85             if (isCloseSession(netconfMessage)) {
86                 closeNetconfSession(session);
87             }
88
89         } catch (final RuntimeException e) {
90             // TODO: should send generic error or close session?
91             logger.error("Unexpected exception", e);
92             session.onIncommingRpcFail();
93             throw new RuntimeException("Unable to process incoming message " + netconfMessage, e);
94         } catch (NetconfDocumentedException e) {
95             session.onOutgoingRpcError();
96             session.onIncommingRpcFail();
97             SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
98         }
99     }
100
101     private void closeNetconfSession(NetconfServerSession session) {
102         // destroy NetconfOperationService
103         session.close();
104         logger.info("Session {} closed successfully", session.getSessionId());
105     }
106
107     private NetconfMessage processDocument(final NetconfMessage netconfMessage,
108             NetconfServerSession session) throws NetconfDocumentedException {
109
110         final Document incommingDocument = netconfMessage.getDocument();
111         final Node rootNode = incommingDocument.getDocumentElement();
112
113         if (rootNode.getLocalName().equals(XmlNetconfConstants.RPC_KEY)) {
114             final String messageId = rootNode.getAttributes().getNamedItem(MESSAGE_ID).getTextContent();
115             checkState(messageId != null);
116             final Document responseDocument = XmlUtil.newDocument();
117             Document rpcReply = operationRouter.onNetconfMessage(
118                     incommingDocument, session);
119
120             session.onIncommingRpcSuccess();
121
122             responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
123             return new NetconfMessage(responseDocument);
124         } else {
125             // unknown command, send RFC 4741 p.70 unknown-element
126             /*
127              * Tag: unknown-element Error-type: rpc, protocol, application
128              * Severity: error Error-info: <bad-element> : name of the
129              * unexpected element Description: An unexpected element is present.
130              */
131             // TODO add message to error info
132             throw new NetconfDocumentedException("Unknown tag " + rootNode.getNodeName(),
133                     NetconfDocumentedException.ErrorType.protocol, NetconfDocumentedException.ErrorTag.unknown_element,
134                     NetconfDocumentedException.ErrorSeverity.error, ImmutableMap.of("bad-element",
135                             rootNode.getNodeName()));
136         }
137     }
138
139     private static boolean isCloseSession(final NetconfMessage incommingDocument) {
140         final Document document = incommingDocument.getDocument();
141         XmlElement rpcElement = XmlElement.fromDomDocument(document);
142         if (rpcElement.getOnlyChildElementOptionally("close-session").isPresent())
143             return true;
144
145         return false;
146     }
147 }