Fix for issue of server updates its model on both PUT and POST requests even
[platform/upstream/iotivity.git] / service / simulator / src / server / simulator_collection_resource_impl.cpp
1 /******************************************************************
2  *
3  * Copyright 2015 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20
21 #include "simulator_collection_resource_impl.h"
22 #include "simulator_utils.h"
23 #include "simulator_logger.h"
24 #include "logger.h"
25
26 #define TAG "SIM_COLLECTION_RESOURCE"
27
28 SimulatorCollectionResourceImpl::SimulatorCollectionResourceImpl()
29     :   m_type(SimulatorResource::Type::COLLECTION_RESOURCE),
30         m_interfaces {OC::DEFAULT_INTERFACE, OC::LINK_INTERFACE},
31         m_resourceHandle(NULL)
32 {
33     m_property = static_cast<OCResourceProperty>(OC_DISCOVERABLE | OC_OBSERVABLE);
34 }
35
36 std::string SimulatorCollectionResourceImpl::getName() const
37 {
38     return m_name;
39 }
40
41 SimulatorResource::Type SimulatorCollectionResourceImpl::getType() const
42 {
43     return m_type;
44 }
45
46 std::string SimulatorCollectionResourceImpl::getURI() const
47 {
48     return m_uri;
49 }
50
51 std::string SimulatorCollectionResourceImpl::getResourceType() const
52 {
53     return m_resourceType;
54 }
55
56 std::vector<std::string> SimulatorCollectionResourceImpl::getInterface() const
57 {
58     return m_interfaces;
59 }
60
61 void SimulatorCollectionResourceImpl::setInterface(const std::vector<std::string> &interfaces)
62 {
63     m_interfaces = interfaces;
64 }
65
66 void SimulatorCollectionResourceImpl::setName(const std::string &name)
67 {
68     VALIDATE_INPUT(name.empty(), "Name is empty!")
69
70     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
71     if (m_resourceHandle)
72     {
73         throw SimulatorException(SIMULATOR_OPERATION_NOT_ALLOWED,
74                                  "Name can not be set when collection is started!");
75     }
76
77     m_name = name;
78 }
79
80 void SimulatorCollectionResourceImpl::setURI(const std::string &uri)
81 {
82     VALIDATE_INPUT(uri.empty(), "Uri is empty!")
83
84     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
85     if (m_resourceHandle)
86     {
87         throw SimulatorException(SIMULATOR_OPERATION_NOT_ALLOWED,
88                                  "URI can not be set when collection is started!");
89     }
90
91     m_uri = uri;
92 }
93
94 void SimulatorCollectionResourceImpl::setResourceType(const std::string &resourceType)
95 {
96     VALIDATE_INPUT(resourceType.empty(), "Resource type is empty!")
97
98     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
99     if (m_resourceHandle)
100     {
101         throw SimulatorException(SIMULATOR_OPERATION_NOT_ALLOWED,
102                                  "Resource type can not be set when collection is started!");
103     }
104
105     m_resourceType = resourceType;
106 }
107
108 void SimulatorCollectionResourceImpl::addInterface(std::string interfaceType)
109 {
110     VALIDATE_INPUT(interfaceType.empty(), "Interface type is empty!")
111
112     if (interfaceType == OC::GROUP_INTERFACE)
113     {
114         throw NoSupportException("Collection resource does not support this interface type!");
115     }
116
117     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
118     if (m_resourceHandle)
119     {
120         throw SimulatorException(SIMULATOR_OPERATION_NOT_ALLOWED,
121                                  "Interface type can not be set when resource is started!");
122     }
123
124     auto found = std::find(m_interfaces.begin(), m_interfaces.end(), interfaceType);
125     if (found != m_interfaces.end())
126         m_interfaces.push_back(interfaceType);
127 }
128
129 void SimulatorCollectionResourceImpl::setObservable(bool state)
130 {
131     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
132     if (m_resourceHandle)
133     {
134         throw SimulatorException(SIMULATOR_OPERATION_NOT_ALLOWED,
135                                  "Observation state can not be changed when resource is started!");
136     }
137
138     if (true == state)
139         m_property = static_cast<OCResourceProperty>(m_property | OC_OBSERVABLE);
140     else
141         m_property = static_cast<OCResourceProperty>(m_property ^ OC_OBSERVABLE);
142 }
143
144 void SimulatorCollectionResourceImpl::setObserverCallback(ObserverCallback callback)
145 {
146     VALIDATE_CALLBACK(callback)
147     m_observeCallback = callback;
148 }
149
150 void SimulatorCollectionResourceImpl::setModelChangeCallback(ResourceModelChangedCallback callback)
151 {
152     VALIDATE_CALLBACK(callback)
153     m_modelCallback = callback;
154 }
155
156 bool SimulatorCollectionResourceImpl::isObservable()
157 {
158     return (m_property & OC_OBSERVABLE);
159 }
160
161 bool SimulatorCollectionResourceImpl::isStarted()
162 {
163     return (nullptr != m_resourceHandle);
164 }
165
166 void SimulatorCollectionResourceImpl::start()
167 {
168     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
169     if (m_resourceHandle)
170     {
171         throw SimulatorException(SIMULATOR_ERROR, "Collection already registered!");
172     }
173
174     if (m_uri.empty() || m_resourceType.empty())
175     {
176         throw SimulatorException(SIMULATOR_ERROR, "Found incomplete data to start resource!");
177     }
178
179     typedef OCStackResult (*RegisterResource)(OCResourceHandle &, std::string &, const std::string &,
180             const std::string &, OC::EntityHandler, uint8_t);
181
182     invokeocplatform(static_cast<RegisterResource>(OC::OCPlatform::registerResource),
183                      m_resourceHandle, m_uri, m_resourceType, m_interfaces[0],
184                      std::bind(&SimulatorCollectionResourceImpl::handleRequests,
185                                this, std::placeholders::_1), m_property);
186
187     for (size_t index = 1; m_interfaces.size() > 1 && index < m_interfaces.size(); index++)
188     {
189         typedef OCStackResult (*bindInterfaceToResource)(const OCResourceHandle &,
190                 const std::string &);
191
192         try
193         {
194             invokeocplatform(static_cast<bindInterfaceToResource>(
195                                  OC::OCPlatform::bindInterfaceToResource), m_resourceHandle,
196                              m_interfaces[index]);
197         }
198         catch (SimulatorException &e)
199         {
200             stop();
201             throw;
202         }
203     }
204 }
205
206 void SimulatorCollectionResourceImpl::stop()
207 {
208     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
209     if (!m_resourceHandle)
210         return;
211
212     typedef OCStackResult (*UnregisterResource)(const OCResourceHandle &);
213
214     invokeocplatform(static_cast<UnregisterResource>(OC::OCPlatform::unregisterResource),
215                      m_resourceHandle);
216
217     m_resourceHandle = nullptr;
218 }
219
220 SimulatorResourceModel SimulatorCollectionResourceImpl::getResourceModel()
221 {
222     std::lock_guard<std::mutex> lock(m_modelLock);
223     return m_resModel;
224 }
225
226 void SimulatorCollectionResourceImpl::setResourceModel(const SimulatorResourceModel &resModel)
227 {
228     std::lock_guard<std::mutex> lock(m_modelLock);
229     m_resModel = resModel;
230 }
231
232 void SimulatorCollectionResourceImpl::setActionType(std::map<RAML::ActionType, RAML::ActionPtr> &actionType)
233 {
234     m_actionTypes = actionType;
235 }
236
237 std::vector<ObserverInfo> SimulatorCollectionResourceImpl::getObserversList()
238 {
239     return m_observersList;
240 }
241
242 void SimulatorCollectionResourceImpl::notify(int id)
243 {
244     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
245     if (!m_resourceHandle)
246         return;
247
248     OC::ObservationIds observers {static_cast<OCObservationId>(id)};
249     sendNotification(observers);
250 }
251
252 void SimulatorCollectionResourceImpl::notifyAll()
253 {
254     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
255     if (!m_resourceHandle)
256         return;
257
258     if (!m_observersList.size())
259         return;
260
261     OC::ObservationIds observers;
262     for (auto &observer : m_observersList)
263         observers.push_back(observer.id);
264     sendNotification(observers);
265 }
266
267 std::vector<std::string> SimulatorCollectionResourceImpl::getSupportedResources()
268 {
269     return m_supportedTypes;
270 }
271
272 void SimulatorCollectionResourceImpl::addChildResource(SimulatorResourceSP &resource)
273 {
274     VALIDATE_INPUT(!resource, "Invalid child resource!")
275
276     std::lock_guard<std::mutex> lock(m_childResourcesLock);
277     if (m_childResources.end() != m_childResources.find(resource->getURI()))
278     {
279         throw SimulatorException(SIMULATOR_ERROR, "Child resource with same URI is already exisit!");
280     }
281
282     m_childResources[resource->getURI()] = resource;
283     addLink(resource);
284
285     // Notify application and observers
286     if (m_modelCallback)
287         m_modelCallback(m_uri, m_resModel);
288     notifyAll();
289 }
290
291 void SimulatorCollectionResourceImpl::removeChildResource(SimulatorResourceSP &resource)
292 {
293     VALIDATE_INPUT(!resource, "Invalid child resource!")
294
295     std::lock_guard<std::mutex> lock(m_childResourcesLock);
296     if (m_childResources.end() == m_childResources.find(resource->getURI()))
297     {
298         throw SimulatorException(SIMULATOR_ERROR, "Child resource not found in collection!");
299     }
300
301     removeLink(resource->getURI());
302     m_childResources.erase(m_childResources.find(resource->getURI()));
303
304     // Notify application and observers
305     if (m_modelCallback)
306         m_modelCallback(m_uri, m_resModel);
307     notifyAll();
308 }
309
310 void SimulatorCollectionResourceImpl::removeChildResource(const std::string &uri)
311 {
312     VALIDATE_INPUT(uri.empty(), "Uri is empty!")
313
314     std::lock_guard<std::mutex> lock(m_childResourcesLock);
315     if (m_childResources.end() == m_childResources.find(uri))
316     {
317         throw SimulatorException(SIMULATOR_ERROR, "Child resource not found in collection!");
318     }
319
320     removeLink(uri);
321     m_childResources.erase(m_childResources.find(uri));
322
323     // Notify application and observers
324     if (m_modelCallback)
325         m_modelCallback(m_uri, m_resModel);
326     notifyAll();
327 }
328
329 std::vector<SimulatorResourceSP> SimulatorCollectionResourceImpl::getChildResources()
330 {
331     std::lock_guard<std::mutex> lock(m_childResourcesLock);
332
333     std::vector<SimulatorResourceSP> result;
334     for (auto &entry : m_childResources)
335         result.push_back(entry.second);
336
337     return result;
338 }
339
340 OCEntityHandlerResult SimulatorCollectionResourceImpl::handleRequests(
341     std::shared_ptr<OC::OCResourceRequest> request)
342 {
343     if (!request)
344         return OC_EH_ERROR;
345
346     if (OC::RequestHandlerFlag::RequestFlag & request->getRequestHandlerFlag())
347     {
348         {
349             OC::OCRepresentation rep = request->getResourceRepresentation();
350             std::string payload = getPayloadString(rep);
351             SIM_LOG(ILogger::INFO, "[" << m_name << "] " << request->getRequestType()
352                     << " request received. \n**Payload details**\n" << payload)
353         }
354
355         // Handover the request to appropriate interface handler
356         std::string interfaceType(OC::DEFAULT_INTERFACE);
357         OC::QueryParamsMap queryParams = request->getQueryParameters();
358         if (queryParams.end() != queryParams.find("if"))
359             interfaceType = queryParams["if"];
360
361         std::shared_ptr<OC::OCResourceResponse> response;
362         if (interfaceType == OC::DEFAULT_INTERFACE)
363         {
364             response = requestOnBaseLineInterface(request);
365         }
366         else if (interfaceType == OC::LINK_INTERFACE)
367         {
368             response = requestOnLinkListInterface(request);
369         }
370         else if (interfaceType == OC::BATCH_INTERFACE)
371         {
372             response = requestOnBatchInterface(request);
373         }
374
375         // Send response if the request handled by resource
376         if (response)
377         {
378             if (OC_STACK_OK != OC::OCPlatform::sendResponse(response))
379                 return OC_EH_ERROR;
380         }
381         else
382         {
383             SIM_LOG(ILogger::ERROR, "[" << m_name << "] " << "Unsupported request received!")
384             return OC_EH_ERROR;
385         }
386     }
387
388     if (OC::RequestHandlerFlag::ObserverFlag & request->getRequestHandlerFlag())
389     {
390         if (!isObservable())
391         {
392             SIM_LOG(ILogger::INFO, "[" << m_uri << "] OBSERVE request received")
393             SIM_LOG(ILogger::INFO, "[" << m_uri << "] Sending error as resource is in unobservable state!")
394             return OC_EH_ERROR;
395         }
396
397         OC::ObservationInfo observationInfo = request->getObservationInfo();
398         if (OC::ObserveAction::ObserveRegister == observationInfo.action)
399         {
400             SIM_LOG(ILogger::INFO, "[" << m_uri << "] OBSERVE REGISTER request received");
401
402             ObserverInfo info {observationInfo.obsId, observationInfo.address, observationInfo.port};
403             m_observersList.push_back(info);
404
405             if (m_observeCallback)
406                 m_observeCallback(m_uri, ObservationStatus::REGISTER, info);
407         }
408         else if (OC::ObserveAction::ObserveUnregister == observationInfo.action)
409         {
410             SIM_LOG(ILogger::INFO, "[" << m_uri << "] OBSERVE UNREGISTER request received");
411
412             ObserverInfo info;
413             for (auto iter = m_observersList.begin(); iter != m_observersList.end(); iter++)
414             {
415                 if ((info = *iter), info.id == observationInfo.obsId)
416                 {
417                     m_observersList.erase(iter);
418                     break;
419                 }
420             }
421
422             if (m_observeCallback)
423                 m_observeCallback(m_uri, ObservationStatus::UNREGISTER, info);
424         }
425     }
426
427     return OC_EH_OK;
428 }
429
430 std::shared_ptr<OC::OCResourceResponse> SimulatorCollectionResourceImpl::requestOnBaseLineInterface(
431     std::shared_ptr<OC::OCResourceRequest> request)
432 {
433     std::shared_ptr<OC::OCResourceResponse> response;
434
435     RAML::ActionType type = getActionType(request->getRequestType());
436
437     if (!m_actionTypes.empty())
438     {
439         if (m_actionTypes.end() == m_actionTypes.find(type))
440             return response;
441     }
442
443     if ("GET" == request->getRequestType())
444     {
445         // Construct the representation
446         OC::OCRepresentation ocRep = m_resModel.getOCRepresentation();
447         response = std::make_shared<OC::OCResourceResponse>();
448         response->setErrorCode(200);
449         response->setResponseResult(OC_EH_OK);
450         response->setResourceRepresentation(ocRep);
451         std::string resPayload = getPayloadString(ocRep);
452         SIM_LOG(ILogger::INFO, "[" << m_uri <<
453                 "] Sending response for GET request. \n**Payload details**" << resPayload)
454     }
455
456     // TODO: Handle PUT, POST and DELETE requests
457
458     if (response)
459     {
460         response->setRequestHandle(request->getRequestHandle());
461         response->setResourceHandle(request->getResourceHandle());
462     }
463
464     return response;
465 }
466
467 std::shared_ptr<OC::OCResourceResponse> SimulatorCollectionResourceImpl::requestOnLinkListInterface(
468     std::shared_ptr<OC::OCResourceRequest> request)
469 {
470     std::lock_guard<std::mutex> lock(m_childResourcesLock);
471     std::shared_ptr<OC::OCResourceResponse> response;
472
473     RAML::ActionType type = getActionType(request->getRequestType());
474
475     if (!m_actionTypes.empty())
476     {
477         if (m_actionTypes.end() == m_actionTypes.find(type))
478             return response;
479     }
480
481     if ("GET" == request->getRequestType())
482     {
483         // Construct the representation
484         OC::OCRepresentation ocRep;
485         std::vector<OC::OCRepresentation> links;
486         for (auto &entry : m_childResources)
487         {
488             OC::OCRepresentation oicLink;
489             oicLink.setValue("href", entry.second->getURI());
490             oicLink.setValue("rt", entry.second->getResourceType());
491             oicLink.setValue("if", entry.second->getInterface()[0]);
492             links.push_back(oicLink);
493         }
494
495         ocRep.setValue("links", links);
496
497         response = std::make_shared<OC::OCResourceResponse>();
498         response->setRequestHandle(request->getRequestHandle());
499         response->setResourceHandle(request->getResourceHandle());
500         response->setErrorCode(200);
501         response->setResponseResult(OC_EH_OK);
502         response->setResourceRepresentation(ocRep);
503         std::string resPayload = getPayloadString(ocRep);
504         SIM_LOG(ILogger::INFO, "[" << m_uri <<
505                 "] Sending response for GET request. \n**Payload details**" << resPayload)
506     }
507
508     return nullptr;
509 }
510
511 std::shared_ptr<OC::OCResourceResponse> SimulatorCollectionResourceImpl::requestOnBatchInterface(
512     std::shared_ptr<OC::OCResourceRequest>)
513 {
514     // TODO: Handle this interface
515     return nullptr;
516 }
517
518 void SimulatorCollectionResourceImpl::sendNotification(OC::ObservationIds &observers)
519 {
520     std::lock_guard<std::recursive_mutex> lock(m_objectLock);
521     std::shared_ptr<OC::OCResourceResponse> response(new OC::OCResourceResponse());
522     response->setErrorCode(200);
523     response->setResponseResult(OC_EH_OK);
524
525     OC::OCRepresentation ocRep = m_resModel.getOCRepresentation();
526     response->setResourceRepresentation(ocRep, OC::DEFAULT_INTERFACE);
527
528     typedef OCStackResult (*NotifyListOfObservers)(OCResourceHandle, OC::ObservationIds &,
529             const std::shared_ptr<OC::OCResourceResponse>);
530
531     invokeocplatform(static_cast<NotifyListOfObservers>(OC::OCPlatform::notifyListOfObservers),
532                      m_resourceHandle, observers, response);
533 }
534
535 void SimulatorCollectionResourceImpl::addLink(SimulatorResourceSP &resource)
536 {
537     std::lock_guard<std::mutex> lock(m_modelLock);
538     if (!m_resModel.containsAttribute("links"))
539         return;
540
541     // Create new OIC Link
542     SimulatorResourceModel newLink;
543     newLink.add("href", resource->getURI());
544     newLink.add("rt", resource->getResourceType());
545     newLink.add("if", resource->getInterface()[0]);
546
547     // Add OIC Link if it is not present
548     bool found = false;
549     std::vector<SimulatorResourceModel> links =
550         m_resModel.get<std::vector<SimulatorResourceModel>>("links");
551     for (auto &link : links)
552     {
553         std::string linkURI = link.get<std::string>("href");
554         if (linkURI == resource->getURI())
555         {
556             break;
557             found = true;
558         }
559     }
560
561     if (false ==  found)
562     {
563         links.push_back(newLink);
564         m_resModel.updateValue("links", links);
565     }
566 }
567
568 void SimulatorCollectionResourceImpl::removeLink(std::string uri)
569 {
570     std::lock_guard<std::mutex> lock(m_modelLock);
571     if (!m_resModel.containsAttribute("links"))
572         return;
573
574     // Add OIC Link if it is not present
575     std::vector<SimulatorResourceModel> links =
576         m_resModel.get<std::vector<SimulatorResourceModel>>("links");
577     for (size_t i = 0; i < links.size(); i++)
578     {
579         std::string linkURI = links[i].get<std::string>("href");
580         if (linkURI == uri)
581         {
582             links.erase(links.begin() + i);
583             m_resModel.updateValue("links", links);
584             break;
585         }
586     }
587 }
588
589 RAML::ActionType SimulatorCollectionResourceImpl::getActionType(std::string requestType)
590 {
591     if (!requestType.compare("GET"))
592         return RAML::ActionType::GET;
593
594     if (!requestType.compare("PUT"))
595         return RAML::ActionType::PUT;
596
597     if (!requestType.compare("POST"))
598         return RAML::ActionType::POST;
599
600     if (!requestType.compare("DELETE"))
601         return RAML::ActionType::DELETE;
602
603     return RAML::ActionType::NONE;
604 }