blob: aaa106e5f10620d22082c63b2d63dc3b1c7655e3 [file] [log] [blame]
Hesam Rahimi4a409b42016-08-12 18:37:33 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Hesam Rahimi4a409b42016-08-12 18:37:33 -04003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.protocol.restconf.ctl;
17
Hesam Rahimi4a409b42016-08-12 18:37:33 -040018import org.glassfish.jersey.client.ChunkedInput;
19import org.onlab.packet.IpAddress;
20import org.onosproject.net.DeviceId;
21import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
22import org.onosproject.protocol.rest.RestSBDevice;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040023import org.onosproject.protocol.restconf.RestConfSBController;
Henry Yu05dcc212017-01-05 16:05:26 -050024import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070025import org.osgi.service.component.annotations.Activate;
26import org.osgi.service.component.annotations.Component;
27import org.osgi.service.component.annotations.Deactivate;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040028import org.slf4j.Logger;
29import org.slf4j.LoggerFactory;
30
Henry Yu05dcc212017-01-05 16:05:26 -050031import javax.ws.rs.client.WebTarget;
32import javax.ws.rs.core.GenericType;
33import javax.ws.rs.core.Response;
Henry Yu05dcc212017-01-05 16:05:26 -050034import java.util.HashSet;
35import java.util.Map;
36import java.util.Set;
37import java.util.concurrent.ConcurrentHashMap;
38import java.util.concurrent.ExecutorService;
39import java.util.concurrent.Executors;
40
Hesam Rahimi4a409b42016-08-12 18:37:33 -040041/**
42 * The implementation of RestConfSBController.
43 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070044@Component(immediate = true, service = RestConfSBController.class)
Hesam Rahimi4a409b42016-08-12 18:37:33 -040045public class RestConfSBControllerImpl extends HttpSBControllerImpl
46 implements RestConfSBController {
47
48 private static final Logger log = LoggerFactory
49 .getLogger(RestConfSBControllerImpl.class);
50
51 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
52 // fully support root resource discovery, ROOT_RESOURCE constant will be
53 // removed and rather the value would get discovered dynamically.
54 private static final String ROOT_RESOURCE = "/onos/restconf";
55
56 private static final String RESOURCE_PATH_PREFIX = "/data/";
Henry Yudc747af2016-11-16 13:29:54 -050057 private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
Hesam Rahimi4a409b42016-08-12 18:37:33 -040058
Henry Yu05dcc212017-01-05 16:05:26 -050059 private Map<DeviceId, Set<RestconfNotificationEventListener>>
60 restconfNotificationListenerMap = new ConcurrentHashMap<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -040061 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
62
63 ExecutorService executor = Executors.newCachedThreadPool();
64
65 @Activate
66 public void activate() {
67 log.info("RESTCONF SBI Started");
68 }
69
70 @Deactivate
71 public void deactivate() {
72 log.info("RESTCONF SBI Stopped");
73 executor.shutdown();
74 this.getClientMap().clear();
75 this.getDeviceMap().clear();
76 }
77
78 @Override
79 public Map<DeviceId, RestSBDevice> getDevices() {
80 log.trace("RESTCONF SBI::getDevices");
81 return super.getDevices();
82 }
83
84 @Override
85 public RestSBDevice getDevice(DeviceId deviceInfo) {
86 log.trace("RESTCONF SBI::getDevice with deviceId");
87 return super.getDevice(deviceInfo);
88 }
89
90 @Override
91 public RestSBDevice getDevice(IpAddress ip, int port) {
92 log.trace("RESTCONF SBI::getDevice with ip and port");
93 return super.getDevice(ip, port);
94 }
95
96 @Override
97 public void addDevice(RestSBDevice device) {
98 log.trace("RESTCONF SBI::addDevice");
99 super.addDevice(device);
100 }
101
102 @Override
103 public void removeDevice(DeviceId deviceId) {
104 log.trace("RESTCONF SBI::removeDevice");
105 super.removeDevice(deviceId);
106 }
107
108 @Override
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400109 public void enableNotifications(DeviceId device, String request,
Henry Yu05dcc212017-01-05 16:05:26 -0500110 String mediaType,
111 RestconfNotificationEventListener listener) {
112
113 if (isNotificationEnabled(device)) {
114 log.warn("enableNotifications: already enabled on device: {}", device);
115 return;
116 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400117
118 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
119 + request;
120
121 addNotificationListener(device, listener);
122
123 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
124 device);
125 runnableTable.put(device, runnable);
126 executor.execute(runnable);
127 }
128
Henry Yu05c2c762017-01-30 12:11:08 -0500129 public void stopNotifications(DeviceId device) {
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400130 runnableTable.get(device).terminate();
131 runnableTable.remove(device);
Henry Yu05dcc212017-01-05 16:05:26 -0500132 restconfNotificationListenerMap.remove(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400133 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
134
135 }
136
137 public class GetChunksRunnable implements Runnable {
138 private String request;
139 private String mediaType;
140 private DeviceId device;
141
142 private volatile boolean running = true;
143
144 public void terminate() {
145 running = false;
146 }
147
148 /**
Henry Yu05dcc212017-01-05 16:05:26 -0500149 * @param request request
Ray Milkey0bb1e102016-11-10 14:51:27 -0800150 * @param mediaType media type
Henry Yu05dcc212017-01-05 16:05:26 -0500151 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400152 */
153 public GetChunksRunnable(String request, String mediaType,
154 DeviceId device) {
155 this.request = request;
156 this.mediaType = mediaType;
157 this.device = device;
158 }
159
160 @Override
161 public void run() {
162 WebTarget wt = getWebTarget(device, request);
163 Response clientResp = wt.request(mediaType).get();
Henry Yu05dcc212017-01-05 16:05:26 -0500164 Set<RestconfNotificationEventListener> listeners =
165 restconfNotificationListenerMap.get(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400166 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
167 .readEntity(new GenericType<ChunkedInput<String>>() {
168 });
169
170 String chunk;
171 // Note that the read() is a blocking operation and the invoking
172 // thread is blocked until a new chunk comes. Jersey implementation
173 // of this IO operation is in a way that it does not respond to
174 // interrupts.
175 while (running) {
176 chunk = chunkedInput.read();
177 if (chunk != null) {
178 if (running) {
Henry Yu05dcc212017-01-05 16:05:26 -0500179 for (RestconfNotificationEventListener listener : listeners) {
180 listener.handleNotificationEvent(device, chunk);
181 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400182 } else {
183 log.trace("the requesting client is no more interested "
Henry Yu05dcc212017-01-05 16:05:26 -0500184 + "to receive such notifications.");
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400185 }
186 } else {
187 log.trace("The received notification chunk is null. do not continue any more.");
188 break;
189 }
190 }
191 log.trace("out of while loop -- end of run");
192 }
193 }
194
195 public String discoverRootResource(DeviceId device) {
196 // FIXME: send a GET command to the device to discover the root resource.
197 // The plan to fix this is for the Ibis release when the RESTCONF server and
198 // the RESTCONF client both support root resource discovery.
199 return ROOT_RESOURCE;
200 }
201
202 @Override
203 public void addNotificationListener(DeviceId deviceId,
Henry Yu05dcc212017-01-05 16:05:26 -0500204 RestconfNotificationEventListener listener) {
205 Set<RestconfNotificationEventListener> listeners =
206 restconfNotificationListenerMap.get(deviceId);
207 if (listeners == null) {
208 listeners = new HashSet<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400209 }
Henry Yu05dcc212017-01-05 16:05:26 -0500210
211 listeners.add(listener);
212
213 this.restconfNotificationListenerMap.put(deviceId, listeners);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400214 }
215
216 @Override
Henry Yu05dcc212017-01-05 16:05:26 -0500217 public void removeNotificationListener(DeviceId deviceId,
218 RestconfNotificationEventListener listener) {
219 Set<RestconfNotificationEventListener> listeners =
220 restconfNotificationListenerMap.get(deviceId);
221 if (listeners != null) {
222 listeners.remove(listener);
223 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400224 }
225
Henry Yu05dcc212017-01-05 16:05:26 -0500226 public boolean isNotificationEnabled(DeviceId deviceId) {
227 return runnableTable.containsKey(deviceId);
228 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400229}