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.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.SessionIdType;
34 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
35 import org.opendaylight.yangtools.yang.common.ErrorTag;
36 import org.opendaylight.yangtools.yang.common.ErrorType;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.w3c.dom.Document;
41 // Non-final for testing
42 public class NetconfOperationRouterImpl implements NetconfOperationRouter, AutoCloseable {
43 private static final Logger LOG = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
45 private final NetconfOperationService netconfOperationServiceSnapshot;
46 private final Collection<NetconfOperation> allNetconfOperations;
48 public NetconfOperationRouterImpl(final NetconfOperationService netconfOperationServiceSnapshot,
49 final NetconfMonitoringService netconfMonitoringService, final SessionIdType sessionId) {
50 this.netconfOperationServiceSnapshot = requireNonNull(netconfOperationServiceSnapshot);
52 final Set<NetconfOperation> ops = new HashSet<>();
53 ops.add(new DefaultCloseSession(sessionId, this));
54 ops.add(new DefaultStartExi(sessionId));
55 ops.add(new DefaultStopExi(sessionId));
57 ops.addAll(netconfOperationServiceSnapshot.getNetconfOperations());
59 allNetconfOperations = ImmutableSet.copyOf(ops);
62 @SuppressWarnings("checkstyle:IllegalCatch")
64 public Document onNetconfMessage(final Document message, final NetconfServerSession session)
65 throws DocumentedException {
66 requireNonNull(allNetconfOperations, "Operation router was not initialized properly");
68 final NetconfOperationExecution netconfOperationExecution;
70 netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
71 } catch (IllegalArgumentException | IllegalStateException e) {
72 final String messageAsString = XmlUtil.toString(message);
73 LOG.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
75 final ErrorTag tag = e instanceof IllegalArgumentException ? ErrorTag.OPERATION_NOT_SUPPORTED
76 : ErrorTag.OPERATION_FAILED;
78 throw new DocumentedException(
79 String.format("Unable to handle rpc %s on session %s", messageAsString, session), e,
80 ErrorType.APPLICATION, tag, ErrorSeverity.ERROR,
81 // FIXME: i.e. in what namespace are we providing these tags? why is this not just:
83 // <java-throwable xmlns="org.opendaylight.something">
84 // <message>e.getMessage()</message>
87 // for each place where we are mapping Exception.getMessage() ? We probably do not want to propagate
88 // stack traces out, but suppressed exceptions and causal list might be interesting:
90 // <java-throwable xmlns="org.opendaylight.something">
91 // <message>reported exception</message>
93 // <java-throwable xmlns="org.opendaylight.something">
94 // <message>cause of reported exception</message>
96 // <java-throwable xmlns="org.opendaylight.something">
97 // <message>cause of cause of reported exception</message>
99 Map.of(tag.elementBody(), e.getMessage()));
100 } catch (final RuntimeException e) {
101 throw handleUnexpectedEx("sort", e);
105 return executeOperationWithHighestPriority(message, netconfOperationExecution);
106 } catch (final RuntimeException e) {
107 throw handleUnexpectedEx("execution", e);
112 public void close() {
113 netconfOperationServiceSnapshot.close();
116 private static DocumentedException handleUnexpectedEx(final String op, final Exception exception) {
117 LOG.error("Unexpected exception during netconf operation {}", op, exception);
118 return new DocumentedException("Unexpected error",
119 ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR,
120 // FIXME: i.e. <error>exception.toString()</error>? That looks wrong on a few levels.
121 Map.of(ErrorSeverity.ERROR.elementBody(), exception.toString()));
124 private static Document executeOperationWithHighestPriority(final Document message,
125 final NetconfOperationExecution netconfOperationExecution) throws DocumentedException {
126 if (LOG.isDebugEnabled()) {
127 LOG.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message), netconfOperationExecution
131 return netconfOperationExecution.execute(message);
134 private NetconfOperationExecution getNetconfOperationWithHighestPriority(
135 final Document message, final NetconfServerSession session) throws DocumentedException {
137 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority =
138 getSortedNetconfOperationsWithCanHandle(
141 if (sortedByPriority.isEmpty()) {
142 throw new IllegalArgumentException(String.format("No %s available to handle message %s",
143 NetconfOperation.class.getName(), XmlUtil.toString(message)));
146 return NetconfOperationExecution.createExecutionChain(sortedByPriority, sortedByPriority.lastKey());
149 private TreeMap<HandlingPriority, NetconfOperation> getSortedNetconfOperationsWithCanHandle(
150 final Document message, final NetconfServerSession session) throws DocumentedException {
151 final TreeMap<HandlingPriority, NetconfOperation> sortedPriority = new TreeMap<>();
153 for (final NetconfOperation netconfOperation : allNetconfOperations) {
154 final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
155 if (netconfOperation instanceof DefaultNetconfOperation defaultOperation) {
156 defaultOperation.setNetconfSession(session);
158 if (netconfOperation instanceof SessionAwareNetconfOperation sessionAwareOperation) {
159 sessionAwareOperation.setSession(session);
161 if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
163 checkState(!sortedPriority.containsKey(handlingPriority),
164 "Multiple %s available to handle message %s with priority %s, %s and %s",
165 NetconfOperation.class.getName(), message, handlingPriority, netconfOperation, sortedPriority
166 .get(handlingPriority));
167 sortedPriority.put(handlingPriority, netconfOperation);
170 return sortedPriority;
173 private static final class NetconfOperationExecution implements NetconfOperationChainedExecution {
174 private final NetconfOperation netconfOperation;
175 private final NetconfOperationChainedExecution subsequentExecution;
177 private NetconfOperationExecution(final NetconfOperation netconfOperation,
178 final NetconfOperationChainedExecution subsequentExecution) {
179 this.netconfOperation = netconfOperation;
180 this.subsequentExecution = subsequentExecution;
184 public boolean isExecutionTermination() {
189 public Document execute(final Document message) throws DocumentedException {
190 return netconfOperation.handle(message, subsequentExecution);
193 public static NetconfOperationExecution createExecutionChain(
194 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority,
195 final HandlingPriority handlingPriority) {
196 final NetconfOperation netconfOperation = sortedByPriority.get(handlingPriority);
197 final HandlingPriority subsequentHandlingPriority = sortedByPriority.lowerKey(handlingPriority);
199 NetconfOperationChainedExecution subsequentExecution = null;
201 if (subsequentHandlingPriority != null) {
202 subsequentExecution = createExecutionChain(sortedByPriority, subsequentHandlingPriority);
204 subsequentExecution = EXECUTION_TERMINATION_POINT;
207 return new NetconfOperationExecution(netconfOperation, subsequentExecution);
212 public String toString() {
213 return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot