7dd0c2ad6b74d336732d7fb964b8edd4ae324f9a
[netconf.git] / netconf / netconf-impl / src / main / java / org / opendaylight / 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.netconf.impl.osgi;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.collect.ImmutableSet;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.Map;
17 import java.util.NavigableMap;
18 import java.util.Set;
19 import java.util.TreeMap;
20 import org.opendaylight.netconf.api.DocumentedException;
21 import org.opendaylight.netconf.api.monitoring.NetconfMonitoringService;
22 import org.opendaylight.netconf.api.xml.XmlUtil;
23 import org.opendaylight.netconf.impl.NetconfServerSession;
24 import org.opendaylight.netconf.impl.mapping.operations.DefaultCloseSession;
25 import org.opendaylight.netconf.impl.mapping.operations.DefaultNetconfOperation;
26 import org.opendaylight.netconf.impl.mapping.operations.DefaultStartExi;
27 import org.opendaylight.netconf.impl.mapping.operations.DefaultStopExi;
28 import org.opendaylight.netconf.mapping.api.HandlingPriority;
29 import org.opendaylight.netconf.mapping.api.NetconfOperation;
30 import org.opendaylight.netconf.mapping.api.NetconfOperationChainedExecution;
31 import org.opendaylight.netconf.mapping.api.NetconfOperationService;
32 import org.opendaylight.netconf.mapping.api.SessionAwareNetconfOperation;
33 import org.opendaylight.yangtools.yang.common.ErrorSeverity;
34 import org.opendaylight.yangtools.yang.common.ErrorType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37 import org.w3c.dom.Document;
38
39 public class NetconfOperationRouterImpl implements NetconfOperationRouter {
40
41     private static final Logger LOG = LoggerFactory.getLogger(NetconfOperationRouterImpl.class);
42     private final NetconfOperationService netconfOperationServiceSnapshot;
43     private final Collection<NetconfOperation> allNetconfOperations;
44
45     public NetconfOperationRouterImpl(final NetconfOperationService netconfOperationServiceSnapshot,
46                                       final NetconfMonitoringService netconfMonitoringService, final String sessionId) {
47         this.netconfOperationServiceSnapshot = requireNonNull(netconfOperationServiceSnapshot);
48
49         final Set<NetconfOperation> ops = new HashSet<>();
50         ops.add(new DefaultCloseSession(sessionId, this));
51         ops.add(new DefaultStartExi(sessionId));
52         ops.add(new DefaultStopExi(sessionId));
53
54         ops.addAll(netconfOperationServiceSnapshot.getNetconfOperations());
55
56         allNetconfOperations = ImmutableSet.copyOf(ops);
57     }
58
59     @SuppressWarnings("checkstyle:IllegalCatch")
60     @Override
61     public Document onNetconfMessage(final Document message, final NetconfServerSession session) throws
62             DocumentedException {
63         requireNonNull(allNetconfOperations, "Operation router was not initialized properly");
64
65         final NetconfOperationExecution netconfOperationExecution;
66         try {
67             netconfOperationExecution = getNetconfOperationWithHighestPriority(message, session);
68         } catch (IllegalArgumentException | IllegalStateException e) {
69             final String messageAsString = XmlUtil.toString(message);
70             LOG.warn("Unable to handle rpc {} on session {}", messageAsString, session, e);
71
72             final DocumentedException.ErrorTag tag;
73             if (e instanceof IllegalArgumentException) {
74                 tag = DocumentedException.ErrorTag.OPERATION_NOT_SUPPORTED;
75             } else {
76                 tag = DocumentedException.ErrorTag.OPERATION_FAILED;
77             }
78
79             throw new DocumentedException(
80                     String.format("Unable to handle rpc %s on session %s", messageAsString, session),
81                     e, ErrorType.APPLICATION, tag, ErrorSeverity.ERROR, Map.of(tag.toString(), e.getMessage()));
82         } catch (final RuntimeException e) {
83             throw handleUnexpectedEx("sort", e);
84         }
85
86         try {
87             return executeOperationWithHighestPriority(message, netconfOperationExecution);
88         } catch (final RuntimeException e) {
89             throw handleUnexpectedEx("execution", e);
90         }
91     }
92
93     @Override
94     public void close() {
95         netconfOperationServiceSnapshot.close();
96     }
97
98     private static DocumentedException handleUnexpectedEx(final String op, final Exception exception) {
99         LOG.error("Unexpected exception during netconf operation {}", op, exception);
100         return new DocumentedException("Unexpected error",
101                 ErrorType.APPLICATION, DocumentedException.ErrorTag.OPERATION_FAILED, ErrorSeverity.ERROR,
102                 Map.of(ErrorSeverity.ERROR.toString(), exception.toString()));
103     }
104
105     private static Document executeOperationWithHighestPriority(final Document message,
106             final NetconfOperationExecution netconfOperationExecution) throws DocumentedException {
107         if (LOG.isDebugEnabled()) {
108             LOG.debug("Forwarding netconf message {} to {}", XmlUtil.toString(message), netconfOperationExecution
109                     .netconfOperation);
110         }
111
112         return netconfOperationExecution.execute(message);
113     }
114
115     private NetconfOperationExecution getNetconfOperationWithHighestPriority(
116             final Document message, final NetconfServerSession session) throws DocumentedException {
117
118         final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority =
119                 getSortedNetconfOperationsWithCanHandle(
120                 message, session);
121
122         if (sortedByPriority.isEmpty()) {
123             throw new IllegalArgumentException(String.format("No %s available to handle message %s",
124                     NetconfOperation.class.getName(), XmlUtil.toString(message)));
125         }
126
127         return NetconfOperationExecution.createExecutionChain(sortedByPriority, sortedByPriority.lastKey());
128     }
129
130     private TreeMap<HandlingPriority, NetconfOperation> getSortedNetconfOperationsWithCanHandle(
131             final Document message, final NetconfServerSession session) throws DocumentedException {
132         final TreeMap<HandlingPriority, NetconfOperation> sortedPriority = new TreeMap<>();
133
134         for (final NetconfOperation netconfOperation : allNetconfOperations) {
135             final HandlingPriority handlingPriority = netconfOperation.canHandle(message);
136             if (netconfOperation instanceof DefaultNetconfOperation) {
137                 ((DefaultNetconfOperation) netconfOperation).setNetconfSession(session);
138             }
139             if (netconfOperation instanceof SessionAwareNetconfOperation) {
140                 ((SessionAwareNetconfOperation) netconfOperation).setSession(session);
141             }
142             if (!handlingPriority.equals(HandlingPriority.CANNOT_HANDLE)) {
143
144                 checkState(!sortedPriority.containsKey(handlingPriority),
145                         "Multiple %s available to handle message %s with priority %s, %s and %s",
146                         NetconfOperation.class.getName(), message, handlingPriority, netconfOperation, sortedPriority
147                                 .get(handlingPriority));
148                 sortedPriority.put(handlingPriority, netconfOperation);
149             }
150         }
151         return sortedPriority;
152     }
153
154     private static final class NetconfOperationExecution implements NetconfOperationChainedExecution {
155         private final NetconfOperation netconfOperation;
156         private final NetconfOperationChainedExecution subsequentExecution;
157
158         private NetconfOperationExecution(final NetconfOperation netconfOperation,
159                                           final NetconfOperationChainedExecution subsequentExecution) {
160             this.netconfOperation = netconfOperation;
161             this.subsequentExecution = subsequentExecution;
162         }
163
164         @Override
165         public boolean isExecutionTermination() {
166             return false;
167         }
168
169         @Override
170         public Document execute(final Document message) throws DocumentedException {
171             return netconfOperation.handle(message, subsequentExecution);
172         }
173
174         public static NetconfOperationExecution createExecutionChain(
175                 final NavigableMap<HandlingPriority, NetconfOperation> sortedByPriority,
176                 final HandlingPriority handlingPriority) {
177             final NetconfOperation netconfOperation = sortedByPriority.get(handlingPriority);
178             final HandlingPriority subsequentHandlingPriority = sortedByPriority.lowerKey(handlingPriority);
179
180             NetconfOperationChainedExecution subsequentExecution = null;
181
182             if (subsequentHandlingPriority != null) {
183                 subsequentExecution = createExecutionChain(sortedByPriority, subsequentHandlingPriority);
184             } else {
185                 subsequentExecution = EXECUTION_TERMINATION_POINT;
186             }
187
188             return new NetconfOperationExecution(netconfOperation, subsequentExecution);
189         }
190     }
191
192     @Override
193     public String toString() {
194         return "NetconfOperationRouterImpl{" + "netconfOperationServiceSnapshot=" + netconfOperationServiceSnapshot
195                 + '}';
196     }
197 }