2 * //******************************************************************
4 * // Copyright 2016 Samsung Electronics All Rights Reserved.
6 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
8 * // Licensed under the Apache License, Version 2.0 (the "License");
9 * // you may not use this file except in compliance with the License.
10 * // You may obtain a copy of the License at
12 * // http://www.apache.org/licenses/LICENSE-2.0
14 * // Unless required by applicable law or agreed to in writing, software
15 * // distributed under the License is distributed on an "AS IS" BASIS,
16 * // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * // See the License for the specific language governing permissions and
18 * // limitations under the License.
20 * //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
22 package org.iotivity.cloud.ciserver;
24 import java.util.HashMap;
25 import java.util.Iterator;
27 import org.iotivity.cloud.base.OICConstants;
28 import org.iotivity.cloud.base.ServerSystem;
29 import org.iotivity.cloud.base.connector.ConnectorPool;
30 import org.iotivity.cloud.base.device.CoapDevice;
31 import org.iotivity.cloud.base.device.Device;
32 import org.iotivity.cloud.base.device.IRequestChannel;
33 import org.iotivity.cloud.base.exception.ClientException;
34 import org.iotivity.cloud.base.exception.ServerException;
35 import org.iotivity.cloud.base.exception.ServerException.BadRequestException;
36 import org.iotivity.cloud.base.exception.ServerException.UnAuthorizedException;
37 import org.iotivity.cloud.base.protocols.MessageBuilder;
38 import org.iotivity.cloud.base.protocols.coap.CoapRequest;
39 import org.iotivity.cloud.base.protocols.coap.CoapResponse;
40 import org.iotivity.cloud.base.protocols.enums.ContentFormat;
41 import org.iotivity.cloud.base.protocols.enums.RequestMethod;
42 import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
43 import org.iotivity.cloud.base.server.CoapServer;
44 import org.iotivity.cloud.base.server.HttpServer;
45 import org.iotivity.cloud.base.server.Server;
46 import org.iotivity.cloud.util.Bytes;
47 import org.iotivity.cloud.util.Cbor;
48 import org.iotivity.cloud.util.Log;
50 import io.netty.channel.ChannelDuplexHandler;
51 import io.netty.channel.ChannelHandler.Sharable;
52 import io.netty.channel.ChannelHandlerContext;
53 import io.netty.channel.ChannelPromise;
57 * This class provides a set of APIs to manage all of request
61 public class DeviceServerSystem extends ServerSystem {
63 IRequestChannel mRDServer = null;
65 public DeviceServerSystem() {
66 mRDServer = ConnectorPool.getConnection("rd");
71 * This class provides a set of APIs to manage device pool.
74 public class CoapDevicePool {
75 HashMap<String, Device> mMapDevice = new HashMap<>();
78 * API for adding device information into pool.
83 public void addDevice(Device device) {
84 String deviceId = ((CoapDevice) device).getDeviceId();
85 synchronized (mMapDevice) {
86 mMapDevice.put(deviceId, device);
91 * API for removing device information into pool.
94 * device to be removed
96 public void removeDevice(Device device) throws ClientException {
97 String deviceId = ((CoapDevice) device).getDeviceId();
98 synchronized (mMapDevice) {
99 if (mMapDevice.get(deviceId) == device) {
100 mMapDevice.remove(deviceId);
103 removeObserveDevice(device);
106 private void removeObserveDevice(Device device) throws ClientException {
107 Iterator<String> iterator = mMapDevice.keySet().iterator();
108 while (iterator.hasNext()) {
109 String deviceId = iterator.next();
110 CoapDevice getDevice = (CoapDevice) mDevicePool
111 .queryDevice(deviceId);
112 getDevice.removeObserveChannel(
113 ((CoapDevice) device).getRequestChannel());
118 * API for getting device information.
121 * device id to get device
123 public Device queryDevice(String deviceId) {
124 Device device = null;
125 synchronized (mMapDevice) {
126 device = mMapDevice.get(deviceId);
132 CoapDevicePool mDevicePool = new CoapDevicePool();
136 * This class provides a set of APIs to manage life cycle of coap message.
140 class CoapLifecycleHandler extends ChannelDuplexHandler {
142 public void channelRead(ChannelHandlerContext ctx, Object msg) {
144 if (msg instanceof CoapRequest) {
146 CoapDevice coapDevice = (CoapDevice) ctx.channel()
147 .attr(keyDevice).get();
149 if (coapDevice.isExpiredTime()) {
150 throw new UnAuthorizedException("token is expired");
153 CoapRequest coapRequest = (CoapRequest) msg;
154 IRequestChannel targetChannel = null;
155 if (coapRequest.getUriPathSegments()
156 .contains(Constants.REQ_DEVICE_ID)) {
157 CoapDevice targetDevice = (CoapDevice) mDevicePool
158 .queryDevice(coapRequest.getUriPathSegments()
160 targetChannel = targetDevice.getRequestChannel();
162 switch (coapRequest.getObserve()) {
164 coapDevice.addObserveRequest(
165 Bytes.bytesToLong(coapRequest.getToken()),
167 coapDevice.addObserveChannel(targetChannel);
170 coapDevice.removeObserveChannel(targetChannel);
171 coapDevice.removeObserveRequest(
172 Bytes.bytesToLong(coapRequest.getToken()));
178 } catch (Throwable t) {
179 Log.f(ctx.channel(), t);
180 ResponseStatus responseStatus = t instanceof ServerException
181 ? ((ServerException) t).getErrorResponse()
182 : ResponseStatus.INTERNAL_SERVER_ERROR;
183 ctx.writeAndFlush(MessageBuilder
184 .createResponse((CoapRequest) msg, responseStatus));
189 ctx.fireChannelRead(msg);
193 public void channelActive(ChannelHandlerContext ctx) {
194 Device device = ctx.channel().attr(keyDevice).get();
195 // Authenticated device connected
197 sendDevicePresence(device.getDeviceId(), "on");
198 mDevicePool.addDevice(device);
200 device.onConnected();
204 public void channelInactive(ChannelHandlerContext ctx)
205 throws ClientException {
206 Device device = ctx.channel().attr(keyDevice).get();
207 // Some cases, this event occurs after new device connected using
209 // So compare actual value, and remove if same.
210 if (device != null) {
211 sendDevicePresence(device.getDeviceId(), "off");
213 device.onDisconnected();
215 mDevicePool.removeDevice(device);
216 ctx.channel().attr(keyDevice).remove();
222 * API for sending state to resource directory
225 * device id to be sent to resource directory
227 * device state to be sent to resource directory
229 public void sendDevicePresence(String deviceId, String state) {
231 Cbor<HashMap<String, Object>> cbor = new Cbor<>();
232 HashMap<String, Object> payload = new HashMap<String, Object>();
233 payload.put(Constants.REQ_DEVICE_ID, deviceId);
234 payload.put(Constants.PRESENCE_STATE, state);
235 StringBuffer uriPath = new StringBuffer();
236 uriPath.append("/" + Constants.PREFIX_OIC);
237 uriPath.append("/" + Constants.DEVICE_PRESENCE_URI);
238 mRDServer.sendRequest(MessageBuilder.createRequest(
239 RequestMethod.POST, uriPath.toString(), null,
240 ContentFormat.APPLICATION_CBOR,
241 cbor.encodingPayloadToCbor(payload)), null);
245 CoapLifecycleHandler mLifeCycleHandler = new CoapLifecycleHandler();
248 class CoapAuthHandler extends ChannelDuplexHandler {
249 private Cbor<HashMap<String, Object>> mCbor = new Cbor<HashMap<String, Object>>();
252 public void channelActive(ChannelHandlerContext ctx) {
253 // Actual channel active should decided after authentication.
257 public void write(ChannelHandlerContext ctx, Object msg,
258 ChannelPromise promise) {
262 if (!(msg instanceof CoapResponse)) {
263 throw new BadRequestException(
264 "this msg type is not CoapResponse");
266 // This is CoapResponse
267 // Once the response is valid, add this to deviceList
268 CoapResponse response = (CoapResponse) msg;
270 switch (response.getUriPath()) {
272 case OICConstants.ACCOUNT_SESSION_FULL_URI:
273 HashMap<String, Object> payloadData = mCbor
274 .parsePayloadFromCbor(response.getPayload(),
277 if (response.getStatus() != ResponseStatus.CHANGED) {
278 throw new UnAuthorizedException();
281 if (payloadData == null) {
282 throw new BadRequestException("payload is empty");
284 int remainTime = (int) payloadData
285 .get(Constants.EXPIRES_IN);
287 Device device = ctx.channel().attr(keyDevice).get();
288 ((CoapDevice) device).setExpiredPolicy(remainTime);
290 // Remove current auth handler and replace to
292 ctx.channel().pipeline().replace(this,
293 "LifeCycleHandler", mLifeCycleHandler);
295 // Raise event that we have Authenticated device
296 ctx.fireChannelActive();
301 ctx.writeAndFlush(msg);
303 } catch (Throwable t) {
304 Log.f(ctx.channel(), t);
305 ctx.writeAndFlush(msg);
311 public void channelRead(ChannelHandlerContext ctx, Object msg) {
314 if (!(msg instanceof CoapRequest)) {
315 throw new BadRequestException(
316 "this msg type is not CoapRequest");
319 // And check first response is VALID then add or cut
320 CoapRequest request = (CoapRequest) msg;
322 switch (request.getUriPath()) {
323 // Check whether request is about account
324 case OICConstants.ACCOUNT_FULL_URI:
325 case OICConstants.ACCOUNT_TOKENREFRESH_FULL_URI:
327 if (ctx.channel().attr(keyDevice).get() == null) {
328 // Create device first and pass to upperlayer
329 Device device = new CoapDevice(ctx);
330 ctx.channel().attr(keyDevice).set(device);
335 case OICConstants.ACCOUNT_SESSION_FULL_URI:
337 HashMap<String, Object> authPayload = mCbor
338 .parsePayloadFromCbor(request.getPayload(),
341 Device device = ctx.channel().attr(keyDevice).get();
343 if (device == null) {
344 device = new CoapDevice(ctx);
345 ctx.channel().attr(keyDevice).set(device);
348 if (authPayload == null) {
349 throw new BadRequestException("payload is empty");
352 ((CoapDevice) device).updateDevice(
353 (String) authPayload.get(Constants.DEVICE_ID),
354 (String) authPayload.get(Constants.USER_ID),
356 .get(Constants.ACCESS_TOKEN));
360 case OICConstants.KEEP_ALIVE_FULL_URI:
361 // TODO: Pass ping request to upper layer
365 throw new UnAuthorizedException(
366 "authentication required first");
369 ctx.fireChannelRead(msg);
371 } catch (Throwable t) {
372 ResponseStatus responseStatus = t instanceof ServerException
373 ? ((ServerException) t).getErrorResponse()
374 : ResponseStatus.UNAUTHORIZED;
375 ctx.writeAndFlush(MessageBuilder
376 .createResponse((CoapRequest) msg, responseStatus));
377 Log.f(ctx.channel(), t);
383 class HttpAuthHandler extends ChannelDuplexHandler {
385 public void channelActive(ChannelHandlerContext ctx) throws Exception {
386 // After current channel authenticated, raise to upper layer
391 public void addServer(Server server) {
392 if (server instanceof CoapServer) {
393 server.addHandler(new CoapAuthHandler());
396 if (server instanceof HttpServer) {
397 server.addHandler(new HttpAuthHandler());
400 super.addServer(server);
403 public CoapDevicePool getDevicePool() {