81f23c0417230c6bd258a8a3ec77b3e8d30c05d7
[netconf.git] / restconf / restconf-nb-rfc8040 / src / main / java / org / opendaylight / restconf / nb / rfc8040 / rests / 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.nb.rfc8040.rests.services.impl;
9
10 import static com.google.common.base.Preconditions.checkState;
11
12 import java.net.URI;
13 import java.util.Optional;
14 import javax.ws.rs.Path;
15 import javax.ws.rs.core.UriInfo;
16 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
17 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
18 import org.opendaylight.restconf.common.context.InstanceIdentifierContext;
19 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
20 import org.opendaylight.restconf.nb.rfc8040.NotificationQueryParams;
21 import org.opendaylight.restconf.nb.rfc8040.databind.jaxrs.UriInfoSupport;
22 import org.opendaylight.restconf.nb.rfc8040.handlers.SchemaContextHandler;
23 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
24 import org.opendaylight.restconf.nb.rfc8040.rests.services.api.RestconfStreamsSubscriptionService;
25 import org.opendaylight.restconf.nb.rfc8040.rests.utils.RestconfStreamsConstants;
26 import org.opendaylight.restconf.nb.rfc8040.streams.Configuration;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.Module;
33 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
34 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 /**
39  * Implementation of {@link RestconfStreamsSubscriptionService}.
40  */
41 @Path("/")
42 public class RestconfStreamsSubscriptionServiceImpl implements RestconfStreamsSubscriptionService {
43     private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsSubscriptionServiceImpl.class);
44     private static final QName LOCATION_QNAME =
45         QName.create("subscribe:to:notification", "2016-10-28", "location").intern();
46     private static final NodeIdentifier LOCATION_NODEID = NodeIdentifier.create(LOCATION_QNAME);
47     private static final QName NOTIFI_QNAME = QName.create(LOCATION_QNAME, "notifi").intern();
48     private static final YangInstanceIdentifier LOCATION_PATH =
49         YangInstanceIdentifier.create(NodeIdentifier.create(NOTIFI_QNAME), LOCATION_NODEID);
50
51     private final SubscribeToStreamUtil streamUtils;
52     private final HandlersHolder handlersHolder;
53
54     /**
55      * Initialize holder of handlers with holders as parameters.
56      *
57      * @param dataBroker {@link DOMDataBroker}
58      * @param notificationService {@link DOMNotificationService}
59      * @param schemaHandler
60      *             handler of {@link SchemaContext}
61      * @param configuration
62      *             configuration for restconf {@link Configuration}}
63      */
64     public RestconfStreamsSubscriptionServiceImpl(final DOMDataBroker dataBroker,
65             final DOMNotificationService notificationService, final SchemaContextHandler schemaHandler,
66             final Configuration configuration) {
67         handlersHolder = new HandlersHolder(dataBroker, notificationService, schemaHandler);
68         streamUtils = configuration.isUseSSE() ? SubscribeToStreamUtil.serverSentEvents()
69                 : SubscribeToStreamUtil.webSockets();
70     }
71
72     @Override
73     public NormalizedNodePayload subscribeToStream(final String identifier, final UriInfo uriInfo) {
74         final NotificationQueryParams notificationQueryParams = UriInfoSupport.newNotificationQueryParams(uriInfo);
75
76         final URI response;
77         if (identifier.contains(RestconfStreamsConstants.DATA_SUBSCRIPTION)) {
78             response = streamUtils.subscribeToDataStream(identifier, uriInfo, notificationQueryParams, handlersHolder);
79         } else if (identifier.contains(RestconfStreamsConstants.NOTIFICATION_STREAM)) {
80             response = streamUtils.subscribeToYangStream(identifier, uriInfo, notificationQueryParams, handlersHolder);
81         } else {
82             final String msg = "Bad type of notification of sal-remote";
83             LOG.warn(msg);
84             throw new RestconfDocumentedException(msg);
85         }
86
87         // prepare node with value of location
88         return NormalizedNodePayload.ofLocation(prepareIIDSubsStreamOutput(handlersHolder.getSchemaHandler()),
89             LOCATION_NODEID, response);
90     }
91
92     /**
93      * Prepare InstanceIdentifierContext for Location leaf.
94      *
95      * @param schemaHandler Schema context handler.
96      * @return InstanceIdentifier of Location leaf.
97      */
98     private static InstanceIdentifierContext<?> prepareIIDSubsStreamOutput(final SchemaContextHandler schemaHandler) {
99         final Optional<Module> module = schemaHandler.get().findModule(NOTIFI_QNAME.getModule());
100         checkState(module.isPresent());
101         final DataSchemaNode notify = module.get().dataChildByName(NOTIFI_QNAME);
102         checkState(notify instanceof ContainerSchemaNode, "Unexpected non-container %s", notify);
103         final DataSchemaNode location = ((ContainerSchemaNode) notify).dataChildByName(LOCATION_QNAME);
104         checkState(location != null, "Missing location");
105
106         return new InstanceIdentifierContext<SchemaNode>(LOCATION_PATH, location, null, schemaHandler.get());
107     }
108
109     /**
110      * Holder of all handlers for notifications.
111      */
112     // FIXME: why do we even need this class?!
113     public static final class HandlersHolder {
114         private final DOMDataBroker dataBroker;
115         private final DOMNotificationService notificationService;
116         private final SchemaContextHandler schemaHandler;
117
118         private HandlersHolder(final DOMDataBroker dataBroker, final DOMNotificationService notificationService,
119                 final SchemaContextHandler schemaHandler) {
120             this.dataBroker = dataBroker;
121             this.notificationService = notificationService;
122             this.schemaHandler = schemaHandler;
123         }
124
125         /**
126          * Get {@link DOMDataBroker}.
127          *
128          * @return the dataBroker
129          */
130         public DOMDataBroker getDataBroker() {
131             return dataBroker;
132         }
133
134         /**
135          * Get {@link DOMNotificationService}.
136          *
137          * @return the notificationService
138          */
139         public DOMNotificationService getNotificationServiceHandler() {
140             return notificationService;
141         }
142
143         /**
144          * Get {@link SchemaContextHandler}.
145          *
146          * @return the schemaHandler
147          */
148         public SchemaContextHandler getSchemaHandler() {
149             return schemaHandler;
150         }
151     }
152 }