Resolve Bug:713 - Open snapshot only once per session.
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / NetconfServerSessionNegotiatorFactory.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 io.netty.channel.Channel;
13 import io.netty.util.Timer;
14 import io.netty.util.concurrent.Promise;
15 import org.opendaylight.controller.netconf.api.NetconfServerSessionPreferences;
16 import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
17 import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
18 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
19 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
20 import org.opendaylight.controller.netconf.util.NetconfUtil;
21 import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessage;
22 import org.opendaylight.controller.netconf.util.xml.XMLNetconfUtil;
23 import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
24 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
25 import org.opendaylight.protocol.framework.SessionListenerFactory;
26 import org.opendaylight.protocol.framework.SessionNegotiator;
27 import org.opendaylight.protocol.framework.SessionNegotiatorFactory;
28 import org.w3c.dom.Document;
29 import org.w3c.dom.Element;
30 import org.w3c.dom.Node;
31
32 import javax.xml.xpath.XPathConstants;
33 import javax.xml.xpath.XPathExpression;
34 import java.io.InputStream;
35
36 import static org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider.NetconfOperationProviderUtil.getNetconfSessionIdForReporting;
37
38 public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorFactory<NetconfHelloMessage, NetconfServerSession, NetconfServerSessionListener> {
39
40     public static final String SERVER_HELLO_XML_LOCATION = "/server_hello.xml";
41
42     private final Timer timer;
43
44     private static final Document helloMessageTemplate = loadHelloMessageTemplate();
45     private final SessionIdProvider idProvider;
46     private final NetconfOperationProvider netconfOperationProvider;
47     private final long connectionTimeoutMillis;
48     private final DefaultCommitNotificationProducer commitNotificationProducer;
49     private final SessionMonitoringService monitoringService;
50
51     public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
52                                                  SessionIdProvider idProvider, long connectionTimeoutMillis,
53                                                  DefaultCommitNotificationProducer commitNot, SessionMonitoringService monitoringService) {
54         this.timer = timer;
55         this.netconfOperationProvider = netconfOperationProvider;
56         this.idProvider = idProvider;
57         this.connectionTimeoutMillis = connectionTimeoutMillis;
58         this.commitNotificationProducer = commitNot;
59         this.monitoringService = monitoringService;
60     }
61
62     private static Document loadHelloMessageTemplate() {
63         InputStream resourceAsStream = NetconfServerSessionNegotiatorFactory.class
64                 .getResourceAsStream(SERVER_HELLO_XML_LOCATION);
65         Preconditions.checkNotNull(resourceAsStream, "Unable to load server hello message blueprint from %s",
66                 SERVER_HELLO_XML_LOCATION);
67         return NetconfUtil.createMessage(resourceAsStream).getDocument();
68     }
69
70     /**
71      *
72      * @param defunctSessionListenerFactory will not be taken into account as session listener factory can
73      *                                      only be created after snapshot is opened, thus this method constructs
74      *                                      proper session listener factory.
75      * @param channel Underlying channel
76      * @param promise Promise to be notified
77      * @return session negotiator
78      */
79     @Override
80     public SessionNegotiator<NetconfServerSession> getSessionNegotiator(SessionListenerFactory<NetconfServerSessionListener> defunctSessionListenerFactory,
81                                                                         Channel channel, Promise<NetconfServerSession> promise) {
82         long sessionId = idProvider.getNextSessionId();
83         NetconfOperationServiceSnapshot netconfOperationServiceSnapshot = netconfOperationProvider.openSnapshot(
84                 getNetconfSessionIdForReporting(sessionId));
85         CapabilityProvider capabilityProvider = new CapabilityProviderImpl(netconfOperationServiceSnapshot);
86
87         NetconfServerSessionPreferences proposal = new NetconfServerSessionPreferences(
88                 createHelloMessage(sessionId, capabilityProvider), sessionId);
89
90         NetconfServerSessionListenerFactory sessionListenerFactory = new NetconfServerSessionListenerFactory(
91                 commitNotificationProducer, monitoringService,
92                 netconfOperationServiceSnapshot, capabilityProvider);
93
94         return new NetconfServerSessionNegotiator(proposal, promise, channel, timer,
95                 sessionListenerFactory.getSessionListener(), connectionTimeoutMillis);
96     }
97
98     private static final XPathExpression sessionIdXPath = XMLNetconfUtil
99             .compileXPath("/netconf:hello/netconf:session-id");
100     private static final XPathExpression capabilitiesXPath = XMLNetconfUtil
101             .compileXPath("/netconf:hello/netconf:capabilities");
102
103     private NetconfHelloMessage createHelloMessage(long sessionId, CapabilityProvider capabilityProvider) {
104         Document helloMessageTemplate = getHelloTemplateClone();
105
106         // change session ID
107         final Node sessionIdNode = (Node) XmlUtil.evaluateXPath(sessionIdXPath, helloMessageTemplate,
108                 XPathConstants.NODE);
109         sessionIdNode.setTextContent(String.valueOf(sessionId));
110
111         // add capabilities from yang store
112         final Element capabilitiesElement = (Element) XmlUtil.evaluateXPath(capabilitiesXPath, helloMessageTemplate,
113                 XPathConstants.NODE);
114
115         for (String capability : capabilityProvider.getCapabilities()) {
116             final Element capabilityElement = helloMessageTemplate.createElement(XmlNetconfConstants.CAPABILITY);
117             capabilityElement.setTextContent(capability);
118             capabilitiesElement.appendChild(capabilityElement);
119         }
120         return new NetconfHelloMessage(helloMessageTemplate);
121     }
122
123     private synchronized Document getHelloTemplateClone() {
124         return (Document) helloMessageTemplate.cloneNode(true);
125     }
126 }