Remove shared_ptrs from fetch document interface, and add a 'free' callback that is used to release memory after parsing

This commit is contained in:
Tristan Penman 2016-02-13 20:52:23 -08:00
parent 348ba26596
commit 7f4db481c7
2 changed files with 92 additions and 49 deletions

View File

@ -57,10 +57,16 @@ public:
* @brief Struct to contain templated function type for fetching documents * @brief Struct to contain templated function type for fetching documents
*/ */
template<typename AdapterType> template<typename AdapterType>
struct FetchDocumentFunction { struct FunctionPtrs
/// Functor for dereferencing JSON References {
typedef boost::function<boost::shared_ptr<const AdapterType>( typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
const std::string &uri)> Type; DocumentType;
/// Templated function pointer type for fetching remote documents
typedef const AdapterType * (*FetchDoc)(const std::string &uri);
/// Templated function pointer type for freeing fetched documents
typedef void (*FreeDoc)(const AdapterType *);
}; };
/** /**
@ -79,13 +85,26 @@ public:
void populateSchema( void populateSchema(
const AdapterType &node, const AdapterType &node,
Schema &schema, Schema &schema,
boost::optional<typename FetchDocumentFunction<AdapterType>::Type> typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc = NULL,
fetchDoc = boost::none) typename FunctionPtrs<AdapterType>::FreeDoc freeDoc = NULL)
{ {
if ((fetchDoc == NULL) ^ (freeDoc == NULL)) {
throw std::runtime_error(
"Remote document fetching cannot be enabled without both "
"fetch and free functions");
}
typename DocumentCache<AdapterType>::Type docCache; typename DocumentCache<AdapterType>::Type docCache;
SchemaCache schemaCache; SchemaCache schemaCache;
populateSchema(schema, node, node, schema, boost::none, "", fetchDoc, try {
NULL, NULL, docCache, schemaCache); populateSchema(schema, node, node, schema, boost::none, "",
fetchDoc, NULL, NULL, docCache, schemaCache);
} catch (...) {
freeDocumentCache<AdapterType>(docCache, freeDoc);
throw;
}
freeDocumentCache<AdapterType>(docCache, freeDoc);
} }
private: private:
@ -96,11 +115,31 @@ private:
typedef typename adapters::AdapterTraits<AdapterType>::DocumentType typedef typename adapters::AdapterTraits<AdapterType>::DocumentType
DocumentType; DocumentType;
typedef std::map<std::string, const DocumentType*> Type; typedef std::map<std::string, const AdapterType*> Type;
}; };
typedef std::map<std::string, boost::shared_ptr<Schema> > SchemaCache; typedef std::map<std::string, boost::shared_ptr<Schema> > SchemaCache;
/**
* @brief Free memory used by fetched documents
*
* If a custom 'free' function has not been provided, then the default
* delete operator will be used.
*
* @param docCache collection of fetched documents to free
* @param freeDoc optional custom free function
*/
template<typename AdapterType>
void freeDocumentCache(const typename DocumentCache<AdapterType>::Type
&docCache, typename FunctionPtrs<AdapterType>::FreeDoc freeDoc)
{
typedef typename DocumentCache<AdapterType>::Type DocCacheType;
BOOST_FOREACH( const typename DocCacheType::value_type &v, docCache ) {
freeDoc(v.second);
}
}
/** /**
* @brief Populate a Schema object from JSON Schema document * @brief Populate a Schema object from JSON Schema document
* *
@ -135,8 +174,7 @@ private:
const Subschema &subschema, const Subschema &subschema,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
const Subschema *parentSubschema, const Subschema *parentSubschema,
const std::string *ownName, const std::string *ownName,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
@ -473,8 +511,7 @@ private:
const Subschema &subschema, const Subschema &subschema,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
const Subschema *parentSubschema, const Subschema *parentSubschema,
const std::string *ownName, const std::string *ownName,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
@ -496,16 +533,27 @@ private:
"Support for JSON References not enabled."); "Support for JSON References not enabled.");
} }
// Returns a shared pointer to the remote document that was const AdapterType * docPtr = NULL;
// retrieved, or null if retrieval failed. The resulting document const typename DocumentCache<AdapterType>::Type::const_iterator
// must remain in scope until populateSchema returns. docCacheItr = docCache.find(*documentUri);
boost::shared_ptr<const AdapterType> docPtr = if (docCacheItr == docCache.end()) {
(*fetchDoc)(*documentUri); // Returns a shared pointer to the remote document that was
// retrieved, or null if retrieval failed. The resulting
// document must remain in scope until populateSchema returns.
docPtr = (*fetchDoc)(*documentUri);
// Can't proceed without the remote document // Can't proceed without the remote document
if (!docPtr) { if (!docPtr) {
throw std::runtime_error( throw std::runtime_error(
"Failed to fetch referenced schema document."); "Failed to fetch referenced schema document.");
}
// TODO: If this fails, how would the document be freed?
docCache.insert(
typename DocumentCache<AdapterType>::Type::value_type(
*documentUri, docPtr));
} else {
docPtr = docCacheItr->second;
} }
const AdapterType &ref = internal::json_pointer::resolveJsonPointer( const AdapterType &ref = internal::json_pointer::resolveJsonPointer(
@ -552,8 +600,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -610,8 +657,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -686,8 +732,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -816,8 +861,7 @@ private:
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &itemsPath, const std::string &itemsPath,
const std::string &additionalItemsPath, const std::string &additionalItemsPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -923,8 +967,7 @@ private:
const AdapterType &items, const AdapterType &items,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &itemsPath, const std::string &itemsPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -1260,8 +1303,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -1303,8 +1345,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {
@ -1390,8 +1431,7 @@ private:
const std::string &propertiesPath, const std::string &propertiesPath,
const std::string &patternPropertiesPath, const std::string &patternPropertiesPath,
const std::string &additionalPropertiesPath, const std::string &additionalPropertiesPath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
const Subschema *parentSubschema, const Subschema *parentSubschema,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
@ -1551,8 +1591,7 @@ private:
const AdapterType &node, const AdapterType &node,
const boost::optional<std::string> currentScope, const boost::optional<std::string> currentScope,
const std::string &nodePath, const std::string &nodePath,
const boost::optional<typename FetchDocumentFunction<AdapterType>::Type> const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
fetchDoc,
typename DocumentCache<AdapterType>::Type &docCache, typename DocumentCache<AdapterType>::Type &docCache,
SchemaCache &schemaCache) SchemaCache &schemaCache)
{ {

View File

@ -13,9 +13,6 @@ using valijson::SchemaParser;
using valijson::adapters::RapidJsonAdapter; using valijson::adapters::RapidJsonAdapter;
using valijson::Validator; using valijson::Validator;
typedef SchemaParser::FetchDocumentFunction<RapidJsonAdapter>::Type
FetchDocumentFunction;
namespace { namespace {
static rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> allocator; static rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator> allocator;
@ -29,7 +26,7 @@ class TestFetchDocumentCallback : public ::testing::Test
}; };
boost::shared_ptr<const RapidJsonAdapter> fetchDocument(const std::string &uri) const RapidJsonAdapter * fetchDocument(const std::string &uri)
{ {
EXPECT_STREQ("test", uri.c_str()); EXPECT_STREQ("test", uri.c_str());
@ -49,7 +46,12 @@ boost::shared_ptr<const RapidJsonAdapter> fetchDocument(const std::string &uri)
// Have to ensure that fetchedRoot exists for at least as long as the // Have to ensure that fetchedRoot exists for at least as long as the
// shared pointer that we return here // shared pointer that we return here
return boost::make_shared<RapidJsonAdapter>(fetchedRoot); return new RapidJsonAdapter(fetchedRoot);
}
void freeDocument(const RapidJsonAdapter *adapter)
{
delete adapter;
} }
TEST_F(TestFetchDocumentCallback, Basics) TEST_F(TestFetchDocumentCallback, Basics)
@ -63,19 +65,21 @@ TEST_F(TestFetchDocumentCallback, Basics)
// Parse schema document // Parse schema document
Schema schema; Schema schema;
SchemaParser schemaParser; SchemaParser schemaParser;
schemaParser.populateSchema(schemaDocumentAdapter, schema, schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchDocument,
boost::make_optional<FetchDocumentFunction>(fetchDocument)); freeDocument);
// Test resulting schema with a valid document // Test resulting schema with a valid document
rapidjson::Document validDocument; rapidjson::Document validDocument;
validDocument.SetObject(); validDocument.SetObject();
validDocument.AddMember("test", "valid", allocator); validDocument.AddMember("test", "valid", allocator);
Validator validator; Validator validator;
EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument), NULL)); EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument),
NULL));
// Test resulting schema with an invalid document // Test resulting schema with an invalid document
rapidjson::Document invalidDocument; rapidjson::Document invalidDocument;
invalidDocument.SetObject(); invalidDocument.SetObject();
invalidDocument.AddMember("test", 123, allocator); invalidDocument.AddMember("test", 123, allocator);
EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument), NULL)); EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument),
NULL));
} }