From 45d140a657d184f27fbd0603e3d91f7c5fb09158 Mon Sep 17 00:00:00 2001 From: Erich Keane Date: Wed, 22 Oct 2014 13:31:13 -0700 Subject: [PATCH] Implemented JSON Serialization using Cereal Library Previously we were hand-mangling strings for JSON serialization and using boost::property_tree for parsing. Now we are using an open source library (cereal) to do both sides in the C++ stack Change-Id: I2db2657552a31cdf37c2f2dbbcfdb34e48549f00 Signed-off-by: Erich Keane --- .gitignore | 3 + resource/examples/fridgeserver.cpp | 4 +- resource/examples/garageserver.cpp | 2 +- resource/examples/makefile | 4 + resource/examples/ocicuc/Makefile | 3 +- resource/examples/ocicuc/client.cpp | 5 +- resource/examples/ocicuc/demo_client.hpp | 24 +- resource/examples/ocicuc/server.cpp | 2 +- resource/examples/ocicuc/utility.hpp | 21 +- resource/examples/roomserver.cpp | 12 +- resource/examples/simpleclient.cpp | 2 + resource/include/AttributeValue.h | 83 +++ resource/include/IClientWrapper.h | 11 +- resource/include/IServerWrapper.h | 4 +- resource/include/InProcClientWrapper.h | 52 +- resource/include/InProcServerWrapper.h | 2 +- resource/include/OCApi.h | 539 +------------------ resource/include/OCRepresentation.h | 325 ++++++++++- resource/include/OCResource.h | 6 +- resource/include/OCResourceRequest.h | 67 +-- resource/include/OCResourceResponse.h | 253 ++------- resource/include/OCSerialization.h | 275 ++++++++++ resource/include/OicJsonSerializer.hpp | 862 ++++++++++++++++++++++++++++++ resource/include/OutOfProcClientWrapper.h | 9 +- resource/include/OutOfProcServerWrapper.h | 5 +- resource/include/ResourceInitException.h | 37 +- resource/include/StringConstants.h | 7 +- resource/include/WrapperFactory.h | 14 +- resource/makefile | 32 +- resource/patches/cereal_gcc46.patch | 485 +++++++++++++++++ resource/src/InProcClientWrapper.cpp | 338 +++--------- resource/src/InProcServerWrapper.cpp | 5 +- resource/src/OCPlatform_impl.cpp | 10 +- resource/src/OCRepresentation.cpp | 486 +++++++++++++++++ resource/src/OCResource.cpp | 2 +- resource/unittests/makefile | 8 + 36 files changed, 2836 insertions(+), 1163 deletions(-) create mode 100644 resource/include/AttributeValue.h create mode 100644 resource/include/OCSerialization.h create mode 100644 resource/include/OicJsonSerializer.hpp create mode 100644 resource/patches/cereal_gcc46.patch create mode 100644 resource/src/OCRepresentation.cpp diff --git a/.gitignore b/.gitignore index 7583ff5..c8d1cf5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ csdk/stack/samples/linux/SimpleClientServer/debug/ *.settings/ *.cproject *.project + +# Ignore dependencies folder, which should be generated +/dependencies diff --git a/resource/examples/fridgeserver.cpp b/resource/examples/fridgeserver.cpp index 3700e30..f27f8a5 100644 --- a/resource/examples/fridgeserver.cpp +++ b/resource/examples/fridgeserver.cpp @@ -18,7 +18,7 @@ // //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -/// The purpose of this server is to simulate a refridgerator that contains a device resource for +/// The purpose of this server is to simulate a refrigerator that contains a device resource for /// its description, a light resource for the internal light, and 2 door resources for the purpose /// of representing the doors attached to this fridge. This is used by the fridgeclient to /// demonstrate using std::bind to attach to instances of a class as well as using @@ -88,7 +88,7 @@ class DeviceResource : public Resource private: OCRepresentation get() { - m_rep.setValue("device_name", std::string("Intel Powered 2 door, 1 light refridgerator")); + m_rep.setValue("device_name", std::string("Intel Powered 2 door, 1 light refrigerator")); return m_rep; } diff --git a/resource/examples/garageserver.cpp b/resource/examples/garageserver.cpp index e033504..069b362 100644 --- a/resource/examples/garageserver.cpp +++ b/resource/examples/garageserver.cpp @@ -104,7 +104,7 @@ public: // setting json string std::string json = "{\"num\":10,\"rno\":23.5,\"aoa\":[[1,2],[3]],\"str\":\"john\",\ \"object\":{\"bl1\":false,\"ar\":[2,3]}, \"objects\":[{\"bl2\":true,\"nl\":null},{\"ar1\":[1,2]}]}"; - m_garageRep.setValue("json", escapeString(json)); + m_garageRep.setValue("json", json); } /* Note that this does not need to be a member function: for classes you do not have diff --git a/resource/examples/makefile b/resource/examples/makefile index c97f2aa..dcae88e 100644 --- a/resource/examples/makefile +++ b/resource/examples/makefile @@ -26,6 +26,9 @@ CXX := g++ #CXX := clang OUT_DIR := $(BUILD) +DEPEND_DIR:= ../dependencies +CEREAL_DIR:= $(DEPEND_DIR)/cereal + CXX_FLAGS.debug := -O0 -g3 -std=c++0x -Wall -pthread CXX_FLAGS.release := -O3 -std=c++0x -Wall -pthread @@ -37,6 +40,7 @@ CXX_INC += -I../csdk/ocsocket/include CXX_INC += -I../csdk/ocrandom/include CXX_INC += -I../csdk/logger/include CXX_INC += -I../csdk/libcoap +CXX_INC += -I$(CEREAL_DIR)/include LIB_OC_LOGGER := ../oc_logger/lib/oc_logger.a diff --git a/resource/examples/ocicuc/Makefile b/resource/examples/ocicuc/Makefile index 0acf542..9b69e61 100644 --- a/resource/examples/ocicuc/Makefile +++ b/resource/examples/ocicuc/Makefile @@ -41,7 +41,8 @@ CXX_INC=-I$(OCLIB)/include \ -I../../csdk/stack/include \ -I../../csdk/ocsocket/include \ -I../../csdk/ocrandom/include \ - -I../../csdk/logger/include + -I../../csdk/logger/include \ + -I../../dependencies/cereal/include BOOST_LIBS=-lboost_program_options #BOOST_LIBS=-L/usr/local/boost/lib/ -lboost_program_options # for boost libraries at the specified path diff --git a/resource/examples/ocicuc/client.cpp b/resource/examples/ocicuc/client.cpp index d2edcfc..c3771bb 100644 --- a/resource/examples/ocicuc/client.cpp +++ b/resource/examples/ocicuc/client.cpp @@ -18,6 +18,10 @@ // //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// This contains the Boost MPL defines required for the boost_variant +// serialization, so it must go before the boost/program_options +#include "OCApi.h" + #include #include #include @@ -28,7 +32,6 @@ #include -#include "OCApi.h" #include "OCResource.h" #include "OCPlatform.h" diff --git a/resource/examples/ocicuc/demo_client.hpp b/resource/examples/ocicuc/demo_client.hpp index 47830b0..d5ce578 100644 --- a/resource/examples/ocicuc/demo_client.hpp +++ b/resource/examples/ocicuc/demo_client.hpp @@ -143,20 +143,23 @@ void resource_handle::onResourceGet(const OC::HeaderOptions& headerOptions, return; } - std::cout << "input attributes:\n" << rep.getAttributeMap() << '\n'; + std::cout << "input attributes:\n"; + std::cout << "Attribute \"" << "state" << "\": "<< rep.getValue("state")<<"; "; + std::cout << "Attribute \"" << "power" << "\": "<< rep.getValue("power")<<"; \n"; // Now, make a change to the light representation (replacing, rather than parsing): - OC::AttributeMap attrs { - { "state", { "true" } }, - { "power", { "10" } } - }; + bool state = true; + int power = 10; - std::cout << "output attributes:\n" << attrs << '\n'; + std::cout << "output attributes:\n"; + std::cout << "Attribute \"" << "state" << "\": "<< state<<"; "; + std::cout << "Attribute \"" << "power" << "\": "<< power<<"; \n"; call_timer.mark("put_resource"); OC::OCRepresentation out_rep; - out_rep.setAttributeMap(attrs); + out_rep.setValue("state", state); + out_rep.setValue("power", power); resource->put(out_rep, OC::QueryParamsMap(), std::bind(&resource_handle::onResourcePut, this, std::placeholders::_1, @@ -179,7 +182,9 @@ void resource_handle::onResourcePut(const OC::HeaderOptions& headerOptions, throw OC::OCException(os.str()); } - std::cout << "input attributes:\n" << rep.getAttributeMap() << '\n'; + std::cout << "input attributes:\n"; + std::cout << "Attribute \"" << "state" << "\": "<< rep.getValue("state")<<"; "; + std::cout << "Attribute \"" << "power" << "\": "<< rep.getValue("power")<<"; \n"; call_timer.mark("observe_resource"); @@ -204,7 +209,8 @@ void resource_handle::onObserve(const OC::HeaderOptions& headerOptions, call_timer.report_and_reset("observe_resource"); - std::cout << rep.getAttributeMap() << '\n'; + std::cout << "Attribute \"" << "state" << "\": "<< rep.getValue("state")<<"; "; + std::cout << "Attribute \"" << "power" << "\": "<< rep.getValue("power")<<"; \n"; const auto oc = observe_count(); diff --git a/resource/examples/ocicuc/server.cpp b/resource/examples/ocicuc/server.cpp index 47b909c..65fca0e 100644 --- a/resource/examples/ocicuc/server.cpp +++ b/resource/examples/ocicuc/server.cpp @@ -18,9 +18,9 @@ // //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -#include "exec.hpp" #include "utility.hpp" +#include "exec.hpp" #include "light_resource.hpp" namespace Intel { namespace OCDemo { diff --git a/resource/examples/ocicuc/utility.hpp b/resource/examples/ocicuc/utility.hpp index 6b92fd9..bc734d7 100644 --- a/resource/examples/ocicuc/utility.hpp +++ b/resource/examples/ocicuc/utility.hpp @@ -30,22 +30,13 @@ namespace Intel { namespace OCDemo { -// Prettyprinter for AttributeMaps: -inline std::ostream& operator<<(std::ostream& os, const OC::AttributeMap& attrs) -{ - for(const auto& attr : attrs) - os << "Attribute \"" << attr.first << "\": " << attr.second << "; "; - - return os; -} - /* A static observation counter: */ int observe_count(); /* Helpers for measuring request times: */ -typedef std::pair< - std::chrono::time_point, - std::chrono::time_point +typedef std::pair< + std::chrono::time_point, + std::chrono::time_point > clock_interval; struct call_times @@ -64,7 +55,7 @@ struct call_times call_times(const bool& display_reports_) : display_reports(display_reports_) {} - + public: void reset(const std::string& entry); void mark(const std::string& name); @@ -73,8 +64,8 @@ struct call_times void report_and_reset(const std::string& name); }; -extern call_times call_timer; - +extern call_times call_timer; + }} // namespace Intel::OCDemo #endif diff --git a/resource/examples/roomserver.cpp b/resource/examples/roomserver.cpp index f9d3978..029ab3f 100644 --- a/resource/examples/roomserver.cpp +++ b/resource/examples/roomserver.cpp @@ -221,16 +221,10 @@ public: OCRepresentation getRoomRepresentation(void) { - std::vector children; - - OCRepresentation light = getLightRepresentation(); - children.push_back(light); - - OCRepresentation fan = getFanRepresentation(); - children.push_back(fan); - - m_roomRep.setChildren(children); + m_roomRep.clearChildren(); + m_roomRep.addChild(getLightRepresentation()); + m_roomRep.addChild(getFanRepresentation()); return m_roomRep; } diff --git a/resource/examples/simpleclient.cpp b/resource/examples/simpleclient.cpp index 400591a..c2d4a48 100644 --- a/resource/examples/simpleclient.cpp +++ b/resource/examples/simpleclient.cpp @@ -365,6 +365,8 @@ int main(int argc, char* argv[]) { OCPlatform::Configure(cfg); try { + // makes it so that all boolean values are printed as 'true/false' in this stream + std::cout.setf(std::ios::boolalpha); // Find all resources OCPlatform::findResource("", "coap://224.0.1.187/oc/core?rt=core.light", &foundResource); std::cout<< "Finding Resource... " < +#include + +namespace OC +{ + class OCRepresentation; + + struct NullType{}; + // Since null needs to be encoded in a special fashion in JSON, the encoder + // needs to know the index of the NullType Sentinel Any time the code does a special + // case for the NullType, we use the AttributeValueNullIndex. This MUST be kept up to date + // with the variant's which() for NullType. + static const int AttributeValueNullIndex = 0; + typedef boost::variant< + + // Base values: + NullType, // Note: this handles the null-type and must match the above static const + int, + double, + bool, + std::string, + OC::OCRepresentation, + + // Sequences: + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + + // Nested sequences: + std::vector>, + std::vector>>, + + std::vector>, + std::vector>>, + + std::vector>, + std::vector>>, + + std::vector>, + std::vector>>, + + std::vector>, + std::vector>> + > AttributeValue; + +} +#endif // __ATTRIBUTEVALUE_H diff --git a/resource/include/IClientWrapper.h b/resource/include/IClientWrapper.h index a03ae76..8ec23a5 100644 --- a/resource/include/IClientWrapper.h +++ b/resource/include/IClientWrapper.h @@ -33,13 +33,11 @@ namespace OC class IClientWrapper : public std::enable_shared_from_this { protected: - OCPlatform_impl& m_owner; public: typedef std::shared_ptr Ptr; - IClientWrapper(OCPlatform_impl& owner) - : m_owner(owner) + IClientWrapper() {} virtual OCStackResult ListenForResource(const std::string& serviceUrl, @@ -81,13 +79,6 @@ namespace OC virtual OCStackResult GetDefaultQos(QualityOfService& qos) = 0; virtual ~IClientWrapper(){} - - - // Note: this should never be called by anyone but the handler for the listen command. - // It is public becuase that needs to be a non-instance callback - virtual std::shared_ptr parseOCResource(IClientWrapper::Ptr clientWrapper, - OCDevAddr& addr, const boost::property_tree::ptree resourceNode)=0; - private: }; } diff --git a/resource/include/IServerWrapper.h b/resource/include/IServerWrapper.h index 1796b02..a6a2d10 100644 --- a/resource/include/IServerWrapper.h +++ b/resource/include/IServerWrapper.h @@ -34,13 +34,11 @@ namespace OC class IServerWrapper { protected: - OCPlatform_impl& m_owner; public: typedef std::shared_ptr Ptr; - IServerWrapper(OCPlatform_impl& owner) - : m_owner(owner) + IServerWrapper() {} virtual ~IServerWrapper(){}; diff --git a/resource/include/InProcClientWrapper.h b/resource/include/InProcClientWrapper.h index 5fecf6d..39e1df4 100644 --- a/resource/include/InProcClientWrapper.h +++ b/resource/include/InProcClientWrapper.h @@ -26,9 +26,6 @@ #include #include -#include -#include - #include #include #include @@ -37,17 +34,46 @@ namespace OC { - class InProcClientWrapper : public IClientWrapper + namespace ClientCallbackContext { + struct GetContext + { + GetCallback callback; + }; - public: - enum OCSecureType + struct SetContext + { + PutCallback callback; + }; + + struct ListenContext + { + FindCallback callback; + IClientWrapper::Ptr clientWrapper; + }; + + struct SubscribePresenceContext { - IPV4Secure, - IPV4 + SubscribeCallback callback; }; - InProcClientWrapper(OC::OCPlatform_impl& owner, std::weak_ptr csdkLock, + struct DeleteContext + { + DeleteCallback callback; + }; + + struct ObserveContext + { + ObserveCallback callback; + }; + } + + class InProcClientWrapper : public IClientWrapper + { + + public: + + InProcClientWrapper(std::weak_ptr csdkLock, PlatformConfig cfg); virtual ~InProcClientWrapper(); @@ -84,15 +110,8 @@ namespace OC const std::string& resourceType, SubscribeCallback& presenceHandler); virtual OCStackResult UnsubscribePresence(OCDoHandle handle); - // Note: this should never be called by anyone but the handler for the listen command. - // It is public becuase that needs to be a non-instance callback - virtual std::shared_ptr parseOCResource(IClientWrapper::Ptr clientWrapper, - OCDevAddr& addr, const boost::property_tree::ptree resourceNode); - OCStackResult GetDefaultQos(QualityOfService& QoS); private: - std::string convertOCAddrToString(OCDevAddr& addr, - OCSecureType type, const std::string &portStr = std::string()); void listeningFunc(); std::string assembleSetResourceUri(std::string uri, const QueryParamsMap& queryParams); std::string assembleSetResourcePayload(const OCRepresentation& attributes); @@ -103,7 +122,6 @@ namespace OC std::weak_ptr m_csdkLock; private: - OC::OCPlatform_impl& m_owner; PlatformConfig m_cfg; }; } diff --git a/resource/include/InProcServerWrapper.h b/resource/include/InProcServerWrapper.h index c74f838..dfed790 100644 --- a/resource/include/InProcServerWrapper.h +++ b/resource/include/InProcServerWrapper.h @@ -32,7 +32,7 @@ namespace OC class InProcServerWrapper : public IServerWrapper { public: - InProcServerWrapper(OC::OCPlatform_impl& owner, std::weak_ptr csdkLock, + InProcServerWrapper(std::weak_ptr csdkLock, PlatformConfig cfg); virtual ~InProcServerWrapper(); diff --git a/resource/include/OCApi.h b/resource/include/OCApi.h index ec49526..94f997f 100644 --- a/resource/include/OCApi.h +++ b/resource/include/OCApi.h @@ -28,16 +28,14 @@ #include #include -#include -#include -#include - #include "ocstack.h" #include "OCHeaderOption.h" #include #include "StringConstants.h" #include "oc_logger.hpp" +#include + namespace OC { class OCResource; @@ -160,538 +158,7 @@ namespace OC Observe, ObserveAll }; - - // Helper function to escape character in a string. - std::string escapeString(const std::string& value); - - typedef std::map AttributeMap; - - class OCRepresentation - { - private: - std::string m_uri; - AttributeMap m_attributeMap; - std::vector m_resourceTypes; - std::vector m_resourceInterfaces; - int errorCode; - - std::vector m_children; - - public: - OCRepresentation() {} - - bool erase(const std::string& str) - { - return m_attributeMap.erase(str) != 0; - } - - std::string getUri(void) const - { - return m_uri; - } - - template - void setValue(const std::string& str, const T& val); - - template - bool getValue(const std::string& str, T& val) const; - - template - T getValue(const std::string& str) const; - - bool hasAttribute(const std::string& str) const - { - return m_attributeMap.find(str) != m_attributeMap.end(); - } - - void setNULL(const std::string& str) - { - m_attributeMap[str] = "null"; - } - - bool isNULL(const std::string& str) const - { - auto x = m_attributeMap.find(str); - - if(m_attributeMap.end() != x) - { - return x->second.compare("null") == 0; - } - else - { - std::ostringstream message; - message << "attribute: " << str << " doesn't exist\n"; - throw OCException(message.str()); - } - - return false; - } - - int numberOfAttributes() const - { - return m_attributeMap.size(); - } - - void setUri(std::string uri) - { - m_uri = uri; - } - - std::vector getChildren(void) const - { - return m_children; - } - - void setChildren(const std::vector& children) - { - m_children = children; - } - - std::weak_ptr getResource() const - { - // TODO Needs to be implemented - std::weak_ptr wp; - return wp; - } - - AttributeMap getAttributeMap() const - { - return m_attributeMap; - } - - void setAttributeMap(const AttributeMap& map) - { - m_attributeMap = map; - } - - std::string getJSONRepresentation(void) const - { - std::ostringstream json; - - json << "{"; - - for(auto itr = m_attributeMap.begin(); itr!= m_attributeMap.end(); ++ itr) - { - if(itr != m_attributeMap.begin()) - { - json << ','; - } - json << "\""<first<<"\":"<< itr->second; - } - json << "}"; - - return json.str(); - } - - std::vector getResourceTypes() const - { - return m_resourceTypes; - } - - void setResourceTypes(const std::vector& resourceTypes) - { - m_resourceTypes = resourceTypes; - } - - std::vector getResourceInterfaces(void) const - { - return m_resourceInterfaces; - } - - void setResourceInterfaces(const std::vector& resourceInterfaces) - { - m_resourceInterfaces = resourceInterfaces; - } - }; - - OCRepresentation parseJSONToRepresentation(const std::string& str); - - inline OCRepresentation parseJSONToRepresentation(const std::string& str) - { - OCRepresentation rep; - - AttributeMap attributeMap; - - std::stringstream requestStream; - requestStream << str; - boost::property_tree::ptree payload; - try - { - boost::property_tree::read_json(requestStream, payload); - } - catch(boost::property_tree::json_parser::json_parser_error &e) - { - throw OCException(OC::Exception::GENERAL_JSON_PARSE_FAILED); - } - - for(auto& item: payload) - { - std::string name = item.first.data(); - std::string value = item.second.data(); - - attributeMap[name] = value; - } - - rep.setAttributeMap(attributeMap); - - return rep; - } - - typedef boost::variant< - int, - double, - bool, - OCRepresentation, - std::string, - std::vector, - std::vector, - std::vector, - std::vector, - std::vector - > AttributeValue; - - template - inline std::string getJSONFromVector(const std::vector& v) - { - std::ostringstream json; - - json << "\"["; - if(v.size() != 0) - { - std::copy(v.begin(), v.end() - 1, std::ostream_iterator(json, ",")); - json << v.back(); - } - json << "]\""; - - return json.str(); - } - - class ComposeVisitor : public boost::static_visitor - { - public: - - // TODO different int sizes - std::string operator() (const int i) const - { - return std::to_string(i); - } - - std::string operator() (const double d) const - { - return std::to_string(d); - } - - std::string operator() (const std::string& str) const - { - std::ostringstream json; - json << "\""; - json << str; - json << "\""; - - return json.str(); - } - - std::string operator() (const bool b) const - { - if(b) - { - return "true"; - } - else - { - return "false"; - } - } - - std::string operator() (const std::vector& numbers) const - { - return getJSONFromVector(numbers); - } - - std::string operator() (const std::vector& numbers) const - { - return getJSONFromVector(numbers); - } - - std::string operator() (const std::vector& bools) const - { - std::ostringstream json; - int first = 1; - - json << "\"["; - for(auto b: bools) - { - if(first) - { - b ? json << "true" : json << "false"; - first = 0; - } - else - { - b ? json << ",true" : json << ",false"; - } - } - json << "]\""; - - return json.str(); - } - - std::string operator() (const std::vector& strings) const - { - return getJSONFromVector(strings); - } - - std::string operator() (const OCRepresentation& rep) const - { - std::ostringstream json; - - json << "\""; - - json << escapeString(rep.getJSONRepresentation()); - - json << "\""; - - return json.str(); - } - - std::string operator() (const std::vector& reps) const - { - std::ostringstream json; - int first = 1; - - json << "\"["; - for(auto rep: reps) - { - if(first) - { - first = 0; - json << escapeString(rep.getJSONRepresentation()); - } - else - { - json << ","; - json << escapeString(rep.getJSONRepresentation()); - } - } - json << "]\""; - - return json.str(); - } - - - }; - - inline void split(std::string input, char delimiter, std::vector& tokens) - { - std::stringstream ss(input); - std::string item; - - while(std::getline(ss, item, delimiter)) - { - tokens.push_back(item); - } - } - - class ParseVisitor : public boost::static_visitor - { - public: - - ParseVisitor(std::string str): m_str(str) - { - } - - void operator() (int& i) const - { - i = std::stoi(m_str); - } - - void operator() (double& d) const - { - d = std::stod(m_str); - } - - void operator() (std::string& str) const - { - str = m_str; - } - - void operator() (bool& b) const - { - b = m_str.compare("true") == 0; - } - - void operator() (std::vector& numbers) const - { - numbers.clear(); - - if(m_str.length() >= 2) - { - std::string str = m_str.substr(1, m_str.length()-2); - - std::vector tokens; - split(str, ',', tokens); - - for(auto s: tokens) - { - numbers.push_back(std::stoi(s)); - } - } - else - { - throw OCException(OC::Exception::INVALID_ARRAY); - } - - } - - void operator() (std::vector& numbers) const - { - numbers.clear(); - - if(m_str.length() >= 2) - { - std::string str = m_str.substr(1, m_str.length()-2); - std::vector tokens; - split(str, ',', tokens); - - for(auto s: tokens) - { - numbers.push_back(std::stod(s)); - } - } - else - { - throw OCException(OC::Exception::INVALID_ARRAY); - } - } - - void operator() (std::vector& bools) const - { - bools.clear(); - - if(m_str.length() >= 2) - { - std::string str = m_str.substr(2, m_str.length()-3); - - std::vector tokens; - split(str, ',', tokens); - - for(auto s: tokens) - { - bools.push_back(s.compare("true") == 0); - } - } - else - { - throw OCException(OC::Exception::INVALID_ARRAY); - } - - } - - void operator() (std::vector& strings) const - { - strings.clear(); - - if(m_str.length() >= 2) - { - std::string str = m_str.substr(1, m_str.length()-2); - - std::vector tokens; - split(str, ',', tokens); - - for(auto s: tokens) - { - strings.push_back(s); - } - } - else - { - throw OCException(OC::Exception::INVALID_ARRAY); - } - } - - void operator() (std::vector& reps) const - { - reps.clear(); - - if(m_str.length() >= 2) - { - std::string str = m_str.substr(1, m_str.length()-2); - - std::vector tokens; - split(str, ',', tokens); - - for(auto s: tokens) - { - reps.push_back(parseJSONToRepresentation(s)); - } - } - else - { - throw OCException(OC::Exception::INVALID_ARRAY); - } - } - - void operator() (OCRepresentation& rep) const - { - rep = parseJSONToRepresentation(m_str); - } - - private: - std::string m_str; - }; - - - inline std::string getJSON(const AttributeValue& v) - { - return boost::apply_visitor(ComposeVisitor(), v); - } - - inline void parseJSON(AttributeValue& v, std::string str) - { - boost::apply_visitor(ParseVisitor(str), v); - } - - template - void OCRepresentation::setValue(const std::string& str, const T& val) - { - m_attributeMap[str] = getJSON(val); - } - - template - T OCRepresentation::getValue(const std::string& str) const - { - T val = T(); - - auto x = m_attributeMap.find(str); - - if(m_attributeMap.end() != x) - { - AttributeValue v = val; - parseJSON(v, x->second); - val = boost::get(v); - } - - return val; - } - - template - bool OCRepresentation::getValue(const std::string& str, T& val) const - { - auto x = m_attributeMap.find(str); - - if(m_attributeMap.end() != x) - { - AttributeValue v = val; - parseJSON(v, x->second); - val = boost::get(v); - return true; - } - else - { - return false; - } - } - + // // Typedef for header option vector // OCHeaderOption class is in HeaderOption namespace typedef std::vector HeaderOptions; diff --git a/resource/include/OCRepresentation.h b/resource/include/OCRepresentation.h index 0a0ba16..c27d0c3 100644 --- a/resource/include/OCRepresentation.h +++ b/resource/include/OCRepresentation.h @@ -20,19 +20,336 @@ /// @file OCRepresentation.h -/// @brief This file contains the declaration of classes and its members +/// @brief This file contains the declaration of classes and its members /// related to OCRepresentation #ifndef __OCREPRESENTATION_H #define __OCREPRESENTATION_H -#include -#include +#include +#include +#include +#include +#include +#include + +// note: is there any way to move this later so that the implementers don't need to +// reference them? +#include +#include +#include +#include // Customized Cereal serializer, required for a few functions +#include + +#include namespace OC { - + + enum class InterfaceType + { + None, + LinkParent, + BatchParent, + DefaultParent, + LinkChild, + BatchChild, + DefaultChild + }; + + // The consumer requires resource info to be printed in 2 different ways, both with the "oc":[] + // and without. This enum is used to differentiate between the two situations. When the + // serialize is called with Include OC, we encode OC, otherwise we skip it and return just the + // contents of the array. + enum class OCInfoFormat + { + IncludeOC, + ExcludeOC + }; + + class MessageContainer + { + public: + void setJSONRepresentation(const std::string& payload); + + void setJSONRepresentation(const unsigned char* payload); + + std::string getJSONRepresentation(OCInfoFormat f) const; + + const std::vector& representations() const; + + void addRepresentation(const OCRepresentation& rep); + + const OCRepresentation& operator[](int index) const + { + return m_reps[index]; + } + + const OCRepresentation& back() const + { + return m_reps.back(); + } + private: + std::vector m_reps; + }; + class OCRepresentation + { + public: + OCRepresentation(); + std::string getJSONRepresentation() const; + + void addChild(const OCRepresentation&); + + void clearChildren(); + + const std::vector& getChildren() const; + + void setChildren(const std::vector& children); + + void setUri(const std::string& uri); + + std::string getUri() const; + + const std::vector& getResourceTypes() const; + + void setResourceTypes(const std::vector& resourceTypes); + + const std::vector& getResourceInterfaces() const; + + void setResourceInterfaces(const std::vector& resourceInterfaces); + + bool empty() const; + + int numberOfAttributes() const; + + bool erase(const std::string& str); + + template + void setValue(const std::string& str, const T& val) + { + m_values[str] = val; + } + + template + bool getValue(const std::string& str, T& val) const + { + auto x = m_values.find(str); + + if(x!= m_values.end()) + { + val = boost::get(x->second); + return true; + } + else + { + val = T(); + return false; + } + } + + template + T getValue(const std::string& str) const + { + T val = T(); + auto x = m_values.find(str); + if(x != m_values.end()) + { + val = boost::get(x->second); + } + return val; + } + + bool hasAttribute(const std::string& str) const; + + void setNULL(const std::string& str); + + bool isNULL(const std::string& str) const; + private: + friend class OCResourceResponse; + friend class cereal::access; + + // the root node has a slightly different JSON version + // based on the interface type configured in ResourceResponse. + // This allows ResourceResponse to set it, so that the save function + // doesn't serialize things that it isn't supposed to serialize. + void setInterfaceType(InterfaceType ift) + { + m_interfaceType = ift; + } + + // class used to wrap the 'prop' feature of the save/load + class Prop + { + public: + Prop(std::vector& resourceTypes, + std::vector& interfaces) + : m_types(resourceTypes), m_interfaces(interfaces) + {} + + /* Prop(const std::vector& resourceTypes, + const std::vector& interfaces) + :m_types(resourceTypes), + m_interfaces(interfaces) + {}*/ + private: + friend class cereal::access; + template + void save(Archive& ar) const + { + if(m_types.size() > 0) + { + ar(cereal::make_nvp(Key::RESOURCETYPESKEY, m_types)); + } + + if(m_interfaces.size()>0) + { + ar(cereal::make_nvp(Key::INTERFACESKEY, m_interfaces)); + } + } + + template + void load(Archive& ar) + { + optional_load(ar, cereal::make_nvp(Key::RESOURCETYPESKEY, m_types)); + optional_load(ar, cereal::make_nvp(Key::INTERFACESKEY, m_interfaces)); + } + + std::vector& m_types; + std::vector& m_interfaces; + }; + template + static void optional_load(Archive& ar, Val&& v) + { + try + { + ar(v); + } + catch(cereal::Exception& e) + { + ar.setNextName(nullptr); + // Loading a key that doesn't exist results in an exception + // Since "Not Found" is a valid condition for us, we swallow + // this exception and the archive will not load anything + } + } + template + void save(Archive& ar) const + { + // printed for all interface types + if(!m_uri.empty()) + { + ar(cereal::make_nvp(Key::URIKEY, m_uri)); + } + + if((m_interfaceType == InterfaceType::None + || m_interfaceType==InterfaceType::DefaultChild + || m_interfaceType==InterfaceType::LinkChild) + && (m_resourceTypes.size()>0 || m_interfaces.size()>0)) + { + // The Prop object requires that it refer to non-const vectors + // so that it can alter them in the 'load' case. In the save case + // (initiated here) it will not modify the object. So, to keep the + // compiler happy, removing the 'const' context here is necessary. + const std::vector& rt(m_resourceTypes); + const std::vector& intf(m_interfaces); + Prop temp(const_cast&>(rt), + const_cast&>(intf)); + ar(cereal::make_nvp(Key::PROPERTYKEY, temp)); + } + + // printed only for BatchChildren and DefaultParent + if((m_interfaceType == InterfaceType::None + || m_interfaceType == InterfaceType::BatchChild + || m_interfaceType == InterfaceType::DefaultParent) + && m_values.size()>0) + { + ar(cereal::make_nvp(Key::REPKEY, m_values)); + } + } + + template + void load(Archive& ar) + { + optional_load(ar, cereal::make_nvp(Key::URIKEY, m_uri)); + { + Prop temp(m_resourceTypes, m_interfaces); + optional_load(ar, cereal::make_nvp(Key::PROPERTYKEY, temp)); + } + optional_load(ar, cereal::make_nvp(Key::REPKEY, m_values)); + } + + private: + std::string m_uri; + std::vector m_children; + std::map m_values; + std::vector m_resourceTypes; + std::vector m_interfaces; + + InterfaceType m_interfaceType; + }; } // namespace OC +// code needed to serialize a string::Attribute value map +namespace OC +{ + namespace detail + { + template + class WriteAttributeValue : public boost::static_visitor<> + { + public: + WriteAttributeValue(const std::string& name, Archive& ar) + :m_name(name), m_archive(ar) + {} + + template + void operator()(const T& value) const + { + m_archive(cereal::make_nvp(m_name, value)); + } + private: + std::string m_name; + Archive& m_archive; + }; + } +} + +namespace cereal +{ + // take no action when serializing the null type, because the 'save' below + // doesn't use the visitor for this type. + template + void serialize(Archive&, OC::NullType t) + {} + + template + void save(Archive& ar, const std::map& vals) + { + for(const auto& kv : vals) + { + const auto& k = kv.first; + const auto& v = kv.second; + + if(v.which() != OC::AttributeValueNullIndex) + { + OC::detail::WriteAttributeValue writer(k,ar); + boost::apply_visitor(writer, v); + } + else + { + ar.setNextName(k.c_str()); + ar.writeName(); + ar.saveValue(); + } + } + } + + template + void load(Archive& ar, std::map& vals) + { + ar.loadAttributeValues(vals); + } +} + #endif //__OCREPRESENTATION_H diff --git a/resource/include/OCResource.h b/resource/include/OCResource.h index 5d5802c..63c4253 100644 --- a/resource/include/OCResource.h +++ b/resource/include/OCResource.h @@ -30,9 +30,6 @@ #include #include -#include -#include - #include #include #include @@ -52,8 +49,7 @@ namespace OC class OCResource { friend class OCPlatform_impl; - friend class InProcClientWrapper; - + friend class ListenOCContainer; public: typedef std::shared_ptr Ptr; /** diff --git a/resource/include/OCResourceRequest.h b/resource/include/OCResourceRequest.h index 890a4a7..5b7e206 100644 --- a/resource/include/OCResourceRequest.h +++ b/resource/include/OCResourceRequest.h @@ -26,12 +26,14 @@ #ifndef __OCRESOURCEREQUEST_H #define __OCRESOURCEREQUEST_H -#include -#include - #include "OCApi.h" #include "OCRepresentation.h" +void formResourceRequest(OCEntityHandlerFlag, + OCEntityHandlerRequest*, + std::shared_ptr); + + namespace OC { /** @@ -118,74 +120,53 @@ namespace OC ObservationInfo m_observationInfo; HeaderOptions m_headerOptions; - public: - // TODO: This is not a public API for app developers. - // This function will not be exposed in future + private: + friend void (::formResourceRequest)(OCEntityHandlerFlag, OCEntityHandlerRequest*, + std::shared_ptr); void setRequestType(const std::string& requestType) { m_requestType = requestType; } - // TODO: This is not a public API for app developers. - // This function will not be exposed in future void setPayload(const std::string& requestPayload) { - AttributeMap attributeMap; - // TODO: The following JSON Parse implementation should be seperated into utitilites - // and used wherever required. - // e.g. parse(std::string& payload, Attributemap& attributeMap) - - std::stringstream requestStream; - requestStream << requestPayload; - boost::property_tree::ptree root; - try - { - boost::property_tree::read_json(requestStream, root); - } - catch(boost::property_tree::json_parser::json_parser_error &e) + MessageContainer info; + info.setJSONRepresentation(requestPayload); + + const std::vector& reps = info.representations(); + if(reps.size() >0) { - //TOD: log this - return; + std::vector::const_iterator itr = reps.begin(); + std::vector::const_iterator back = reps.end(); + m_representation = *itr; + ++itr; + + for(;itr != back; ++itr) + { + m_representation.addChild(*itr); + } } - - // TODO this expects the representation oc:{} and not oc:[{}] - // this representation is fine when setting for simple resource. - boost::property_tree::ptree payload = root.get_child(OC::Key::OCKEY, boost::property_tree::ptree()); - - for(auto& item: payload) + else { - std::string name = item.first.data(); - std::string value = item.second.data(); - - attributeMap[name] = value; + throw OCException(OC::Exception::INVALID_REPRESENTATION); } - - m_representation.setAttributeMap(attributeMap); } - // TODO: This is not a public API for app developers. - // This function will not be exposed in future void setQueryParams(QueryParamsMap& queryParams) { m_queryParameters = queryParams; } - // TODO: This is not a public API for app developers. - // This function will not be exposed in future void setRequestHandlerFlag(int requestHandlerFlag) { m_requestHandlerFlag = requestHandlerFlag; } - // TODO: This is not a public API for app developers. - // This function will not be exposed in future void setObservationInfo(const ObservationInfo& observationInfo) { m_observationInfo = observationInfo; } - // TODO: This is not a public API for app developers. - // This function will not be exposed in future void setHeaderOptions(const HeaderOptions& headerOptions) { m_headerOptions = headerOptions; diff --git a/resource/include/OCResourceResponse.h b/resource/include/OCResourceResponse.h index 4cd371f..ee660e3 100644 --- a/resource/include/OCResourceResponse.h +++ b/resource/include/OCResourceResponse.h @@ -33,6 +33,12 @@ using namespace std; + +void (processResourceResponse)(OCEntityHandlerFlag, + OCEntityHandlerRequest*, + std::shared_ptr pResponse); + + namespace OC { /** @@ -43,14 +49,9 @@ namespace OC public: typedef std::shared_ptr Ptr; - /** - * Default destructor - */ - OCResourceResponse() {} + OCResourceResponse() + {} - /** - * Virtual destructor - */ virtual ~OCResourceResponse(void) {} /** @@ -93,19 +94,8 @@ namespace OC * @param interface specifies the interface */ void setResourceRepresentation(OCRepresentation& rep, std::string interface) { - if(!interface.compare(LINK_INTERFACE)) - { - setResourceRepresentationLL(rep); - } - else if(!interface.compare(BATCH_INTERFACE)) - { - setResourceRepresentationBatch(rep); - } - else - { - setResourceRepresentationDefault(rep); - } - // TODO other interfaces + m_interface = interface; + m_representation = rep; } /** @@ -125,7 +115,8 @@ namespace OC */ void setResourceRepresentation(OCRepresentation& rep) { // Call the default - setResourceRepresentationDefault(rep); + m_interface = DEFAULT_INTERFACE; + m_representation = rep; } /** @@ -137,187 +128,59 @@ namespace OC // Call the above function setResourceRepresentation(rep); } + private: + std::string m_newResourceUri; + int m_errorCode; + HeaderOptions m_headerOptions; + std::string m_interface; + OCRepresentation m_representation; + private: + friend void (::processResourceResponse)(OCEntityHandlerFlag, + OCEntityHandlerRequest*, + std::shared_ptr pResponse); + std::string getPayload() const + { + MessageContainer inf; + OCRepresentation first(m_representation); - /** - * API to set the entire resource attribute representation (Linked List Interface)) - * @param attributeMap reference containing the name value pairs representing the resource's - * attributes - */ - void setResourceRepresentationLL(OCRepresentation& rep) { - - // Default Set - - ostringstream payload; - - // Parent - payload << "{"; - payload << "\"href\":"; - payload << "\"" ; - payload << rep.getUri(); - payload << "\"" ; - payload << "}"; - - // Children stuff - std::vector children = rep.getChildren(); - - for(auto oitr = children.begin(); oitr != children.end(); ++oitr) + if(m_interface==LINK_INTERFACE) { - payload << ",{\"href\":"; - - payload << "\"" ; - payload << oitr->getUri(); - payload << "\"" ; - - payload << ",\"prop\":{"; - - payload << "\"rt\":["; - std::vector types = oitr->getResourceTypes(); - for(auto itr = types.begin(); itr != types.end(); ++itr) - { - if(itr != types.begin()) - { - payload << ','; - } - - payload << *itr; - } - payload << "],"; - - payload << "\"if\":["; - std::vector interfaces = oitr->getResourceInterfaces(); - for(auto itr = interfaces.begin(); itr != interfaces.end(); ++itr) - { - if(itr != interfaces.begin()) - { - payload << ','; - } - - payload << "\"" << *itr << "\""; - } - payload << "]"; - - payload << "}}"; + first.setInterfaceType(InterfaceType::LinkParent); } - - m_payload = payload.str(); - } - - /** - * API to set the entire resource attribute representation (Default)) - * @param attributeMap reference containing the name value pairs representing the resource's - * attributes - */ - void setResourceRepresentationDefault(OCRepresentation& rep) { - - // Default Set - - ostringstream payload; - - // Parent - payload << "{"; - payload << "\"href\":"; - payload << "\"" ; - payload << rep.getUri(); - payload << "\"" ; - - payload << ",\"rep\":"; - - payload << rep.getJSONRepresentation(); - - payload << "}"; - - // Children stuff - std::vector children = rep.getChildren(); - - for(auto oitr = children.begin(); oitr != children.end(); ++oitr) + else if(m_interface==BATCH_INTERFACE) { - payload << ",{\"href\":"; + first.setInterfaceType(InterfaceType::BatchParent); + } + else + { + first.setInterfaceType(InterfaceType::DefaultParent); + } - payload << "\"" ; - payload << oitr->getUri(); - payload << "\"" ; + inf.addRepresentation(first); - payload << ",\"prop\":{"; + for(const OCRepresentation& rep : m_representation.getChildren()) + { + OCRepresentation cur(rep); - payload << "\"rt\":["; - std::vector types = oitr->getResourceTypes(); - for(auto itr = types.begin(); itr != types.end(); ++itr) + if(m_interface==LINK_INTERFACE) { - if(itr != types.begin()) - { - payload << ','; - } - - payload << "\"" << *itr << "\""; + cur.setInterfaceType(InterfaceType::LinkChild); } - payload << "],"; - - payload << "\"if\":["; - std::vector interfaces = oitr->getResourceInterfaces(); - for(auto itr = interfaces.begin(); itr != interfaces.end(); ++itr) + else if(m_interface==BATCH_INTERFACE) { - if(itr != interfaces.begin()) - { - payload << ','; - } - - payload << "\"" << *itr << "\""; + cur.setInterfaceType(InterfaceType::BatchChild); + } + else + { + cur.setInterfaceType(InterfaceType::DefaultChild); } - payload << "]"; - - payload << "}}"; - } - - m_payload = payload.str(); - } - - /** - * API to set the entire resource attribute representation (BATCH) - * @param attributeMap reference containing the name value pairs representing the resource's - * attributes - */ - void setResourceRepresentationBatch(OCRepresentation& rep) { - ostringstream payload; - - // Parent - payload << "{"; - payload << "\"href\":"; - payload << "\"" ; - payload << rep.getUri(); - payload << "\"" ; - payload << "}"; - - std::vector children = rep.getChildren(); - - for(auto oitr = children.begin(); oitr != children.end(); ++oitr) - { - payload << ','; - - payload << "{"; - - payload << "\"href\":"; - - payload << "\"" ; - payload << oitr->getUri(); - payload << "\"" ; - - payload << ",\"rep\":"; - payload << oitr->getJSONRepresentation(); + inf.addRepresentation(cur); - payload << "}"; } - m_payload = payload.str(); + return inf.getJSONRepresentation(OCInfoFormat::ExcludeOC); } - - private: - std::string m_newResourceUri; - std::string m_payload; - int m_errorCode; - HeaderOptions m_headerOptions; - - // TODO only stack should have visibility and apps should not public: /** @@ -326,22 +189,18 @@ namespace OC int getErrorCode() const; /** - * This API allows to retrieve headerOptions from a response - */ - const HeaderOptions& getHeaderOptions() const + * Get the Response Representation + */ + const OCRepresentation& getResourceRepresentation() const { - return m_headerOptions; + return m_representation; } - /** - * Get the resource attribute representation + * This API allows to retrieve headerOptions from a response */ - AttributeMap& getResourceRepresentation() const; - - // TODO This should go away & just use getResourceRepresentation - std::string getPayload() + const HeaderOptions& getHeaderOptions() const { - return m_payload; + return m_headerOptions; } }; diff --git a/resource/include/OCSerialization.h b/resource/include/OCSerialization.h new file mode 100644 index 0000000..bbdd200 --- /dev/null +++ b/resource/include/OCSerialization.h @@ -0,0 +1,275 @@ +//****************************************************************** +// +// Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +#include +#include +#include +#include + +#include + +namespace OC +{ + class ListenOCContainer + { + private: + enum class OCSecureType + { + IPv4Secure, + IPv4 + }; + + class ListenResourceContainer + { + class ListenResourcePropertiesContainer + { + friend class cereal::access; + friend class ListenResourceContainer; + + template + void serialize(Archive& ar) + { + try + { + m_observable=false; + int obsTemp; + ar(cereal::make_nvp(OC::Key::OBSERVABLEKEY, obsTemp)); + m_observable = obsTemp != 0; + } + catch(cereal::Exception&) + { + // we swallow this exception, since it means the key + // doesn't exist, allowing these to be optional + } + + try + { + m_secure = false; + int secureTemp; + ar(cereal::make_nvp(OC::Key::SECUREKEY, secureTemp)); + m_secure = secureTemp != 0; + + m_port = -1; + ar(cereal::make_nvp(OC::Key::PORTKEY, m_port)); + } + catch(cereal::Exception&) + {} + + try + { + ar(cereal::make_nvp(OC::Key::RESOURCETYPESKEY,m_resourceTypes)); + } + catch(cereal::Exception&) + {} + try + { + ar(cereal::make_nvp(OC::Key::INTERFACESKEY, m_interfaces)); + } + catch(cereal::Exception&) + {} + } + + bool m_observable; + std::vector m_resourceTypes; + std::vector m_interfaces; + bool m_secure; + int m_port; + }; + + public: + ListenResourceContainer() : m_loaded(false) + {} + + private: + friend class cereal::access; + friend class ListenOCContainer; + + template + void serialize(Archive& ar) + { + try + { + ar(cereal::make_nvp(OC::Key::URIKEY, m_uri)); + m_loaded=true; + } + catch(cereal::Exception&) + {} + try + { + ar(cereal::make_nvp(OC::Key::PROPERTYKEY, m_props)); + m_loaded=true; + } + catch(cereal::Exception&) + {} + } + + + std::string m_uri; + bool m_loaded; + ListenResourcePropertiesContainer m_props; + + bool loaded() const + { + return m_loaded; + } + + bool observable() const + { + return m_props.m_observable; + } + + OCSecureType secureType() const + { + return m_props.m_secure?OCSecureType::IPv4Secure :OCSecureType::IPv4; + } + + int port() const + { + return m_props.m_port; + } + + std::vector resourceTypes() const + { + return m_props.m_resourceTypes; + } + + std::vector interfaces() const + { + return m_props.m_interfaces; + } + }; + + private: + friend class cereal::access; + template + void serialize(Archive& ar) + { + std::vector resources; + ar(resources); + } + public: + ListenOCContainer(std::weak_ptr cw, const OCDevAddr& address, + std::stringstream& json): + m_clientWrapper(cw), m_address(address) + { + LoadFromJson(json); + } + + const std::vector>& Resources() const + { + return m_resources; + } + + private: + std::string ConvertOCAddrToString(OCSecureType sec, int secureport) + { + uint8_t addr1; + uint8_t addr2; + uint8_t addr3; + uint8_t addr4; + uint16_t port; + + ostringstream os; + + if(sec== OCSecureType::IPv4) + { + os<<"coap://"; + } + else if(sec == OCSecureType::IPv4Secure) + { + os<<"coaps://"; + } + else + { + oclog() << "ConvertOCAddrToString(): invalid SecureType"<(addr1)<<'.' + <(addr2)<<'.' + <(addr3)<<'.' + <(addr4); + + if(sec == OCSecureType::IPv4Secure && secureport>0 && secureport<=65535) + { + port = static_cast(secureport); + } + else if(sec == OCSecureType::IPv4 && 0==OCDevAddrToPort(&m_address, &port)) + { + // nothing to do, this is a successful case + } + else + { + oclog() << "ConvertOCAddrToString() : Invalid Port" + <(port); + + return os.str(); + } + + void LoadFromJson(std::stringstream& json) + { + cereal::JSONInputArchive archive(json); + + std::vector resources; + archive(cereal::make_nvp(OC::Key::OCKEY, resources)); + + m_resources.clear(); + + for(const auto& res : resources) + { + try + { + if(res.loaded()) + { + m_resources.push_back(std::shared_ptr( + new OCResource(m_clientWrapper, + ConvertOCAddrToString(res.secureType(),res.port()), + res.m_uri, res.observable(), res.resourceTypes(), + res.interfaces()))); + } + + } + catch(ResourceInitException& e) + { + oclog() << "listenCallback(): failed to create resource: " << e.what() + << std::flush; + } + } + } + std::vector> m_resources; + std::weak_ptr m_clientWrapper; + OCDevAddr m_address; + }; +} diff --git a/resource/include/OicJsonSerializer.hpp b/resource/include/OicJsonSerializer.hpp new file mode 100644 index 0000000..ed07b9d --- /dev/null +++ b/resource/include/OicJsonSerializer.hpp @@ -0,0 +1,862 @@ +/*! \file OicJsonSerializer.hpp + \brief JSON input and output archives. +Note: this has been customized by Intel(R) for usage in the OIC project. +Nearly the entire file is from Cereal (see copyright notice below) other than specified +below +Added: +#include of AttributeValue Type +JSONOutputArchive::saveValue() to add JSON null value +loadAttributeValues to get attribute values out of a map (implemented in OCRepresentation) + +*/ +/* + Copyright (c) 2014, Randolph Voorhies, Shane Grant + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of cereal nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES OR SHANE GRANT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CEREAL_ARCHIVES_JSON_HPP_ +#define CEREAL_ARCHIVES_JSON_HPP_ + +#include +#include +#include +namespace cereal +{ + //! An exception thrown when rapidjson fails an internal assertion + /*! @ingroup Utility */ + struct RapidJSONException : Exception + { RapidJSONException( const char * what_ ) : Exception( what_ ) {} }; +} + +// Override rapidjson assertions to throw exceptions by default +#ifndef RAPIDJSON_ASSERT +#define RAPIDJSON_ASSERT(x) if(!(x)){ \ + throw ::cereal::RapidJSONException("rapidjson internal assertion failure: " #x); } +#endif // RAPIDJSON_ASSERT + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace cereal +{ + // ###################################################################### + //! An output archive designed to save data to JSON + /*! This archive uses RapidJSON to build serialie data to JSON. + + JSON archives provides a human readable output but at decreased + performance (both in time and space) compared to binary archives. + + JSON benefits greatly from name-value pairs, which if present, will + name the nodes in the output. If these are not present, each level + of the output will be given an automatically generated delimited name. + + The precision of the output archive controls the number of decimals output + for floating point numbers and should be sufficiently large (i.e. at least 20) + if there is a desire to have binary equality between the numbers output and + those read in. In general you should expect a loss of precision when going + from floating point to text and back. + + JSON archives do not output the size information for any dynamically sized structure + and instead infer it from the number of children for a node. This means that data + can be hand edited for dynamic sized structures and will still be readable. This + is accomplished through the cereal::SizeTag object, which will cause the archive + to output the data as a JSON array (e.g. marked by [] instead of {}), which indicates + that the container is variable sized and may be edited. + + \ingroup Archives */ + class JSONOutputArchive : public OutputArchive + { + enum class NodeType { StartObject, InObject, StartArray, InArray }; + + typedef rapidjson::GenericWriteStream WriteStream; + typedef rapidjson::Writer JSONWriter; + + public: + /*! @name Common Functionality + Common use cases for directly interacting with an JSONOutputArchive */ + //! @{ + + //! A class containing various advanced options for the JSON archive + class Options + { + public: + //! Default options + static Options Default(){ return Options(); } + + //! Specify specific options for the JSONOutputArchive + /*! @param precision The precision used for floating point numbers*/ + explicit Options( int precision = std::numeric_limits::max_digits10) : + itsPrecision( precision ) { } + + private: + friend class JSONOutputArchive; + int itsPrecision; + }; + + //! Construct, outputting to the provided stream + /*! @param stream The stream to output to. + @param options The JSON specific options to use. See the Options struct + for the values of default parameters */ + JSONOutputArchive(std::ostream & stream, Options const & options = Options::Default() ) : + OutputArchive(this), + itsWriteStream(stream), + itsWriter(itsWriteStream, options.itsPrecision), + itsNextName(nullptr) + { + itsNameCounter.push(0); + itsNodeStack.push(NodeType::StartObject); + } + + //! Destructor, flushes the JSON + ~JSONOutputArchive() + { + itsWriter.EndObject(); + } + + //! Saves some binary data, encoded as a base64 string, with an optional name + /*! This will create a new node, optionally named, and insert a value that consists of + the data encoded as a base64 string */ + void saveBinaryValue( const void * data, size_t size, const char * name = nullptr ) + { + setNextName( name ); + writeName(); + + auto base64string = base64::encode( reinterpret_cast( data ), size ); + saveValue( base64string ); + }; + + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the JSONOutputArchive */ + //! @{ + + //! Starts a new node in the JSON output + /*! The node can optionally be given a name by calling setNextName prior + to creating the node + + Nodes only need to be started for types that are themselves objects or arrays */ + void startNode() + { + writeName(); + itsNodeStack.push(NodeType::StartObject); + itsNameCounter.push(0); + } + + //! Designates the most recently added node as finished + void finishNode() + { + // if we ended up serializing an empty object or array, writeName + // will never have been called - so start and then immediately end + // the object/array. + // + // We'll also end any object/arrays we happen to be in + switch(itsNodeStack.top()) + { + case NodeType::StartArray: + itsWriter.StartArray(); + case NodeType::InArray: + itsWriter.EndArray(); + break; + case NodeType::StartObject: + itsWriter.StartObject(); + case NodeType::InObject: + itsWriter.EndObject(); + break; + } + + itsNodeStack.pop(); + itsNameCounter.pop(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNextName = name; + } + + //! Saves a null to the current node, added by Intel + void saveValue() { itsWriter.Null_(); } + //! Saves a bool to the current node + void saveValue(bool b) { itsWriter.Bool_(b); } + //! Saves an int to the current node + void saveValue(int i) { itsWriter.Int(i); } + //! Saves a uint to the current node + void saveValue(unsigned u) { itsWriter.Uint(u); } + //! Saves an int64 to the current node + void saveValue(int64_t i64) { itsWriter.Int64(i64); } + //! Saves a uint64 to the current node + void saveValue(uint64_t u64) { itsWriter.Uint64(u64); } + //! Saves a double to the current node + void saveValue(double d) { itsWriter.Double(d); } + //! Saves a string to the current node + void saveValue(std::string const & s) { itsWriter.String(s.c_str(), static_cast( s.size() )); } + //! Saves a const char * to the current node + void saveValue(char const * s) { itsWriter.String(s); } + + private: + // Some compilers/OS have difficulty disambiguating the above for various flavors of longs, so we provide + // special overloads to handle these cases. + + //! 32 bit signed long saving to current node + template inline + typename std::enable_if::value, void>::type + saveLong(T l){ saveValue( static_cast( l ) ); } + + //! non 32 bit signed long saving to current node + template inline + typename std::enable_if::value, void>::type + saveLong(T l){ saveValue( static_cast( l ) ); } + + //! 32 bit unsigned long saving to current node + template inline + typename std::enable_if::value, void>::type + saveLong(T lu){ saveValue( static_cast( lu ) ); } + + //! non 32 bit unsigned long saving to current node + template inline + typename std::enable_if::value, void>::type + saveLong(T lu){ saveValue( static_cast( lu ) ); } + + public: +#ifdef _MSC_VER + //! MSVC only long overload to current node + void saveValue( unsigned long lu ){ saveLong( lu ); }; +#else // _MSC_VER + //! Serialize a long if it would not be caught otherwise + template inline + typename std::enable_if::value && + !std::is_same::value && + !std::is_same::value, void>::type + saveValue( T t ){ saveLong( t ); } + + //! Serialize an unsigned long if it would not be caught otherwise + template inline + typename std::enable_if::value && + !std::is_same::value && + !std::is_same::value, void>::type + saveValue( T t ){ saveLong( t ); } +#endif // _MSC_VER + + //! Save exotic arithmetic as strings to current node + /*! Handles long long (if distinct from other types), unsigned long (if distinct), and long double */ + template inline + typename std::enable_if::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long)), void>::type + saveValue(T const & t) + { + std::stringstream ss; ss.precision( std::numeric_limits::max_digits10 ); + ss << t; + saveValue( ss.str() ); + } + + //! Write the name of the upcoming node and prepare object/array state + /*! Since writeName is called for every value that is output, regardless of + whether it has a name or not, it is the place where we will do a deferred + check of our node state and decide whether we are in an array or an object. + + The general workflow of saving to the JSON archive is: + + 1. (optional) Set the name for the next node to be created, usually done by an NVP + 2. Start the node + 3. (if there is data to save) Write the name of the node (this function) + 4. (if there is data to save) Save the data (with saveValue) + 5. Finish the node + */ + void writeName() + { + NodeType const & nodeType = itsNodeStack.top(); + + // Start up either an object or an array, depending on state + if(nodeType == NodeType::StartArray) + { + itsWriter.StartArray(); + itsNodeStack.top() = NodeType::InArray; + } + else if(nodeType == NodeType::StartObject) + { + itsNodeStack.top() = NodeType::InObject; + itsWriter.StartObject(); + } + + // Array types do not output names + if(nodeType == NodeType::InArray) return; + + if(itsNextName == nullptr) + { + std::string name = "value" + std::to_string( itsNameCounter.top()++ ) + "\0"; + saveValue(name); + } + else + { + saveValue(itsNextName); + itsNextName = nullptr; + } + } + + //! Designates that the current node should be output as an array, not an object + void makeArray() + { + itsNodeStack.top() = NodeType::StartArray; + } + + //! @} + + private: + WriteStream itsWriteStream; //!< Rapidjson write stream + JSONWriter itsWriter; //!< Rapidjson writer + char const * itsNextName; //!< The next name + std::stack itsNameCounter; //!< Counter for creating unique names for unnamed nodes + std::stack itsNodeStack; + }; // JSONOutputArchive + + // ###################################################################### + //! An input archive designed to load data from JSON + /*! This archive uses RapidJSON to read in a JSON archive. + + Input JSON should have been produced by the JSONOutputArchive. Data can + only be added to dynamically sized containers (marked by JSON arrays) - + the input archive will determine their size by looking at the number of child nodes. + Only JSON originating from a JSONOutputArchive is officially supported, but data + from other sources may work if properly formatted. + + The JSONInputArchive does not require that nodes are loaded in the same + order they were saved by JSONOutputArchive. Using name value pairs (NVPs), + it is possible to load in an out of order fashion or otherwise skip/select + specific nodes to load. + + The default behavior of the input archive is to read sequentially starting + with the first node and exploring its children. When a given NVP does + not match the read in name for a node, the archive will search for that + node at the current level and load it if it exists. After loading an out of + order node, the archive will then proceed back to loading sequentially from + its new position. + + Consider this simple example where loading of some data is skipped: + + @code{cpp} + // imagine the input file has someData(1-9) saved in order at the top level node + ar( someData1, someData2, someData3 ); // XML loads in the order it sees in the file + ar( cereal::make_nvp( "hello", someData6 ) ); // NVP given does not + // match expected NVP name, so we search + // for the given NVP and load that value + ar( someData7, someData8, someData9 ); // with no NVP given, loading resumes at its + // current location, proceeding sequentially + @endcode + + \ingroup Archives */ + class JSONInputArchive : public InputArchive + { + private: + typedef rapidjson::GenericReadStream ReadStream; + typedef rapidjson::GenericValue> JSONValue; + typedef JSONValue::ConstMemberIterator MemberIterator; + typedef JSONValue::ConstValueIterator ValueIterator; + typedef rapidjson::Document::GenericValue GenericValue; + + public: + /*! @name Common Functionality + Common use cases for directly interacting with an JSONInputArchive */ + //! @{ + + //! Construct, reading from the provided stream + /*! @param stream The stream to read from */ + JSONInputArchive(std::istream & stream) : + InputArchive(this), + itsNextName( nullptr ), + itsReadStream(stream) + { + itsDocument.ParseStream<0>(itsReadStream); + itsIteratorStack.emplace_back(itsDocument.MemberBegin(), itsDocument.MemberEnd()); + } + + //! Loads some binary data, encoded as a base64 string + /*! This will automatically start and finish a node to load the data, and can be called directly by + users. + + Note that this follows the same ordering rules specified in the class description in regards + to loading in/out of order */ + void loadBinaryValue( void * data, size_t size, const char * name = nullptr ) + { + itsNextName = name; + + std::string encoded; + loadValue( encoded ); + auto decoded = base64::decode( encoded ); + + if( size != decoded.size() ) + throw Exception("Decoded binary data size does not match specified size"); + + std::memcpy( data, decoded.data(), decoded.size() ); + itsNextName = nullptr; + }; + + // Intel Added this as a custom parsing hook for the AttributeValue map + void loadAttributeValues(std::map& map); + + private: + //! @} + /*! @name Internal Functionality + Functionality designed for use by those requiring control over the inner mechanisms of + the JSONInputArchive */ + //! @{ + + //! An internal iterator that handles both array and object types + /*! This class is a variant and holds both types of iterators that + rapidJSON supports - one for arrays and one for objects. */ + class Iterator + { + public: + friend class cereal::JSONInputArchive; + Iterator() : itsIndex( 0 ), itsType(Null_) {} + + Iterator(MemberIterator begin, MemberIterator end) : + itsMemberItBegin(begin), itsMemberItEnd(end), itsIndex(0), itsType(Member) + { } + + Iterator(ValueIterator begin, ValueIterator end) : + itsValueItBegin(begin), itsValueItEnd(end), itsIndex(0), itsType(Value) + { } + + //! Advance to the next node + Iterator & operator++() + { + ++itsIndex; + return *this; + } + + //! Get the value of the current node + GenericValue const & value() + { + switch(itsType) + { + case Value : return itsValueItBegin[itsIndex]; + case Member: return itsMemberItBegin[itsIndex].value; + default: throw cereal::Exception("Invalid Iterator Type!"); + } + } + + //! Get the name of the current node, or nullptr if it has no name + const char * name() const + { + if( itsType == Member && (itsMemberItBegin + itsIndex) != itsMemberItEnd ) + return itsMemberItBegin[itsIndex].name.GetString(); + else + return nullptr; + } + + //! Adjust our position such that we are at the node with the given name + /*! @throws Exception if no such named node exists */ + inline void search( const char * searchName ) + { + const auto len = std::strlen( searchName ); + size_t index = 0; + for( auto it = itsMemberItBegin; it != itsMemberItEnd; ++it, ++index ) + if( std::strncmp( searchName, it->name.GetString(), len ) == 0 ) + { + itsIndex = index; + return; + } + + throw Exception("JSON Parsing failed - provided NVP not found"); + } + + private: + MemberIterator itsMemberItBegin, itsMemberItEnd; //!< The member iterator (object) + ValueIterator itsValueItBegin, itsValueItEnd; //!< The value iterator (array) + size_t itsIndex; //!< The current index of this iterator + enum Type {Value, Member, Null_} itsType; //!< Whether this holds values (array) or members (objects) or nothing + }; + + //! Searches for the expectedName node if it doesn't match the actualName + /*! This needs to be called before every load or node start occurs. This function will + check to see if an NVP has been provided (with setNextName) and if so, see if that name matches the actual + next name given. If the names do not match, it will search in the current level of the JSON for that name. + If the name is not found, an exception will be thrown. + + Resets the NVP name after called. + + @throws Exception if an expectedName is given and not found */ + inline void search() + { + // The name an NVP provided with setNextName() + if( itsNextName ) + { + // The actual name of the current node + auto const actualName = itsIteratorStack.back().name(); + + // Do a search if we don't see a name coming up, or if the names don't match + if( !actualName || std::strcmp( itsNextName, actualName ) != 0 ) + itsIteratorStack.back().search( itsNextName ); + } + + itsNextName = nullptr; + } + + public: + //! Starts a new node, going into its proper iterator + /*! This places an iterator for the next node to be parsed onto the iterator stack. If the next + node is an array, this will be a value iterator, otherwise it will be a member iterator. + + By default our strategy is to start with the document root node and then recursively iterate through + all children in the order they show up in the document. + We don't need to know NVPs to do this; we'll just blindly load in the order things appear in. + + If we were given an NVP, we will search for it if it does not match our the name of the next node + that would normally be loaded. This functionality is provided by search(). */ + void startNode() + { + search(); + + if(itsIteratorStack.back().value().IsArray()) + itsIteratorStack.emplace_back(itsIteratorStack.back().value().Begin(), itsIteratorStack.back().value().End()); + else + itsIteratorStack.emplace_back(itsIteratorStack.back().value().MemberBegin(), itsIteratorStack.back().value().MemberEnd()); + } + + //! Finishes the most recently started node + void finishNode() + { + itsIteratorStack.pop_back(); + ++itsIteratorStack.back(); + } + + //! Sets the name for the next node created with startNode + void setNextName( const char * name ) + { + itsNextName = name; + } + + //! Loads a value from the current node - small signed overload + template inline + typename std::enable_if::value && sizeof(T) < sizeof(int64_t), void>::type + loadValue(T & val) + { + search(); + + val = itsIteratorStack.back().value().GetInt(); + ++itsIteratorStack.back(); + } + + //! Loads a value from the current node - small unsigned overload + template inline + typename std::enable_if<(std::is_unsigned::value && sizeof(T) < sizeof(uint64_t)) && + !std::is_same::value, void>::type + loadValue(T & val) + { + search(); + + val = itsIteratorStack.back().value().GetUint(); + ++itsIteratorStack.back(); + } + + //! Loads a value from the current node - bool overload + void loadValue(bool & val) { search(); val = itsIteratorStack.back().value().GetBool_(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - int64 overload + void loadValue(int64_t & val) { search(); val = itsIteratorStack.back().value().GetInt64(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - uint64 overload + void loadValue(uint64_t & val) { search(); val = itsIteratorStack.back().value().GetUint64(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - float overload + void loadValue(float & val) { search(); val = static_cast(itsIteratorStack.back().value().GetDouble()); ++itsIteratorStack.back(); } + //! Loads a value from the current node - double overload + void loadValue(double & val) { search(); val = itsIteratorStack.back().value().GetDouble(); ++itsIteratorStack.back(); } + //! Loads a value from the current node - string overload + void loadValue(std::string & val) { search(); val = itsIteratorStack.back().value().GetString(); ++itsIteratorStack.back(); } + + private: + //! Convert a string to a long long + void stringToNumber( std::string const & str, long long & val ) { val = std::stoll( str ); } + //! Convert a string to an unsigned long long + void stringToNumber( std::string const & str, unsigned long long & val ) { val = std::stoull( str ); } + //! Convert a string to a long double + void stringToNumber( std::string const & str, long double & val ) { val = std::stold( str ); } + + public: + //! Loads a value from the current node - long double and long long overloads + template inline + typename std::enable_if::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + (sizeof(T) >= sizeof(long double) || sizeof(T) >= sizeof(long long)), void>::type + loadValue(T & val) + { + std::string encoded; + loadValue( encoded ); + stringToNumber( encoded, val ); + } + + //! Loads the size for a SizeTag + void loadSize(size_type & size) + { + size = (itsIteratorStack.rbegin() + 1)->value().Size(); + } + + //! @} + + private: + const char * itsNextName; //!< Next name set by NVP + ReadStream itsReadStream; //!< Rapidjson write stream + std::vector itsIteratorStack; //!< 'Stack' of rapidJSON iterators + rapidjson::Document itsDocument; //!< Rapidjson document + }; + + // ###################################################################### + // JSONArchive prologue and epilogue functions + // ###################################################################### + + // ###################################################################### + //! Prologue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void prologue( JSONOutputArchive &, NameValuePair const & ) + { } + + //! Prologue for NVPs for JSON archives + template inline + void prologue( JSONInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Epilogue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void epilogue( JSONOutputArchive &, NameValuePair const & ) + { } + + //! Epilogue for NVPs for JSON archives + /*! NVPs do not start or finish nodes - they just set up the names */ + template inline + void epilogue( JSONInputArchive &, NameValuePair const & ) + { } + + // ###################################################################### + //! Prologue for SizeTags for JSON archives + /*! SizeTags are strictly ignored for JSON, they just indicate + that the current node should be made into an array */ + template inline + void prologue( JSONOutputArchive & ar, SizeTag const & ) + { + ar.makeArray(); + } + + //! Prologue for SizeTags for JSON archives + template inline + void prologue( JSONInputArchive &, SizeTag const & ) + { } + + // ###################################################################### + //! Epilogue for SizeTags for JSON archives + /*! SizeTags are strictly ignored for JSON */ + template inline + void epilogue( JSONOutputArchive &, SizeTag const & ) + { } + + //! Epilogue for SizeTags for JSON archives + template inline + void epilogue( JSONInputArchive &, SizeTag const & ) + { } + + // ###################################################################### + //! Prologue for all other types for JSON archives (except minimal types) + /*! Starts a new node, named either automatically or by some NVP, + that may be given data by the type about to be archived + + Minimal types do not start or finish nodes */ + template inline + typename std::enable_if::value && + !traits::has_minimal_output_serialization::value, void>::type + prologue( JSONOutputArchive & ar, T const & ) + { + ar.startNode(); + } + + //! Prologue for all other types for JSON archives + template inline + typename std::enable_if::value && + !traits::has_minimal_input_serialization::value, void>::type + prologue( JSONInputArchive & ar, T const & ) + { + ar.startNode(); + } + + // ###################################################################### + //! Epilogue for all other types other for JSON archives (except minimal types + /*! Finishes the node created in the prologue + + Minimal types do not start or finish nodes */ + template inline + typename std::enable_if::value && + !traits::has_minimal_output_serialization::value, void>::type + epilogue( JSONOutputArchive & ar, T const & ) + { + ar.finishNode(); + } + + //! Epilogue for all other types other for JSON archives + template inline + typename std::enable_if::value && + !traits::has_minimal_input_serialization::value, void>::type + epilogue( JSONInputArchive & ar, T const & ) + { + ar.finishNode(); + } + + // ###################################################################### + //! Prologue for arithmetic types for JSON archives + template inline + typename std::enable_if::value, void>::type + prologue( JSONOutputArchive & ar, T const & ) + { + ar.writeName(); + } + + //! Prologue for arithmetic types for JSON archives + template inline + typename std::enable_if::value, void>::type + prologue( JSONInputArchive &, T const & ) + { } + + // ###################################################################### + //! Epilogue for arithmetic types for JSON archives + template inline + typename std::enable_if::value, void>::type + epilogue( JSONOutputArchive &, T const & ) + { } + + //! Epilogue for arithmetic types for JSON archives + template inline + typename std::enable_if::value, void>::type + epilogue( JSONInputArchive &, T const & ) + { } + + // ###################################################################### + //! Prologue for strings for JSON archives + template inline + void prologue(JSONOutputArchive & ar, std::basic_string const &) + { + ar.writeName(); + } + + //! Prologue for strings for JSON archives + template inline + void prologue(JSONInputArchive &, std::basic_string const &) + { } + + // ###################################################################### + //! Epilogue for strings for JSON archives + template inline + void epilogue(JSONOutputArchive &, std::basic_string const &) + { } + + //! Epilogue for strings for JSON archives + template inline + void epilogue(JSONInputArchive &, std::basic_string const &) + { } + + // ###################################################################### + // Common JSONArchive serialization functions + // ###################################################################### + + //! Serializing NVP types to JSON + template inline + void save( JSONOutputArchive & ar, NameValuePair const & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + template inline + void load( JSONInputArchive & ar, NameValuePair & t ) + { + ar.setNextName( t.name ); + ar( t.value ); + } + + //! Saving for arithmetic to JSON + template inline + typename std::enable_if::value, void>::type + save(JSONOutputArchive & ar, T const & t) + { + ar.saveValue( t ); + } + + //! Loading arithmetic from JSON + template inline + typename std::enable_if::value, void>::type + load(JSONInputArchive & ar, T & t) + { + ar.loadValue( t ); + } + + //! saving string to JSON + template inline + void save(JSONOutputArchive & ar, std::basic_string const & str) + { + ar.saveValue( str ); + } + + //! loading string from JSON + template inline + void load(JSONInputArchive & ar, std::basic_string & str) + { + ar.loadValue( str ); + } + + // ###################################################################### + //! Saving SizeTags to JSON + template inline + void save( JSONOutputArchive &, SizeTag const & ) + { + // nothing to do here, we don't explicitly save the size + } + + //! Loading SizeTags from JSON + template inline + void load( JSONInputArchive & ar, SizeTag & st ) + { + ar.loadSize( st.size ); + } +} // namespace cereal + +// register archives for polymorphic support +CEREAL_REGISTER_ARCHIVE(cereal::JSONInputArchive) +CEREAL_REGISTER_ARCHIVE(cereal::JSONOutputArchive) + +#endif // CEREAL_ARCHIVES_JSON_HPP_ diff --git a/resource/include/OutOfProcClientWrapper.h b/resource/include/OutOfProcClientWrapper.h index 07f5c6a..dfae653 100644 --- a/resource/include/OutOfProcClientWrapper.h +++ b/resource/include/OutOfProcClientWrapper.h @@ -28,9 +28,8 @@ namespace OC class OutOfProcClientWrapper : public IClientWrapper { public: - OutOfProcClientWrapper(OC::OCPlatform_impl& owner, std::weak_ptr csdkLock, + OutOfProcClientWrapper(std::weak_ptr csdkLock, PlatformConfig cfg) - : IClientWrapper(owner) {} virtual OCStackResult ListenForResource(const std::string& serviceUrl, @@ -69,12 +68,6 @@ namespace OC const std::string& uri, const HeaderOptions& headerOptions, QualityOfService QoS){return OC_STACK_NOTIMPL;} - virtual std::shared_ptr parseOCResource(IClientWrapper::Ptr clientWrapper, - OCDevAddr& addr, const boost::property_tree::ptree resourceNode) - { - return nullptr; - } - virtual OCStackResult SubscribePresence(OCDoHandle* handle, const std::string& host, const std::string& resourceType, SubscribeCallback& presenceHandler) {return OC_STACK_NOTIMPL;} diff --git a/resource/include/OutOfProcServerWrapper.h b/resource/include/OutOfProcServerWrapper.h index 9f3b09c..a38e83c 100644 --- a/resource/include/OutOfProcServerWrapper.h +++ b/resource/include/OutOfProcServerWrapper.h @@ -28,9 +28,8 @@ namespace OC class OutOfProcServerWrapper : public IServerWrapper { public: - OutOfProcServerWrapper(OC::OCPlatform_impl& owner, PlatformConfig cfg) - : IServerWrapper(owner) - {}; + OutOfProcServerWrapper(PlatformConfig cfg) + {} virtual OCStackResult registerResource( OCResourceHandle& resourceHandle, diff --git a/resource/include/ResourceInitException.h b/resource/include/ResourceInitException.h index a3ebea6..c045b6c 100644 --- a/resource/include/ResourceInitException.h +++ b/resource/include/ResourceInitException.h @@ -30,11 +30,32 @@ namespace OC class ResourceInitException : public std::exception { public: - ResourceInitException(bool missingUri, bool missingType, bool missingInterface, bool missingClientWrapper) - : m_missingUri(missingUri), m_missingType(missingType), m_missingInterface(missingInterface), m_missingClientWrapper(missingClientWrapper) + ResourceInitException( + bool missingUri, + bool missingType, + bool missingInterface, + bool missingClientWrapper, + bool invalidPort, + bool invalidIp) + : m_missingUri(missingUri), + m_missingType(missingType), + m_missingInterface(missingInterface), + m_missingClientWrapper(missingClientWrapper), + m_invalidPort(invalidPort), + m_invalidIp(invalidIp) { } + bool isInvalidPort() const + { + return m_invalidPort; + } + + bool isInvalidIp() const + { + return m_invalidIp; + } + bool isClientWrapperMissing() const { return m_missingClientWrapper; @@ -79,6 +100,16 @@ namespace OC ret += OC::InitException::MISSING_CLIENT_WRAPPER; } + if(isInvalidPort()) + { + ret += OC::InitException::INVALID_PORT; + } + + if(isInvalidIp()) + { + ret += OC::InitException::INVALID_IP; + } + return ret.c_str(); } @@ -88,6 +119,8 @@ namespace OC bool m_missingType; bool m_missingInterface; bool m_missingClientWrapper; + bool m_invalidPort; + bool m_invalidIp; }; } diff --git a/resource/include/StringConstants.h b/resource/include/StringConstants.h index e8b4b5a..b169726 100644 --- a/resource/include/StringConstants.h +++ b/resource/include/StringConstants.h @@ -92,7 +92,12 @@ namespace OC static const std::string GENERAL_FAULT = "General Fault"; static const std::string MALFORMED_STACK_RESPONSE = "Response from OC_STACK is malformed"; static const std::string UNKNOWN_ERROR = "Unknown Error"; - + static const std::string INVALID_REPRESENTATION = "Invalid Payload JSON"; + static const std::string INVALID_JSON_TYPE = "Unrecognized JSON Type "; + static const std::string INVALID_JSON_NUMERIC = "Unrecognized JSON Numeric "; + static const std::string INVALID_JSON_ARRAY_DEPTH = "Max JSON Array Depth exceeded"; + static const std::string INVALID_JSON_TYPE_TAG = "Invalid JSON Type Tag"; + static const std::string INVALID_ATTRIBUTE = "Invalid Attribute: "; } namespace Error diff --git a/resource/include/WrapperFactory.h b/resource/include/WrapperFactory.h index 5aa23a2..2f15b80 100644 --- a/resource/include/WrapperFactory.h +++ b/resource/include/WrapperFactory.h @@ -39,9 +39,9 @@ namespace OC public: typedef std::shared_ptr Ptr; - virtual IClientWrapper::Ptr CreateClientWrapper(OC::OCPlatform_impl& owner, + virtual IClientWrapper::Ptr CreateClientWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) =0; - virtual IServerWrapper::Ptr CreateServerWrapper(OC::OCPlatform_impl& owner, + virtual IServerWrapper::Ptr CreateServerWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) =0; virtual ~IWrapperFactory(){} }; @@ -52,28 +52,28 @@ namespace OC public: WrapperFactory(){} - virtual IClientWrapper::Ptr CreateClientWrapper(OC::OCPlatform_impl& owner, + virtual IClientWrapper::Ptr CreateClientWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) { switch(cfg.serviceType) { case ServiceType::InProc: - return std::make_shared(owner, csdkLock, cfg); + return std::make_shared(csdkLock, cfg); break; case ServiceType::OutOfProc: - return std::make_shared(owner, csdkLock, cfg); + return std::make_shared(csdkLock, cfg); break; } return nullptr; } - virtual IServerWrapper::Ptr CreateServerWrapper(OC::OCPlatform_impl& owner, + virtual IServerWrapper::Ptr CreateServerWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) { switch(cfg.serviceType) { case ServiceType::InProc: - return std::make_shared(owner, csdkLock, cfg); + return std::make_shared(csdkLock, cfg); break; case ServiceType::OutOfProc: throw OC::OCException(OC::Exception::SVCTYPE_OUTOFPROC, OC_STACK_NOTIMPL); diff --git a/resource/makefile b/resource/makefile index 3d3d3bd..a710781 100644 --- a/resource/makefile +++ b/resource/makefile @@ -31,6 +31,9 @@ ifeq ($(PLATFORM), ) PLATFORM := "linux" endif +DEPEND_DIR:= $(ROOT_DIR)/dependencies +CEREAL_DIR:= $(DEPEND_DIR)/cereal + OUT_DIR := $(ROOT_DIR)/$(BUILD) OBJ_DIR := $(OUT_DIR)/obj @@ -45,20 +48,34 @@ CXX_INC += -I./csdk/ocsocket/include CXX_INC += -I./csdk/ocrandom/include CXX_INC += -I./csdk/logger/include CXX_INC += -I./csdk/libcoap - +CXX_INC += -I$(CEREAL_DIR)/include # Force metatargets to build: -all.PHONY: prep_dirs c_sdk oc_logger_target liboc.a examples +all.PHONY: applyDepPatches prep_dirs c_sdk oc_logger_target liboc.a examples -buildScript_all.PHONY: prep_dirs oc_logger_target liboc.a +buildScript_all.PHONY: applyDepPatches prep_dirs oc_logger_target liboc.a all: all.PHONY buildScript_all: buildScript_all.PHONY -prep_dirs: +prep_dirs: deps -mkdir -p $(OUT_DIR) -mkdir -p $(OBJ_DIR) +# used to fetch all dependencies +deps: + -mkdir -p $(DEPEND_DIR) +#cereal fetch + if [ ! -d "$(CEREAL_DIR)" ]; then\ + cd $(DEPEND_DIR) && git clone https://github.com/USCiLab/cereal.git;\ + cd $(CEREAL_DIR) && git checkout 7121e91e6ab8c3e6a6516d9d9c3e6804e6f65245;\ + fi + +applyDepPatches: deps +#reset git to the 'base version', so we can apply our patch without issues + cd $(CEREAL_DIR) && git reset --hard 7121e91e6ab8c3e6a6516d9d9c3e6804e6f65245; + cd $(CEREAL_DIR) && git apply $(ROOT_DIR)/patches/cereal_gcc46.patch + c_sdk: cd csdk && $(MAKE) "BUILD=$(BUILD)" "PLATFORM=$(PLATFORM)" @@ -70,8 +87,8 @@ cpp_sdk: prep_dirs c_sdk liboc.a examples: liboc.a cd examples && $(MAKE) apps "BUILD=$(BUILD)" -liboc.a: OCPlatform_impl.o OCPlatform.o OCResource.o OCException.o OCUtilities.o InProcServerWrapper.o InProcClientWrapper.o - ar -cvq $(OBJ_DIR)/liboc.a $(OBJ_DIR)/OCPlatform_impl.o $(OBJ_DIR)/OCPlatform.o $(OBJ_DIR)/OCResource.o $(OBJ_DIR)/OCException.o $(OBJ_DIR)/OCUtilities.o $(OBJ_DIR)/InProcServerWrapper.o $(OBJ_DIR)/InProcClientWrapper.o +liboc.a: OCPlatform_impl.o OCPlatform.o OCResource.o OCException.o OCUtilities.o InProcServerWrapper.o InProcClientWrapper.o OCRepresentation.o + ar -cvq $(OBJ_DIR)/liboc.a $(OBJ_DIR)/OCPlatform_impl.o $(OBJ_DIR)/OCPlatform.o $(OBJ_DIR)/OCResource.o $(OBJ_DIR)/OCException.o $(OBJ_DIR)/OCUtilities.o $(OBJ_DIR)/InProcServerWrapper.o $(OBJ_DIR)/InProcClientWrapper.o $(OBJ_DIR)/OCRepresentation.o OCPlatform_impl.o: src/OCPlatform_impl.cpp $(CXX) $(CXX_FLAGS.$(BUILD)) -o $(OBJ_DIR)/$@ -c src/OCPlatform_impl.cpp $(CXX_INC) @@ -79,6 +96,9 @@ OCPlatform_impl.o: src/OCPlatform_impl.cpp OCPlatform.o: src/OCPlatform.cpp $(CXX) $(CXX_FLAGS.$(BUILD)) -o $(OBJ_DIR)/$@ -c src/OCPlatform.cpp $(CXX_INC) +OCRepresentation.o: src/OCRepresentation.cpp + $(CXX) $(CXX_FLAGS.$(BUILD)) -o $(OBJ_DIR)/$@ -c src/OCRepresentation.cpp $(CXX_INC) + OCResource.o: src/OCResource.cpp $(CXX) $(CXX_FLAGS.$(BUILD)) -o $(OBJ_DIR)/$@ -c src/OCResource.cpp $(CXX_INC) diff --git a/resource/patches/cereal_gcc46.patch b/resource/patches/cereal_gcc46.patch new file mode 100644 index 0000000..c4da84f --- /dev/null +++ b/resource/patches/cereal_gcc46.patch @@ -0,0 +1,485 @@ +From 17300ee96e42f8848d27db6fc97f04de293662d8 Mon Sep 17 00:00:00 2001 +From: Erich Keane +Date: Thu, 6 Nov 2014 14:37:00 -0800 +Subject: [PATCH] Get this to work on g++4.6.3 + +--- + include/cereal/cereal.hpp | 2 +- + include/cereal/details/helpers.hpp | 32 ++++++++-------- + include/cereal/details/traits.hpp | 61 +++++++++++++++++------------- + include/cereal/external/rapidjson/reader.h | 13 ++----- + include/cereal/external/rapidjson/writer.h | 12 ++---- + include/cereal/types/common.hpp | 19 +++++++--- + include/cereal/types/memory.hpp | 10 ++--- + 7 files changed, 77 insertions(+), 72 deletions(-) + +diff --git a/include/cereal/cereal.hpp b/include/cereal/cereal.hpp +index b2858af..a219729 100644 +--- a/include/cereal/cereal.hpp ++++ b/include/cereal/cereal.hpp +@@ -856,7 +856,7 @@ namespace cereal + std::uint32_t version; + + process( make_nvp("cereal_class_version", version) ); +- itsVersionedTypes.emplace_hint( lookupResult, hash, version ); ++ itsVersionedTypes.insert( lookupResult, std::pair(hash, version) ); + + return version; + } +diff --git a/include/cereal/details/helpers.hpp b/include/cereal/details/helpers.hpp +index e792d44..60e13c8 100644 +--- a/include/cereal/details/helpers.hpp ++++ b/include/cereal/details/helpers.hpp +@@ -55,7 +55,7 @@ namespace cereal + /*! To ensure compatability between 32, 64, etc bit machines, we need to use + * a fixed size type instead of size_t, which may vary from machine to + * machine. */ +- using size_type = uint64_t; ++ typedef uint64_t size_type; + + // forward decls + class BinaryOutputArchive; +@@ -138,12 +138,12 @@ namespace cereal + // otherwise, we store a reference. If we were passed an array, don't + // decay the type - keep it as an array, and then proceed as normal + // with the RValue business +- using DT = typename std::conditional::type>::value, ++ typedef typename std::conditional::type>::value, + typename std::remove_cv::type, +- typename std::decay::type>::type; +- using Type = typename std::conditional::value, ++ typename std::decay::type>::type DT; ++ typedef typename std::conditional::value, + DT, +- typename std::add_lvalue_reference
::type>::type; ++ typename std::add_lvalue_reference
::type>::type Type; + // prevent nested nvps + static_assert( !std::is_base_of::value, + "Cannot pair a name to a NameValuePair" ); +@@ -207,9 +207,9 @@ namespace cereal + { + //! Internally store the pointer as a void *, keeping const if created with + //! a const pointer +- using PT = typename std::conditional::type>::value, ++ typedef typename std::conditional::type>::value, + const void *, +- void *>::type; ++ void *>::type PT; + + BinaryData( T && d, uint64_t s ) : data(d), size(s) {} + +@@ -248,10 +248,10 @@ namespace cereal + private: + // If we get passed an RValue, we'll just make a local copy if it here + // otherwise, we store a reference +- using DT = typename std::decay::type; +- using Type = typename std::conditional::value, ++ typedef typename std::decay::type DT; ++ typedef typename std::conditional::value, + DT, +- typename std::add_lvalue_reference
::type>::type; ++ typename std::add_lvalue_reference
::type>::type Type; + + public: + SizeTag( T && sz ) : size(const_cast(sz)) {} +@@ -283,17 +283,17 @@ namespace cereal + template + struct MapItem + { +- using DecayKey = typename std::decay::type; +- using KeyType = typename std::conditional< ++ typedef typename std::decay::type DecayKey; ++ typedef typename std::conditional< + std::is_rvalue_reference::value, + DecayKey, +- typename std::add_lvalue_reference::type>::type; ++ typename std::add_lvalue_reference::type>::type KeyType; + +- using DecayValue = typename std::decay::type; +- using ValueType = typename std::conditional< ++ typedef typename std::decay::type DecayValue; ++ typedef typename std::conditional< + std::is_rvalue_reference::value, + DecayValue, +- typename std::add_lvalue_reference::type>::type; ++ typename std::add_lvalue_reference::type>::type ValueType; + + //! Construct a MapItem from a key and a value + /*! @internal */ +diff --git a/include/cereal/details/traits.hpp b/include/cereal/details/traits.hpp +index 871886f..011054b 100644 +--- a/include/cereal/details/traits.hpp ++++ b/include/cereal/details/traits.hpp +@@ -411,12 +411,12 @@ namespace cereal + }; + + template +- struct get_member_save_minimal_type { using type = void; }; ++ struct get_member_save_minimal_type { typedef void type; }; + + template + struct get_member_save_minimal_type + { +- using type = decltype( cereal::access::member_save_minimal( std::declval(), std::declval() ) ); ++ typedef decltype( cereal::access::member_save_minimal( std::declval(), std::declval() ) ) type; + }; + } // end namespace detail + +@@ -428,7 +428,7 @@ namespace cereal + "cereal detected a non-const member save_minimal. " + "save_minimal member functions must always be const" ); + +- using type = typename detail::get_member_save_minimal_type::type; ++ typedef typename detail::get_member_save_minimal_type::type type; + static_assert( (check::value && is_minimal_type::value) || !check::value, + "cereal detected a member save_minimal with an invalid return type. " + "return type must be arithmetic or string" ); +@@ -473,12 +473,12 @@ namespace cereal + }; + + template +- struct get_member_versioned_save_minimal_type { using type = void; }; ++ struct get_member_versioned_save_minimal_type { typedef void type; }; + + template + struct get_member_versioned_save_minimal_type + { +- using type = decltype( cereal::access::member_save_minimal( std::declval(), std::declval(), 0 ) ); ++ typedef decltype( cereal::access::member_save_minimal( std::declval(), std::declval(), 0 ) ) type; + }; + } // end namespace detail + +@@ -490,7 +490,7 @@ namespace cereal + "cereal detected a versioned non-const member save_minimal. " + "save_minimal member functions must always be const" ); + +- using type = typename detail::get_member_versioned_save_minimal_type::type; ++ typedef typename detail::get_member_versioned_save_minimal_type::type type; + static_assert( (check::value && is_minimal_type::value) || !check::value, + "cereal detected a versioned member save_minimal with an invalid return type. " + "return type must be arithmetic or string" ); +@@ -519,12 +519,12 @@ namespace cereal + }; + + template +- struct get_non_member_save_minimal_type { using type = void; }; ++ struct get_non_member_save_minimal_type { typedef void type; }; + + template + struct get_non_member_save_minimal_type + { +- using type = decltype( save_minimal( std::declval(), std::declval() ) ); ++ typedef decltype( save_minimal( std::declval(), std::declval() ) ) type; + }; + } // end namespace detail + +@@ -536,7 +536,7 @@ namespace cereal + "cereal detected a non-const type parameter in non-member save_minimal. " + "save_minimal non-member functions must always pass their types as const" ); + +- using type = typename detail::get_non_member_save_minimal_type::type; ++ typedef typename detail::get_non_member_save_minimal_type::type type; + static_assert( (check::value && is_minimal_type::value) || !check::value, + "cereal detected a non-member save_minimal with an invalid return type. " + "return type must be arithmetic or string" ); +@@ -565,12 +565,12 @@ namespace cereal + }; + + template +- struct get_non_member_versioned_save_minimal_type { using type = void; }; ++ struct get_non_member_versioned_save_minimal_type { typedef void type; }; + + template + struct get_non_member_versioned_save_minimal_type + { +- using type = decltype( save_minimal( std::declval(), std::declval(), 0 ) ); ++ typedef decltype( save_minimal( std::declval(), std::declval(), 0 ) ) type; + }; + } // end namespace detail + +@@ -582,7 +582,7 @@ namespace cereal + "cereal detected a non-const type parameter in versioned non-member save_minimal. " + "save_minimal non-member functions must always pass their types as const" ); + +- using type = typename detail::get_non_member_versioned_save_minimal_type::type; ++ typedef typename detail::get_non_member_versioned_save_minimal_type::type type; + static_assert( (check::value && is_minimal_type::value) || !check::value, + "cereal detected a non-member versioned save_minimal with an invalid return type. " + "return type must be arithmetic or string" ); +@@ -608,7 +608,7 @@ namespace cereal + template + struct NoConvertConstRef : NoConvertBase + { +- using type = Source; //!< Used to get underlying type easily ++ typedef Source type; //!< Used to get underlying type easily + + template ::value>::type> + operator Dest () = delete; +@@ -626,7 +626,7 @@ namespace cereal + template + struct NoConvertRef : NoConvertBase + { +- using type = Source; //!< Used to get underlying type easily ++ typedef Source type; //!< Used to get underlying type easily + + template ::value>::type> + operator Dest () = delete; +@@ -698,7 +698,7 @@ namespace cereal + "cereal detected member load_minimal but no valid member save_minimal. " + "cannot evaluate correctness of load_minimal without valid save_minimal." ); + +- using SaveType = typename detail::get_member_save_minimal_type::type; ++ typedef typename detail::get_member_save_minimal_type::type SaveType; + const static bool value = has_member_load_minimal_impl::value; + const static bool valid = has_member_load_minimal_type_impl::value; + +@@ -759,7 +759,7 @@ namespace cereal + "cereal detected member versioned load_minimal but no valid member versioned save_minimal. " + "cannot evaluate correctness of load_minimal without valid save_minimal." ); + +- using SaveType = typename detail::get_member_versioned_save_minimal_type::type; ++ typedef typename detail::get_member_versioned_save_minimal_type::type SaveType; + const static bool value = has_member_versioned_load_minimal_impl::value; + const static bool valid = has_member_versioned_load_minimal_type_impl::value; + +@@ -814,8 +814,8 @@ namespace cereal + "cereal detected non-member load_minimal but no valid non-member save_minimal. " + "cannot evaluate correctness of load_minimal without valid save_minimal." ); + +- using SaveType = typename detail::get_non_member_save_minimal_type::type; +- using check = has_non_member_load_minimal_impl; ++ typedef typename detail::get_non_member_save_minimal_type::type SaveType; ++ typedef has_non_member_load_minimal_impl check; + static const bool value = check::exists; + + static_assert( check::valid || !check::exists, "cereal detected different types in corresponding non-member load_minimal and save_minimal functions. " +@@ -866,8 +866,8 @@ namespace cereal + "cereal detected non-member versioned load_minimal but no valid non-member versioned save_minimal. " + "cannot evaluate correctness of load_minimal without valid save_minimal." ); + +- using SaveType = typename detail::get_non_member_versioned_save_minimal_type::type; +- using check = has_non_member_versioned_load_minimal_impl; ++ typedef typename detail::get_non_member_versioned_save_minimal_type::type SaveType; ++ typedef has_non_member_versioned_load_minimal_impl check;; + static const bool value = check::exists; + + static_assert( check::valid || !check::exists, "cereal detected different types in corresponding non-member versioned load_minimal and save_minimal functions. " +@@ -1182,9 +1182,16 @@ namespace cereal + }; + } + ++ // works around the lack of decltype inheritance in GCC 4.6 ++ template ++ struct shared_wrapper ++ { ++ typedef decltype(detail::shared_from_this_wrapper::check(std::declval())) type; ++ ++ }; + //! Determine if T or any base class of T has inherited from std::enable_shared_from_this + template +- struct has_shared_from_this : decltype(detail::shared_from_this_wrapper::check(std::declval())) ++ struct has_shared_from_this : shared_wrapper::type + { }; + + //! Get the type of the base class of T which inherited from std::enable_shared_from_this +@@ -1192,10 +1199,10 @@ namespace cereal + struct get_shared_from_this_base + { + private: +- using PtrType = decltype(detail::shared_from_this_wrapper::get(std::declval())); ++ typedef decltype(detail::shared_from_this_wrapper::get(std::declval())) PtrType; + public: + //! The type of the base of T that inherited from std::enable_shared_from_this +- using type = typename std::decay::type; ++ typedef typename std::decay::type type; + }; + + // ###################################################################### +@@ -1209,14 +1216,14 @@ namespace cereal + template ::value> + struct strip_minimal + { +- using type = T; ++ typedef T type; + }; + + //! Specialization for types wrapped in a NoConvert + template + struct strip_minimal + { +- using type = typename T::type; ++ typedef typename T::type type; + }; + } // namespace traits + +@@ -1232,10 +1239,12 @@ namespace cereal + { return nullptr; } + }; + ++ template ++ struct is_default_constructible : std::is_constructible{}; + template + struct Construct + { +- static_assert( std::is_default_constructible::value, ++ static_assert( is_default_constructible::value, + "Trying to serialize a an object with no default constructor. \n\n " + "Types must either be default constructible or define either a member or non member Construct function. \n " + "Construct functions generally have the signature: \n\n " +diff --git a/include/cereal/external/rapidjson/reader.h b/include/cereal/external/rapidjson/reader.h +index 7790907..3ee838c 100644 +--- a/include/cereal/external/rapidjson/reader.h ++++ b/include/cereal/external/rapidjson/reader.h +@@ -402,20 +402,13 @@ private: + } + + // cereal Temporary until constexpr support is added in RTM +-#ifdef _MSC_VER ++//#ifdef _MSC_VER + template + bool characterOk( Ch c ) + { + return c < 256; + } +- +- template <> +- bool characterOk( Ch ) +- { +- return true; +- } +- +-#else ++/*#else + // As part of a fix for GCC 4.7 + template + static constexpr int to_int( T t ){ return t; } +@@ -432,7 +425,7 @@ private: + characterOk(Ch c) + { return c < 256; } + #endif +- ++*/ + // Parse string, handling the prefix and suffix double quotes and escaping. + template + void ParseString(Stream& stream, Handler& handler) { +diff --git a/include/cereal/external/rapidjson/writer.h b/include/cereal/external/rapidjson/writer.h +index 0f87255..e02c27a 100644 +--- a/include/cereal/external/rapidjson/writer.h ++++ b/include/cereal/external/rapidjson/writer.h +@@ -177,20 +177,14 @@ protected: + } + + // cereal Temporary until constexpr support is added in RTM +-#ifdef _MSC_VER ++//#ifdef _MSC_VER + template + bool characterOk( Ch c ) + { + return c < 256; + } + +- template <> +- bool characterOk( Ch ) +- { +- return true; +- } +- +-#else ++/*#else + // As part of a fix for GCC 4.7 + template + static constexpr int to_int( T t ){ return t; } +@@ -206,7 +200,7 @@ protected: + typename std::enable_if< to_int(std::numeric_limits::max()) >= to_int(256), bool>::type + characterOk(Ch c) + { return c < 256; } +-#endif ++#endif*/ + + //! \todo Optimization with custom double-to-string converter. + void WriteDouble(double d) { +diff --git a/include/cereal/types/common.hpp b/include/cereal/types/common.hpp +index abb8bfd..5c014cd 100644 +--- a/include/cereal/types/common.hpp ++++ b/include/cereal/types/common.hpp +@@ -55,6 +55,15 @@ namespace cereal + + namespace + { ++ template ++ struct underlying_type ++ { ++ typedef typename std::conditional< ++ en(-1)::type, ++ typename std::make_unsigned::type ++ > ::type type; ++ }; + //! Gets the underlying type of an enum + /*! @internal */ + template +@@ -64,7 +73,7 @@ namespace cereal + /*! Specialization for when we actually have an enum + @internal */ + template +- struct enum_underlying_type { using type = typename std::underlying_type::type; }; ++ struct enum_underlying_type { typedef typename underlying_type::type type; }; + } // anon namespace + + //! Checks if a type is an enum +@@ -78,13 +87,13 @@ namespace cereal + class is_enum + { + private: +- using DecayedT = typename std::decay::type; +- using StrippedT = typename ::cereal::traits::strip_minimal::type; ++ typedef typename std::decay::type DecayedT; ++ typedef typename ::cereal::traits::strip_minimal::type StrippedT; + + public: + static const bool value = std::is_enum::value; +- using type = StrippedT; +- using base_type = typename enum_underlying_type::type; ++ typedef StrippedT type; ++ typedef typename enum_underlying_type::type base_type; + }; + } + +diff --git a/include/cereal/types/memory.hpp b/include/cereal/types/memory.hpp +index bf56c92..d2357ff 100644 +--- a/include/cereal/types/memory.hpp ++++ b/include/cereal/types/memory.hpp +@@ -115,9 +115,9 @@ namespace cereal + class EnableSharedStateHelper + { + // typedefs for parent type and storage type +- using BaseType = typename ::cereal::traits::get_shared_from_this_base::type; +- using ParentType = std::enable_shared_from_this; +- using StorageType = typename std::aligned_storage::type; ++ typedef typename ::cereal::traits::get_shared_from_this_base::type BaseType; ++ typedef std::enable_shared_from_this ParentType; ++ typedef typename std::aligned_storage::type StorageType; + + public: + //! Saves the state of some type inheriting from enable_shared_from_this +@@ -257,7 +257,7 @@ namespace cereal + { + // Storage type for the pointer - since we can't default construct this type, + // we'll allocate it using std::aligned_storage and use a custom deleter +- using ST = typename std::aligned_storage::type; ++ typedef typename std::aligned_storage::type ST; + + // Valid flag - set to true once construction finishes + // This prevents us from calling the destructor on +@@ -345,7 +345,7 @@ namespace cereal + { + // Storage type for the pointer - since we can't default construct this type, + // we'll allocate it using std::aligned_storage +- using ST = typename std::aligned_storage::type; ++ typedef typename std::aligned_storage::type ST; + + // Allocate storage - note the ST type so that deleter is correct if + // an exception is thrown before we are initialized +-- +1.9.3 + diff --git a/resource/src/InProcClientWrapper.cpp b/resource/src/InProcClientWrapper.cpp index 6225119..0072783 100644 --- a/resource/src/InProcClientWrapper.cpp +++ b/resource/src/InProcClientWrapper.cpp @@ -18,23 +18,19 @@ // //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -#include - #include "InProcClientWrapper.h" #include "ocstack.h" #include "OCPlatform.h" #include "OCResource.h" - +#include using namespace std; namespace OC { - InProcClientWrapper::InProcClientWrapper(OC::OCPlatform_impl& owner, + InProcClientWrapper::InProcClientWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) - : IClientWrapper(owner), - m_threadRun(false), m_csdkLock(csdkLock), - m_owner(owner), + : m_threadRun(false), m_csdkLock(csdkLock), m_cfg { cfg } { // if the config type is server, we ought to never get called. If the config type @@ -91,90 +87,11 @@ namespace OC } } - std::string InProcClientWrapper::convertOCAddrToString(OCDevAddr& addr, - OCSecureType type, const std::string &portStr) - { - // TODO: we currently assume this is a IPV4 address, need to figure out the actual value - - uint8_t a, b, c, d; - uint16_t port; - - if(OCDevAddrToIPv4Addr(&addr, &a, &b, &c, &d) ==0 && OCDevAddrToPort(&addr, &port)==0) - { - ostringstream os; - if(type == OCSecureType::IPV4) - { - os << "coap://" << static_cast(a) << '.' << - static_cast(b) << '.' << static_cast(c) << - '.' << static_cast(d) << ':' <(port); - } - else if(type == OCSecureType::IPV4Secure) - { - os << "coaps://" << static_cast(a) <<'.' << - static_cast(b) <<'.' << static_cast(c) << - '.' << static_cast(d) << ':' << portStr; - } - return os.str(); - } - else - { - return OC::Error::INVALID_IP; - } - } - - struct ListenContext - { - FindCallback callback; - IClientWrapper::Ptr clientWrapper; - }; - - - std::shared_ptr InProcClientWrapper::parseOCResource( - IClientWrapper::Ptr clientWrapper, OCDevAddr& addr, - const boost::property_tree::ptree resourceNode) - { - std::string uri = resourceNode.get(OC::Key::URIKEY, ""); - bool obs = resourceNode.get(OC::Key::OBSERVABLEKEY,0) == 1; - std::vector rTs; - std::vector ifaces; - - boost::property_tree::ptree properties = - resourceNode.get_child(OC::Key::PROPERTYKEY, boost::property_tree::ptree()); - - boost::property_tree::ptree rT = - properties.get_child(OC::Key::RESOURCETYPESKEY, boost::property_tree::ptree()); - for(auto itr : rT) - { - rTs.push_back(itr.second.data()); - } - bool secure = properties.get(OC::Key::SECUREKEY,0) == 1; - - boost::property_tree::ptree iF = - properties.get_child(OC::Key::INTERFACESKEY, boost::property_tree::ptree()); - for(auto itr : iF) - { - ifaces.push_back(itr.second.data()); - } - - std::string host; - if(secure) - { - string port = properties.get(OC::Key::PORTKEY,""); - host= convertOCAddrToString(addr, OCSecureType::IPV4Secure, port); - } - else - { - host= convertOCAddrToString(addr, OCSecureType::IPV4); - } - - return std::shared_ptr( - new OCResource(clientWrapper, host, uri, obs, rTs, ifaces)); - } - OCStackApplicationResult listenCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - ListenContext* context = static_cast(ctx); + ClientCallbackContext::ListenContext* context = + static_cast(ctx); if(clientResponse->result != OC_STACK_OK) { @@ -188,48 +105,31 @@ namespace OC std::stringstream requestStream; requestStream << clientResponse->resJSONPayload; - boost::property_tree::ptree root; - try { - boost::property_tree::read_json(requestStream, root); - } - catch(boost::property_tree::json_parser::json_parser_error &e) - { - oclog() << "listenCallback(): read_json() failed: " << e.what() - << std::flush; + ListenOCContainer container(context->clientWrapper, *clientResponse->addr, + requestStream); - return OC_STACK_KEEP_TRANSACTION; - } - - boost::property_tree::ptree payload = - root.get_child(OC::Key::OCKEY, boost::property_tree::ptree()); + // loop to ensure valid construction of all resources + for(auto resource : container.Resources()) + { + std::thread exec(context->callback, resource); + exec.detach(); + } - for(auto payloadItr : payload) + } + catch(const std::exception& e) { - try - { - std::shared_ptr resource = - context->clientWrapper->parseOCResource(context->clientWrapper, - *clientResponse->addr, - payloadItr.second); - - // Note: the call to detach allows the underlying thread to continue until - // completion and allows us to destroy the exec object. This is apparently NOT - // a memory leak, as the thread will apparently take care of itself. - // Additionally, the only parameter here is a shared ptr, so OCResource will be - // disposed of properly upon completion of the callback handler. - std::thread exec(context->callback,resource); - exec.detach(); - } - catch(ResourceInitException& e) - { - oclog() << "listenCallback(): failed to create resource: " << e.what() - << std::flush; - } + oclog() << "listenCallback failed to parse a malformed message: " + << e.what() + << std::endl <result + << std::flush; + return OC_STACK_KEEP_TRANSACTION; } return OC_STACK_KEEP_TRANSACTION; + } OCStackResult InProcClientWrapper::ListenForResource(const std::string& serviceUrl, @@ -239,13 +139,13 @@ namespace OC OCCallbackData cbdata = {0}; - ListenContext* context = new ListenContext(); + ClientCallbackContext::ListenContext* context = new ClientCallbackContext::ListenContext(); context->callback = callback; context->clientWrapper = shared_from_this(); cbdata.context = static_cast(context); cbdata.cb = listenCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; auto cLock = m_csdkLock.lock(); if(cLock) @@ -266,124 +166,31 @@ namespace OC return result; } - struct GetContext - { - GetCallback callback; - }; - - struct SetContext - { - PutCallback callback; - }; - - OCRepresentation parseGetSetCallback(OCClientResponse* clientResponse) { - std::stringstream requestStream; - requestStream<resJSONPayload; - if(strlen((char*)clientResponse->resJSONPayload) == 0) + if(clientResponse->resJSONPayload == nullptr || clientResponse->resJSONPayload[0] == '\0') { return OCRepresentation(); } - boost::property_tree::ptree root; - try - { - boost::property_tree::read_json(requestStream, root); - } - catch(boost::property_tree::json_parser::json_parser_error &e) + MessageContainer oc; + oc.setJSONRepresentation(clientResponse->resJSONPayload); + + std::vector::const_iterator it = oc.representations().begin(); + if(it == oc.representations().end()) { return OCRepresentation(); } - boost::property_tree::ptree payload = root.get_child(OC::Key::OCKEY, boost::property_tree::ptree()); - OCRepresentation root_resource; - std::vector children; - bool isRoot = true; - for ( auto payloadItr : payload) - { - OCRepresentation child; - try - { - auto resourceNode = payloadItr.second; - std::string uri = resourceNode.get(OC::Key::URIKEY, ""); - - if (isRoot) - { - root_resource.setUri(uri); - } - else - { - child.setUri(uri); - } - - if( resourceNode.count(OC::Key::PROPERTYKEY) != 0 ) - { - std::vector rTs; - std::vector ifaces; - boost::property_tree::ptree properties = - resourceNode.get_child(OC::Key::PROPERTYKEY, boost::property_tree::ptree()); - - boost::property_tree::ptree rT = - properties.get_child(OC::Key::RESOURCETYPESKEY, - boost::property_tree::ptree()); - for(auto itr : rT) - { - rTs.push_back(itr.second.data()); - } - - boost::property_tree::ptree iF = - properties.get_child(OC::Key::INTERFACESKEY, boost::property_tree::ptree()); - for(auto itr : iF) - { - ifaces.push_back(itr.second.data()); - } - if (isRoot) - { - root_resource.setResourceInterfaces(ifaces); - root_resource.setResourceTypes(rTs); - } - else - { - child.setResourceInterfaces(ifaces); - child.setResourceTypes(rTs); - } - } - - if( resourceNode.count(OC::Key::REPKEY) != 0 ) - { - boost::property_tree::ptree rep = - resourceNode.get_child(OC::Key::REPKEY, boost::property_tree::ptree()); - AttributeMap attrs; - for( auto item : rep) - { - std::string name = item.first.data(); - std::string value = item.second.data(); - attrs[name] = value; - } - if (isRoot) - { - root_resource.setAttributeMap(attrs); - } - else - { - child.setAttributeMap(attrs); - } - } - - if (!isRoot) - children.push_back(child); - } - catch (...) - { - // TODO - } - isRoot = false; - } + // first one is considered the root, everything else is considered a child of this one. + OCRepresentation root = *it; + ++it; - root_resource.setChildren(children); + std::for_each(it, oc.representations().end(), + [&root](const OCRepresentation& repItr) + {root.addChild(repItr);}); + return root; - return root_resource; } void parseServerHeaderOptions(OCClientResponse* clientResponse, @@ -415,7 +222,8 @@ namespace OC OCStackApplicationResult getResourceCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - GetContext* context = static_cast(ctx); + ClientCallbackContext::GetContext* context = + static_cast(ctx); OCRepresentation rep; HeaderOptions serverHeaderOptions; @@ -438,11 +246,11 @@ namespace OC OCStackResult result; OCCallbackData cbdata = {0}; - GetContext* ctx = new GetContext(); + ClientCallbackContext::GetContext* ctx = new ClientCallbackContext::GetContext(); ctx->callback = callback; cbdata.context = static_cast(ctx); cbdata.cb = &getResourceCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; auto cLock = m_csdkLock.lock(); @@ -473,7 +281,8 @@ namespace OC OCStackApplicationResult setResourceCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - SetContext* context = static_cast(ctx); + ClientCallbackContext::SetContext* context = + static_cast(ctx); OCRepresentation attrs; HeaderOptions serverHeaderOptions; @@ -521,14 +330,9 @@ namespace OC std::string InProcClientWrapper::assembleSetResourcePayload(const OCRepresentation& rep) { - ostringstream payload; - // TODO need to change the format to "{"oc":[]}" - payload << "{\"oc\":"; - - payload << rep.getJSONRepresentation(); - - payload << "}"; - return payload.str(); + MessageContainer ocInfo; + ocInfo.addRepresentation(rep); + return ocInfo.getJSONRepresentation(OCInfoFormat::IncludeOC); } OCStackResult InProcClientWrapper::PostResourceRepresentation(const std::string& host, @@ -539,10 +343,10 @@ namespace OC OCStackResult result; OCCallbackData cbdata = {0}; - SetContext* ctx = new SetContext(); + ClientCallbackContext::SetContext* ctx = new ClientCallbackContext::SetContext(); ctx->callback = callback; cbdata.cb = &setResourceCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; cbdata.context = static_cast(ctx); // TODO: in the future the cstack should be combining these two strings! @@ -582,10 +386,10 @@ namespace OC OCStackResult result; OCCallbackData cbdata = {0}; - SetContext* ctx = new SetContext(); + ClientCallbackContext::SetContext* ctx = new ClientCallbackContext::SetContext(); ctx->callback = callback; cbdata.cb = &setResourceCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; cbdata.context = static_cast(ctx); // TODO: in the future the cstack should be combining these two strings! @@ -617,16 +421,11 @@ namespace OC return result; } - struct DeleteContext - { - DeleteCallback callback; - }; - OCStackApplicationResult deleteResourceCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - DeleteContext* context = static_cast(ctx); - OCRepresentation attrs; + ClientCallbackContext::DeleteContext* context = + static_cast(ctx); HeaderOptions serverHeaderOptions; if(clientResponse->result == OC_STACK_OK) @@ -645,10 +444,10 @@ namespace OC OCStackResult result; OCCallbackData cbdata = {0}; - DeleteContext* ctx = new DeleteContext(); + ClientCallbackContext::DeleteContext* ctx = new ClientCallbackContext::DeleteContext(); ctx->callback = callback; cbdata.cb = &deleteResourceCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; cbdata.context = static_cast(ctx); ostringstream os; @@ -678,15 +477,11 @@ namespace OC return result; } - struct ObserveContext - { - ObserveCallback callback; - }; - OCStackApplicationResult observeResourceCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - ObserveContext* context = static_cast(ctx); + ClientCallbackContext::ObserveContext* context = + static_cast(ctx); OCRepresentation attrs; HeaderOptions serverHeaderOptions; uint32_t sequenceNumber = clientResponse->sequenceNumber; @@ -709,11 +504,11 @@ namespace OC OCStackResult result; OCCallbackData cbdata = {0}; - ObserveContext* ctx = new ObserveContext(); + ClientCallbackContext::ObserveContext* ctx = new ClientCallbackContext::ObserveContext(); ctx->callback = callback; cbdata.context = static_cast(ctx); cbdata.cb = &observeResourceCallback; - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c){delete static_cast(c);}; OCMethod method; if (observeType == ObserveType::Observe) @@ -768,7 +563,8 @@ namespace OC OCHeaderOption options[MAX_HEADER_OPTIONS]; assembleHeaderOptions(options, headerOptions); - result = OCCancel(handle, static_cast(QoS), options, headerOptions.size()); + result = OCCancel(handle, static_cast(QoS), options, + headerOptions.size()); } else { @@ -778,15 +574,11 @@ namespace OC return result; } - struct SubscribePresenceContext - { - SubscribeCallback callback; - }; - OCStackApplicationResult subscribePresenceCallback(void* ctx, OCDoHandle handle, OCClientResponse* clientResponse) { - SubscribePresenceContext* context = static_cast(ctx); + ClientCallbackContext::SubscribePresenceContext* context = + static_cast(ctx); std::thread exec(context->callback, clientResponse->result, clientResponse->sequenceNumber); exec.detach(); @@ -799,11 +591,13 @@ namespace OC { OCCallbackData cbdata = {0}; - SubscribePresenceContext* ctx = new SubscribePresenceContext(); + ClientCallbackContext::SubscribePresenceContext* ctx = + new ClientCallbackContext::SubscribePresenceContext(); ctx->callback = presenceHandler; cbdata.cb = &subscribePresenceCallback; cbdata.context = static_cast(ctx); - cbdata.cd = [](void* c){delete static_cast(c);}; + cbdata.cd = [](void* c) + {delete static_cast(c);}; auto cLock = m_csdkLock.lock(); std::ostringstream os; diff --git a/resource/src/InProcServerWrapper.cpp b/resource/src/InProcServerWrapper.cpp index c057c7d..751dc87 100644 --- a/resource/src/InProcServerWrapper.cpp +++ b/resource/src/InProcServerWrapper.cpp @@ -280,10 +280,9 @@ OCEntityHandlerResult EntityHandlerWrapper(OCEntityHandlerFlag flag, namespace OC { - InProcServerWrapper::InProcServerWrapper(OC::OCPlatform_impl& owner, + InProcServerWrapper::InProcServerWrapper( std::weak_ptr csdkLock, PlatformConfig cfg) - : IServerWrapper(owner), - m_csdkLock(csdkLock) + : m_csdkLock(csdkLock) { OCMode initType; diff --git a/resource/src/OCPlatform_impl.cpp b/resource/src/OCPlatform_impl.cpp index 4671c2b..fee95c9 100644 --- a/resource/src/OCPlatform_impl.cpp +++ b/resource/src/OCPlatform_impl.cpp @@ -68,16 +68,16 @@ namespace OC switch(config.mode) { case ModeType::Server: - m_server = m_WrapperInstance->CreateServerWrapper(*this, m_csdkLock, config); + m_server = m_WrapperInstance->CreateServerWrapper(m_csdkLock, config); break; case ModeType::Client: - m_client = m_WrapperInstance->CreateClientWrapper(*this, m_csdkLock, config); + m_client = m_WrapperInstance->CreateClientWrapper(m_csdkLock, config); break; case ModeType::Both: - m_server = m_WrapperInstance->CreateServerWrapper(*this, m_csdkLock, config); - m_client = m_WrapperInstance->CreateClientWrapper(*this, m_csdkLock, config); + m_server = m_WrapperInstance->CreateServerWrapper(m_csdkLock, config); + m_client = m_WrapperInstance->CreateClientWrapper(m_csdkLock, config); break; } } @@ -129,7 +129,7 @@ namespace OC return result_guard(OC_STACK_ERROR); } - std::string payload(pResponse->getPayload()); + std::string payload(pResponse->getResourceRepresentation().getJSONRepresentation()); return result_guard( OCNotifyListOfObservers(resourceHandle, diff --git a/resource/src/OCRepresentation.cpp b/resource/src/OCRepresentation.cpp new file mode 100644 index 0000000..f251818 --- /dev/null +++ b/resource/src/OCRepresentation.cpp @@ -0,0 +1,486 @@ +//****************************************************************** +// +// Copyright 2014 Intel Mobile Communications GmbH All Rights Reserved. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +/// @file OCRepresentation.cpp + +/// @brief This file contains the implementation of classes and its members +/// related to OCRepresentation + +#include +#include +#include +#include +#include +#include +namespace OC +{ + typedef cereal::JSONOutputArchive OutputArchiveType; + typedef cereal::JSONInputArchive InputArchiveType; + + void MessageContainer::setJSONRepresentation(const std::string& payload) + { + std::stringstream os(payload); + { + InputArchiveType archive(os); + archive(cereal::make_nvp(OC::Key::OCKEY, m_reps)); + } + } + + void MessageContainer::setJSONRepresentation(const unsigned char* payload) + { + setJSONRepresentation(std::string(reinterpret_cast(payload))); + } + + std::string MessageContainer::getJSONRepresentation(OCInfoFormat f) const + { + std::stringstream os; + + // note: the block is required because cereal closes the JSON string + // upon destruction, so the archive needs to be destroyed before accessing + // the data + { + if(f == OCInfoFormat::IncludeOC) + { + OutputArchiveType archive(os); + archive(cereal::make_nvp(OC::Key::OCKEY, m_reps)); + } + else if(f== OCInfoFormat::ExcludeOC) + { + bool firstPrinted = false; + for(std::vector::size_type i = 0; i< m_reps.size();++i) + { + if(!m_reps[i].empty()) + { + if(firstPrinted) + { + os<<','; + } + firstPrinted=true; + os << m_reps[i].getJSONRepresentation(); + } + } + } + } + return os.str(); + } + + const std::vector& MessageContainer::representations() const + { + return m_reps; + } + + void MessageContainer::addRepresentation(const OCRepresentation& rep) + { + m_reps.push_back(rep); + } +} + +namespace OC +{ + OCRepresentation::OCRepresentation() + :m_interfaceType(InterfaceType::None) + { } + std::string OCRepresentation::getJSONRepresentation() const + { + std::stringstream os; + + // note: the block is required because cereal closes the JSON string + // upon destruction, so the archive needs to be destroyed before accessing + // the data + { + OutputArchiveType archive (os); + save(archive); + } + + return os.str(); + } + + void OCRepresentation::addChild(const OCRepresentation& rep) + { + m_children.push_back(rep); + } + + void OCRepresentation::clearChildren() + { + m_children.clear(); + } + + const std::vector& OCRepresentation::getChildren() const + { + return m_children; + } + + void OCRepresentation::setUri(const std::string& uri) + { + m_uri = uri; + } + + std::string OCRepresentation::getUri() const + { + return m_uri; + } + + const std::vector& OCRepresentation::getResourceTypes() const + { + return m_resourceTypes; + } + + void OCRepresentation::setResourceTypes(const std::vector& resourceTypes) + { + m_resourceTypes = resourceTypes; + } + + const std::vector& OCRepresentation::getResourceInterfaces() const + { + return m_interfaces; + } + + void OCRepresentation::setResourceInterfaces(const std::vector& resourceInterfaces) + { + m_interfaces = resourceInterfaces; + } + + bool OCRepresentation::hasAttribute(const std::string& str) const + { + return m_values.find(str) != m_values.end(); + } + + bool OCRepresentation::empty() const + { + // This logic is meant to determine whether based on the JSON serialization rules + // if this object will result in empty JSON. URI is only serialized if there is valid + // data, ResourceType and Interfaces are only serialized if we are a nothing, a + // child of a default or link item. + // Our values array is only printed in the if we are the child of a Batch resource, + // the parent in a 'default' situation, or not in a child/parent relationship. + if(!m_uri.empty()) + { + return false; + } + else if ((m_interfaceType == InterfaceType::None + || m_interfaceType==InterfaceType::DefaultChild + || m_interfaceType==InterfaceType::LinkChild) + && (m_resourceTypes.size()>0 || m_interfaces.size()>0)) + { + return false; + } + else if((m_interfaceType == InterfaceType::None + || m_interfaceType == InterfaceType::BatchChild + || m_interfaceType == InterfaceType::DefaultParent) + && m_values.size()>0) + { + return false; + } + + return true; + } + + int OCRepresentation::numberOfAttributes() const + { + return m_values.size(); + } + + bool OCRepresentation::erase(const std::string& str) + { + return m_values.erase(str); + } + + void OCRepresentation::setNULL(const std::string& str) + { + m_values[str] = OC::NullType(); + } + + bool OCRepresentation::isNULL(const std::string& str) const + { + auto x = m_values.find(str); + + if(m_values.end() != x) + { + return x->second.which() == AttributeValueNullIndex; + } + else + { + throw OCException(OC::Exception::INVALID_ATTRIBUTE+ str); + } + } +} + +// note: the below is used to load an AttributeValue map out of JSON +namespace OC +{ + namespace detail + { + enum class typeTag:uint8_t + { + NOTHING = 0, + _string, + _int, + _double, + _bool, + _representation + }; + + typedef rapidjson::Document::GenericValue GenericValue; + + AttributeValue parseAttributeValue(const GenericValue& v); + AttributeValue parseAttributeValue(const GenericValue& v, + const unsigned int curLevel, unsigned int& maxDepth, typeTag& t); + AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t); + AttributeValue parseAttributeValueArray(const GenericValue& v, + const unsigned int curLevel, unsigned int& maxDepth, typeTag& t); + AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t); + + AttributeValue parseAttributeValue(const GenericValue& v) + { + // base entrance, start everything at '0' + unsigned int max_depth {0}; + typeTag t {typeTag::NOTHING}; + + return parseAttributeValue(v, 0, max_depth, t); + } + + AttributeValue parseAttributeValue(const GenericValue& v, + const unsigned int curLevel, unsigned int& maxDepth, typeTag& t) + { + if(v.IsObject()) + { + return parseAttributeValueObject(v, t); + } + else if(v.IsArray()) + { + return parseAttributeValueArray(v, curLevel + 1, maxDepth, t); + } + else + { + return parseAttributeValuePrimitive(v,t); + } + } + + AttributeValue parseAttributeValueObject(const GenericValue& v, typeTag& t) + { + typedef rapidjson::Value::ConstMemberIterator CMI; + t = typeTag::_representation; + OC::OCRepresentation rep; + + for(CMI itr = v.MemberBegin(); itr!= v.MemberEnd(); ++itr) + { + std::string keyName = itr->name.GetString(); + + if(keyName == OC::Key::URIKEY) + { + rep.setUri(boost::get(parseAttributeValue(itr->value))); + } + else if (keyName == OC::Key::PROPERTYKEY) + { + for(CMI itr2 = itr->value.MemberBegin(); + itr->value.MemberEnd()!=itr2; + ++itr2) + { + if(keyName == OC::Key::RESOURCETYPESKEY) + { + rep.setResourceTypes( + boost::get>( + parseAttributeValue(itr->value))); + } + else if(keyName == OC::Key::PROPERTYKEY) + { + rep.setResourceInterfaces( + boost::get>( + parseAttributeValue(itr->value))); + } + } + } + else if (keyName == OC::Key::REPKEY) + { + for(CMI itr2 = itr->value.MemberBegin(); + itr->value.MemberEnd()!=itr2; + ++itr2) + { + rep.setValue(itr2->name.GetString(), + parseAttributeValue(itr2->value)); + } + } + } + + return rep; + } + + AttributeValue parseAttributeValuePrimitive(const GenericValue& v, typeTag& t) + { + if(v.IsString()) + { + t = typeTag::_string; + return std::string(v.GetString()); + } + else if (v.IsNumber()) + { + if(v.IsDouble()) + { + t = typeTag::_double; + return double(v.GetDouble()); + } + else if (v.IsInt()) + { + t = typeTag::_int; + return int(v.GetInt()); + } + else + { + throw OC::OCException(OC::Exception::INVALID_JSON_NUMERIC + + std::to_string(v.GetType())); + } + } + else if(v.IsBool_()) + { + t=typeTag::_bool; + return bool(v.GetBool_()); + } + else if(v.IsNull_()) + { + return OC::NullType(); + } + else + { + throw OC::OCException(OC::Exception::INVALID_JSON_TYPE + + std::to_string(v.GetType())); + } + } + + std::vector gatherArrayContents(const GenericValue& v, + const unsigned int curLevel, unsigned int& maxDepth, typeTag& t) + { + std::vector out; + + std::transform(v.Begin(), v.End(), back_inserter(out), + [curLevel, &maxDepth, &t](const GenericValue& x) + { + return parseAttributeValue(x, curLevel, maxDepth, t); + }); + return out; + } + + template + struct valueToConcrete + { + OutT operator()(const AttributeValue& v) + { + return boost::get(v); + } + + }; + + template + OutSeqT valuesToConcreteVectors(const std::vector& vs) + { + OutSeqT ret; + + std::transform(begin(vs),end(vs), back_inserter(ret), + valueToConcrete()); + return ret; + } + + template + AttributeValue remapArrayDepth(const unsigned int curLevel, + const std::vector& vs) + { + switch(curLevel) + { + default: + throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH); + break; + case 1: + return valuesToConcreteVectors>(vs); + break; + case 2: + return valuesToConcreteVectors>>(vs); + break; + case 3: + return valuesToConcreteVectors + >>>(vs); + break; + } + } + + AttributeValue convertArrayToConcretes(const typeTag t, + const unsigned int curLevel, const std::vector& vs) + { + // This function converts a std::vector of AttributeValue to a std::vector + // of concrete types. Since we don't use a recursive Variant, we need + // to get back to a 'base' primitive type + switch(t) + { + default: + case typeTag::NOTHING: + throw OC::OCException(OC::Exception::INVALID_JSON_TYPE_TAG); + break; + case typeTag::_string: + return remapArrayDepth(curLevel, vs); + break; + case typeTag::_int: + return remapArrayDepth(curLevel, vs); + break; + case typeTag::_double: + return remapArrayDepth(curLevel, vs); + break; + case typeTag::_bool: + return remapArrayDepth(curLevel, vs); + break; + case typeTag::_representation: + return remapArrayDepth(curLevel, vs); + break; + } + } + + AttributeValue parseAttributeValueArray(const GenericValue& v, + const unsigned int curLevel, unsigned int& maxDepth, typeTag& t) + { + const unsigned int max_level = 3; + + if(curLevel > max_level) + { + throw OC::OCException(OC::Exception::INVALID_JSON_ARRAY_DEPTH); + } + + if(curLevel > maxDepth) + { + maxDepth = curLevel; + } + + auto arrayItems = gatherArrayContents(v, curLevel, maxDepth, t); + const int remapLevel = maxDepth - (curLevel -1); + return convertArrayToConcretes(t, remapLevel, arrayItems); + } + } +} + +namespace cereal +{ + void JSONInputArchive::loadAttributeValues(std::map& map) + { + for(auto&b = itsIteratorStack.back(); + b.Member && b.itsMemberItEnd != b.itsMemberItBegin+b.itsIndex; + ++b) + { + std::string key = b.itsMemberItBegin[b.itsIndex].name.GetString(); + const GenericValue& v = itsIteratorStack.back().value(); + map[key] = OC::detail::parseAttributeValue(v); + } + } +} diff --git a/resource/src/OCResource.cpp b/resource/src/OCResource.cpp index b3062e9..2ec4eb6 100644 --- a/resource/src/OCResource.cpp +++ b/resource/src/OCResource.cpp @@ -43,7 +43,7 @@ OCResource::OCResource(std::weak_ptr clientWrapper, const std::s m_clientWrapper.expired()) { throw ResourceInitException(m_uri.empty(), resourceTypes.empty(), - interfaces.empty(), m_clientWrapper.expired()); + interfaces.empty(), m_clientWrapper.expired(), false, false); } } diff --git a/resource/unittests/makefile b/resource/unittests/makefile index e8283c0..ae26745 100644 --- a/resource/unittests/makefile +++ b/resource/unittests/makefile @@ -38,6 +38,13 @@ include $(ROOT_DIR)/../csdk/local.properties OUT_DIR := $(BUILD) +ifeq ($(ROOT_DIR),) + ROOT_DIR:=$(PWD) +endif + +DEPEND_DIR:=$(ROOT_DIR)/../dependencies +CEREAL_DIR:=$(DEPEND_DIR)/cereal + INC_DIRS := -I../include/ INC_DIRS += -I../oc_logger/include INC_DIRS += -I../csdk/stack/include @@ -46,6 +53,7 @@ INC_DIRS += -I../csdk/ocrandom/include INC_DIRS += -I../csdk/logger/include INC_DIRS += -I../csdk/libcoap INC_DIRS += -I$(GTEST_DIR)/include +INC_DIRS += -I$(CEREAL_DIR)/include LIB_OC_LOGGER := ../oc_logger/lib/oc_logger.a -- 2.7.4