36873a18932fed25518f78b4ed29fb3b15569cde
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / restconf / restful / services / impl / RestconfStreamsSubscriptionServiceImpl.java
1 /*
2  * Copyright (c) 2016 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.restconf.restful.services.impl;
9
10 import java.net.URI;
11 import java.time.Instant;
12 import java.util.HashMap;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Map.Entry;
16 import java.util.Optional;
17 import javax.annotation.Nonnull;
18 import javax.ws.rs.core.UriInfo;
19 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
20 import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
21 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
22 import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
23 import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
24 import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
25 import org.opendaylight.restconf.handlers.DOMDataBrokerHandler;
26 import org.opendaylight.restconf.handlers.NotificationServiceHandler;
27 import org.opendaylight.restconf.handlers.SchemaContextHandler;
28 import org.opendaylight.restconf.handlers.TransactionChainHandler;
29 import org.opendaylight.restconf.restful.services.api.RestconfStreamsSubscriptionService;
30 import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants;
31 import org.opendaylight.restconf.restful.utils.SubscribeToStreamUtil;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
34 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
35 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
36 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Implementation of {@link RestconfStreamsSubscriptionService}.
43  *
44  */
45 public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSubscriptionService {
46
47     private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsSubscriptionServiceImpl.class);
48
49     private final HandlersHolder handlersHolder;
50
51     /**
52      * Initialize holder of handlers with holders as parameters.
53      *
54      * @param domDataBrokerHandler
55      *             handler of {@link DOMDataBroker}
56      * @param notificationServiceHandler
57      *             handler of {@link DOMNotificationService}
58      * @param schemaHandler
59      *             handler of {@link SchemaContext}
60      * @param transactionChainHandler
61      *             handler of {@link DOMTransactionChain}
62      */
63     public RestconfStreamsSubscriptionServiceImpl(final DOMDataBrokerHandler domDataBrokerHandler,
64             final NotificationServiceHandler notificationServiceHandler, final SchemaContextHandler schemaHandler,
65             final TransactionChainHandler transactionChainHandler) {
66         this.handlersHolder = new HandlersHolder(domDataBrokerHandler, notificationServiceHandler,
67                 transactionChainHandler, schemaHandler);
68     }
69
70     @Override
71     public NormalizedNodeContext subscribeToStream(final String identifier, final UriInfo uriInfo) {
72         final NotificationQueryParams notificationQueryParams = NotificationQueryParams.fromUriInfo(uriInfo);
73
74         URI response = null;
75         if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCR)) {
76             response = SubscribeToStreamUtil.notifiDataStream(identifier, uriInfo, notificationQueryParams,
77                     this.handlersHolder);
78         } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
79             response = SubscribeToStreamUtil.notifYangStream(identifier, uriInfo, notificationQueryParams,
80                     this.handlersHolder);
81         }
82
83         if (response != null) {
84             // prepare node with value of location
85             final InstanceIdentifierContext<?> iid =
86                     SubscribeToStreamUtil.prepareIIDSubsStreamOutput(this.handlersHolder.getSchemaHandler());
87             final NormalizedNodeAttrBuilder<NodeIdentifier, Object, LeafNode<Object>> builder =
88                     ImmutableLeafNodeBuilder.create().withValue(response.toString());
89             builder.withNodeIdentifier(
90                     NodeIdentifier.create(QName.create("subscribe:to:notification", "2016-10-28", "location")));
91
92             // prepare new header with location
93             final Map<String, Object> headers = new HashMap<>();
94             headers.put("Location", response);
95
96             return new NormalizedNodeContext(iid, builder.build(), headers);
97         }
98
99         final String msg = "Bad type of notification of sal-remote";
100         LOG.warn(msg);
101         throw new RestconfDocumentedException(msg);
102     }
103
104     /**
105      * Holder of all handlers for notifications.
106      */
107     public final class HandlersHolder {
108
109         private final DOMDataBrokerHandler domDataBrokerHandler;
110         private final NotificationServiceHandler notificationServiceHandler;
111         private final TransactionChainHandler transactionChainHandler;
112         private final SchemaContextHandler schemaHandler;
113
114         private HandlersHolder(final DOMDataBrokerHandler domDataBrokerHandler,
115                 final NotificationServiceHandler notificationServiceHandler,
116                 final TransactionChainHandler transactionChainHandler, final SchemaContextHandler schemaHandler) {
117             this.domDataBrokerHandler = domDataBrokerHandler;
118             this.notificationServiceHandler = notificationServiceHandler;
119             this.transactionChainHandler = transactionChainHandler;
120             this.schemaHandler = schemaHandler;
121         }
122
123         /**
124          * Get {@link DOMDataBrokerHandler}.
125          *
126          * @return the domDataBrokerHandler
127          */
128         public DOMDataBrokerHandler getDomDataBrokerHandler() {
129             return this.domDataBrokerHandler;
130         }
131
132         /**
133          * Get {@link NotificationServiceHandler}.
134          *
135          * @return the notificationServiceHandler
136          */
137         public NotificationServiceHandler getNotificationServiceHandler() {
138             return this.notificationServiceHandler;
139         }
140
141         /**
142          * Get {@link TransactionChainHandler}.
143          *
144          * @return the transactionChainHandler
145          */
146         public TransactionChainHandler getTransactionChainHandler() {
147             return this.transactionChainHandler;
148         }
149
150         /**
151          * Get {@link SchemaContextHandler}.
152          *
153          * @return the schemaHandler
154          */
155         public SchemaContextHandler getSchemaHandler() {
156             return this.schemaHandler;
157         }
158     }
159
160     /**
161      * Parser and holder of query paramteres from uriInfo for notifications.
162      *
163      */
164     public static final class NotificationQueryParams {
165
166         private final Instant start;
167         private final Instant stop;
168         private final String filter;
169
170         private NotificationQueryParams(final Instant start, final Instant stop, final String filter) {
171             this.start = start == null ? Instant.now() : start;
172             this.stop = stop;
173             this.filter = filter;
174         }
175
176         static NotificationQueryParams fromUriInfo(final UriInfo uriInfo) {
177             Instant start = null;
178             boolean startTimeUsed = false;
179             Instant stop = null;
180             boolean stopTimeUsed = false;
181             String filter = null;
182             boolean filterUsed = false;
183
184             for (final Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) {
185                 switch (entry.getKey()) {
186                     case "start-time":
187                         if (!startTimeUsed) {
188                             startTimeUsed = true;
189                             start = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
190                         } else {
191                             throw new RestconfDocumentedException("Start-time parameter can be used only once.");
192                         }
193                         break;
194                     case "stop-time":
195                         if (!stopTimeUsed) {
196                             stopTimeUsed = true;
197                             stop = SubscribeToStreamUtil.parseDateFromQueryParam(entry);
198                         } else {
199                             throw new RestconfDocumentedException("Stop-time parameter can be used only once.");
200                         }
201                         break;
202                     case "filter":
203                         if (!filterUsed) {
204                             filterUsed = true;
205                             filter = entry.getValue().iterator().next();
206                         }
207                         break;
208                     default:
209                         throw new RestconfDocumentedException(
210                                 "Bad parameter used with notifications: " + entry.getKey());
211                 }
212             }
213             if (!startTimeUsed && stopTimeUsed) {
214                 throw new RestconfDocumentedException("Stop-time parameter has to be used with start-time parameter.");
215             }
216
217             return new NotificationQueryParams(start, stop, filter);
218         }
219
220         /**
221          * Get start-time query parameter.
222          *
223          * @return start-time
224          */
225         @Nonnull
226         public Instant getStart() {
227             return start;
228         }
229
230         /**
231          * Get stop-time query parameter.
232          *
233          * @return stop-time
234          */
235         public Optional<Instant> getStop() {
236             return Optional.ofNullable(stop);
237         }
238
239         /**
240          * Get filter query parameter.
241          *
242          * @return filter
243          */
244         public Optional<String> getFilter() {
245             return Optional.ofNullable(filter);
246         }
247     }
248
249 }