blob: 77e87ef36b3802ee5b88a91fdec03b978a313dc9 [file] [log] [blame]
Hesam Rahimi4a409b42016-08-12 18:37:33 -04001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
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.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
Hesam Rahimi1856ed92016-10-27 18:46:42 -040021import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040023import org.apache.felix.scr.annotations.Service;
24import org.glassfish.jersey.client.ChunkedInput;
25import org.onlab.packet.IpAddress;
26import org.onosproject.net.DeviceId;
27import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
28import org.onosproject.protocol.rest.RestSBDevice;
29import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
30import org.onosproject.protocol.restconf.RestConfSBController;
Henry Yu05c2c762017-01-30 12:11:08 -050031import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
Hesam Rahimi1856ed92016-10-27 18:46:42 -040032import org.onosproject.yms.ych.YangProtocolEncodingFormat;
33import org.onosproject.yms.ymsm.YmsService;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040034import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36
Henry Yu05c2c762017-01-30 12:11:08 -050037import javax.ws.rs.client.WebTarget;
38import javax.ws.rs.core.GenericType;
39import javax.ws.rs.core.Response;
40import java.io.InputStream;
41import java.util.HashSet;
42import java.util.Map;
43import java.util.Set;
44import java.util.concurrent.ConcurrentHashMap;
45import java.util.concurrent.ExecutorService;
46import java.util.concurrent.Executors;
47
Hesam Rahimi4a409b42016-08-12 18:37:33 -040048/**
49 * The implementation of RestConfSBController.
50 */
51@Component(immediate = true)
52@Service
53public class RestConfSBControllerImpl extends HttpSBControllerImpl
54 implements RestConfSBController {
55
56 private static final Logger log = LoggerFactory
57 .getLogger(RestConfSBControllerImpl.class);
58
59 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
60 // fully support root resource discovery, ROOT_RESOURCE constant will be
61 // removed and rather the value would get discovered dynamically.
62 private static final String ROOT_RESOURCE = "/onos/restconf";
63
64 private static final String RESOURCE_PATH_PREFIX = "/data/";
Henry Yudc747af2016-11-16 13:29:54 -050065 private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
Hesam Rahimi4a409b42016-08-12 18:37:33 -040066
Henry Yu05c2c762017-01-30 12:11:08 -050067 private Map<DeviceId, Set<RestconfNotificationEventListener>>
68 restconfNotificationListenerMap = new ConcurrentHashMap<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -040069 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
70
Hesam Rahimi1856ed92016-10-27 18:46:42 -040071 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected YmsService ymsService;
73
Hesam Rahimi4a409b42016-08-12 18:37:33 -040074 ExecutorService executor = Executors.newCachedThreadPool();
75
76 @Activate
77 public void activate() {
78 log.info("RESTCONF SBI Started");
Hesam Rahimi1856ed92016-10-27 18:46:42 -040079 if (ymsService != null) {
80 ymsService
81 .registerDefaultCodec(new JsonYdtCodec(ymsService),
Shankara-Huaweid5823ab2016-11-22 10:14:52 +053082 YangProtocolEncodingFormat.JSON);
Hesam Rahimi1856ed92016-10-27 18:46:42 -040083 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -040084 }
85
86 @Deactivate
87 public void deactivate() {
88 log.info("RESTCONF SBI Stopped");
89 executor.shutdown();
90 this.getClientMap().clear();
91 this.getDeviceMap().clear();
92 }
93
94 @Override
95 public Map<DeviceId, RestSBDevice> getDevices() {
96 log.trace("RESTCONF SBI::getDevices");
97 return super.getDevices();
98 }
99
100 @Override
101 public RestSBDevice getDevice(DeviceId deviceInfo) {
102 log.trace("RESTCONF SBI::getDevice with deviceId");
103 return super.getDevice(deviceInfo);
104 }
105
106 @Override
107 public RestSBDevice getDevice(IpAddress ip, int port) {
108 log.trace("RESTCONF SBI::getDevice with ip and port");
109 return super.getDevice(ip, port);
110 }
111
112 @Override
113 public void addDevice(RestSBDevice device) {
114 log.trace("RESTCONF SBI::addDevice");
115 super.addDevice(device);
116 }
117
118 @Override
119 public void removeDevice(DeviceId deviceId) {
120 log.trace("RESTCONF SBI::removeDevice");
121 super.removeDevice(deviceId);
122 }
123
124 @Override
125 public boolean post(DeviceId device, String request, InputStream payload,
126 String mediaType) {
127 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
128 + request;
129 return super.post(device, request, payload, mediaType);
130 }
131
132 @Override
133 public <T> T post(DeviceId device, String request, InputStream payload,
134 String mediaType, Class<T> responseClass) {
135 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
136 + request;
137 return super.post(device, request, payload, mediaType, responseClass);
138 }
139
140 @Override
141 public boolean put(DeviceId device, String request, InputStream payload,
142 String mediaType) {
143 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
144 + request;
145 return super.put(device, request, payload, mediaType);
146 }
147
148 @Override
149 public InputStream get(DeviceId device, String request, String mediaType) {
150 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
151 + request;
152 return super.get(device, request, mediaType);
153 }
154
155 @Override
156 public boolean patch(DeviceId device, String request, InputStream payload,
157 String mediaType) {
158 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
159 + request;
160 return super.patch(device, request, payload, mediaType);
161 }
162
163 @Override
164 public boolean delete(DeviceId device, String request, InputStream payload,
165 String mediaType) {
166 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
167 + request;
168 return super.delete(device, request, payload, mediaType);
169 }
170
171 @Override
172 public void enableNotifications(DeviceId device, String request,
Henry Yu05c2c762017-01-30 12:11:08 -0500173 String mediaType,
174 RestconfNotificationEventListener listener) {
175
176 if (isNotificationEnabled(device)) {
177 log.warn("enableNotifications: already enabled on device: {}", device);
178 return;
179 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400180
181 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
182 + request;
183
184 addNotificationListener(device, listener);
185
186 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
187 device);
188 runnableTable.put(device, runnable);
189 executor.execute(runnable);
190 }
191
Henry Yu05c2c762017-01-30 12:11:08 -0500192 @Override
193 public void enableNotifications(DeviceId device, String request,
194 String mediaType,
195 RestConfNotificationEventListener callBackListener) {
196 //TODO: to be removed once the call to the API is updated.
197 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400198
Henry Yu05c2c762017-01-30 12:11:08 -0500199 public void stopNotifications(DeviceId device) {
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400200 runnableTable.get(device).terminate();
201 runnableTable.remove(device);
Henry Yu05c2c762017-01-30 12:11:08 -0500202 restconfNotificationListenerMap.remove(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400203 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
204
205 }
206
207 public class GetChunksRunnable implements Runnable {
208 private String request;
209 private String mediaType;
210 private DeviceId device;
211
212 private volatile boolean running = true;
213
214 public void terminate() {
215 running = false;
216 }
217
218 /**
Henry Yu05c2c762017-01-30 12:11:08 -0500219 * @param request request
Ray Milkey0bb1e102016-11-10 14:51:27 -0800220 * @param mediaType media type
Henry Yu05c2c762017-01-30 12:11:08 -0500221 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400222 */
223 public GetChunksRunnable(String request, String mediaType,
224 DeviceId device) {
225 this.request = request;
226 this.mediaType = mediaType;
227 this.device = device;
228 }
229
230 @Override
231 public void run() {
232 WebTarget wt = getWebTarget(device, request);
233 Response clientResp = wt.request(mediaType).get();
Henry Yu05c2c762017-01-30 12:11:08 -0500234 Set<RestconfNotificationEventListener> listeners =
235 restconfNotificationListenerMap.get(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400236 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
237 .readEntity(new GenericType<ChunkedInput<String>>() {
238 });
239
240 String chunk;
241 // Note that the read() is a blocking operation and the invoking
242 // thread is blocked until a new chunk comes. Jersey implementation
243 // of this IO operation is in a way that it does not respond to
244 // interrupts.
245 while (running) {
246 chunk = chunkedInput.read();
247 if (chunk != null) {
248 if (running) {
Henry Yu05c2c762017-01-30 12:11:08 -0500249 for (RestconfNotificationEventListener listener : listeners) {
250 listener.handleNotificationEvent(device, chunk);
251 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400252 } else {
253 log.trace("the requesting client is no more interested "
Henry Yu05c2c762017-01-30 12:11:08 -0500254 + "to receive such notifications.");
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400255 }
256 } else {
257 log.trace("The received notification chunk is null. do not continue any more.");
258 break;
259 }
260 }
261 log.trace("out of while loop -- end of run");
262 }
263 }
264
265 public String discoverRootResource(DeviceId device) {
266 // FIXME: send a GET command to the device to discover the root resource.
267 // The plan to fix this is for the Ibis release when the RESTCONF server and
268 // the RESTCONF client both support root resource discovery.
269 return ROOT_RESOURCE;
270 }
271
272 @Override
273 public void addNotificationListener(DeviceId deviceId,
Henry Yu05c2c762017-01-30 12:11:08 -0500274 RestconfNotificationEventListener listener) {
275 Set<RestconfNotificationEventListener> listeners =
276 restconfNotificationListenerMap.get(deviceId);
277 if (listeners == null) {
278 listeners = new HashSet<>();
279 }
280
281 listeners.add(listener);
282
283 this.restconfNotificationListenerMap.put(deviceId, listeners);
284 }
285
286 @Override
287 public void removeNotificationListener(DeviceId deviceId,
288 RestconfNotificationEventListener listener) {
289 Set<RestconfNotificationEventListener> listeners =
290 restconfNotificationListenerMap.get(deviceId);
291 if (listeners != null) {
292 listeners.remove(listener);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400293 }
294 }
295
296 @Override
297 public void removeNotificationListener(DeviceId deviceId) {
Henry Yu05c2c762017-01-30 12:11:08 -0500298 //TODO: This API is obsolete.
299 return;
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400300 }
301
Henry Yu05c2c762017-01-30 12:11:08 -0500302 public boolean isNotificationEnabled(DeviceId deviceId) {
303 return runnableTable.containsKey(deviceId);
304 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400305}