blob: 39347dedf217418267b0797dbaaed012464830b4 [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;
Hesam Rahimi1856ed92016-10-27 18:46:42 -040031import org.apache.felix.scr.annotations.Reference;
32import org.apache.felix.scr.annotations.ReferenceCardinality;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040033import org.apache.felix.scr.annotations.Service;
34import org.glassfish.jersey.client.ChunkedInput;
35import org.onlab.packet.IpAddress;
36import org.onosproject.net.DeviceId;
37import org.onosproject.protocol.http.ctl.HttpSBControllerImpl;
38import org.onosproject.protocol.rest.RestSBDevice;
39import org.onosproject.protocol.restconf.RestConfNotificationEventListener;
40import org.onosproject.protocol.restconf.RestConfSBController;
Hesam Rahimi1856ed92016-10-27 18:46:42 -040041import org.onosproject.yms.ych.YangProtocolEncodingFormat;
42import org.onosproject.yms.ymsm.YmsService;
Hesam Rahimi4a409b42016-08-12 18:37:33 -040043import org.slf4j.Logger;
44import org.slf4j.LoggerFactory;
45
46/**
47 * The implementation of RestConfSBController.
48 */
49@Component(immediate = true)
50@Service
51public class RestConfSBControllerImpl extends HttpSBControllerImpl
52 implements RestConfSBController {
53
54 private static final Logger log = LoggerFactory
55 .getLogger(RestConfSBControllerImpl.class);
56
57 // TODO: for the Ibis release when both RESTCONF server and RESTCONF client
58 // fully support root resource discovery, ROOT_RESOURCE constant will be
59 // removed and rather the value would get discovered dynamically.
60 private static final String ROOT_RESOURCE = "/onos/restconf";
61
62 private static final String RESOURCE_PATH_PREFIX = "/data/";
63 private static final String NOTIFICATION_PATH_PREFIX = "/data/";
64
65 private Map<DeviceId, RestConfNotificationEventListener>
66 restconfNotificationListenerMap = new ConcurrentHashMap<>();
67 private Map<DeviceId, GetChunksRunnable> runnableTable = new ConcurrentHashMap<>();
68
Hesam Rahimi1856ed92016-10-27 18:46:42 -040069 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected YmsService ymsService;
71
Hesam Rahimi4a409b42016-08-12 18:37:33 -040072 ExecutorService executor = Executors.newCachedThreadPool();
73
74 @Activate
75 public void activate() {
76 log.info("RESTCONF SBI Started");
Hesam Rahimi1856ed92016-10-27 18:46:42 -040077 if (ymsService != null) {
78 ymsService
79 .registerDefaultCodec(new JsonYdtCodec(ymsService),
80 YangProtocolEncodingFormat.JSON_ENCODING);
81 }
Hesam Rahimi4a409b42016-08-12 18:37:33 -040082 }
83
84 @Deactivate
85 public void deactivate() {
86 log.info("RESTCONF SBI Stopped");
87 executor.shutdown();
88 this.getClientMap().clear();
89 this.getDeviceMap().clear();
90 }
91
92 @Override
93 public Map<DeviceId, RestSBDevice> getDevices() {
94 log.trace("RESTCONF SBI::getDevices");
95 return super.getDevices();
96 }
97
98 @Override
99 public RestSBDevice getDevice(DeviceId deviceInfo) {
100 log.trace("RESTCONF SBI::getDevice with deviceId");
101 return super.getDevice(deviceInfo);
102 }
103
104 @Override
105 public RestSBDevice getDevice(IpAddress ip, int port) {
106 log.trace("RESTCONF SBI::getDevice with ip and port");
107 return super.getDevice(ip, port);
108 }
109
110 @Override
111 public void addDevice(RestSBDevice device) {
112 log.trace("RESTCONF SBI::addDevice");
113 super.addDevice(device);
114 }
115
116 @Override
117 public void removeDevice(DeviceId deviceId) {
118 log.trace("RESTCONF SBI::removeDevice");
119 super.removeDevice(deviceId);
120 }
121
122 @Override
123 public boolean post(DeviceId device, String request, InputStream payload,
124 String mediaType) {
125 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
126 + request;
127 return super.post(device, request, payload, mediaType);
128 }
129
130 @Override
131 public <T> T post(DeviceId device, String request, InputStream payload,
132 String mediaType, Class<T> responseClass) {
133 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
134 + request;
135 return super.post(device, request, payload, mediaType, responseClass);
136 }
137
138 @Override
139 public boolean put(DeviceId device, String request, InputStream payload,
140 String mediaType) {
141 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
142 + request;
143 return super.put(device, request, payload, mediaType);
144 }
145
146 @Override
147 public InputStream get(DeviceId device, String request, String mediaType) {
148 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
149 + request;
150 return super.get(device, request, mediaType);
151 }
152
153 @Override
154 public boolean patch(DeviceId device, String request, InputStream payload,
155 String mediaType) {
156 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
157 + request;
158 return super.patch(device, request, payload, mediaType);
159 }
160
161 @Override
162 public boolean delete(DeviceId device, String request, InputStream payload,
163 String mediaType) {
164 request = discoverRootResource(device) + RESOURCE_PATH_PREFIX
165 + request;
166 return super.delete(device, request, payload, mediaType);
167 }
168
169 @Override
170 public void enableNotifications(DeviceId device, String request,
171 String mediaType,
172 RestConfNotificationEventListener listener) {
173
174 request = discoverRootResource(device) + NOTIFICATION_PATH_PREFIX
175 + request;
176
177 addNotificationListener(device, listener);
178
179 GetChunksRunnable runnable = new GetChunksRunnable(request, mediaType,
180 device);
181 runnableTable.put(device, runnable);
182 executor.execute(runnable);
183 }
184
185 public void stopNotifications(DeviceId device) {
186
187 runnableTable.get(device).terminate();
188 runnableTable.remove(device);
189 removeNotificationListener(device);
190 log.debug("Stop sending notifications for device URI: " + device.uri().toString());
191
192 }
193
194 public class GetChunksRunnable implements Runnable {
195 private String request;
196 private String mediaType;
197 private DeviceId device;
198
199 private volatile boolean running = true;
200
201 public void terminate() {
202 running = false;
203 }
204
205 /**
Ray Milkey0bb1e102016-11-10 14:51:27 -0800206 * @param request request
207 * @param mediaType media type
208 * @param device device identifier
Hesam Rahimi4a409b42016-08-12 18:37:33 -0400209 */
210 public GetChunksRunnable(String request, String mediaType,
211 DeviceId device) {
212 this.request = request;
213 this.mediaType = mediaType;
214 this.device = device;
215 }
216
217 @Override
218 public void run() {
219 WebTarget wt = getWebTarget(device, request);
220 Response clientResp = wt.request(mediaType).get();
221 RestConfNotificationEventListener listener = restconfNotificationListenerMap
222 .get(device);
223 final ChunkedInput<String> chunkedInput = (ChunkedInput<String>) clientResp
224 .readEntity(new GenericType<ChunkedInput<String>>() {
225 });
226
227 String chunk;
228 // Note that the read() is a blocking operation and the invoking
229 // thread is blocked until a new chunk comes. Jersey implementation
230 // of this IO operation is in a way that it does not respond to
231 // interrupts.
232 while (running) {
233 chunk = chunkedInput.read();
234 if (chunk != null) {
235 if (running) {
236 listener.handleNotificationEvent(device, chunk);
237 } else {
238 log.trace("the requesting client is no more interested "
239 + "to receive such notifications.");
240 }
241 } else {
242 log.trace("The received notification chunk is null. do not continue any more.");
243 break;
244 }
245 }
246 log.trace("out of while loop -- end of run");
247 }
248 }
249
250 public String discoverRootResource(DeviceId device) {
251 // FIXME: send a GET command to the device to discover the root resource.
252 // The plan to fix this is for the Ibis release when the RESTCONF server and
253 // the RESTCONF client both support root resource discovery.
254 return ROOT_RESOURCE;
255 }
256
257 @Override
258 public void addNotificationListener(DeviceId deviceId,
259 RestConfNotificationEventListener listener) {
260 if (!restconfNotificationListenerMap.containsKey(deviceId)) {
261 this.restconfNotificationListenerMap.put(deviceId, listener);
262 }
263 }
264
265 @Override
266 public void removeNotificationListener(DeviceId deviceId) {
267 this.restconfNotificationListenerMap.remove(deviceId);
268 }
269
270}