Merge "Fixed bug when Binding-Aware Data Change Listeners we're not triggered."
[controller.git] / opendaylight / netconf / netconf-impl / src / main / java / org / opendaylight / controller / netconf / impl / osgi / NetconfOperationRouterImpl.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 package org.opendaylight.controller.netconf.impl.osgi;
9
10 import java.util.Collections;
11 import java.util.HashSet;
12 import java.util.Iterator;
13 import java.util.LinkedList;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.TreeMap;
17 import java.util.TreeSet;
18
19 import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
20 import org.opendaylight.controller.netconf.api.NetconfOperationRouter;
21 import org.opendaylight.controller.netconf.api.NetconfSession;
22 import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
23 import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider;
24 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession;
25 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCommit;
26 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultGetSchema;
27 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStartExi;
28 import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultStopExi;
29 import org.opendaylight.controller.netconf.mapping.api.DefaultNetconfOperation;
30 import org.opendaylight.controller.netconf.mapping.api.HandlingPriority;
31 import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
32 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilter;
33 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationFilterChain;
34 import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
35 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.w3c.dom.Document;
39
40 import com.google.common.base.Preconditions;
41 import com.google.common.collect.Maps;
42 import com.google.common.collect.Sets;
43
44 public class NetconfOperationRouterImpl implements NetconfOperationRouter {
45
46     private static final Logger logger = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
47
48     private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot;
49
50     private final Set<NetconfOperation> allNetconfOperations;
51     private final TreeSet<NetconfOperationFilter> allSortedFilters;
52
53     private final CapabilityProvider capabilityProvider;
54
55
56     public NetconfOperationRouterImpl(NetconfOperationServiceSnapshot netconfOperationServiceSnapshot,
57             CapabilityProvider capabilityProvider, DefaultCommitNotificationProducer commitNotifier) {
58
59         this.netconfOperationServiceSnapshot = Preconditions.checkNotNull(netconfOperationServiceSnapshot);
60         this.capabilityProvider = Preconditions.checkNotNull(capabilityProvider);
61
62         final String sessionId = netconfOperationServiceSnapshot.getNetconfSessionIdForReporting();
63         final Set<NetconfOperation> defaultNetconfOperations = Sets.newHashSet();
64         defaultNetconfOperations.add(new DefaultGetSchema(capabilityProvider, sessionId));
65         defaultNetconfOperations.add(new DefaultCloseSession(sessionId));
66         defaultNetconfOperations.add(new DefaultStartExi(sessionId));
67         defaultNetconfOperations.add(new DefaultStopExi(sessionId));
68
69         allNetconfOperations = getAllNetconfOperations(defaultNetconfOperations, netconfOperationServiceSnapshot);
70
71         DefaultCommit defaultCommit = new DefaultCommit(commitNotifier, capabilityProvider, sessionId);
72         Set<NetconfOperationFilter> defaultFilters = Sets.<NetconfOperationFilter> newHashSet(defaultCommit);
73         allSortedFilters = getAllNetconfFilters(defaultFilters, netconfOperationServiceSnapshot);
74     }
75
76     private static Set<NetconfOperation> getAllNetconfOperations(Set<NetconfOperation> defaultNetconfOperations,
77             NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
78         Set<NetconfOperation> result = new HashSet<>();
79         result.addAll(defaultNetconfOperations);
80
81         for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
82             final Set<NetconfOperation> netOpsFromService = netconfOperationService.getNetconfOperations();
83             for (NetconfOperation netconfOperation : netOpsFromService) {
84                 Preconditions.checkState(result.contains(netconfOperation) == false,
85                         "Netconf operation %s already present", netconfOperation);
86                 result.add(netconfOperation);
87             }
88         }
89         return Collections.unmodifiableSet(result);
90     }
91
92     private static TreeSet<NetconfOperationFilter> getAllNetconfFilters(Set<NetconfOperationFilter> defaultFilters,
93             NetconfOperationServiceSnapshot netconfOperationServiceSnapshot) {
94         TreeSet<NetconfOperationFilter> result = new TreeSet<>(defaultFilters);
95         for (NetconfOperationService netconfOperationService : netconfOperationServiceSnapshot.getServices()) {
96             final Set<NetconfOperationFilter> filtersFromService = netconfOperationService.getFilters();
97             for (NetconfOperationFilter filter : filtersFromService) {
98                 Preconditions.checkState(result.contains(filter) == false,
99                         "Filter %s already present, all filters so far: %s", filter, result);
100                 result.add(filter);
101             }
102         }
103         return result;
104     }
105
106     public CapabilityProvider getCapabilityProvider() {
107         return capabilityProvider;
108     }
109
110     @Override
111     public synchronized Document onNetconfMessage(Document message,
112             NetconfSession session) throws NetconfDocumentedException {
113         NetconfOperationExecution netconfOperationExecution;
114
115         String messageAsString = XmlUtil.toString(message);
116
117         try {
118             netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
119         } catch (IllegalArgumentException | IllegalStateException e) {
120             logger.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
121
122             String errorMessage = String.format("Unable to handle rpc %s on session %s", messageAsString, session);
123             Map<String, String> errorInfo = Maps.newHashMap();
124
125             NetconfDocumentedException.ErrorTag tag = null;
126             if (e instanceof IllegalArgumentException) {
127                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_not_supported.toString(), e.getMessage());
128                 tag = NetconfDocumentedException.ErrorTag.operation_not_supported;
129             } else if (e instanceof IllegalStateException) {
130                 errorInfo.put(NetconfDocumentedException.ErrorTag.operation_failed.toString(), e.getMessage());
131                 tag = NetconfDocumentedException.ErrorTag.operation_failed;
132             }
133
134             throw new NetconfDocumentedException(errorMessage, e, NetconfDocumentedException.ErrorType.application,
135                     tag, NetconfDocumentedException.ErrorSeverity.error, errorInfo);
136         } catch (RuntimeException e) {
137             throw handleUnexpectedEx("Unexpected exception during netconf operation sort", e);
138         }
139
140         try {
141             return executeOperationWithHighestPriority(message, netconfOperationExecution, messageAsString);
142         } catch (RuntimeException e) {
143             throw handleUnexpectedEx("Unexpected exception during netconf operation execution", e);
144         }
145     }
146
147     private NetconfDocumentedException handleUnexpectedEx(String s, Exception e) throws NetconfDocumentedException {
148         logger.error(s, e);
149
150         Map<String, String> info = Maps.newHashMap();
151         info.put(NetconfDocumentedException.ErrorSeverity.error.toString(), e.toString());
152         return new NetconfDocumentedException("Unexpected error",
153                 NetconfDocumentedException.ErrorType.application,
154                 NetconfDocumentedException.ErrorTag.operation_failed,
155                 NetconfDocumentedException.ErrorSeverity.error, info);
156     }
157
158     private Document executeOperationWithHighestPriority(Document message, NetconfOperationExecution netconfOperationExecution, String messageAsString) throws NetconfDocumentedException {
159         logger.debug("Forwarding netconf message {} to {}", messageAsString,
160                 netconfOperationExecution.operationWithHighestPriority);
161
162         final LinkedList<NetconfOperationFilterChain> chain = new LinkedList<>();
163         chain.push(netconfOperationExecution);
164
165         for (Iterator<NetconfOperationFilter> it = allSortedFilters.descendingIterator(); it.hasNext();) {
166             final NetconfOperationFilter filter = it.next();
167             final NetconfOperationFilterChain prevItem = chain.getFirst();
168             NetconfOperationFilterChain currentItem = new NetconfOperationFilterChain() {
169                 @Override
170                 public Document execute(Document message, NetconfOperationRouter operationRouter)
171                         throws NetconfDocumentedException {
172                     logger.trace("Entering {}", filter);
173                     return filter.doFilter(message, operationRouter, prevItem);
174                 }
175             };
176             chain.push(currentItem);
177         }
178         return chain.getFirst().execute(message, this);
179     }
180
181     private NetconfOperationExecution getNetconfOperationWithHighestPriority(
182             Document message, NetconfSession session) {
183
184         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = getSortedNetconfOperationsWithCanHandle(
185                 message, session);
186
187         Preconditions.checkArgument(sortedPriority.isEmpty() == false, "No %s available to handle message %s",
188                 NetconfOperation.class.getName(), XmlUtil.toString(message));
189
190         HandlingPriority highestFoundPriority = sortedPriority.lastKey();
191
192         int netconfOperationsWithHighestPriority = sortedPriority.get(highestFoundPriority).size();
193
194         Preconditions.checkState(netconfOperationsWithHighestPriority == 1,
195                 "Multiple %s available to handle message %s", NetconfOperation.class.getName(), message);
196
197         return new NetconfOperationExecution(sortedPriority, highestFoundPriority);
198     }
199
200     private TreeMap<HandlingPriority, Set<NetconfOperation>> getSortedNetconfOperationsWithCanHandle(
201             Document message, NetconfSession session) {
202         TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority = Maps.newTreeMap();
203
204         for (NetconfOperation netconfOperation : allNetconfOperations) {
205             final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
206             if (netconfOperation instanceof DefaultNetconfOperation) {
207                 ((DefaultNetconfOperation) netconfOperation)
208                         .setNetconfSession(session);
209             }
210             if (handlingPriority.equals(HandlingPriority.CANNOT_HANDLE) == false) {
211                 Set<NetconfOperation> netconfOperations = sortedPriority.get(handlingPriority);
212                 netconfOperations = checkIfNoOperationsOnPriority(sortedPriority, handlingPriority, netconfOperations);
213                 netconfOperations.add(netconfOperation);
214             }
215         }
216         return sortedPriority;
217     }
218
219     private Set<NetconfOperation> checkIfNoOperationsOnPriority(
220             TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority, HandlingPriority handlingPriority,
221             Set<NetconfOperation> netconfOperations) {
222         if (netconfOperations == null) {
223             netconfOperations = Sets.newHashSet();
224             sortedPriority.put(handlingPriority, netconfOperations);
225         }
226         return netconfOperations;
227     }
228
229     @Override
230     public void close() {
231         netconfOperationServiceSnapshot.close();
232     }
233
234     private class NetconfOperationExecution implements NetconfOperationFilterChain {
235         private final NetconfOperation operationWithHighestPriority;
236
237         public NetconfOperationExecution(TreeMap<HandlingPriority, Set<NetconfOperation>> sortedPriority,
238                 HandlingPriority highestFoundPriority) {
239             operationWithHighestPriority = sortedPriority.get(highestFoundPriority).iterator().next();
240             sortedPriority.remove(highestFoundPriority);
241         }
242
243         @Override
244         public Document execute(Document message, NetconfOperationRouter router) throws NetconfDocumentedException {
245             return operationWithHighestPriority.handle(message, router);
246         }
247     }
248
249     @Override
250     public String toString() {
251         return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
252                 + '}';
253     }
254
255
256
257 }