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;
26 import org.iotivity.cloud.base.OICConstants;
27 import org.iotivity.cloud.base.ServerSystem;
28 import org.iotivity.cloud.base.connector.ConnectorPool;
29 import org.iotivity.cloud.base.device.CoapDevice;
30 import org.iotivity.cloud.base.device.Device;
31 import org.iotivity.cloud.base.device.IRequestChannel;
32 import org.iotivity.cloud.base.exception.ServerException;
33 import org.iotivity.cloud.base.exception.ServerException.BadRequestException;
34 import org.iotivity.cloud.base.exception.ServerException.UnAuthorizedException;
35 import org.iotivity.cloud.base.protocols.MessageBuilder;
36 import org.iotivity.cloud.base.protocols.coap.CoapRequest;
37 import org.iotivity.cloud.base.protocols.coap.CoapResponse;
38 import org.iotivity.cloud.base.protocols.enums.ContentFormat;
39 import org.iotivity.cloud.base.protocols.enums.RequestMethod;
40 import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
41 import org.iotivity.cloud.base.server.CoapServer;
42 import org.iotivity.cloud.base.server.HttpServer;
43 import org.iotivity.cloud.base.server.Server;
44 import org.iotivity.cloud.util.Cbor;
45 import org.iotivity.cloud.util.Log;
47 import io.netty.channel.ChannelDuplexHandler;
48 import io.netty.channel.ChannelHandler.Sharable;
49 import io.netty.channel.ChannelHandlerContext;
50 import io.netty.channel.ChannelPromise;
52 public class DeviceServerSystem extends ServerSystem {
54 public class CoapDevicePool {
55 HashMap<String, Device> mMapDevice = new HashMap<>();
57 public void addDevice(Device device) {
58 String deviceId = ((CoapDevice) device).getDeviceId();
59 synchronized (mMapDevice) {
60 mMapDevice.put(deviceId, device);
64 public void removeDevice(Device device) {
65 String deviceId = ((CoapDevice) device).getDeviceId();
66 synchronized (mMapDevice) {
67 if (mMapDevice.get(deviceId) == device) {
68 mMapDevice.remove(deviceId);
73 public Device queryDevice(String deviceId) {
75 synchronized (mMapDevice) {
76 device = mMapDevice.get(deviceId);
82 CoapDevicePool mDevicePool = new CoapDevicePool();
85 class CoapLifecycleHandler extends ChannelDuplexHandler {
87 public void channelRead(ChannelHandlerContext ctx, Object msg) {
89 if (msg instanceof CoapRequest) {
91 CoapDevice coapDevice = (CoapDevice) ctx.channel()
92 .attr(keyDevice).get();
94 if (coapDevice.isExpiredTime()) {
95 throw new UnAuthorizedException("token is expired");
98 } catch (Throwable t) {
99 Log.f(ctx.channel(), t);
100 ResponseStatus responseStatus = t instanceof ServerException
101 ? ((ServerException) t).getErrorResponse()
102 : ResponseStatus.INTERNAL_SERVER_ERROR;
103 ctx.writeAndFlush(MessageBuilder
104 .createResponse((CoapRequest) msg, responseStatus));
109 ctx.fireChannelRead(msg);
113 public void channelActive(ChannelHandlerContext ctx) {
115 // Authenticated device connected
116 Device device = ctx.channel().attr(keyDevice).get();
117 mDevicePool.addDevice(device);
118 device.onConnected();
120 sendDevicePresence(device.getDeviceId(), "on");
124 public void channelInactive(ChannelHandlerContext ctx) {
125 Device device = ctx.channel().attr(keyDevice).get();
126 // Some cases, this event occurs after new device connected using
128 // So compare actual value, and remove if same.
129 if (device != null) {
130 mDevicePool.removeDevice(device);
131 device.onDisconnected();
132 ctx.channel().attr(keyDevice).remove();
134 sendDevicePresence(device.getDeviceId(), "off");
138 private void sendDevicePresence(String deviceId, String state) {
140 Cbor<HashMap<String, Object>> cbor = new Cbor<>();
141 IRequestChannel RDServer = ConnectorPool.getConnection("rd");
142 HashMap<String, Object> payload = new HashMap<String, Object>();
143 payload.put(Constants.REQ_DEVICE_ID, deviceId);
144 payload.put(Constants.PRESENCE_STATE, state);
145 StringBuffer uriPath = new StringBuffer();
146 uriPath.append("/" + Constants.PREFIX_OIC);
147 uriPath.append("/" + Constants.DEVICE_PRESENCE_URI);
148 RDServer.sendRequest(MessageBuilder.createRequest(
149 RequestMethod.POST, uriPath.toString(), null,
150 ContentFormat.APPLICATION_CBOR,
151 cbor.encodingPayloadToCbor(payload)), null);
155 CoapLifecycleHandler mLifeCycleHandler = new CoapLifecycleHandler();
158 class CoapAuthHandler extends ChannelDuplexHandler {
159 private Cbor<HashMap<String, Object>> mCbor = new Cbor<HashMap<String, Object>>();
162 public void channelActive(ChannelHandlerContext ctx) {
163 // Actual channel active should decided after authentication.
167 public void write(ChannelHandlerContext ctx, Object msg,
168 ChannelPromise promise) {
172 if (!(msg instanceof CoapResponse)) {
173 throw new BadRequestException(
174 "this msg type is not CoapResponse");
176 // This is CoapResponse
177 // Once the response is valid, add this to deviceList
178 CoapResponse response = (CoapResponse) msg;
180 switch (response.getUriPath()) {
182 case OICConstants.ACCOUNT_SESSION_FULL_URI:
184 if (response.getStatus() != ResponseStatus.CHANGED) {
185 throw new UnAuthorizedException();
188 HashMap<String, Object> payloadData = mCbor
189 .parsePayloadFromCbor(response.getPayload(),
191 int remainTime = (int) payloadData
192 .get(Constants.EXPIRES_IN);
194 Device device = ctx.channel().attr(keyDevice).get();
195 ((CoapDevice) device).setExpiredPolicy(remainTime);
197 // Remove current auth handler and replace to
199 ctx.channel().pipeline().replace(this,
200 "LifeCycleHandler", mLifeCycleHandler);
202 // Raise event that we have Authenticated device
203 ctx.fireChannelActive();
208 ctx.writeAndFlush(msg);
210 } catch (Throwable t) {
211 Log.f(ctx.channel(), t);
212 ctx.writeAndFlush(msg);
218 public void channelRead(ChannelHandlerContext ctx, Object msg) {
221 if (!(msg instanceof CoapRequest)) {
222 throw new BadRequestException(
223 "this msg type is not CoapRequest");
226 // And check first response is VALID then add or cut
227 CoapRequest request = (CoapRequest) msg;
229 HashMap<String, Object> authPayload = null;
230 Device device = null;
232 switch (request.getUriPath()) {
233 // Check whether request is about account
234 case OICConstants.ACCOUNT_FULL_URI:
235 case OICConstants.ACCOUNT_TOKENREFRESH_FULL_URI:
237 if (ctx.channel().attr(keyDevice).get() == null) {
238 // Create device first and pass to upperlayer
239 device = new CoapDevice(ctx);
240 ctx.channel().attr(keyDevice).set(device);
245 case OICConstants.ACCOUNT_SESSION_FULL_URI:
247 authPayload = mCbor.parsePayloadFromCbor(
248 request.getPayload(), HashMap.class);
250 device = ctx.channel().attr(keyDevice).get();
252 if (device == null) {
253 device = new CoapDevice(ctx);
254 ctx.channel().attr(keyDevice).set(device);
257 ((CoapDevice) device).updateDevice(
258 (String) authPayload.get(Constants.DEVICE_ID),
259 (String) authPayload.get(Constants.USER_ID),
261 .get(Constants.ACCESS_TOKEN));
265 case OICConstants.KEEP_ALIVE_FULL_URI:
266 // TODO: Pass ping request to upper layer
270 throw new UnAuthorizedException(
271 "authentication required first");
274 ctx.fireChannelRead(msg);
276 } catch (Throwable t) {
277 ResponseStatus responseStatus = t instanceof ServerException
278 ? ((ServerException) t).getErrorResponse()
279 : ResponseStatus.UNAUTHORIZED;
280 ctx.writeAndFlush(MessageBuilder
281 .createResponse((CoapRequest) msg, responseStatus));
282 Log.f(ctx.channel(), t);
288 class HttpAuthHandler extends ChannelDuplexHandler {
290 public void channelActive(ChannelHandlerContext ctx) throws Exception {
291 // After current channel authenticated, raise to upper layer
296 public void addServer(Server server) {
297 if (server instanceof CoapServer) {
298 server.addHandler(new CoapAuthHandler());
301 if (server instanceof HttpServer) {
302 server.addHandler(new HttpAuthHandler());
305 super.addServer(server);
308 public CoapDevicePool getDevicePool() {