blob: 897f6c7b98171e99846a6fd8782fafbdcb47ff9c [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;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040029import org.onosproject.protocol.restconf.RestConfSBController;
Henry Yu05dcc212017-01-05 16:05:26 -050030import org.onosproject.protocol.restconf.RestconfNotificationEventListener;
Hesam Rahimi1856ed92016-10-27 18:46:42 -040031import org.onosproject.yms.ych.YangProtocolEncodingFormat;
32import org.onosproject.yms.ymsm.YmsService;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Henry Yu05dcc212017-01-05 16:05:26 -050036import javax.ws.rs.client.WebTarget;
37import javax.ws.rs.core.GenericType;
38import javax.ws.rs.core.Response;
39import java.io.InputStream;
40import java.util.HashSet;
41import java.util.Map;
42import java.util.Set;
43import java.util.concurrent.ConcurrentHashMap;
44import java.util.concurrent.ExecutorService;
45import java.util.concurrent.Executors;
46
Hesam Rahimi4a409b42016-08-12 18:37:33 -040047/**
48 * The implementation of RestConfSBController.
49 */
50@Component(immediate = true)
51@Service
52public class RestConfSBControllerImpl extends HttpSBControllerImpl
53 implements RestConfSBController {
54
55 private static final Logger log = LoggerFactory
56 .getLogger(RestConfSBControllerImpl.class);
57
58 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
59 // fully support root resource discovery, ROOT_RESOURCE constant will be
60 // removed and rather the value would get discovered dynamically.
61 private static final String ROOT_RESOURCE = "/onos/restconf";
62
63 private static final String RESOURCE_PATH_PREFIX = "/data/";
Henry Yudc747af2016-11-16 13:29:54 -050064 private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
Hesam Rahimi4a409b42016-08-12 18:37:33 -040065
Henry Yu05dcc212017-01-05 16:05:26 -050066 private Map<DeviceId, Set<RestconfNotificationEventListener>>
67 restconfNotificationListenerMap = new ConcurrentHashMap<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -040068 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
69
Hesam Rahimi1856ed92016-10-27 18:46:42 -040070 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected YmsService ymsService;
72
Hesam Rahimi4a409b42016-08-12 18:37:33 -040073 ExecutorService executor = Executors.newCachedThreadPool();
74
75 @Activate
76 public void activate() {
77 log.info("RESTCONF SBI Started");
Hesam Rahimi1856ed92016-10-27 18:46:42 -040078 if (ymsService != null) {
79 ymsService
80 .registerDefaultCodec(new JsonYdtCodec(ymsService),
Shankara-Huaweid5823ab2016-11-22 10:14:52 +053081 YangProtocolEncodingFormat.JSON);
Hesam Rahimi1856ed92016-10-27 18:46:42 -040082 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -040083 }
84
85 @Deactivate
86 public void deactivate() {
87 log.info("RESTCONF SBI Stopped");
88 executor.shutdown();
89 this.getClientMap().clear();
90 this.getDeviceMap().clear();
91 }
92
93 @Override
94 public Map<DeviceId, RestSBDevice> getDevices() {
95 log.trace("RESTCONF SBI::getDevices");
96 return super.getDevices();
97 }
98
99 @Override
100 public RestSBDevice getDevice(DeviceId deviceInfo) {
101 log.trace("RESTCONF SBI::getDevice with deviceId");
102 return super.getDevice(deviceInfo);
103 }
104
105 @Override
106 public RestSBDevice getDevice(IpAddress ip, int port) {
107 log.trace("RESTCONF SBI::getDevice with ip and port");
108 return super.getDevice(ip, port);
109 }
110
111 @Override
112 public void addDevice(RestSBDevice device) {
113 log.trace("RESTCONF SBI::addDevice");
114 super.addDevice(device);
115 }
116
117 @Override
118 public void removeDevice(DeviceId deviceId) {
119 log.trace("RESTCONF SBI::removeDevice");
120 super.removeDevice(deviceId);
121 }
122
123 @Override
124 public boolean post(DeviceId device, String request, InputStream payload,
125 String mediaType) {
126 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
127 + request;
128 return super.post(device, request, payload, mediaType);
129 }
130
131 @Override
132 public <T> T post(DeviceId device, String request, InputStream payload,
133 String mediaType, Class<T> responseClass) {
134 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
135 + request;
136 return super.post(device, request, payload, mediaType, responseClass);
137 }
138
139 @Override
140 public boolean put(DeviceId device, String request, InputStream payload,
141 String mediaType) {
142 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
143 + request;
144 return super.put(device, request, payload, mediaType);
145 }
146
147 @Override
148 public InputStream get(DeviceId device, String request, String mediaType) {
149 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
150 + request;
151 return super.get(device, request, mediaType);
152 }
153
154 @Override
155 public boolean patch(DeviceId device, String request, InputStream payload,
156 String mediaType) {
157 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
158 + request;
159 return super.patch(device, request, payload, mediaType);
160 }
161
162 @Override
163 public boolean delete(DeviceId device, String request, InputStream payload,
164 String mediaType) {
165 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
166 + request;
167 return super.delete(device, request, payload, mediaType);
168 }
169
170 @Override
171 public void enableNotifications(DeviceId device, String request,
Henry Yu05dcc212017-01-05 16:05:26 -0500172 String mediaType,
173 RestconfNotificationEventListener listener) {
174
175 if (isNotificationEnabled(device)) {
176 log.warn("enableNotifications: already enabled on device: {}", device);
177 return;
178 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400179
180 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
181 + request;
182
183 addNotificationListener(device, listener);
184
185 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
186 device);
187 runnableTable.put(device, runnable);
188 executor.execute(runnable);
189 }
190
Henry Yu05c2c762017-01-30 12:11:08 -0500191 public void stopNotifications(DeviceId device) {
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400192 runnableTable.get(device).terminate();
193 runnableTable.remove(device);
Henry Yu05dcc212017-01-05 16:05:26 -0500194 restconfNotificationListenerMap.remove(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400195 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
196
197 }
198
199 public class GetChunksRunnable implements Runnable {
200 private String request;
201 private String mediaType;
202 private DeviceId device;
203
204 private volatile boolean running = true;
205
206 public void terminate() {
207 running = false;
208 }
209
210 /**
Henry Yu05dcc212017-01-05 16:05:26 -0500211 * @param request request
Ray Milkey0bb1e102016-11-10 14:51:27 -0800212 * @param mediaType media type
Henry Yu05dcc212017-01-05 16:05:26 -0500213 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400214 */
215 public GetChunksRunnable(String request, String mediaType,
216 DeviceId device) {
217 this.request = request;
218 this.mediaType = mediaType;
219 this.device = device;
220 }
221
222 @Override
223 public void run() {
224 WebTarget wt = getWebTarget(device, request);
225 Response clientResp = wt.request(mediaType).get();
Henry Yu05dcc212017-01-05 16:05:26 -0500226 Set<RestconfNotificationEventListener> listeners =
227 restconfNotificationListenerMap.get(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400228 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
229 .readEntity(new GenericType<ChunkedInput<String>>() {
230 });
231
232 String chunk;
233 // Note that the read() is a blocking operation and the invoking
234 // thread is blocked until a new chunk comes. Jersey implementation
235 // of this IO operation is in a way that it does not respond to
236 // interrupts.
237 while (running) {
238 chunk = chunkedInput.read();
239 if (chunk != null) {
240 if (running) {
Henry Yu05dcc212017-01-05 16:05:26 -0500241 for (RestconfNotificationEventListener listener : listeners) {
242 listener.handleNotificationEvent(device, chunk);
243 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400244 } else {
245 log.trace("the requesting client is no more interested "
Henry Yu05dcc212017-01-05 16:05:26 -0500246 + "to receive such notifications.");
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400247 }
248 } else {
249 log.trace("The received notification chunk is null. do not continue any more.");
250 break;
251 }
252 }
253 log.trace("out of while loop -- end of run");
254 }
255 }
256
257 public String discoverRootResource(DeviceId device) {
258 // FIXME: send a GET command to the device to discover the root resource.
259 // The plan to fix this is for the Ibis release when the RESTCONF server and
260 // the RESTCONF client both support root resource discovery.
261 return ROOT_RESOURCE;
262 }
263
264 @Override
265 public void addNotificationListener(DeviceId deviceId,
Henry Yu05dcc212017-01-05 16:05:26 -0500266 RestconfNotificationEventListener listener) {
267 Set<RestconfNotificationEventListener> listeners =
268 restconfNotificationListenerMap.get(deviceId);
269 if (listeners == null) {
270 listeners = new HashSet<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400271 }
Henry Yu05dcc212017-01-05 16:05:26 -0500272
273 listeners.add(listener);
274
275 this.restconfNotificationListenerMap.put(deviceId, listeners);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400276 }
277
278 @Override
Henry Yu05dcc212017-01-05 16:05:26 -0500279 public void removeNotificationListener(DeviceId deviceId,
280 RestconfNotificationEventListener listener) {
281 Set<RestconfNotificationEventListener> listeners =
282 restconfNotificationListenerMap.get(deviceId);
283 if (listeners != null) {
284 listeners.remove(listener);
285 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400286 }
287
Henry Yu05dcc212017-01-05 16:05:26 -0500288 public boolean isNotificationEnabled(DeviceId deviceId) {
289 return runnableTable.containsKey(deviceId);
290 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400291}