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
8 package org.opendaylight.netconf.server.osgi;
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collection;
15 import java.util.HashSet;
17 import java.util.NavigableMap;
19 import java.util.TreeMap;
20 import org.opendaylight.netconf.api.DocumentedException;
21 import org.opendaylight.netconf.api.xml.XmlUtil;
22 import org.opendaylight.netconf.server.NetconfServerSession;
23 import org.opendaylight.netconf.server.api.monitoring.NetconfMonitoringService;
24 import org.opendaylight.netconf.server.api.operations.HandlingPriority;
25 import org.opendaylight.netconf.server.api.operations.NetconfOperation;
26 import org.opendaylight.netconf.server.api.operations.NetconfOperationChainedExecution;
27 import org.opendaylight.netconf.server.api.operations.NetconfOperationService;
28 import org.opendaylight.netconf.server.api.operations.SessionAwareNetconfOperation;
29 import org.opendaylight.netconf.server.mapping.operations.DefaultCloseSession;
30 import org.opendaylight.netconf.server.mapping.operations.DefaultNetconfOperation;
31 import org.opendaylight.netconf.server.mapping.operations.DefaultStartExi;
32 import org.opendaylight.netconf.server.mapping.operations.DefaultStopExi;
33 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
34 import org.opendaylight.yangtools.yang.common.ErrorTag;
35 import org.opendaylight.yangtools.yang.common.ErrorType;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.w3c.dom.Document;
40 // Non-final for testing
41 public class NetconfOperationRouterImpl implements NetconfOperationRouter, AutoCloseable {
42 private static final Logger LOG = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
44 private final NetconfOperationService netconfOperationServiceSnapshot;
45 private final Collection<NetconfOperation> allNetconfOperations;
47 public NetconfOperationRouterImpl(final NetconfOperationService netconfOperationServiceSnapshot,
48 final NetconfMonitoringService netconfMonitoringService, final String sessionId) {
49 this.netconfOperationServiceSnapshot = requireNonNull(netconfOperationServiceSnapshot);
51 final Set<NetconfOperation> ops = new HashSet<>();
52 ops.add(new DefaultCloseSession(sessionId, this));
53 ops.add(new DefaultStartExi(sessionId));
54 ops.add(new DefaultStopExi(sessionId));
56 ops.addAll(netconfOperationServiceSnapshot.getNetconfOperations());
58 allNetconfOperations = ImmutableSet.copyOf(ops);
61 @SuppressWarnings("checkstyle:IllegalCatch")
63 public Document onNetconfMessage(final Document message, final NetconfServerSession session)
64 throws DocumentedException {
65 requireNonNull(allNetconfOperations, "Operation router was not initialized properly");
67 final NetconfOperationExecution netconfOperationExecution;
69 netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
70 } catch (IllegalArgumentException | IllegalStateException e) {
71 final String messageAsString = XmlUtil.toString(message);
72 LOG.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
74 final ErrorTag tag = e instanceof IllegalArgumentException ? ErrorTag.OPERATION_NOT_SUPPORTED
75 : ErrorTag.OPERATION_FAILED;
77 throw new DocumentedException(
78 String.format("Unable to handle rpc %s on session %s", messageAsString, session), e,
79 ErrorType.APPLICATION, tag, ErrorSeverity.ERROR,
80 // FIXME: i.e. in what namespace are we providing these tags? why is this not just:
82 // <java-throwable xmlns="org.opendaylight.something">
83 // <message>e.getMessage()</message>
86 // for each place where we are mapping Exception.getMessage() ? We probably do not want to propagate
87 // stack traces out, but suppressed exceptions and causal list might be interesting:
89 // <java-throwable xmlns="org.opendaylight.something">
90 // <message>reported exception</message>
92 // <java-throwable xmlns="org.opendaylight.something">
93 // <message>cause of reported exception</message>
95 // <java-throwable xmlns="org.opendaylight.something">
96 // <message>cause of cause of reported exception</message>
98 Map.of(tag.elementBody(), e.getMessage()));
99 } catch (final RuntimeException e) {
100 throw handleUnexpectedEx("sort", e);
104 return executeOperationWithHighestPriority(message, netconfOperationExecution);
105 } catch (final RuntimeException e) {
106 throw handleUnexpectedEx("execution", e);
111 public void close() {
112 netconfOperationServiceSnapshot.close();
115 private static DocumentedException handleUnexpectedEx(final String op, final Exception exception) {
116 LOG.error("Unexpected exception during netconf operation {}", op, exception);
117 return new DocumentedException("Unexpected error",
118 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR,
119 // FIXME: i.e. <error>exception.toString()</error>? That looks wrong on a few levels.
120 Map.of(ErrorSeverity.ERROR.elementBody(), exception.toString()));
123 private static Document executeOperationWithHighestPriority(final Document message,
124 final NetconfOperationExecution netconfOperationExecution) throws DocumentedException {
125 if (LOG.isDebugEnabled()) {
126 LOG.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message), netconfOperationExecution
130 return netconfOperationExecution.execute(message);
133 private NetconfOperationExecution getNetconfOperationWithHighestPriority(
134 final Document message, final NetconfServerSession session) throws DocumentedException {
136 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority =
137 getSortedNetconfOperationsWithCanHandle(
140 if (sortedByPriority.isEmpty()) {
141 throw new IllegalArgumentException(String.format("No %s available to handle message %s",
142 NetconfOperation.class.getName(), XmlUtil.toString(message)));
145 return NetconfOperationExecution.createExecutionChain(sortedByPriority, sortedByPriority.lastKey());
148 private TreeMap<HandlingPriority, NetconfOperation> getSortedNetconfOperationsWithCanHandle(
149 final Document message, final NetconfServerSession session) throws DocumentedException {
150 final TreeMap<HandlingPriority, NetconfOperation> sortedPriority = new TreeMap<>();
152 for (final NetconfOperation netconfOperation : allNetconfOperations) {
153 final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
154 if (netconfOperation instanceof DefaultNetconfOperation defaultOperation) {
155 defaultOperation.setNetconfSession(session);
157 if (netconfOperation instanceof SessionAwareNetconfOperation sessionAwareOperation) {
158 sessionAwareOperation.setSession(session);
160 if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
162 checkState(!sortedPriority.containsKey(handlingPriority),
163 "Multiple %s available to handle message %s with priority %s, %s and %s",
164 NetconfOperation.class.getName(), message, handlingPriority, netconfOperation, sortedPriority
165 .get(handlingPriority));
166 sortedPriority.put(handlingPriority, netconfOperation);
169 return sortedPriority;
172 private static final class NetconfOperationExecution implements NetconfOperationChainedExecution {
173 private final NetconfOperation netconfOperation;
174 private final NetconfOperationChainedExecution subsequentExecution;
176 private NetconfOperationExecution(final NetconfOperation netconfOperation,
177 final NetconfOperationChainedExecution subsequentExecution) {
178 this.netconfOperation = netconfOperation;
179 this.subsequentExecution = subsequentExecution;
183 public boolean isExecutionTermination() {
188 public Document execute(final Document message) throws DocumentedException {
189 return netconfOperation.handle(message, subsequentExecution);
192 public static NetconfOperationExecution createExecutionChain(
193 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority,
194 final HandlingPriority handlingPriority) {
195 final NetconfOperation netconfOperation = sortedByPriority.get(handlingPriority);
196 final HandlingPriority subsequentHandlingPriority = sortedByPriority.lowerKey(handlingPriority);
198 NetconfOperationChainedExecution subsequentExecution = null;
200 if (subsequentHandlingPriority != null) {
201 subsequentExecution = createExecutionChain(sortedByPriority, subsequentHandlingPriority);
203 subsequentExecution = EXECUTION_TERMINATION_POINT;
206 return new NetconfOperationExecution(netconfOperation, subsequentExecution);
211 public String toString() {
212 return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot