statistical defects fixed
[iotivity.git] / cloud / resourcedirectory / src / main / java / org / iotivity / cloud / rdserver / resources / presence / PresenceManager.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.rdserver.resources.presence;
23
24 import java.io.ByteArrayOutputStream;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.List;
29
30 import org.iotivity.cloud.base.device.Device;
31 import org.iotivity.cloud.base.exception.ServerException.InternalServerErrorException;
32 import org.iotivity.cloud.base.protocols.IRequest;
33 import org.iotivity.cloud.base.protocols.MessageBuilder;
34 import org.iotivity.cloud.base.protocols.enums.ContentFormat;
35 import org.iotivity.cloud.base.protocols.enums.ResponseStatus;
36 import org.iotivity.cloud.rdserver.Constants;
37 import org.iotivity.cloud.rdserver.db.DBManager;
38 import org.iotivity.cloud.util.Cbor;
39 import org.iotivity.cloud.util.Log;
40
41 import com.fasterxml.jackson.core.JsonEncoding;
42 import com.fasterxml.jackson.core.JsonGenerator;
43 import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
44 import com.fasterxml.jackson.dataformat.cbor.CBORGenerator;
45
46 /**
47  * 
48  * This class provides a set of APIs handle requests about presence
49  *
50  */
51 public class PresenceManager {
52     private static PresenceManager        mPresenceManager = new PresenceManager();
53
54     private Cbor<HashMap<String, Object>> mCbor            = new Cbor<>();
55
56     private static class PresenceSubscriber {
57         PresenceSubscriber(Device subscriber, IRequest request) {
58             mSubscriber = subscriber;
59             mRequest = request;
60         }
61
62         public Device   mSubscriber;
63         public IRequest mRequest;
64     }
65
66     private static class PresenceInfo {
67
68         PresenceInfo() {
69             mSubscriber = new HashMap<>();
70             mSubscribedDevices = new HashMap<>();
71             mSequenceNumber = new HashMap<>();
72         }
73
74         // di , token, Subscriber list
75         private HashMap<String, HashMap<String, PresenceSubscriber>> mSubscriber;
76         // token, di list
77         private HashMap<String, List<String>>                        mSubscribedDevices;
78         private HashMap<String, Long>                                mSequenceNumber;
79     }
80
81     private PresenceInfo mDevicePresence   = null;
82     private PresenceInfo mResourcePresence = null;
83
84     private PresenceManager() {
85         mDevicePresence = new PresenceInfo();
86         mResourcePresence = new PresenceInfo();
87     }
88
89     /**
90      * API to return PresenceManager object
91      * 
92      * @return PresenceManager object
93      */
94     public static PresenceManager getInstance() {
95         return mPresenceManager;
96     }
97
98     /**
99      * API to add observer
100      * 
101      * @param srcDevice
102      *            channel information
103      * @param request
104      *            request message
105      * @param deviceIdList
106      *            subscribed device list
107      * @param presenceType
108      *            device presence or resource presence
109      */
110     public void subscribePresence(Device srcDevice, IRequest request,
111             List<String> deviceIdList, String presenceType) {
112
113         PresenceInfo presenceInfo = getPresenceInfo(presenceType);
114
115         for (String deviceId : deviceIdList) {
116             HashMap<String, PresenceSubscriber> subscribers = presenceInfo.mSubscriber
117                     .get(deviceId);
118
119             if (subscribers == null) {
120                 subscribers = new HashMap<>();
121                 presenceInfo.mSubscriber.put(deviceId, subscribers);
122             }
123
124             subscribers.put(request.getRequestId(),
125                     new PresenceSubscriber(srcDevice, request));
126         }
127
128         presenceInfo.mSubscribedDevices.put(request.getRequestId(),
129                 deviceIdList);
130         presenceInfo.mSequenceNumber.put(request.getRequestId(), (long) 1);
131     }
132
133     /**
134      * API to remove observer
135      * 
136      * @param request
137      *            request message
138      * @param deviceIdList
139      *            unsubscribed device list
140      * @param presenceType
141      *            device presence or resource presence
142      */
143     public void unsubscribePresence(IRequest request, List<String> deviceIdList,
144             String presenceType) {
145
146         PresenceInfo presenceInfo = getPresenceInfo(presenceType);
147
148         for (String deviceId : deviceIdList) {
149             HashMap<String, PresenceSubscriber> subscribers = presenceInfo.mSubscriber
150                     .get(deviceId);
151
152             if (subscribers == null) {
153                 continue;
154             }
155
156             subscribers.remove(request.getRequestId());
157         }
158     }
159
160     /**
161      * API for notifying to observers about device presence
162      * 
163      * @param deviceId
164      *            device id
165      */
166     public void notifyToObservers(String deviceId) {
167
168         HashMap<String, PresenceSubscriber> tokenNSubscribers = mDevicePresence.mSubscriber
169                 .get(deviceId);
170
171         if (tokenNSubscribers != null) {
172             byte[] payload = makeResponsePayload(Arrays.asList(deviceId));
173
174             for (PresenceSubscriber subscriber : tokenNSubscribers.values()) {
175
176                 subscriber.mSubscriber.sendResponse(
177                         MessageBuilder.createResponse(subscriber.mRequest,
178                                 ResponseStatus.CONTENT,
179                                 ContentFormat.APPLICATION_CBOR, payload));
180             }
181         }
182     }
183
184     /**
185      * API to make response payload about device presence
186      * 
187      * @param deviceList
188      *            device id list
189      * @return payload data
190      */
191     public byte[] makeResponsePayload(List<String> deviceList) {
192
193         HashMap<String, Object> getPayload = new HashMap<>();
194         ArrayList<HashMap<String, Object>> prsList = new ArrayList<>();
195
196         for (String deviceId : deviceList) {
197             HashMap<String, Object> payloadSegment = new HashMap<>();
198
199             String deviceState = getDeviceState(deviceId);
200
201             payloadSegment.put(Constants.DEVICE_ID, deviceId);
202
203             if (deviceState != null) {
204                 payloadSegment.put(Constants.PRESENCE_STATE, deviceState);
205             } else {
206                 payloadSegment.put(Constants.PRESENCE_STATE,
207                         Constants.PRESENCE_OFF);
208             }
209             prsList.add(payloadSegment);
210         }
211         getPayload.put(Constants.PRESENCE_LIST, prsList);
212         Log.i("Device presence observe response : " + getPayload.toString());
213
214         return mCbor.encodingPayloadToCbor(getPayload);
215
216     }
217
218     private String getDeviceState(String deviceId) {
219
220         HashMap<String, Object> condition = new HashMap<>();
221         condition.put(Constants.DEVICE_ID, deviceId);
222
223         String state = null;
224
225         ArrayList<HashMap<String, Object>> readRecords = DBManager.getInstance()
226                 .selectRecord(Constants.PRESENCE_TABLE, condition);
227
228         if (!readRecords.isEmpty()
229                 && readRecords.get(0).get(Constants.PRESENCE_STATE) != null) {
230             state = readRecords.get(0).get(Constants.PRESENCE_STATE).toString();
231         }
232
233         return state;
234     }
235
236     private PresenceInfo getPresenceInfo(String presenceType) {
237
238         PresenceInfo presenceInfo = null;
239         switch (presenceType) {
240             case Constants.DEVICE_PRESENCE:
241                 presenceInfo = mDevicePresence;
242                 break;
243             case Constants.RESOURCE_PRESENCE:
244                 presenceInfo = mResourcePresence;
245                 break;
246             default:
247         }
248         return presenceInfo;
249     }
250
251     /**
252      * API for notifying to observers about resource presence
253      * 
254      * @param resourceInfo
255      *            resource information
256      */
257     public void notifyToObservers(
258             ArrayList<HashMap<String, Object>> resourceInfo) {
259
260         if (resourceInfo.isEmpty()) {
261             return;
262         }
263
264         Object obj = resourceInfo.get(0).get(Constants.DEVICE_ID);
265
266         if (obj == null) {
267             return;
268         }
269
270         String deviceId = obj.toString();
271
272         HashMap<String, PresenceSubscriber> tokenNSubscribers = mResourcePresence.mSubscriber
273                 .get(deviceId);
274
275         if (tokenNSubscribers != null) {
276
277             for (PresenceSubscriber subscriber : tokenNSubscribers.values()) {
278
279                 for (HashMap<String, Object> resource : resourceInfo) {
280                     subscriber.mSubscriber.sendResponse(
281                             MessageBuilder.createResponse(subscriber.mRequest,
282                                     ResponseStatus.CONTENT,
283                                     ContentFormat.APPLICATION_CBOR,
284                                     makeResponsePayload(
285                                             subscriber.mRequest.getRequestId(),
286                                             resource)));
287                 }
288             }
289         }
290     }
291
292     private byte[] makeResponsePayload(String requestId,
293             HashMap<String, Object> resource) {
294
295         ByteArrayOutputStream out = new ByteArrayOutputStream();
296         CBORFactory f = new CBORFactory();
297         try {
298             JsonGenerator gen = f.createGenerator(out, JsonEncoding.UTF8);
299             gen.writeStartObject();
300             long sequenceId = mResourcePresence.mSequenceNumber.get(requestId);
301             gen.writeNumberField(Constants.NON, sequenceId);
302             mResourcePresence.mSequenceNumber.put(requestId, sequenceId + 1);
303             gen.writeNumberField(Constants.RESOURCE_TTL, Long.parseLong(
304                     checkPayload(resource, Constants.RESOURCE_TTL).toString()));
305
306             gen.writeFieldName(Constants.TRIGGER);
307
308             ((CBORGenerator) gen).writeRaw((byte) (224
309                     + (byte) (checkPayload(resource, Constants.TRIGGER))));
310
311             gen.writeStringField(Constants.RESOURCE_TYPE,
312                     checkPayload(resource, Constants.RESOURCE_TYPE).toString());
313
314             gen.writeStringField(Constants.HREF,
315                     checkPayload(resource, Constants.HREF).toString());
316             gen.writeEndObject();
317
318             gen.close();
319         } catch (Exception e) {
320             throw new InternalServerErrorException(
321                     "notification payload cbor encoding error");
322         }
323
324         return out.toByteArray();
325     }
326
327     private Object checkPayload(HashMap<String, Object> resource, String key) {
328         Object obj = resource.get(key);
329
330         if (obj == null) {
331             throw new InternalServerErrorException(
332                     "property (" + key + ") is null");
333         }
334
335         return obj;
336     }
337
338     /**
339      * API to update device state
340      * 
341      * @param payload
342      *            payload included device state
343      */
344     public void updateDevicePresence(HashMap<String, Object> payload) {
345         DBManager.getInstance().insertAndReplaceRecord(Constants.PRESENCE_TABLE,
346                 payload);
347     }
348 }