[Simulator] Minor UI changes fixing the IOT-1087.
[platform/upstream/iotivity.git] / cloud / interface / src / main / java / org / iotivity / cloud / ciserver / protocols / CoapRelayHandler.java
1 /*
2  *******************************************************************
3  *
4  * Copyright 2016 Samsung Electronics All Rights Reserved.
5  *
6  *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7  *
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
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21  */
22 package org.iotivity.cloud.ciserver.protocols;
23
24 import java.net.InetSocketAddress;
25 import java.nio.charset.StandardCharsets;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29
30 import org.iotivity.cloud.base.CoapClient;
31 import org.iotivity.cloud.base.SessionManager;
32 import org.iotivity.cloud.base.protocols.coap.CoapRequest;
33 import org.iotivity.cloud.base.protocols.coap.CoapResponse;
34 import org.iotivity.cloud.base.protocols.coap.enums.CoapMethod;
35 import org.iotivity.cloud.base.protocols.coap.enums.CoapStatus;
36 import org.iotivity.cloud.ciserver.Constants;
37 import org.iotivity.cloud.util.Logger;
38 import org.iotivity.cloud.util.Net;
39
40 import io.netty.channel.ChannelDuplexHandler;
41 import io.netty.channel.ChannelHandler.Sharable;
42 import io.netty.channel.ChannelHandlerContext;
43 import io.netty.channel.SimpleChannelInboundHandler;
44 import io.netty.util.AttributeKey;
45
46 /**
47  *
48  * This class is relay handler relating Cloud Interface
49  *
50  */
51 @Sharable
52 public class CoapRelayHandler extends ChannelDuplexHandler {
53
54     /////////// Handler for Resource Directory
55     private static final AttributeKey<ChannelHandlerContext> keyRDClient = AttributeKey
56             .newInstance("rdCtx");
57
58     private static class RDHandler
59             extends SimpleChannelInboundHandler<CoapResponse> {
60         @Override
61         public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
62                 throws Exception {
63             Logger.d("Receive response from RD, forward to client");
64
65             ChannelHandlerContext ctxToDevice = ctx.channel().attr(keyRDClient)
66                     .get();
67             ctxToDevice.writeAndFlush(msg);
68         }
69
70         @Override
71         public void exceptionCaught(ChannelHandlerContext ctx,
72                 Throwable cause) {
73             cause.printStackTrace();
74             ctx.close();
75         }
76     }
77
78     private CoapClient rdClient = new CoapClient();
79     ///////////
80
81     ////////// Handler for Account Server
82     private static final AttributeKey<List<CoapRequest>> keyAccountClient = AttributeKey
83             .newInstance("accountCtx");
84
85     private class AccountHandler
86             extends SimpleChannelInboundHandler<CoapResponse> {
87         @Override
88         public void channelRead0(ChannelHandlerContext ctx, CoapResponse msg)
89                 throws Exception {
90             Logger.d("Receive response from AS, make request to RD");
91
92             CoapRequest rdRequest = null;
93
94             switch (msg.getResponseCode()) {
95                 case CREATED:
96                     // response of POST request
97                     rdRequest = ctx.channel().attr(keyAccountClient).get()
98                             .remove(0);
99                     rdClient.sendRequest(rdRequest);
100                     break;
101
102                 case CONTENT:
103                     // response of GET request, contains did list
104                     rdRequest = ctx.channel().attr(keyAccountClient).get()
105                             .remove(0);
106                     // change uri to send RD
107                     rdRequest.setUriPath(Constants.RD_URI);
108                     rdRequest.setUriQuery(Constants.DEVICE_LIST);
109                     rdRequest.setPayload(msg.getPayload());
110                     rdClient.sendRequest(rdRequest);
111                     break;
112
113                 default:
114                     break;
115             }
116         }
117
118         @Override
119         public void exceptionCaught(ChannelHandlerContext ctx,
120                 Throwable cause) {
121             cause.printStackTrace();
122             ctx.close();
123         }
124     }
125
126     private CoapClient asClient = new CoapClient();
127     //////////
128
129     private SessionManager sessionManager = null;
130
131     public CoapRelayHandler(SessionManager sessionManager, String rdAddress,
132             int rdPort, String acAddress, int acPort) {
133         this.sessionManager = sessionManager;
134
135         rdClient.addHandler(new RDHandler());
136
137         asClient.addHandler(new AccountHandler());
138
139         try {
140             rdClient.startClient(new InetSocketAddress(rdAddress, rdPort));
141             asClient.startClient(new InetSocketAddress(acAddress, acPort));
142         } catch (InterruptedException e) {
143             // TODO Auto-generated catch block
144             e.printStackTrace();
145         }
146
147         asClient.getChannelFuture().channel().attr(keyAccountClient)
148                 .set(new ArrayList<CoapRequest>());
149     }
150
151     private static final AttributeKey<ChannelHandlerContext> keyDevice = AttributeKey
152             .newInstance("deviceCtx");
153
154     private HashMap<String, CoapClient> ciRelayClients = new HashMap<String, CoapClient>();
155
156     @Override
157     public void channelRead(ChannelHandlerContext ctx, Object msg)
158             throws Exception {
159
160         if (msg instanceof CoapRequest) {
161             CoapRequest request = (CoapRequest) msg;
162             // Parse uri, send to RD
163             String uriPath = request.getUriPath();
164             CoapRequest accountRequest = null;
165             String userId, deviceId, authPayload;
166             CoapResponse response = null;
167
168             Logger.d("Request received, URI: " + uriPath);
169             switch (uriPath) {
170                 case Constants.AUTH_URI:
171                     // This case user wants to logout
172                     if (request.getUriQuery().endsWith("logout")) {
173                         ctx.channel().attr(Constants.Attribute_UserId).remove();
174                         response = new CoapResponse(CoapStatus.DELETED);
175                     } else {
176                         response = new CoapResponse(CoapStatus.BAD_REQUEST);
177                     }
178                     ctx.writeAndFlush(response);
179                     break;
180
181                 case Constants.RD_URI:
182                     // RD POST means publish device to server
183                     switch (request.getRequestMethod()) {
184                         case POST:
185                             userId = ctx.channel()
186                                     .attr(Constants.Attribute_UserId).get();
187                             deviceId = request.decodeDeviceId();
188                             authPayload = String.format(
189                                     "{\"userid\":\"%s\",\"deviceid\":\"%s\"}",
190                                     userId, deviceId);
191                             accountRequest = new CoapRequest(CoapMethod.POST);
192                             accountRequest.setUriPath(Constants.ACCOUNT_URI);
193                             accountRequest.setUriQuery("reqtype=publish");
194                             accountRequest.setToken(request.getToken());
195                             accountRequest.setPayload(authPayload.getBytes(StandardCharsets.UTF_8));
196
197                             // TODO: deviceId must be registered after session
198                             // granted
199                             Logger.d("Adding deviceId to session: " + deviceId);
200                             sessionManager.addSession(deviceId, ctx);
201                             break;
202
203                         default:
204                             Logger.e("Unsupported request type");
205                             break;
206                     }
207
208                     rdClient.getChannelFuture().channel().attr(keyRDClient)
209                             .set(ctx);
210
211                     // Add original request to list for future use
212                     asClient.getChannelFuture().channel().attr(keyAccountClient)
213                             .get().add(request);
214                     asClient.sendRequest(accountRequest);
215                     return;
216
217                 case Constants.WELL_KNOWN_URI:
218                     switch (request.getRequestMethod()) {
219                         case GET:
220                             userId = ctx.channel()
221                                     .attr(Constants.Attribute_UserId).get();
222                             authPayload = String.format("{\"userid\":\"%s\"}",
223                                     userId);
224                             accountRequest = new CoapRequest(CoapMethod.GET);
225                             accountRequest.setUriPath(Constants.ACCOUNT_URI);
226                             accountRequest.setUriQuery("reqtype=find");
227                             accountRequest.setToken(request.getToken());
228                             accountRequest.setPayload(authPayload.getBytes());
229                             break;
230
231                         default:
232                             Logger.e("Unsupported request type");
233                             break;
234                     }
235
236                     rdClient.getChannelFuture().channel().attr(keyRDClient)
237                             .set(ctx);
238
239                     // Add original request to list for future use
240                     asClient.getChannelFuture().channel().attr(keyAccountClient)
241                             .get().add(request);
242                     asClient.sendRequest(accountRequest);
243                     return;
244
245                 case Constants.KEEP_ALIVE_URI:
246                     break;
247
248                 default:
249                     List<String> uriPathList = request.getUriPathSegments();
250                     String originUriPathList = request.getUriPath();
251                     Logger.i("uriPahtList: " + uriPathList.toString());
252                     String ciAddress = uriPathList.get(0);
253                     String did = uriPathList.get(1);
254
255                     Logger.i("CI address: " + ciAddress);
256                     Logger.i("did: " + did);
257
258                     // TODO: getMyIP ?
259                     String hostAddress = Net.getMyIpAddress().replace("/", "");
260                     Logger.i("hostAddress : " + hostAddress);
261                     // if published CI is mine
262                     if (hostAddress.equals(ciAddress) == true) {
263                         // find ctx about did, and send msg
264                         Logger.d("published CI is mine");
265                         String resource = new String();
266                         List<String> pathSegments = uriPathList.subList(2,
267                                 uriPathList.size());
268                         for (String path : pathSegments) {
269                             resource += "/";
270                             resource += path;
271                         }
272                         Logger.i("resource: " + resource);
273                         request.setUriPath(resource);
274
275                         ChannelHandlerContext deviceCtx = sessionManager
276                                 .querySession(did);
277                         if (deviceCtx != null) {
278                             deviceCtx.attr(keyDevice).set(ctx);
279                             deviceCtx.writeAndFlush(request);
280                         } else {
281                             Logger.e("deviceCtx is null");
282                             response = new CoapResponse(CoapStatus.FORBIDDEN);
283                             response.setToken(request.getToken());
284                             ctx.writeAndFlush(response);
285                         }
286                     } else {
287                         // if CI is not connected, connect and send msg
288                         CoapClient otherCI = null;
289                         synchronized (ciRelayClients) {
290                             otherCI = ciRelayClients.get(ciAddress);
291                             if (otherCI == null) {
292                                 otherCI = new CoapClient();
293                                 otherCI.startClient(
294                                         new InetSocketAddress(ciAddress, 5683));
295                                 ciRelayClients.put(ciAddress, otherCI);
296                             }
297                         }
298                         request.setUriPath(originUriPathList);
299                         otherCI.sendRequest(request);
300                     }
301                     return;
302             }
303
304         } else if (msg instanceof CoapResponse) {
305             if (ctx.attr(keyDevice).get() != null) {
306                 Logger.i("ctx.channel : "
307                         + ctx.attr(keyDevice).get().channel().toString());
308                 ctx.attr(keyDevice).get().writeAndFlush(msg);
309                 return;
310             }
311         }
312
313         super.channelRead(ctx, msg);
314     }
315
316     @Override
317     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
318
319         cause.printStackTrace();
320         ctx.close();
321     }
322 }