2 * Copyright (c) 2013 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.netconf.impl;
11 import com.google.common.base.Preconditions;
12 import com.google.common.collect.ImmutableMap;
14 import java.util.regex.PatternSyntaxException;
16 import org.opendaylight.netconf.api.DocumentedException;
17 import org.opendaylight.netconf.api.NetconfMessage;
18 import org.opendaylight.netconf.api.NetconfSessionListener;
19 import org.opendaylight.netconf.api.NetconfTerminationReason;
20 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
21 import org.opendaylight.netconf.api.monitoring.SessionEvent;
22 import org.opendaylight.netconf.api.monitoring.SessionListener;
23 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
24 import org.opendaylight.netconf.api.xml.XmlUtil;
25 import org.opendaylight.netconf.impl.osgi.NetconfOperationRouter;
26 import org.opendaylight.netconf.notifications.NetconfNotification;
27 import org.opendaylight.netconf.util.messages.SendErrorExceptionUtil;
28 import org.opendaylight.netconf.util.messages.SubtreeFilter;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Element;
33 import org.w3c.dom.NamedNodeMap;
34 import org.w3c.dom.Node;
35 import org.w3c.dom.NodeList;
37 public class NetconfServerSessionListener implements NetconfSessionListener<NetconfServerSession> {
39 private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionListener.class);
40 private final SessionListener monitoringSessionListener;
41 private final NetconfOperationRouter operationRouter;
42 private final AutoCloseable onSessionDownCloseable;
44 public NetconfServerSessionListener(final NetconfOperationRouter operationRouter,
45 final NetconfMonitoringService monitoringService, final AutoCloseable onSessionDownCloseable) {
46 this.operationRouter = operationRouter;
47 this.monitoringSessionListener = monitoringService.getSessionListener();
48 this.onSessionDownCloseable = onSessionDownCloseable;
52 public void onSessionUp(final NetconfServerSession netconfNetconfServerSession) {
53 monitoringSessionListener.onSessionUp(netconfNetconfServerSession);
57 public void onSessionDown(final NetconfServerSession netconfNetconfServerSession, final Exception cause) {
58 LOG.debug("Session {} down, reason: {}", netconfNetconfServerSession, cause.getMessage());
59 onDown(netconfNetconfServerSession);
62 @SuppressWarnings("checkstyle:IllegalCatch")
63 public void onDown(final NetconfServerSession netconfNetconfServerSession) {
64 monitoringSessionListener.onSessionDown(netconfNetconfServerSession);
67 operationRouter.close();
68 } catch (final Exception closingEx) {
69 LOG.debug("Ignoring exception while closing operationRouter", closingEx);
72 onSessionDownCloseable.close();
73 } catch (final Exception ex) {
74 LOG.debug("Ignoring exception while closing onSessionDownCloseable", ex);
79 public void onSessionTerminated(final NetconfServerSession netconfNetconfServerSession,
80 final NetconfTerminationReason netconfTerminationReason) {
81 LOG.debug("Session {} terminated, reason: {}", netconfNetconfServerSession,
82 netconfTerminationReason.getErrorMessage());
83 onDown(netconfNetconfServerSession);
86 @SuppressWarnings("checkstyle:IllegalCatch")
88 public void onMessage(final NetconfServerSession session, final NetconfMessage netconfMessage) {
91 Preconditions.checkState(operationRouter != null, "Cannot handle message, session up was not yet received");
92 // there is no validation since the document may contain yang schemas
93 final NetconfMessage message = processDocument(netconfMessage, session);
94 final NetconfMessage modifiedmessage = modifyType(message);
95 LOG.debug("Responding with message {}", modifiedmessage);
96 session.sendMessage(message);
97 monitoringSessionListener.onSessionEvent(SessionEvent.inRpcSuccess(session));
98 } catch (final RuntimeException e) {
99 // TODO: should send generic error or close session?
100 LOG.error("Unexpected exception", e);
101 session.onIncommingRpcFail();
102 monitoringSessionListener.onSessionEvent(SessionEvent.inRpcFail(session));
103 throw new IllegalStateException("Unable to process incoming message " + netconfMessage, e);
104 } catch (final DocumentedException e) {
105 LOG.trace("Error occurred while processing message", e);
106 session.onOutgoingRpcError();
107 session.onIncommingRpcFail();
108 monitoringSessionListener.onSessionEvent(SessionEvent.inRpcFail(session));
109 monitoringSessionListener.onSessionEvent(SessionEvent.outRpcError(session));
110 SendErrorExceptionUtil.sendErrorMessage(session, e, netconfMessage);
114 private NetconfMessage modifyType(NetconfMessage message) {
115 Document doc = message.getDocument();
116 NodeList result = doc.getElementsByTagName("components");
117 if (result.getLength() > 0) {
118 NodeList components = doc.getElementsByTagName("component");
119 for (int i = 0; i < components.getLength(); i++) {
121 Element component = (Element) components.item(i);
122 Element state = getChild(component, "state");
124 LOG.debug("state gets : {}", state.getTextContent());
125 Element type = getChild(state, "type");
127 LOG.debug("type gets : {}", type.getTextContent());
128 LOG.debug("formatting ...");
129 String textContent = type.getTextContent();
130 LOG.debug("formatting : {}", textContent);
131 String[] splitValue = textContent.split("\\)");
133 String namespace = null;
134 if (splitValue.length == 2) {
135 namespace = splitValue[0];
136 value = splitValue[1];
137 if ((value != null) && (namespace != null)) {
138 if (namespace.contains("http://openconfig.net/yang/platform-types")) {
139 type.setAttribute("xmlns:oc-platform-types",
140 "http://openconfig.net/yang/platform-types");
141 type.setTextContent("oc-platform-types:" + value);
142 } else if (namespace.contains("http://openconfig.net/yang/transport-types")) {
143 type.setAttribute("xmlns:oc-opt-types",
144 "http://openconfig.net/yang/transport-types");
145 type.setTextContent("oc-opt-types:" + value);
148 LOG.debug("value or namespace is null !");
152 LOG.debug("tag <state> doesn't have type value !");
155 LOG.debug("tag <component> doesn't have state value !");
157 } catch (PatternSyntaxException | ArrayIndexOutOfBoundsException | NullPointerException e) {
158 LOG.warn("failed to get type value!", e);
162 LOG.debug("No <components> tag present in xml doc");
164 return new NetconfMessage(doc);
167 private Element getChild(Element parent, String name) {
168 for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) {
169 if ((child instanceof Element) && name.equals(child.getNodeName())) {
170 return (Element) child;
176 public void onNotification(final NetconfServerSession session, final NetconfNotification notification) {
177 monitoringSessionListener.onSessionEvent(SessionEvent.notification(session));
180 private NetconfMessage processDocument(final NetconfMessage netconfMessage, final NetconfServerSession session)
181 throws DocumentedException {
183 final Document incomingDocument = netconfMessage.getDocument();
184 final Node rootNode = incomingDocument.getDocumentElement();
186 if (rootNode.getLocalName().equals(XmlNetconfConstants.RPC_KEY)) {
187 final Document responseDocument = XmlUtil.newDocument();
188 checkMessageId(rootNode);
190 Document rpcReply = operationRouter.onNetconfMessage(incomingDocument, session);
192 rpcReply = SubtreeFilter.applyRpcSubtreeFilter(incomingDocument, rpcReply);
194 session.onIncommingRpcSuccess();
196 responseDocument.appendChild(responseDocument.importNode(rpcReply.getDocumentElement(), true));
197 return new NetconfMessage(responseDocument);
199 // unknown command, send RFC 4741 p.70 unknown-element
201 * Tag: unknown-element Error-type: rpc, protocol, application Severity: error
202 * Error-info: <bad-element> : name of the unexpected element Description: An
203 * unexpected element is present.
205 throw new DocumentedException("Unknown tag " + rootNode.getNodeName() + " in message:\n" + netconfMessage,
206 DocumentedException.ErrorType.PROTOCOL, DocumentedException.ErrorTag.UNKNOWN_ELEMENT,
207 DocumentedException.ErrorSeverity.ERROR, ImmutableMap.of("bad-element", rootNode.getNodeName()));
211 private static void checkMessageId(final Node rootNode) throws DocumentedException {
213 final NamedNodeMap attributes = rootNode.getAttributes();
215 if (attributes.getNamedItemNS(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0,
216 XmlNetconfConstants.MESSAGE_ID) != null) {
220 if (attributes.getNamedItem(XmlNetconfConstants.MESSAGE_ID) != null) {
224 throw new DocumentedException("Missing attribute " + rootNode.getNodeName(), DocumentedException.ErrorType.RPC,
225 DocumentedException.ErrorTag.MISSING_ATTRIBUTE, DocumentedException.ErrorSeverity.ERROR,
226 ImmutableMap.of("bad-attribute", XmlNetconfConstants.MESSAGE_ID, "bad-element",
227 XmlNetconfConstants.RPC_KEY));