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