blob: 38f296d5899a09d339182e16ae523b3ccedec8a1 [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.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;
Henry Yu05dcc212017-01-05 16:05:26 -050035import java.util.HashSet;
36import java.util.Map;
37import java.util.Set;
38import java.util.concurrent.ConcurrentHashMap;
39import java.util.concurrent.ExecutorService;
40import java.util.concurrent.Executors;
41
Hesam Rahimi4a409b42016-08-12 18:37:33 -040042/**
43 * The implementation of RestConfSBController.
44 */
45@Component(immediate = true)
46@Service
47public class RestConfSBControllerImpl extends HttpSBControllerImpl
48 implements RestConfSBController {
49
50 private static final Logger log = LoggerFactory
51 .getLogger(RestConfSBControllerImpl.class);
52
53 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
54 // fully support root resource discovery, ROOT_RESOURCE constant will be
55 // removed and rather the value would get discovered dynamically.
56 private static final String ROOT_RESOURCE = "/onos/restconf";
57
58 private static final String RESOURCE_PATH_PREFIX = "/data/";
Henry Yudc747af2016-11-16 13:29:54 -050059 private static final String NOTIFICATION_PATH_PREFIX = "/streams/";
Hesam Rahimi4a409b42016-08-12 18:37:33 -040060
Henry Yu05dcc212017-01-05 16:05:26 -050061 private Map<DeviceId, Set<RestconfNotificationEventListener>>
62 restconfNotificationListenerMap = new ConcurrentHashMap<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -040063 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
64
65 ExecutorService executor = Executors.newCachedThreadPool();
66
67 @Activate
68 public void activate() {
69 log.info("RESTCONF SBI Started");
70 }
71
72 @Deactivate
73 public void deactivate() {
74 log.info("RESTCONF SBI Stopped");
75 executor.shutdown();
76 this.getClientMap().clear();
77 this.getDeviceMap().clear();
78 }
79
80 @Override
81 public Map<DeviceId, RestSBDevice> getDevices() {
82 log.trace("RESTCONF SBI::getDevices");
83 return super.getDevices();
84 }
85
86 @Override
87 public RestSBDevice getDevice(DeviceId deviceInfo) {
88 log.trace("RESTCONF SBI::getDevice with deviceId");
89 return super.getDevice(deviceInfo);
90 }
91
92 @Override
93 public RestSBDevice getDevice(IpAddress ip, int port) {
94 log.trace("RESTCONF SBI::getDevice with ip and port");
95 return super.getDevice(ip, port);
96 }
97
98 @Override
99 public void addDevice(RestSBDevice device) {
100 log.trace("RESTCONF SBI::addDevice");
101 super.addDevice(device);
102 }
103
104 @Override
105 public void removeDevice(DeviceId deviceId) {
106 log.trace("RESTCONF SBI::removeDevice");
107 super.removeDevice(deviceId);
108 }
109
110 @Override
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400111 public void enableNotifications(DeviceId device, String request,
Henry Yu05dcc212017-01-05 16:05:26 -0500112 String mediaType,
113 RestconfNotificationEventListener listener) {
114
115 if (isNotificationEnabled(device)) {
116 log.warn("enableNotifications: already enabled on device: {}", device);
117 return;
118 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400119
120 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
121 + request;
122
123 addNotificationListener(device, listener);
124
125 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
126 device);
127 runnableTable.put(device, runnable);
128 executor.execute(runnable);
129 }
130
Henry Yu05c2c762017-01-30 12:11:08 -0500131 public void stopNotifications(DeviceId device) {
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400132 runnableTable.get(device).terminate();
133 runnableTable.remove(device);
Henry Yu05dcc212017-01-05 16:05:26 -0500134 restconfNotificationListenerMap.remove(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400135 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
136
137 }
138
139 public class GetChunksRunnable implements Runnable {
140 private String request;
141 private String mediaType;
142 private DeviceId device;
143
144 private volatile boolean running = true;
145
146 public void terminate() {
147 running = false;
148 }
149
150 /**
Henry Yu05dcc212017-01-05 16:05:26 -0500151 * @param request request
Ray Milkey0bb1e102016-11-10 14:51:27 -0800152 * @param mediaType media type
Henry Yu05dcc212017-01-05 16:05:26 -0500153 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400154 */
155 public GetChunksRunnable(String request, String mediaType,
156 DeviceId device) {
157 this.request = request;
158 this.mediaType = mediaType;
159 this.device = device;
160 }
161
162 @Override
163 public void run() {
164 WebTarget wt = getWebTarget(device, request);
165 Response clientResp = wt.request(mediaType).get();
Henry Yu05dcc212017-01-05 16:05:26 -0500166 Set<RestconfNotificationEventListener> listeners =
167 restconfNotificationListenerMap.get(device);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400168 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
169 .readEntity(new GenericType<ChunkedInput<String>>() {
170 });
171
172 String chunk;
173 // Note that the read() is a blocking operation and the invoking
174 // thread is blocked until a new chunk comes. Jersey implementation
175 // of this IO operation is in a way that it does not respond to
176 // interrupts.
177 while (running) {
178 chunk = chunkedInput.read();
179 if (chunk != null) {
180 if (running) {
Henry Yu05dcc212017-01-05 16:05:26 -0500181 for (RestconfNotificationEventListener listener : listeners) {
182 listener.handleNotificationEvent(device, chunk);
183 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400184 } else {
185 log.trace("the requesting client is no more interested "
Henry Yu05dcc212017-01-05 16:05:26 -0500186 + "to receive such notifications.");
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400187 }
188 } else {
189 log.trace("The received notification chunk is null. do not continue any more.");
190 break;
191 }
192 }
193 log.trace("out of while loop -- end of run");
194 }
195 }
196
197 public String discoverRootResource(DeviceId device) {
198 // FIXME: send a GET command to the device to discover the root resource.
199 // The plan to fix this is for the Ibis release when the RESTCONF server and
200 // the RESTCONF client both support root resource discovery.
201 return ROOT_RESOURCE;
202 }
203
204 @Override
205 public void addNotificationListener(DeviceId deviceId,
Henry Yu05dcc212017-01-05 16:05:26 -0500206 RestconfNotificationEventListener listener) {
207 Set<RestconfNotificationEventListener> listeners =
208 restconfNotificationListenerMap.get(deviceId);
209 if (listeners == null) {
210 listeners = new HashSet<>();
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400211 }
Henry Yu05dcc212017-01-05 16:05:26 -0500212
213 listeners.add(listener);
214
215 this.restconfNotificationListenerMap.put(deviceId, listeners);
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400216 }
217
218 @Override
Henry Yu05dcc212017-01-05 16:05:26 -0500219 public void removeNotificationListener(DeviceId deviceId,
220 RestconfNotificationEventListener listener) {
221 Set<RestconfNotificationEventListener> listeners =
222 restconfNotificationListenerMap.get(deviceId);
223 if (listeners != null) {
224 listeners.remove(listener);
225 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400226 }
227
Henry Yu05dcc212017-01-05 16:05:26 -0500228 public boolean isNotificationEnabled(DeviceId deviceId) {
229 return runnableTable.containsKey(deviceId);
230 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400231}