0079716ca8f7538e30289f5b9a72fee97b684107
[platform/upstream/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 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 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             if (deviceState != null) {
201                 payloadSegment.put(Constants.DEVICE_ID, deviceId);
202                 payloadSegment.put(Constants.PRESENCE_STATE, deviceState);
203                 prsList.add(payloadSegment);
204             }
205         }
206         getPayload.put(Constants.PRESENCE_LIST, prsList);
207         Log.i("Device presence observe response : " + getPayload.toString());
208
209         return mCbor.encodingPayloadToCbor(getPayload);
210
211     }
212
213     private String getDeviceState(String deviceId) {
214
215         HashMap<String, Object> condition = new HashMap<>();
216         condition.put(Constants.DEVICE_ID, deviceId);
217
218         String state = null;
219
220         ArrayList<HashMap<String, Object>> readRecords = DBManager.getInstance()
221                 .selectRecord(Constants.PRESENCE_TABLE, condition);
222
223         if (!readRecords.isEmpty()
224                 && readRecords.get(0).get(Constants.PRESENCE_STATE) != null) {
225             state = readRecords.get(0).get(Constants.PRESENCE_STATE).toString();
226         }
227
228         return state;
229     }
230
231     private PresenceInfo getPresenceInfo(String presenceType) {
232
233         PresenceInfo presenceInfo = null;
234         switch (presenceType) {
235             case Constants.DEVICE_PRESENCE:
236                 presenceInfo = mDevicePresence;
237                 break;
238             case Constants.RESOURCE_PRESENCE:
239                 presenceInfo = mResourcePresence;
240                 break;
241             default:
242         }
243         return presenceInfo;
244     }
245
246     /**
247      * API for notifying to observers about resource presence
248      * 
249      * @param resourceInfo
250      *            resource information
251      */
252     public void notifyToObservers(
253             ArrayList<HashMap<String, Object>> resourceInfo) {
254
255         if (resourceInfo.isEmpty()) {
256             return;
257         }
258
259         Object obj = resourceInfo.get(0).get(Constants.DEVICE_ID);
260
261         if (obj == null) {
262             return;
263         }
264
265         String deviceId = obj.toString();
266
267         HashMap<String, PresenceSubscriber> tokenNSubscribers = mResourcePresence.mSubscriber
268                 .get(deviceId);
269
270         if (tokenNSubscribers != null) {
271
272             for (PresenceSubscriber subscriber : tokenNSubscribers.values()) {
273
274                 for (HashMap<String, Object> resource : resourceInfo) {
275                     subscriber.mSubscriber.sendResponse(
276                             MessageBuilder.createResponse(subscriber.mRequest,
277                                     ResponseStatus.CONTENT,
278                                     ContentFormat.APPLICATION_CBOR,
279                                     makeResponsePayload(
280                                             subscriber.mRequest.getRequestId(),
281                                             resource)));
282                 }
283             }
284         }
285     }
286
287     private byte[] makeResponsePayload(String requestId,
288             HashMap<String, Object> resource) {
289
290         ByteArrayOutputStream out = new ByteArrayOutputStream();
291         CBORFactory f = new CBORFactory();
292         try {
293             JsonGenerator gen = f.createGenerator(out, JsonEncoding.UTF8);
294             gen.writeStartObject();
295             long sequenceId = mResourcePresence.mSequenceNumber.get(requestId);
296             gen.writeNumberField(Constants.NON, sequenceId);
297             mResourcePresence.mSequenceNumber.put(requestId, sequenceId + 1);
298             gen.writeNumberField(Constants.RESOURCE_TTL, Long.parseLong(
299                     checkPayload(resource, Constants.RESOURCE_TTL).toString()));
300
301             gen.writeFieldName(Constants.TRIGGER);
302
303             ((CBORGenerator) gen).writeRaw((byte) (224
304                     + (byte) (checkPayload(resource, Constants.TRIGGER))));
305
306             gen.writeStringField(Constants.RESOURCE_TYPE,
307                     checkPayload(resource, Constants.RESOURCE_TYPE).toString());
308
309             gen.writeStringField(Constants.HREF,
310                     checkPayload(resource, Constants.HREF).toString());
311             gen.writeEndObject();
312
313             gen.close();
314         } catch (Exception e) {
315             throw new InternalServerErrorException(
316                     "notification payload cbor encoding error");
317         }
318
319         return out.toByteArray();
320     }
321
322     private Object checkPayload(HashMap<String, Object> resource, String key) {
323         Object obj = resource.get(key);
324
325         if (obj == null) {
326             throw new InternalServerErrorException(
327                     "property (" + key + ") is null");
328         }
329
330         return obj;
331     }
332
333     /**
334      * API to update device state
335      * 
336      * @param payload
337      *            payload included device state
338      */
339     public void updateDevicePresence(HashMap<String, Object> payload) {
340         DBManager.getInstance().insertAndReplaceRecord(Constants.PRESENCE_TABLE,
341                 payload);
342     }
343 }