blob: 1d812b571a971de6e56d55a63a3eda2b4f0e681c [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;
21import org.apache.felix.scr.annotations.Service;
22import org.glassfish.jersey.client.ChunkedInput;
23import org.onlab.packet.IpAddress;
24import org.onosproject.net.DeviceId;
25import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
26import org.onosproject.protocol.rest.RestSBDevice;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040027import org.onosproject.protocol.restconf.RestConfSBController;
Henry Yu05dcc212017-01-05 16:05:26 -050028import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040029import org.slf4j.Logger;
30import org.slf4j.LoggerFactory;
31
Henry Yu05dcc212017-01-05 16:05:26 -050032import javax.ws.rs.client.WebTarget;
33import javax.ws.rs.core.GenericType;
34import javax.ws.rs.core.Response;
35import java.io.InputStream;
36import java.util.HashSet;
37import java.util.Map;
38import java.util.Set;
39import java.util.concurrent.ConcurrentHashMap;
40import java.util.concurrent.ExecutorService;
41import java.util.concurrent.Executors;
42
Hesam Rahimi4a409b42016-08-12 18:37:33 -040043/**
44 * The implementation of RestConfSBController.
45 */
46@Component(immediate = true)
47@Service
48public class RestConfSBControllerImpl extends HttpSBControllerImpl
49 implements RestConfSBController {
50
51 private static final Logger log = LoggerFactory
52 .getLogger(RestConfSBControllerImpl.class);
53
54 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
55 // fully support root resource discovery, ROOT_RESOURCE constant will be
56 // removed and rather the value would get discovered dynamically.
57 private static final String ROOT_RESOURCE = "/onos/restconf";
58
59 private static final String RESOURCE_PATH_PREFIX = "/data/";
Henry Yudc747af2016-11-16 13:29:54 -050060 private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
Hesam Rahimi4a409b42016-08-12 18:37:33 -040061
Henry Yu05dcc212017-01-05 16:05:26 -050062 private Map<DeviceId, Set<RestconfNotificationEventListener>>
63 restconfNotificationListenerMap = new ConcurrentHashMap<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -040064 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
65
66 ExecutorService executor = Executors.newCachedThreadPool();
67
68 @Activate
69 public void activate() {
70 log.info("RESTCONF SBI Started");
71 }
72
73 @Deactivate
74 public void deactivate() {
75 log.info("RESTCONF SBI Stopped");
76 executor.shutdown();
77 this.getClientMap().clear();
78 this.getDeviceMap().clear();
79 }
80
81 @Override
82 public Map<DeviceId, RestSBDevice> getDevices() {
83 log.trace("RESTCONF SBI::getDevices");
84 return super.getDevices();
85 }
86
87 @Override
88 public RestSBDevice getDevice(DeviceId deviceInfo) {
89 log.trace("RESTCONF SBI::getDevice with deviceId");
90 return super.getDevice(deviceInfo);
91 }
92
93 @Override
94 public RestSBDevice getDevice(IpAddress ip, int port) {
95 log.trace("RESTCONF SBI::getDevice with ip and port");
96 return super.getDevice(ip, port);
97 }
98
99 @Override
100 public void addDevice(RestSBDevice device) {
101 log.trace("RESTCONF SBI::addDevice");
102 super.addDevice(device);
103 }
104
105 @Override
106 public void removeDevice(DeviceId deviceId) {
107 log.trace("RESTCONF SBI::removeDevice");
108 super.removeDevice(deviceId);
109 }
110
111 @Override
112 public boolean post(DeviceId device, String request, InputStream payload,
113 String mediaType) {
114 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
115 + request;
116 return super.post(device, request, payload, mediaType);
117 }
118
119 @Override
120 public <T> T post(DeviceId device, String request, InputStream payload,
121 String mediaType, Class<T> responseClass) {
122 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
123 + request;
124 return super.post(device, request, payload, mediaType, responseClass);
125 }
126
127 @Override
128 public boolean put(DeviceId device, String request, InputStream payload,
129 String mediaType) {
130 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
131 + request;
132 return super.put(device, request, payload, mediaType);
133 }
134
135 @Override
136 public InputStream get(DeviceId device, String request, String mediaType) {
137 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
138 + request;
139 return super.get(device, request, mediaType);
140 }
141
142 @Override
143 public boolean patch(DeviceId device, String request, InputStream payload,
144 String mediaType) {
145 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
146 + request;
147 return super.patch(device, request, payload, mediaType);
148 }
149
150 @Override
151 public boolean delete(DeviceId device, String request, InputStream payload,
152 String mediaType) {
153 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
154 + request;
155 return super.delete(device, request, payload, mediaType);
156 }
157
158 @Override
159 public void enableNotifications(DeviceId device, String request,
Henry Yu05dcc212017-01-05 16:05:26 -0500160 String mediaType,
161 RestconfNotificationEventListener listener) {
162
163 if (isNotificationEnabled(device)) {
164 log.warn("enableNotifications: already enabled on device: {}", device);
165 return;
166 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400167
168 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
169 + request;
170
171 addNotificationListener(device, listener);
172
173 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
174 device);
175 runnableTable.put(device, runnable);
176 executor.execute(runnable);
177 }
178
Henry Yu05c2c762017-01-30 12:11:08 -0500179 public void stopNotifications(DeviceId device) {
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400180 runnableTable.get(device).terminate();
181 runnableTable.remove(device);
Henry Yu05dcc212017-01-05 16:05:26 -0500182 restconfNotificationListenerMap.remove(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400183 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
184
185 }
186
187 public class GetChunksRunnable implements Runnable {
188 private String request;
189 private String mediaType;
190 private DeviceId device;
191
192 private volatile boolean running = true;
193
194 public void terminate() {
195 running = false;
196 }
197
198 /**
Henry Yu05dcc212017-01-05 16:05:26 -0500199 * @param request request
Ray Milkey0bb1e102016-11-10 14:51:27 -0800200 * @param mediaType media type
Henry Yu05dcc212017-01-05 16:05:26 -0500201 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400202 */
203 public GetChunksRunnable(String request, String mediaType,
204 DeviceId device) {
205 this.request = request;
206 this.mediaType = mediaType;
207 this.device = device;
208 }
209
210 @Override
211 public void run() {
212 WebTarget wt = getWebTarget(device, request);
213 Response clientResp = wt.request(mediaType).get();
Henry Yu05dcc212017-01-05 16:05:26 -0500214 Set<RestconfNotificationEventListener> listeners =
215 restconfNotificationListenerMap.get(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400216 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
217 .readEntity(new GenericType<ChunkedInput<String>>() {
218 });
219
220 String chunk;
221 // Note that the read() is a blocking operation and the invoking
222 // thread is blocked until a new chunk comes. Jersey implementation
223 // of this IO operation is in a way that it does not respond to
224 // interrupts.
225 while (running) {
226 chunk = chunkedInput.read();
227 if (chunk != null) {
228 if (running) {
Henry Yu05dcc212017-01-05 16:05:26 -0500229 for (RestconfNotificationEventListener listener : listeners) {
230 listener.handleNotificationEvent(device, chunk);
231 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400232 } else {
233 log.trace("the requesting client is no more interested "
Henry Yu05dcc212017-01-05 16:05:26 -0500234 + "to receive such notifications.");
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400235 }
236 } else {
237 log.trace("The received notification chunk is null. do not continue any more.");
238 break;
239 }
240 }
241 log.trace("out of while loop -- end of run");
242 }
243 }
244
245 public String discoverRootResource(DeviceId device) {
246 // FIXME: send a GET command to the device to discover the root resource.
247 // The plan to fix this is for the Ibis release when the RESTCONF server and
248 // the RESTCONF client both support root resource discovery.
249 return ROOT_RESOURCE;
250 }
251
252 @Override
253 public void addNotificationListener(DeviceId deviceId,
Henry Yu05dcc212017-01-05 16:05:26 -0500254 RestconfNotificationEventListener listener) {
255 Set<RestconfNotificationEventListener> listeners =
256 restconfNotificationListenerMap.get(deviceId);
257 if (listeners == null) {
258 listeners = new HashSet<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400259 }
Henry Yu05dcc212017-01-05 16:05:26 -0500260
261 listeners.add(listener);
262
263 this.restconfNotificationListenerMap.put(deviceId, listeners);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400264 }
265
266 @Override
Henry Yu05dcc212017-01-05 16:05:26 -0500267 public void removeNotificationListener(DeviceId deviceId,
268 RestconfNotificationEventListener listener) {
269 Set<RestconfNotificationEventListener> listeners =
270 restconfNotificationListenerMap.get(deviceId);
271 if (listeners != null) {
272 listeners.remove(listener);
273 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400274 }
275
Henry Yu05dcc212017-01-05 16:05:26 -0500276 public boolean isNotificationEnabled(DeviceId deviceId) {
277 return runnableTable.containsKey(deviceId);
278 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400279}