blob: e5fb37719d4636d11813adcae7e0ee06c7a8f11c [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
18import java.io.InputStream;
19import java.util.Map;
20import java.util.concurrent.ConcurrentHashMap;
21import java.util.concurrent.ExecutorService;
22import java.util.concurrent.Executors;
23
24import javax.ws.rs.client.WebTarget;
25import javax.ws.rs.core.GenericType;
26import javax.ws.rs.core.Response;
27
28import org.apache.felix.scr.annotations.Activate;
29import org.apache.felix.scr.annotations.Component;
30import org.apache.felix.scr.annotations.Deactivate;
31import org.apache.felix.scr.annotations.Service;
32import org.glassfish.jersey.client.ChunkedInput;
33import org.onlab.packet.IpAddress;
34import org.onosproject.net.DeviceId;
35import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
36import org.onosproject.protocol.rest.RestSBDevice;
37import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
38import org.onosproject.protocol.restconf.RestConfSBController;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
42/**
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/";
59 private static final String NOTIFICATION_PATH_PREFIX = "/data/";
60
61 private Map<DeviceId, RestConfNotificationEventListener>
62 restconfNotificationListenerMap = new ConcurrentHashMap<>();
63 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
111 public boolean post(DeviceId device, String request, InputStream payload,
112 String mediaType) {
113 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
114 + request;
115 return super.post(device, request, payload, mediaType);
116 }
117
118 @Override
119 public <T> T post(DeviceId device, String request, InputStream payload,
120 String mediaType, Class<T> responseClass) {
121 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
122 + request;
123 return super.post(device, request, payload, mediaType, responseClass);
124 }
125
126 @Override
127 public boolean put(DeviceId device, String request, InputStream payload,
128 String mediaType) {
129 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
130 + request;
131 return super.put(device, request, payload, mediaType);
132 }
133
134 @Override
135 public InputStream get(DeviceId device, String request, String mediaType) {
136 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
137 + request;
138 return super.get(device, request, mediaType);
139 }
140
141 @Override
142 public boolean patch(DeviceId device, String request, InputStream payload,
143 String mediaType) {
144 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
145 + request;
146 return super.patch(device, request, payload, mediaType);
147 }
148
149 @Override
150 public boolean delete(DeviceId device, String request, InputStream payload,
151 String mediaType) {
152 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
153 + request;
154 return super.delete(device, request, payload, mediaType);
155 }
156
157 @Override
158 public void enableNotifications(DeviceId device, String request,
159 String mediaType,
160 RestConfNotificationEventListener listener) {
161
162 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
163 + request;
164
165 addNotificationListener(device, listener);
166
167 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
168 device);
169 runnableTable.put(device, runnable);
170 executor.execute(runnable);
171 }
172
173 public void stopNotifications(DeviceId device) {
174
175 runnableTable.get(device).terminate();
176 runnableTable.remove(device);
177 removeNotificationListener(device);
178 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
179
180 }
181
182 public class GetChunksRunnable implements Runnable {
183 private String request;
184 private String mediaType;
185 private DeviceId device;
186
187 private volatile boolean running = true;
188
189 public void terminate() {
190 running = false;
191 }
192
193 /**
194 * @param request
195 * @param mediaType
196 * @param device
197 */
198 public GetChunksRunnable(String request, String mediaType,
199 DeviceId device) {
200 this.request = request;
201 this.mediaType = mediaType;
202 this.device = device;
203 }
204
205 @Override
206 public void run() {
207 WebTarget wt = getWebTarget(device, request);
208 Response clientResp = wt.request(mediaType).get();
209 RestConfNotificationEventListener listener = restconfNotificationListenerMap
210 .get(device);
211 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
212 .readEntity(new GenericType<ChunkedInput<String>>() {
213 });
214
215 String chunk;
216 // Note that the read() is a blocking operation and the invoking
217 // thread is blocked until a new chunk comes. Jersey implementation
218 // of this IO operation is in a way that it does not respond to
219 // interrupts.
220 while (running) {
221 chunk = chunkedInput.read();
222 if (chunk != null) {
223 if (running) {
224 listener.handleNotificationEvent(device, chunk);
225 } else {
226 log.trace("the requesting client is no more interested "
227 + "to receive such notifications.");
228 }
229 } else {
230 log.trace("The received notification chunk is null. do not continue any more.");
231 break;
232 }
233 }
234 log.trace("out of while loop -- end of run");
235 }
236 }
237
238 public String discoverRootResource(DeviceId device) {
239 // FIXME: send a GET command to the device to discover the root resource.
240 // The plan to fix this is for the Ibis release when the RESTCONF server and
241 // the RESTCONF client both support root resource discovery.
242 return ROOT_RESOURCE;
243 }
244
245 @Override
246 public void addNotificationListener(DeviceId deviceId,
247 RestConfNotificationEventListener listener) {
248 if (!restconfNotificationListenerMap.containsKey(deviceId)) {
249 this.restconfNotificationListenerMap.put(deviceId, listener);
250 }
251 }
252
253 @Override
254 public void removeNotificationListener(DeviceId deviceId) {
255 this.restconfNotificationListenerMap.remove(deviceId);
256 }
257
258}