From a7c80f6cf26e0f6b059961ba8a3b8a955286f561 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 10 Dec 2021 13:00:44 +0100 Subject: [PATCH 01/21] Adding content filter interfaces (#2286) * Refs 12663. Adding ContentFilteredTopic header. Signed-off-by: Miguel Company * Refs 12663. Adding ContentFilteredTopic basic code. Signed-off-by: Miguel Company * Refs 12663. Moving code to DomainParticipantImpl. Signed-off-by: Miguel Company * Refs 12663. Adding filtered_topics_ to DomainParticipantImpl. Signed-off-by: Miguel Company * Refs 12663. Implementing create_contentfilteredtopic. Signed-off-by: Miguel Company * Refs 12663. Implementing delete_contentfilteredtopic. Signed-off-by: Miguel Company * Refs 12663. ContentFilteredTopicImpl moved to its own header. Signed-off-by: Miguel Company * Refs 12663. ContentFilteredTopicImpl implements IReaderDataFilter. Signed-off-by: Miguel Company * Refs 12663. Adding data_filter_ to RTPSReader. Signed-off-by: Miguel Company * Refs 12663. Link filter on RTPS reader creation. Signed-off-by: Miguel Company * Refs 12663. Filtering data on StatefulReader. Signed-off-by: Miguel Company * Refs 12663. Filtering data on StatelessReader. Signed-off-by: Miguel Company * Refs 12663. Fixed unit test build error. Signed-off-by: Miguel Company * Refs 12663. Use topic name from related topic. Signed-off-by: Miguel Company * Refs 12540. Adding filter_name. Signed-off-by: Miguel Company * Refs 12540. Adding content filter interfaces. Signed-off-by: Miguel Company * Refs 12540. Using IContentFilterFactory. Signed-off-by: Miguel Company * Refs 12540. Adding get_descriptor to type support. Signed-off-by: Miguel Company * Refs 12540. Use filter factory and instance on ContentFilteredTopicImpl. Signed-off-by: Miguel Company * Refs 12540. Moved IReaderDataFilter to base rtps folder. Signed-off-by: Miguel Company * Refs 12540. Added custom filter interfaces to DomainParticipant. Signed-off-by: Miguel Company * Refs 12540. Added DynamicPubSubType::get_descriptor. Signed-off-by: Miguel Company * Refs 12540. Linters. Signed-off-by: Miguel Company * Refs 12663. Fixed warning Signed-off-by: Miguel Company * Refs 12540. Add reader_guid to IContentFilter. Signed-off-by: Miguel Company * Refs 12540. Fix method names, Signed-off-by: Miguel Company * Refs 12540. Use constexpr for default filter name. Signed-off-by: Miguel Company * Refs 12540. Fix warnings. Signed-off-by: Miguel Company * Refs 12540. Uncrustify. Signed-off-by: Miguel Company * Refs 12540. Include what you use on DomainParticipant.hpp. Signed-off-by: Miguel Company * Refs 12540. Adressed some review comments on DomainParticipant. Signed-off-by: Miguel Company * Refs 12540. More review comments addressed. Signed-off-by: Miguel Company * Refs 12540. Custom filter methods renamed. Signed-off-by: Miguel Company * Refs 12540. Add implementation for create_contentfilteredtopic overload. Signed-off-by: Miguel Company * Refs 12540. Fail when filter_name is nullptr. Signed-off-by: Miguel Company * Refs 12540. Parameter filter_name renamed to filter_class_name. Signed-off-by: Miguel Company * Refs 12540. Fixing build of statistics tests. Signed-off-by: Miguel Company * Refs 12540. Add get_content_filter to RTPSReader. Signed-off-by: Miguel Company * Refs 12540. IReaderDataFilter.hpp moved to interfaces folder. Signed-off-by: Miguel Company * Refs 12540. Uncrustify. Signed-off-by: Miguel Company * Refs 12540. Adding some error messages. Signed-off-by: Miguel Company * Refs 12540. Allowing a factory to manage several filter classes. Signed-off-by: Miguel Company * Refs 12540. Adding empty implementation of custom factory registry methods. Signed-off-by: Miguel Company * Refs 12540. Adding implementation of custom factory registry methods. Signed-off-by: Miguel Company * Refs 12540. Lookup factory on create_contentfilteredtopic. Signed-off-by: Miguel Company * Refs 12540. Uncrustify. Signed-off-by: Miguel Company * Apply suggestions from code review Signed-off-by: Miguel Company Co-authored-by: Eduardo Ponz Segrelles * Refs 12540. Suggestions on ContentFilteredTopic header. Signed-off-by: Miguel Company * Refs 12540. Add alias for GUID_t on IContentFilter. Signed-off-by: Miguel Company * Refs 12540. Additional RETCODE_PRECONDITION_NOT_MET on register_content_filter_factory doxydoc. Signed-off-by: Miguel Company * Refs 12540. Interfaces don't have destructors. Signed-off-by: Miguel Company * Apply suggestions from code review Signed-off-by: Miguel Company Co-authored-by: Eduardo Ponz Segrelles Co-authored-by: Eduardo Ponz Segrelles --- .../fastdds/dds/domain/DomainParticipant.hpp | 113 +++++++++- .../dds/topic/ContentFilteredTopic.hpp | 104 +++++++++ include/fastdds/dds/topic/IContentFilter.hpp | 60 +++++ .../dds/topic/IContentFilterFactory.hpp | 57 +++++ include/fastdds/dds/topic/TopicDataType.hpp | 19 +- include/fastdds/dds/topic/TypeSupport.hpp | 10 + .../rtps/interfaces/IReaderDataFilter.hpp | 55 +++++ include/fastdds/rtps/reader/RTPSReader.h | 19 ++ .../fastdds/rtps/writer/IReaderDataFilter.hpp | 45 +--- include/fastdds/rtps/writer/StatefulWriter.h | 2 +- include/fastrtps/types/DynamicPubSubType.h | 4 +- include/fastrtps/types/TypeDescriptor.h | 3 +- src/cpp/CMakeLists.txt | 1 + src/cpp/dynamic-types/DynamicPubSubType.cpp | 30 ++- src/cpp/fastdds/domain/DomainParticipant.cpp | 43 +++- .../fastdds/domain/DomainParticipantImpl.cpp | 212 +++++++++++++++++- .../fastdds/domain/DomainParticipantImpl.hpp | 27 +++ src/cpp/fastdds/subscriber/DataReaderImpl.cpp | 18 +- .../fastdds/topic/ContentFilteredTopic.cpp | 107 +++++++++ .../topic/ContentFilteredTopicImpl.hpp | 70 ++++++ .../fastdds/topic/TopicDescriptionImpl.hpp | 4 +- src/cpp/fastdds/topic/TopicImpl.hpp | 5 + .../database/DiscoveryDataFilter.hpp | 2 +- src/cpp/rtps/reader/StatefulReader.cpp | 13 ++ src/cpp/rtps/reader/StatelessReader.cpp | 13 +- src/cpp/rtps/writer/StatefulWriter.cpp | 3 +- .../fastdds/domain/DomainParticipantImpl.hpp | 20 ++ .../fastdds/rtps/reader/RTPSReader.h | 4 + test/unittest/dds/status/CMakeLists.txt | 1 + 29 files changed, 973 insertions(+), 91 deletions(-) create mode 100644 include/fastdds/dds/topic/ContentFilteredTopic.hpp create mode 100644 include/fastdds/dds/topic/IContentFilter.hpp create mode 100644 include/fastdds/dds/topic/IContentFilterFactory.hpp create mode 100644 include/fastdds/rtps/interfaces/IReaderDataFilter.hpp create mode 100644 src/cpp/fastdds/topic/ContentFilteredTopic.cpp create mode 100644 src/cpp/fastdds/topic/ContentFilteredTopicImpl.hpp diff --git a/include/fastdds/dds/domain/DomainParticipant.hpp b/include/fastdds/dds/domain/DomainParticipant.hpp index ce3ccccc068..68ae716c139 100644 --- a/include/fastdds/dds/domain/DomainParticipant.hpp +++ b/include/fastdds/dds/domain/DomainParticipant.hpp @@ -20,23 +20,26 @@ #ifndef _FASTDDS_DOMAIN_PARTICIPANT_HPP_ #define _FASTDDS_DOMAIN_PARTICIPANT_HPP_ -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include +#include #include - - -#include +#include using eprosima::fastrtps::types::ReturnCode_t; @@ -76,7 +79,6 @@ class SubscriberListener; class TopicQos; // Not implemented classes -class ContentFilteredTopic; class MultiTopic; /** @@ -262,14 +264,40 @@ class DomainParticipant : public Entity * @param related_topic Related Topic to being subscribed * @param filter_expression Logic expression to create filter * @param expression_parameters Parameters to filter content - * @return Pointer to the created ContentFilteredTopic, nullptr in error case + * @return Pointer to the created ContentFilteredTopic. + * @return nullptr if @c related_topic does not belong to this participant. + * @return nullptr if a topic with the specified @c name has already been created. + * @return nullptr if a filter cannot be created with the specified @c filter_expression and + * @c expression_parameters. */ RTPS_DllAPI ContentFilteredTopic* create_contentfilteredtopic( const std::string& name, - const Topic* related_topic, + Topic* related_topic, const std::string& filter_expression, const std::vector& expression_parameters); + /** + * Create a ContentFilteredTopic in this Participant using a custom filter. + * @param name Name of the ContentFilteredTopic + * @param related_topic Related Topic to being subscribed + * @param filter_expression Logic expression to create filter + * @param expression_parameters Parameters to filter content + * @param filter_class_name Name of the filter class to use + * + * @return Pointer to the created ContentFilteredTopic. + * @return nullptr if @c related_topic does not belong to this participant. + * @return nullptr if a topic with the specified @c name has already been created. + * @return nullptr if a filter cannot be created with the specified @c filter_expression and + * @c expression_parameters. + * @return nullptr if the specified @c filter_class_name has not been registered. + */ + RTPS_DllAPI ContentFilteredTopic* create_contentfilteredtopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters, + const char* filter_class_name); + /** * Deletes an existing ContentFilteredTopic. * @param a_contentfilteredtopic ContentFilteredTopic to be deleted @@ -720,6 +748,71 @@ class DomainParticipant : public Entity const std::string& type_name, std::function& callback); + /** + * Register a custom content filter factory, which can be used to create a ContentFilteredTopic. + * + * DDS specifies a SQL-like content filter to be used by content filtered topics. + * If this filter does not meet your filtering requirements, you can register a custom filter factory. + * + * To use a custom filter, a factory for it must be registered in the following places: + * - In any application that uses the custom filter factory to create a ContentFilteredTopic and the corresponding + * DataReader. + * - In each application that writes the data to the applications mentioned above. + * + * For example, suppose Application A on the subscription side creates a Topic named X and a ContentFilteredTopic + * named filteredX (and a corresponding DataReader), using a previously registered content filter factory, myFilterFactory. + * With only that, you will have filtering at the subscription side. + * If you also want to perform filtering in any application that publishes Topic X, then you also need to register + * the same definition of the ContentFilterFactory myFilterFactory in that application. + * + * Each @c filter_class_name can only be used to register a content filter factory once per DomainParticipant. + * + * @param filter_class_name Name of the filter class. Cannot be nullptr, must not exceed 255 characters, and must + * be unique within this DomainParticipant. + * @param filter_factory Factory of content filters to be registered. Cannot be nullptr. + * + * @return RETCODE_BAD_PARAMETER if any parameter is nullptr, or the filter_class_name exceeds 255 characters. + * @return RETCODE_PRECONDITION_NOT_MET if the filter_class_name has been already registered. + * @return RETCODE_PRECONDITION_NOT_MET if filter_class_name is FASTDDS_SQLFILTER_NAME. + * @return RETCODE_OK if the filter is correctly registered. + */ + RTPS_DllAPI ReturnCode_t register_content_filter_factory( + const char* filter_class_name, + IContentFilterFactory* const filter_factory); + + /** + * Lookup a custom content filter factory previously registered with register_content_filter_factory. + * + * @param filter_class_name Name of the filter class. Cannot be nullptr. + * + * @return nullptr if the given filter_class_name has not been previously registered on this DomainParticipant. + * Otherwise, the content filter factory previously registered with the given filter_class_name. + */ + RTPS_DllAPI IContentFilterFactory* lookup_content_filter_factory( + const char* filter_class_name); + + /** + * Unregister a custom content filter factory previously registered with register_content_filter_factory. + * + * A filter_class_name can be unregistered only if it has been previously registered to the DomainParticipant with + * register_content_filter_factory. + * + * The unregistration of filter is not allowed if there are any existing ContentFilteredTopic objects that are + * using the filter. + * + * If there is any existing discovered DataReader with the same filter_class_name, filtering on the writer side will be + * stopped, but this operation will not fail. + * + * @param filter_class_name Name of the filter class. Cannot be nullptr. + * + * @return RETCODE_BAD_PARAMETER if the filter_class_name is nullptr. + * @return RERCODE_PRECONDITION_NOT_MET if the filter_class_name has not been previously registered. + * @return RERCODE_PRECONDITION_NOT_MET if there is any ContentFilteredTopic referencing the filter. + * @return RETCODE_OK if the filter is correctly unregistered. + */ + RTPS_DllAPI ReturnCode_t unregister_content_filter_factory( + const char* filter_class_name); + /** * @brief Check if the Participant has any Publisher, Subscriber or Topic * @return true if any, false otherwise. diff --git a/include/fastdds/dds/topic/ContentFilteredTopic.hpp b/include/fastdds/dds/topic/ContentFilteredTopic.hpp new file mode 100644 index 00000000000..48c294283b5 --- /dev/null +++ b/include/fastdds/dds/topic/ContentFilteredTopic.hpp @@ -0,0 +1,104 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 ContentFilteredTopic.hpp + */ + +#ifndef _FASTDDS_DDS_TOPIC_CONTENTFILTEREDTOPIC_HPP_ +#define _FASTDDS_DDS_TOPIC_CONTENTFILTEREDTOPIC_HPP_ + +#include +#include +#include + +#define FASTDDS_SQLFILTER_NAME eprosima::fastdds::dds::sqlfilter_name + +using eprosima::fastrtps::types::ReturnCode_t; + +namespace eprosima { +namespace fastdds { +namespace dds { + +class DomainParticipant; +class DomainParticipantImpl; +class ContentFilteredTopicImpl; + +constexpr const char* const sqlfilter_name = "DDSSQL"; + +/** + * Specialization of TopicDescription that allows for content-based subscriptions + * @ingroup FASTDDS_MODULE + */ +class ContentFilteredTopic : public TopicDescription +{ + friend class DomainParticipantImpl; + +private: + + RTPS_DllAPI ContentFilteredTopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters); + +public: + + /** + * @brief Destructor + */ + RTPS_DllAPI virtual ~ContentFilteredTopic(); + + /** + * @brief Getter for the related topic. + * This operation returns the Topic associated with the ContentFilteredTopic. + * That is, the Topic specified when the ContentFilteredTopic was created. + */ + RTPS_DllAPI Topic* get_related_topic() const; + + RTPS_DllAPI const std::string& get_filter_expression() const; + + RTPS_DllAPI ReturnCode_t get_expression_parameters( + std::vector& expression_parameters) const; + + RTPS_DllAPI ReturnCode_t set_expression_parameters( + const std::vector& expression_parameters); + + RTPS_DllAPI ReturnCode_t set_filter_expression( + const std::string& filter_expression, + const std::vector& expression_parameters); + + /** + * @brief Getter for the DomainParticipant + * @return DomainParticipant pointer + */ + virtual DomainParticipant* get_participant() const override; + + /** + * @brief Getter for the TopicDescriptionImpl + * @return pointer to TopicDescriptionImpl + */ + TopicDescriptionImpl* get_impl() const override; + +protected: + + ContentFilteredTopicImpl* impl_; + +}; + +} // namespace dds +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_DDS_TOPIC_CONTENTFILTEREDTOPIC_HPP_ diff --git a/include/fastdds/dds/topic/IContentFilter.hpp b/include/fastdds/dds/topic/IContentFilter.hpp new file mode 100644 index 00000000000..25cbdb4f676 --- /dev/null +++ b/include/fastdds/dds/topic/IContentFilter.hpp @@ -0,0 +1,60 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 IContentFilter.hpp + */ + +#ifndef _FASTDDS_DDS_TOPIC_ICONTENTFILTER_HPP_ +#define _FASTDDS_DDS_TOPIC_ICONTENTFILTER_HPP_ + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace dds { + +struct IContentFilter +{ + using SerializedPayload = eprosima::fastrtps::rtps::SerializedPayload_t; + using GUID_t = fastrtps::rtps::GUID_t; + + struct FilterSampleInfo + { + using SampleIdentity = eprosima::fastrtps::rtps::SampleIdentity; + + SampleIdentity sample_identity; + SampleIdentity related_sample_identity; + }; + + virtual bool evaluate( + const SerializedPayload& payload, + const FilterSampleInfo& sample_info, + const GUID_t& reader_guid) const = 0; +}; + +} // namespace dds +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_DDS_TOPIC_ICONTENTFILTER_HPP_ diff --git a/include/fastdds/dds/topic/IContentFilterFactory.hpp b/include/fastdds/dds/topic/IContentFilterFactory.hpp new file mode 100644 index 00000000000..6473bc9e22f --- /dev/null +++ b/include/fastdds/dds/topic/IContentFilterFactory.hpp @@ -0,0 +1,57 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 IContentFilter.hpp + */ + +#ifndef _FASTDDS_DDS_TOPIC_ICONTENTFILTERFACTORY_HPP_ +#define _FASTDDS_DDS_TOPIC_ICONTENTFILTERFACTORY_HPP_ + +#include + +#include +#include + +#include +#include + +namespace eprosima { +namespace fastdds { +namespace dds { + +struct IContentFilterFactory +{ + using ReturnCode_t = eprosima::fastrtps::types::ReturnCode_t; + using ParameterSeq = LoanableTypedCollection; + using TypeDescriptor = eprosima::fastrtps::types::TypeDescriptor; + + virtual ReturnCode_t create_content_filter( + const char* filter_class_name, + const char* type_name, + const TypeDescriptor* type_description, + const char* filter_expression, + const ParameterSeq& filter_parameters, + IContentFilter*& filter_instance) = 0; + + virtual ReturnCode_t delete_content_filter( + const char* filter_class_name, + IContentFilter* filter_instance) = 0; +}; + +} // namespace dds +} // namespace fastdds +} // namespace eprosima + +#endif // _FASTDDS_DDS_TOPIC_ICONTENTFILTERFACTORY_HPP_ diff --git a/include/fastdds/dds/topic/TopicDataType.hpp b/include/fastdds/dds/topic/TopicDataType.hpp index 8228077a980..8ec99a12879 100644 --- a/include/fastdds/dds/topic/TopicDataType.hpp +++ b/include/fastdds/dds/topic/TopicDataType.hpp @@ -19,13 +19,15 @@ #ifndef _FASTDDS_TOPICDATATYPE_HPP_ #define _FASTDDS_TOPICDATATYPE_HPP_ -#include #include +#include +#include #include #include #include +#include #include // This version of TypeSupport has `is_bounded()` @@ -37,6 +39,9 @@ // This version of TypeSupport has `construct_sample()` #define TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE +// This version of TypeSupport has `get_descriptor()` +#define TOPIC_DATA_TYPE_API_HAS_GET_DESCRIPTOR + namespace eprosima { namespace fastrtps { @@ -60,6 +65,8 @@ class TopicDataType { public: + using TypeDescriptor = eprosima::fastrtps::types::TypeDescriptor; + /** * @brief Constructor */ @@ -305,6 +312,16 @@ class TopicDataType return false; } + /** + * Get the structure representing the description of the type + * + * @return pointer to the type descriptor + */ + RTPS_DllAPI virtual inline const TypeDescriptor* get_desciptor() const + { + return nullptr; + } + //! Maximum serialized size of the type in bytes. //! If the type has unbounded fields, and therefore cannot have a maximum size, use 0. uint32_t m_typeSize; diff --git a/include/fastdds/dds/topic/TypeSupport.hpp b/include/fastdds/dds/topic/TypeSupport.hpp index 42dd3bfa690..19a4bf27da9 100644 --- a/include/fastdds/dds/topic/TypeSupport.hpp +++ b/include/fastdds/dds/topic/TypeSupport.hpp @@ -237,6 +237,16 @@ class TypeSupport : public std::shared_ptr return get()->is_plain(); } + /** + * Get the structure representing the description of the type + * + * @return pointer to the type descriptor + */ + RTPS_DllAPI virtual inline const TopicDataType::TypeDescriptor* get_desciptor() const + { + return get()->get_desciptor(); + } + RTPS_DllAPI bool operator !=( std::nullptr_t) const { diff --git a/include/fastdds/rtps/interfaces/IReaderDataFilter.hpp b/include/fastdds/rtps/interfaces/IReaderDataFilter.hpp new file mode 100644 index 00000000000..4173af8e092 --- /dev/null +++ b/include/fastdds/rtps/interfaces/IReaderDataFilter.hpp @@ -0,0 +1,55 @@ +// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 IReaderDataFilter.hpp + * + */ + +#ifndef _FASTDDS_RTPS_IREADERDATAFILTER_HPP_ +#define _FASTDDS_RTPS_IREADERDATAFILTER_HPP_ + +#include +#include + + +namespace eprosima { +namespace fastdds { +namespace rtps { + +/** + * Abstract class IReaderDataFilter that acts as virtual interface for data filters in ReaderProxy. + * @ingroup RTPS_MODULE + */ +class IReaderDataFilter +{ +public: + + /** + * This method checks whether a CacheChange_t is relevant for the specified reader + * This callback should return always the same result given the same arguments + * @param change The CacheChange_t to be evaluated + * @param reader_guid remote reader GUID_t + * @return true if relevant, false otherwise. + */ + virtual bool is_relevant( + const fastrtps::rtps::CacheChange_t& change, + const fastrtps::rtps::GUID_t& reader_guid) const = 0; +}; + +} /* namespace rtps */ +} /* namespace fastdds */ +} /* namespace eprosima */ + +#endif /* _FASTDDS_RTPS_IREADERDATAFILTER_HPP_ */ diff --git a/include/fastdds/rtps/reader/RTPSReader.h b/include/fastdds/rtps/reader/RTPSReader.h index dbaffbb67c8..f9fdfbcef71 100644 --- a/include/fastdds/rtps/reader/RTPSReader.h +++ b/include/fastdds/rtps/reader/RTPSReader.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -251,6 +252,22 @@ class RTPSReader return mp_history; } + //! @return The content filter associated to this reader. + RTPS_DllAPI eprosima::fastdds::rtps::IReaderDataFilter* get_content_filter() const + { + std::lock_guard guard(mp_mutex); + return data_filter_; + } + + //! Set the content filter associated to this reader. + //! @param filter Pointer to the content filter to associate to this reader. + RTPS_DllAPI void set_content_filter( + eprosima::fastdds::rtps::IReaderDataFilter* filter) + { + std::lock_guard guard(mp_mutex); + data_filter_ = filter; + } + /*! * @brief Returns there is a clean state with all Writers. * It occurs when the Reader received all samples sent by Writers. In other words, @@ -470,6 +487,8 @@ class RTPSReader //! The listener for the datasharing notifications std::unique_ptr datasharing_listener_; + eprosima::fastdds::rtps::IReaderDataFilter* data_filter_ = nullptr; + private: RTPSReader& operator =( diff --git a/include/fastdds/rtps/writer/IReaderDataFilter.hpp b/include/fastdds/rtps/writer/IReaderDataFilter.hpp index 14e29b04ed3..36834a22b63 100644 --- a/include/fastdds/rtps/writer/IReaderDataFilter.hpp +++ b/include/fastdds/rtps/writer/IReaderDataFilter.hpp @@ -14,48 +14,9 @@ /** * @file IReaderDataFilter.hpp - * */ -#ifndef _FASTDDS_RTPS_IREADERDATA_FILTER_H_ -#define _FASTDDS_RTPS_IREADERDATA_FILTER_H_ +// This file was moved as part of the implementation of content filtered topics +#include -#include -#include - - -namespace eprosima { -namespace fastdds { -namespace rtps { - -/** - * Abstract class IReaderDataFilter that acts as virtual interface for data filters in ReaderProxy. - *@ingroup WRITER_MODULE - */ -class IReaderDataFilter -{ -public: - - /** - * This method checks whether a CacheChange_t is relevant for the remote reader - * This callback should return always the same result given the same arguments - * @param change The CacheChange_t to be evaluated - * @param reader_guid remote reader GUID_t - * @return true if relevant, false otherwise. - */ - virtual bool is_relevant( - const fastrtps::rtps::CacheChange_t& change, - const fastrtps::rtps::GUID_t& reader_guid) const - { - (void)change; - (void)reader_guid; - return true; - } - -}; - -} /* namespace rtps */ -} /* namespace fastdds */ -} /* namespace eprosima */ - -#endif /* _FASTDDS_RTPS_IREADERDATA_FILTER_H_ */ +FASTDDS_TODO_BEFORE(3, 0, "This header should be removed") diff --git a/include/fastdds/rtps/writer/StatefulWriter.h b/include/fastdds/rtps/writer/StatefulWriter.h index cf445b96e62..8adfd7c200b 100644 --- a/include/fastdds/rtps/writer/StatefulWriter.h +++ b/include/fastdds/rtps/writer/StatefulWriter.h @@ -22,7 +22,7 @@ #ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC #include -#include +#include #include #include #include diff --git a/include/fastrtps/types/DynamicPubSubType.h b/include/fastrtps/types/DynamicPubSubType.h index f43f38c0278..094b81d47e6 100644 --- a/include/fastrtps/types/DynamicPubSubType.h +++ b/include/fastrtps/types/DynamicPubSubType.h @@ -47,7 +47,7 @@ class DynamicPubSubType : public eprosima::fastdds::dds::TopicDataType RTPS_DllAPI void* createData() override; RTPS_DllAPI void deleteData ( - void * data) override; + void* data) override; RTPS_DllAPI bool deserialize ( eprosima::fastrtps::rtps::SerializedPayload_t* payload, @@ -65,6 +65,8 @@ class DynamicPubSubType : public eprosima::fastdds::dds::TopicDataType void* data, eprosima::fastrtps::rtps::SerializedPayload_t* payload) override; + RTPS_DllAPI const TypeDescriptor* get_desciptor() const override; + RTPS_DllAPI void CleanDynamicType(); RTPS_DllAPI DynamicType_ptr GetDynamicType() const; diff --git a/include/fastrtps/types/TypeDescriptor.h b/include/fastrtps/types/TypeDescriptor.h index 2d2c7a24df6..5b1ba850ca2 100644 --- a/include/fastrtps/types/TypeDescriptor.h +++ b/include/fastrtps/types/TypeDescriptor.h @@ -15,8 +15,9 @@ #ifndef TYPES_TYPE_DESCRIPTOR_H #define TYPES_TYPE_DESCRIPTOR_H -#include +#include #include +#include class MemberDescriptor; class DynamicType; diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt index 16d4f1c8e56..e8e38f74927 100644 --- a/src/cpp/CMakeLists.txt +++ b/src/cpp/CMakeLists.txt @@ -89,6 +89,7 @@ set(${PROJECT_NAME}_source_files fastdds/publisher/DataWriter.cpp fastdds/subscriber/DataReaderImpl.cpp fastdds/publisher/DataWriterImpl.cpp + fastdds/topic/ContentFilteredTopic.cpp fastdds/topic/Topic.cpp fastdds/topic/TopicImpl.cpp fastdds/topic/TypeSupport.cpp diff --git a/src/cpp/dynamic-types/DynamicPubSubType.cpp b/src/cpp/dynamic-types/DynamicPubSubType.cpp index 1a5d1eddf36..d5816920024 100644 --- a/src/cpp/dynamic-types/DynamicPubSubType.cpp +++ b/src/cpp/dynamic-types/DynamicPubSubType.cpp @@ -32,7 +32,8 @@ DynamicPubSubType::DynamicPubSubType() { } -DynamicPubSubType::DynamicPubSubType(DynamicType_ptr pType) +DynamicPubSubType::DynamicPubSubType( + DynamicType_ptr pType) : dynamic_type_(pType) , m_keyBuffer(nullptr) { @@ -57,7 +58,8 @@ DynamicType_ptr DynamicPubSubType::GetDynamicType() const return dynamic_type_; } -ReturnCode_t DynamicPubSubType::SetDynamicType(DynamicData_ptr pData) +ReturnCode_t DynamicPubSubType::SetDynamicType( + DynamicData_ptr pData) { if (dynamic_type_ == nullptr) { @@ -72,7 +74,8 @@ ReturnCode_t DynamicPubSubType::SetDynamicType(DynamicData_ptr pData) } } -ReturnCode_t DynamicPubSubType::SetDynamicType(DynamicType_ptr pType) +ReturnCode_t DynamicPubSubType::SetDynamicType( + DynamicType_ptr pType) { if (dynamic_type_ == nullptr) { @@ -92,7 +95,8 @@ void* DynamicPubSubType::createData() return DynamicDataFactory::get_instance()->create_data(dynamic_type_); } -void DynamicPubSubType::deleteData(void* data) +void DynamicPubSubType::deleteData( + void* data) { DynamicDataFactory::get_instance()->delete_data((DynamicData*)data); } @@ -103,8 +107,8 @@ bool DynamicPubSubType::deserialize( { eprosima::fastcdr::FastBuffer fastbuffer((char*)payload->data, payload->length); // Object that manages the raw buffer. eprosima::fastcdr::Cdr deser(fastbuffer, eprosima::fastcdr::Cdr::DEFAULT_ENDIAN, - eprosima::fastcdr::Cdr::DDS_CDR); // Object that deserializes the data. - // Deserialize encapsulation. + eprosima::fastcdr::Cdr::DDS_CDR); // Object that deserializes the data. + // Deserialize encapsulation. deser.read_encapsulation(); payload->encapsulation = deser.endianness() == eprosima::fastcdr::Cdr::BIG_ENDIANNESS ? CDR_BE : CDR_LE; @@ -160,12 +164,13 @@ bool DynamicPubSubType::getKey( return true; } -std::function DynamicPubSubType::getSerializedSizeProvider(void* data) +std::function DynamicPubSubType::getSerializedSizeProvider( + void* data) { return [data]() -> uint32_t - { - return (uint32_t)DynamicData::getCdrSerializedSize((DynamicData*)data) + 4 /*encapsulation*/; - }; + { + return (uint32_t)DynamicData::getCdrSerializedSize((DynamicData*)data) + 4 /*encapsulation*/; + }; } bool DynamicPubSubType::serialize( @@ -195,6 +200,11 @@ bool DynamicPubSubType::serialize( return true; } +const TypeDescriptor* DynamicPubSubType::get_desciptor() const +{ + return nullptr == dynamic_type_ ? nullptr : dynamic_type_->get_descriptor(); +} + void DynamicPubSubType::UpdateDynamicTypeInfo() { if (dynamic_type_ != nullptr) diff --git a/src/cpp/fastdds/domain/DomainParticipant.cpp b/src/cpp/fastdds/domain/DomainParticipant.cpp index 62a71577d72..c38ac8c6a13 100644 --- a/src/cpp/fastdds/domain/DomainParticipant.cpp +++ b/src/cpp/fastdds/domain/DomainParticipant.cpp @@ -169,23 +169,29 @@ ReturnCode_t DomainParticipant::delete_topic( ContentFilteredTopic* DomainParticipant::create_contentfilteredtopic( const std::string& name, - const Topic* related_topic, + Topic* related_topic, const std::string& filter_expression, const std::vector& expression_parameters) { - static_cast (name); - static_cast (related_topic); - static_cast (filter_expression); - static_cast (expression_parameters); - logWarning(DOMAIN_PARTICIPANT, "create_contentfilteredtopic method not implemented"); - return nullptr; + return impl_->create_contentfilteredtopic(name, related_topic, filter_expression, expression_parameters, + FASTDDS_SQLFILTER_NAME); +} + +ContentFilteredTopic* DomainParticipant::create_contentfilteredtopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters, + const char* filter_class_name) +{ + return impl_->create_contentfilteredtopic(name, related_topic, filter_expression, expression_parameters, + filter_class_name); } ReturnCode_t DomainParticipant::delete_contentfilteredtopic( const ContentFilteredTopic* a_contentfilteredtopic) { - static_cast (a_contentfilteredtopic); - return ReturnCode_t::RETCODE_UNSUPPORTED; + return impl_->delete_contentfilteredtopic(a_contentfilteredtopic); } MultiTopic* DomainParticipant::create_multitopic( @@ -209,6 +215,25 @@ ReturnCode_t DomainParticipant::delete_multitopic( return ReturnCode_t::RETCODE_UNSUPPORTED; } +ReturnCode_t DomainParticipant::register_content_filter_factory( + const char* filter_class_name, + IContentFilterFactory* const filter_factory) +{ + return impl_->register_content_filter_factory(filter_class_name, filter_factory); +} + +IContentFilterFactory* DomainParticipant::lookup_content_filter_factory( + const char* filter_class_name) +{ + return impl_->lookup_content_filter_factory(filter_class_name); +} + +ReturnCode_t DomainParticipant::unregister_content_filter_factory( + const char* filter_class_name) +{ + return impl_->unregister_content_filter_factory(filter_class_name); +} + Topic* DomainParticipant::find_topic( const std::string& topic_name, const fastrtps::Duration_t& timeout) diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp index 77631bf45eb..4c95228a755 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp @@ -24,13 +24,15 @@ #include #include #include +#include #include -#include #include -#include +#include +#include +#include -#include #include +#include #include #include #include @@ -40,17 +42,18 @@ #include #include -#include #include #include #include #include +#include #include #include #include #include +#include #include @@ -223,6 +226,8 @@ DomainParticipantImpl::~DomainParticipantImpl() { std::lock_guard lock(mtx_topics_); + filtered_topics_.clear(); + for (auto topic_it = topics_.begin(); topic_it != topics_.end(); ++topic_it) { delete topic_it->second; @@ -467,6 +472,191 @@ ReturnCode_t DomainParticipantImpl::delete_topic( return ReturnCode_t::RETCODE_ERROR; } +ContentFilteredTopic* DomainParticipantImpl::create_contentfilteredtopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters, + const char* filter_class_name) +{ + if ((nullptr == related_topic) || (nullptr == filter_class_name)) + { + return nullptr; + } + + std::lock_guard lock(mtx_topics_); + + // Check there is no Topic with the same name + if ((topics_.find(name) != topics_.end()) || + (filtered_topics_.find(name) != filtered_topics_.end())) + { + logError(PARTICIPANT, "Topic with name : " << name << " already exists"); + return nullptr; + } + + if (related_topic->get_participant() != this->participant_) + { + logError(PARTICIPANT, "Creating ContentFilteredTopic with name " << name << + ": related_topic not from this participant"); + return nullptr; + } + + IContentFilterFactory* filter_factory = find_content_filter_factory(filter_class_name); + if (nullptr == filter_factory) + { + logError(PARTICIPANT, "Could not find factory for filter class " << filter_class_name); + return nullptr; + } + + TopicImpl* topic_impl = dynamic_cast(related_topic->get_impl()); + assert(nullptr != topic_impl); + const TypeSupport& type = topic_impl->get_type(); + const IContentFilterFactory::TypeDescriptor* type_descriptor = type->get_desciptor(); + LoanableSequence::size_type n_params; + n_params = static_cast::size_type>(expression_parameters.size()); + LoanableSequence filter_parameters(n_params); + while (n_params > 0) + { + n_params--; + filter_parameters[n_params] = expression_parameters[n_params].c_str(); + } + + // Tell filter factory to compile the expression + IContentFilter* filter_instance = nullptr; + if (ReturnCode_t::RETCODE_OK != + filter_factory->create_content_filter(filter_class_name, related_topic->get_type_name().c_str(), + type_descriptor, filter_expression.c_str(), filter_parameters, filter_instance)) + { + logError(PARTICIPANT, "Could not create filter of class " << filter_class_name << " for expression \"" << + filter_expression); + return nullptr; + } + + ContentFilteredTopic* topic; + topic = new ContentFilteredTopic(name, related_topic, filter_expression, expression_parameters); + ContentFilteredTopicImpl* content_topic_impl = static_cast(topic->get_impl()); + content_topic_impl->filter_class_name = filter_class_name; + content_topic_impl->filter_factory = filter_factory; + content_topic_impl->filter_instance = filter_instance; + + // Save the topic into the map + filtered_topics_.emplace(std::make_pair(name, topic)); + + return topic; +} + +ReturnCode_t DomainParticipantImpl::delete_contentfilteredtopic( + const ContentFilteredTopic* topic) +{ + if (topic == nullptr) + { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + if (participant_ != topic->get_participant()) + { + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; + } + + std::lock_guard lock(mtx_topics_); + auto it = filtered_topics_.find(topic->get_name()); + + if (it != filtered_topics_.end()) + { + if (it->second->get_impl()->is_referenced()) + { + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; + } + filtered_topics_.erase(it); + return ReturnCode_t::RETCODE_OK; + } + + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; +} + +ReturnCode_t DomainParticipantImpl::register_content_filter_factory( + const char* filter_class_name, + IContentFilterFactory* const filter_factory) +{ + if (nullptr == filter_class_name || strlen(filter_class_name) > 255) + { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + std::lock_guard lock(mtx_topics_); + auto it = filter_factories_.find(filter_class_name); + if ((it != filter_factories_.end()) || (0 == strcmp(filter_class_name, FASTDDS_SQLFILTER_NAME))) + { + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; + } + + filter_factories_[filter_class_name] = filter_factory; + return ReturnCode_t::RETCODE_OK; +} + +IContentFilterFactory* DomainParticipantImpl::lookup_content_filter_factory( + const char* filter_class_name) +{ + if (nullptr == filter_class_name) + { + return nullptr; + } + + std::lock_guard lock(mtx_topics_); + auto it = filter_factories_.find(filter_class_name); + if ((it == filter_factories_.end()) || (it->first == FASTDDS_SQLFILTER_NAME)) + { + return nullptr; + } + return it->second; +} + +ReturnCode_t DomainParticipantImpl::unregister_content_filter_factory( + const char* filter_class_name) +{ + if (nullptr == filter_class_name) + { + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + std::lock_guard lock(mtx_topics_); + auto it = filter_factories_.find(filter_class_name); + if ((it == filter_factories_.end()) || (it->first == FASTDDS_SQLFILTER_NAME)) + { + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; + } + + for (auto& topic : filtered_topics_) + { + if (topic.second->impl_->filter_class_name == filter_class_name) + { + return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; + } + } + + filter_factories_.erase(it); + + return ReturnCode_t::RETCODE_OK; +} + +IContentFilterFactory* DomainParticipantImpl::find_content_filter_factory( + const char* filter_class_name) const +{ + auto it = filter_factories_.find(filter_class_name); + if (it != filter_factories_.end()) + { + return it->second; + } + + if (0 != strcmp(filter_class_name, FASTDDS_SQLFILTER_NAME)) + { + return nullptr; + } + + // TODO(Miguel C): Create SQLFilter::DDSFilterFactory + return nullptr; +} + const InstanceHandle_t& DomainParticipantImpl::get_instance_handle() const { return static_cast(guid_); @@ -1063,8 +1253,9 @@ Topic* DomainParticipantImpl::create_topic( std::lock_guard lock(mtx_topics_); - //Check there is no Topic with the same name - if (topics_.find(topic_name) != topics_.end()) + // Check there is no Topic with the same name + if ((topics_.find(topic_name) != topics_.end()) || + (filtered_topics_.find(topic_name) != filtered_topics_.end())) { logError(PARTICIPANT, "Topic with name : " << topic_name << " already exists"); return nullptr; @@ -1116,13 +1307,20 @@ Topic* DomainParticipantImpl::create_topic_with_profile( TopicDescription* DomainParticipantImpl::lookup_topicdescription( const std::string& topic_name) const { - auto it = topics_.find(topic_name); + std::lock_guard lock(mtx_topics_); + auto it = topics_.find(topic_name); if (it != topics_.end()) { return it->second->user_topic_; } + auto filtered_it = filtered_topics_.find(topic_name); + if (filtered_it != filtered_topics_.end()) + { + return filtered_it->second.get(); + } + return nullptr; } diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.hpp b/src/cpp/fastdds/domain/DomainParticipantImpl.hpp index 3b776b688d2..116bc1a0c63 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.hpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.hpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -210,6 +212,26 @@ class DomainParticipantImpl ReturnCode_t delete_topic( const Topic* topic); + ContentFilteredTopic* create_contentfilteredtopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters, + const char* filter_class_name); + + ReturnCode_t delete_contentfilteredtopic( + const ContentFilteredTopic* topic); + + ReturnCode_t register_content_filter_factory( + const char* filter_class_name, + IContentFilterFactory* const filter_factory); + + IContentFilterFactory* lookup_content_filter_factory( + const char* filter_class_name); + + ReturnCode_t unregister_content_filter_factory( + const char* filter_class_name); + /** * Looks up an existing, locally created @ref TopicDescription, based on its name. * May be called on a disabled participant. @@ -456,6 +478,8 @@ class DomainParticipantImpl //!Topic map std::map topics_; std::map topics_by_handle_; + std::map> filtered_topics_; + std::map filter_factories_; mutable std::mutex mtx_topics_; TopicQos default_topic_qos_; @@ -580,6 +604,9 @@ class DomainParticipantImpl std::string get_inner_type_name( const fastrtps::rtps::SampleIdentity& id) const; + IContentFilterFactory* find_content_filter_factory( + const char* filter_class_name) const; + /** * Set the DomainParticipantQos checking if the Qos can be updated or not * diff --git a/src/cpp/fastdds/subscriber/DataReaderImpl.cpp b/src/cpp/fastdds/subscriber/DataReaderImpl.cpp index 3c1fe279ee7..61c7eaf8dc6 100644 --- a/src/cpp/fastdds/subscriber/DataReaderImpl.cpp +++ b/src/cpp/fastdds/subscriber/DataReaderImpl.cpp @@ -28,8 +28,8 @@ #include #include #include -#include #include +#include #include #include @@ -40,11 +40,13 @@ #include #include +#include + #include #include #include -#include +#include #include #include @@ -179,7 +181,7 @@ ReturnCode_t DataReaderImpl::enable() // Insert topic_name and partitions Property property; property.name("topic_name"); - property.value(topic_->get_name().c_str()); + property.value(topic_->get_impl()->get_rtps_topic_name().c_str()); att.endpoint.properties.properties().push_back(std::move(property)); std::string* endpoint_partitions = PropertyPolicyHelper::find_property(qos_.properties(), "partitions"); @@ -241,6 +243,12 @@ ReturnCode_t DataReaderImpl::enable() return ReturnCode_t::RETCODE_ERROR; } + auto content_topic = dynamic_cast(topic_->get_impl()); + if (nullptr != content_topic) + { + reader->set_content_filter(content_topic); + } + reader_ = reader; deadline_timer_ = new TimedEvent(subscriber_->get_participant()->get_resource_event(), @@ -1450,7 +1458,7 @@ fastrtps::TopicAttributes DataReaderImpl::topic_attributes() const { fastrtps::TopicAttributes topic_att; topic_att.topicKind = type_->m_isGetKeyDefined ? WITH_KEY : NO_KEY; - topic_att.topicName = topic_->get_name(); + topic_att.topicName = topic_->get_impl()->get_rtps_topic_name(); topic_att.topicDataType = topic_->get_type_name(); topic_att.historyQos = qos_.history(); topic_att.resourceLimitsQos = qos_.resource_limits(); @@ -1497,7 +1505,7 @@ std::shared_ptr DataReaderImpl::get_payload_pool() if (!payload_pool_) { - payload_pool_ = TopicPayloadPoolRegistry::get(topic_->get_name(), config); + payload_pool_ = TopicPayloadPoolRegistry::get(topic_->get_impl()->get_rtps_topic_name(), config); sample_pool_ = std::make_shared(config, type_); } diff --git a/src/cpp/fastdds/topic/ContentFilteredTopic.cpp b/src/cpp/fastdds/topic/ContentFilteredTopic.cpp new file mode 100644 index 00000000000..866f26a8e19 --- /dev/null +++ b/src/cpp/fastdds/topic/ContentFilteredTopic.cpp @@ -0,0 +1,107 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 ContentFilteredTopic.cpp + * + */ + +#include +#include +#include + +#include + +namespace eprosima { +namespace fastdds { +namespace dds { + +ContentFilteredTopic::ContentFilteredTopic( + const std::string& name, + Topic* related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters) + : TopicDescription(name, related_topic->get_type_name()) + , impl_(nullptr) +{ + related_topic->get_impl()->reference(); + + impl_ = new ContentFilteredTopicImpl(); + impl_->related_topic = related_topic; + set_filter_expression(filter_expression, expression_parameters); +} + +ContentFilteredTopic::~ContentFilteredTopic() +{ + impl_->related_topic->get_impl()->dereference(); + impl_->filter_factory->delete_content_filter(impl_->filter_class_name.c_str(), impl_->filter_instance); + delete impl_; +} + +Topic* ContentFilteredTopic::get_related_topic() const +{ + return impl_->related_topic; +} + +const std::string& ContentFilteredTopic::get_filter_expression() const +{ + return impl_->expression; +} + +ReturnCode_t ContentFilteredTopic::get_expression_parameters( + std::vector& expression_parameters) const +{ + expression_parameters = impl_->parameters; + return ReturnCode_t::RETCODE_OK; +} + +ReturnCode_t ContentFilteredTopic::set_expression_parameters( + const std::vector& expression_parameters) +{ + // TODO: Check parameters are valid + impl_->parameters = expression_parameters; + + // TODO: inform data readers + return ReturnCode_t::RETCODE_OK; +} + +ReturnCode_t ContentFilteredTopic::set_filter_expression( + const std::string& filter_expression, + const std::vector& expression_parameters) +{ + // TODO: Check expression and parameters are valid + impl_->expression = filter_expression; + impl_->parameters = expression_parameters; + + // TODO: inform data readers + return ReturnCode_t::RETCODE_OK; +} + +/** + * @brief Getter for the DomainParticipant + * @return DomainParticipant pointer + */ +DomainParticipant* ContentFilteredTopic::get_participant() const +{ + return impl_->related_topic->get_participant(); +} + +TopicDescriptionImpl* ContentFilteredTopic::get_impl() const +{ + return impl_; +} + +} /* namespace dds */ +} /* namespace fastdds */ +} /* namespace eprosima */ diff --git a/src/cpp/fastdds/topic/ContentFilteredTopicImpl.hpp b/src/cpp/fastdds/topic/ContentFilteredTopicImpl.hpp new file mode 100644 index 00000000000..a8e6aa72570 --- /dev/null +++ b/src/cpp/fastdds/topic/ContentFilteredTopicImpl.hpp @@ -0,0 +1,70 @@ +// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima). +// +// 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 ContentFilteredTopicImpl.hpp + */ + +#ifndef _FASTDDS_TOPIC_CONTENTFILTEREDTOPICIMPL_HPP_ +#define _FASTDDS_TOPIC_CONTENTFILTEREDTOPICIMPL_HPP_ + +#include + +#include +#include +#include +#include + +#include + +namespace eprosima { +namespace fastdds { +namespace dds { + +class ContentFilteredTopicImpl final : public TopicDescriptionImpl, public eprosima::fastdds::rtps::IReaderDataFilter +{ +public: + + virtual ~ContentFilteredTopicImpl() final = default; + + const std::string& get_rtps_topic_name() const final + { + return related_topic->get_name(); + } + + bool is_relevant( + const fastrtps::rtps::CacheChange_t& change, + const fastrtps::rtps::GUID_t& reader_guid) const final + { + IContentFilter::FilterSampleInfo filter_info + { + change.write_params.sample_identity(), + change.write_params.related_sample_identity() + }; + return filter_instance->evaluate(change.serializedPayload, filter_info, reader_guid); + } + + IContentFilterFactory* filter_factory = nullptr; + IContentFilter* filter_instance = nullptr; + Topic* related_topic = nullptr; + std::string filter_class_name; + std::string expression; + std::vector parameters; +}; + +} /* namespace dds */ +} /* namespace fastdds */ +} /* namespace eprosima */ + +#endif // _FASTDDS_TOPIC_CONTENTFILTEREDTOPICIMPL_HPP_ diff --git a/src/cpp/fastdds/topic/TopicDescriptionImpl.hpp b/src/cpp/fastdds/topic/TopicDescriptionImpl.hpp index 7fdb96c4a4e..14c3c91338a 100644 --- a/src/cpp/fastdds/topic/TopicDescriptionImpl.hpp +++ b/src/cpp/fastdds/topic/TopicDescriptionImpl.hpp @@ -41,7 +41,6 @@ class TopicDescriptionImpl { } - bool is_referenced() const { return num_refs_ != 0u; @@ -57,7 +56,10 @@ class TopicDescriptionImpl --num_refs_; } + virtual const std::string& get_rtps_topic_name() const = 0; + private: + std::atomic_size_t num_refs_; }; diff --git a/src/cpp/fastdds/topic/TopicImpl.hpp b/src/cpp/fastdds/topic/TopicImpl.hpp index d1e073d2721..628da5f6afa 100644 --- a/src/cpp/fastdds/topic/TopicImpl.hpp +++ b/src/cpp/fastdds/topic/TopicImpl.hpp @@ -82,6 +82,11 @@ class TopicImpl : public TopicDescriptionImpl const TypeSupport& get_type() const; + const std::string& get_rtps_topic_name() const override + { + return user_topic_->get_name(); + } + /** * Returns the most appropriate listener to handle the callback for the given status, * or nullptr if there is no appropriate listener. diff --git a/src/cpp/rtps/builtin/discovery/database/DiscoveryDataFilter.hpp b/src/cpp/rtps/builtin/discovery/database/DiscoveryDataFilter.hpp index 41819d666d1..18862be7efd 100644 --- a/src/cpp/rtps/builtin/discovery/database/DiscoveryDataFilter.hpp +++ b/src/cpp/rtps/builtin/discovery/database/DiscoveryDataFilter.hpp @@ -20,7 +20,7 @@ #ifndef _FASTDDS_RTPS_DISCOVERY_DATA_FILTER_H_ #define _FASTDDS_RTPS_DISCOVERY_DATA_FILTER_H_ -#include +#include namespace eprosima { namespace fastdds { diff --git a/src/cpp/rtps/reader/StatefulReader.cpp b/src/cpp/rtps/reader/StatefulReader.cpp index 5d08dd09b66..3cb0c7939d9 100644 --- a/src/cpp/rtps/reader/StatefulReader.cpp +++ b/src/cpp/rtps/reader/StatefulReader.cpp @@ -466,6 +466,15 @@ bool StatefulReader::processDataMsg( return false; } + if (data_filter_ && !data_filter_->is_relevant(*change, m_guid)) + { + if (pWP) + { + pWP->irrelevant_change_set(change->sequenceNumber); + } + return true; + } + // Ask the pool for a cache change CacheChange_t* change_to_add = nullptr; if (!change_pool_->reserve_cache(change_to_add)) @@ -623,6 +632,10 @@ bool StatefulReader::processDataFragMsg( { mp_history->completed_change(work_change); pWP->received_change_set(work_change->sequenceNumber); + if (data_filter_ && !data_filter_->is_relevant(*work_change, m_guid)) + { + mp_history->remove_change(work_change); + } NotifyChanges(pWP); } } diff --git a/src/cpp/rtps/reader/StatelessReader.cpp b/src/cpp/rtps/reader/StatelessReader.cpp index 738ba165d83..0d67b0dfe78 100644 --- a/src/cpp/rtps/reader/StatelessReader.cpp +++ b/src/cpp/rtps/reader/StatelessReader.cpp @@ -420,6 +420,12 @@ bool StatelessReader::processDataMsg( return false; } + if (data_filter_ && !data_filter_->is_relevant(*change, m_guid)) + { + update_last_notified(change->writerGUID, change->sequenceNumber); + return true; + } + // Ask the pool for a cache change CacheChange_t* change_to_add = nullptr; if (!change_pool_->reserve_cache(change_to_add)) @@ -595,7 +601,12 @@ bool StatelessReader::processDataFragMsg( // If the change was completed, process it. if (change_completed != nullptr) { - if (!change_received(change_completed)) + if (data_filter_ && !data_filter_->is_relevant(*change_completed, m_guid)) + { + update_last_notified(change_completed->writerGUID, change_completed->sequenceNumber); + releaseCache(change_completed); + } + else if (!change_received(change_completed)) { logInfo(RTPS_MSG_IN, IDSTRING "MessageReceiver not add change " << diff --git a/src/cpp/rtps/writer/StatefulWriter.cpp b/src/cpp/rtps/writer/StatefulWriter.cpp index e22f8e2ab5f..693123459ea 100644 --- a/src/cpp/rtps/writer/StatefulWriter.cpp +++ b/src/cpp/rtps/writer/StatefulWriter.cpp @@ -18,8 +18,9 @@ */ #include + +#include #include -#include #include #include diff --git a/test/mock/dds/DomainParticipantImpl/fastdds/domain/DomainParticipantImpl.hpp b/test/mock/dds/DomainParticipantImpl/fastdds/domain/DomainParticipantImpl.hpp index ba216d04d19..10fb84d7e18 100644 --- a/test/mock/dds/DomainParticipantImpl/fastdds/domain/DomainParticipantImpl.hpp +++ b/test/mock/dds/DomainParticipantImpl/fastdds/domain/DomainParticipantImpl.hpp @@ -311,6 +311,26 @@ class DomainParticipantImpl return ReturnCode_t::RETCODE_ERROR; } + MOCK_METHOD5(create_contentfilteredtopic, ContentFilteredTopic * ( + const std::string& name, + Topic * related_topic, + const std::string& filter_expression, + const std::vector& expression_parameters, + const char* filter_class_name)); + + MOCK_METHOD1(delete_contentfilteredtopic, ReturnCode_t( + const ContentFilteredTopic * topic)); + + MOCK_METHOD2(register_content_filter_factory, ReturnCode_t( + const char* filter_class_name, + IContentFilterFactory* const filter_factory)); + + MOCK_METHOD1(lookup_content_filter_factory, IContentFilterFactory * ( + const char* filter_class_name)); + + MOCK_METHOD1(unregister_content_filter_factory, ReturnCode_t ( + const char* filter_class_name)); + TopicDescription* lookup_topicdescription( const std::string& topic_name) const { diff --git a/test/mock/rtps/RTPSReader/fastdds/rtps/reader/RTPSReader.h b/test/mock/rtps/RTPSReader/fastdds/rtps/reader/RTPSReader.h index 17829fb1272..26a8d68f01d 100644 --- a/test/mock/rtps/RTPSReader/fastdds/rtps/reader/RTPSReader.h +++ b/test/mock/rtps/RTPSReader/fastdds/rtps/reader/RTPSReader.h @@ -19,6 +19,8 @@ #ifndef _FASTDDS_RTPS_READER_RTPSREADER_H_ #define _FASTDDS_RTPS_READER_RTPSREADER_H_ +#include + #include #include #include @@ -113,6 +115,8 @@ class RTPSReader : public Endpoint MOCK_METHOD0(get_unread_count, uint64_t()); + MOCK_METHOD1(set_content_filter, void (eprosima::fastdds::rtps::IReaderDataFilter* filter)); + // *INDENT-ON* diff --git a/test/unittest/dds/status/CMakeLists.txt b/test/unittest/dds/status/CMakeLists.txt index 97e3805ca34..e68d3c33ee0 100644 --- a/test/unittest/dds/status/CMakeLists.txt +++ b/test/unittest/dds/status/CMakeLists.txt @@ -39,6 +39,7 @@ set(LISTENERTESTS_SOURCE ListenerTests.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/qos/SubscriberQos.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/qos/DataReaderQos.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/qos/ReaderQos.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/ContentFilteredTopic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/Topic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/qos/TopicQos.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/TopicImpl.cpp From b808ed25b6be02870dc1a0089bf0838e364b6257 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Thu, 16 Dec 2021 08:42:27 +0100 Subject: [PATCH 02/21] Updating content filter interfaces (#2351) * Refs 12540. Update IContentFilterFactory::create_content_filter. Signed-off-by: Miguel Company * Refs 12540. Use updated IContentFilterFactory::create_content_filter. Signed-off-by: Miguel Company * Refs 12540. Rename parameter. Signed-off-by: Miguel Company * Refs 12540. Remove get_descriptor() from TopicDataType. Signed-off-by: Miguel Company --- .../fastdds/dds/topic/IContentFilterFactory.hpp | 4 ++-- include/fastdds/dds/topic/TopicDataType.hpp | 16 ---------------- include/fastdds/dds/topic/TypeSupport.hpp | 10 ---------- include/fastrtps/types/DynamicPubSubType.h | 2 -- src/cpp/dynamic-types/DynamicPubSubType.cpp | 5 ----- src/cpp/fastdds/domain/DomainParticipantImpl.cpp | 3 +-- 6 files changed, 3 insertions(+), 37 deletions(-) diff --git a/include/fastdds/dds/topic/IContentFilterFactory.hpp b/include/fastdds/dds/topic/IContentFilterFactory.hpp index 6473bc9e22f..ec7c7394e63 100644 --- a/include/fastdds/dds/topic/IContentFilterFactory.hpp +++ b/include/fastdds/dds/topic/IContentFilterFactory.hpp @@ -23,9 +23,9 @@ #include #include +#include #include -#include namespace eprosima { namespace fastdds { @@ -40,7 +40,7 @@ struct IContentFilterFactory virtual ReturnCode_t create_content_filter( const char* filter_class_name, const char* type_name, - const TypeDescriptor* type_description, + const TopicDataType* data_type, const char* filter_expression, const ParameterSeq& filter_parameters, IContentFilter*& filter_instance) = 0; diff --git a/include/fastdds/dds/topic/TopicDataType.hpp b/include/fastdds/dds/topic/TopicDataType.hpp index 8ec99a12879..cbe6291ff22 100644 --- a/include/fastdds/dds/topic/TopicDataType.hpp +++ b/include/fastdds/dds/topic/TopicDataType.hpp @@ -27,7 +27,6 @@ #include #include -#include #include // This version of TypeSupport has `is_bounded()` @@ -39,9 +38,6 @@ // This version of TypeSupport has `construct_sample()` #define TOPIC_DATA_TYPE_API_HAS_CONSTRUCT_SAMPLE -// This version of TypeSupport has `get_descriptor()` -#define TOPIC_DATA_TYPE_API_HAS_GET_DESCRIPTOR - namespace eprosima { namespace fastrtps { @@ -65,8 +61,6 @@ class TopicDataType { public: - using TypeDescriptor = eprosima::fastrtps::types::TypeDescriptor; - /** * @brief Constructor */ @@ -312,16 +306,6 @@ class TopicDataType return false; } - /** - * Get the structure representing the description of the type - * - * @return pointer to the type descriptor - */ - RTPS_DllAPI virtual inline const TypeDescriptor* get_desciptor() const - { - return nullptr; - } - //! Maximum serialized size of the type in bytes. //! If the type has unbounded fields, and therefore cannot have a maximum size, use 0. uint32_t m_typeSize; diff --git a/include/fastdds/dds/topic/TypeSupport.hpp b/include/fastdds/dds/topic/TypeSupport.hpp index 19a4bf27da9..42dd3bfa690 100644 --- a/include/fastdds/dds/topic/TypeSupport.hpp +++ b/include/fastdds/dds/topic/TypeSupport.hpp @@ -237,16 +237,6 @@ class TypeSupport : public std::shared_ptr return get()->is_plain(); } - /** - * Get the structure representing the description of the type - * - * @return pointer to the type descriptor - */ - RTPS_DllAPI virtual inline const TopicDataType::TypeDescriptor* get_desciptor() const - { - return get()->get_desciptor(); - } - RTPS_DllAPI bool operator !=( std::nullptr_t) const { diff --git a/include/fastrtps/types/DynamicPubSubType.h b/include/fastrtps/types/DynamicPubSubType.h index 094b81d47e6..f0d2d1b0a74 100644 --- a/include/fastrtps/types/DynamicPubSubType.h +++ b/include/fastrtps/types/DynamicPubSubType.h @@ -65,8 +65,6 @@ class DynamicPubSubType : public eprosima::fastdds::dds::TopicDataType void* data, eprosima::fastrtps::rtps::SerializedPayload_t* payload) override; - RTPS_DllAPI const TypeDescriptor* get_desciptor() const override; - RTPS_DllAPI void CleanDynamicType(); RTPS_DllAPI DynamicType_ptr GetDynamicType() const; diff --git a/src/cpp/dynamic-types/DynamicPubSubType.cpp b/src/cpp/dynamic-types/DynamicPubSubType.cpp index d5816920024..c8919a6ff6f 100644 --- a/src/cpp/dynamic-types/DynamicPubSubType.cpp +++ b/src/cpp/dynamic-types/DynamicPubSubType.cpp @@ -200,11 +200,6 @@ bool DynamicPubSubType::serialize( return true; } -const TypeDescriptor* DynamicPubSubType::get_desciptor() const -{ - return nullptr == dynamic_type_ ? nullptr : dynamic_type_->get_descriptor(); -} - void DynamicPubSubType::UpdateDynamicTypeInfo() { if (dynamic_type_ != nullptr) diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp index 4c95228a755..b9c5c27ffa4 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp @@ -511,7 +511,6 @@ ContentFilteredTopic* DomainParticipantImpl::create_contentfilteredtopic( TopicImpl* topic_impl = dynamic_cast(related_topic->get_impl()); assert(nullptr != topic_impl); const TypeSupport& type = topic_impl->get_type(); - const IContentFilterFactory::TypeDescriptor* type_descriptor = type->get_desciptor(); LoanableSequence::size_type n_params; n_params = static_cast::size_type>(expression_parameters.size()); LoanableSequence filter_parameters(n_params); @@ -525,7 +524,7 @@ ContentFilteredTopic* DomainParticipantImpl::create_contentfilteredtopic( IContentFilter* filter_instance = nullptr; if (ReturnCode_t::RETCODE_OK != filter_factory->create_content_filter(filter_class_name, related_topic->get_type_name().c_str(), - type_descriptor, filter_expression.c_str(), filter_parameters, filter_instance)) + type.get(), filter_expression.c_str(), filter_parameters, filter_instance)) { logError(PARTICIPANT, "Could not create filter of class " << filter_class_name << " for expression \"" << filter_expression); From 4f612f019196e48d4ea2933012462352bade3a5a Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:13:14 +0100 Subject: [PATCH 03/21] Refs 13290. Added new test to ParticipantTests. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 31eef1d3ad9..87023ec8b85 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3056,11 +3056,34 @@ TEST(ParticipantTests, DeleteContainedEntities) } - /* - * This test checks that the following methods are not implemented and returns an error + * This test checks the following methods: * create_contentfilteredtopic * delete_contentfilteredtopic + * register_content_filter_factory + * lookup_content_filter_factory + * unregister_content_filter_factory + */ +TEST(ParticipantTests, ContentFilterInterfaces) +{ + // Create the participant + DomainParticipant* participant = + DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); + ASSERT_NE(participant, nullptr); + + // Create a type and a topic + TypeSupport type(new TopicDataTypeMock()); + ASSERT_EQ(type.register_type(participant), ReturnCode_t::RETCODE_OK); + + Topic* topic = participant->create_topic("topic", type.get_type_name(), TOPIC_QOS_DEFAULT); + ASSERT_NE(topic, nullptr); + + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); + ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); +} + +/* + * This test checks that the following methods are not implemented and returns an error * create_multitopic * delete_multitopic * find_topic @@ -3089,17 +3112,6 @@ TEST(ParticipantTests, UnsupportedMethods) Topic* topic = participant->create_topic("topic", type.get_type_name(), TOPIC_QOS_DEFAULT); ASSERT_NE(topic, nullptr); - ASSERT_EQ( - participant->create_contentfilteredtopic( - "contentfilteredtopic", - topic, - "filter_expression", - std::vector({"a", "b"})), - nullptr); - - // nullptr use as there are not such a class - ASSERT_EQ(participant->delete_contentfilteredtopic(nullptr), ReturnCode_t::RETCODE_UNSUPPORTED); - ASSERT_EQ( participant->create_multitopic( "multitopic", From 239698b8b7ccf3d409156a0c9a3f7d1d4d51f1b2 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:18:53 +0100 Subject: [PATCH 04/21] Refs 13290. Added negative tests for create/delete contentfilteredtopic Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 87023ec8b85..61634414fbd 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3078,6 +3078,24 @@ TEST(ParticipantTests, ContentFilterInterfaces) Topic* topic = participant->create_topic("topic", type.get_type_name(), TOPIC_QOS_DEFAULT); ASSERT_NE(topic, nullptr); + // Negative tests for create_contentfilteredtopic and delete_contentfilteredtopic + { + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic( + "contentfilteredtopic", + topic, + "INVALID SQL EXPRESSION", + std::vector({ "a", "b" }))); + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic( + "contentfilteredtopic", + nullptr, + "INVALID SQL EXPRESSION", + std::vector({ "a", "b" }))); + + EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, participant->delete_contentfilteredtopic(nullptr)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From 5fe24953a08c17417891110b8705fb3bba4e6768 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:19:47 +0100 Subject: [PATCH 05/21] Refs 13290. Added custom filter mock class. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 61634414fbd..934fbd6b90b 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3066,6 +3066,52 @@ TEST(ParticipantTests, DeleteContainedEntities) */ TEST(ParticipantTests, ContentFilterInterfaces) { + struct MockFilter : public IContentFilter, public IContentFilterFactory + { + bool evaluate( + const SerializedPayload& /*payload*/, + const FilterSampleInfo& /*sample_info*/, + const GUID_t& /*reader_guid*/) const override + { + return true; + } + + ReturnCode_t create_content_filter( + const char* /*filter_class_name*/, + const char* /*type_name*/, + const TopicDataType* /*data_type*/, + const char* filter_expression, + const ParameterSeq& filter_parameters, + IContentFilter*& filter_instance) override + { + if (nullptr != filter_expression) + { + std::string s(filter_expression); + if (filter_parameters.length() == std::count(s.begin(), s.end(), '%')) + { + filter_instance = this; + return ReturnCode_t::RETCODE_OK; + } + } + + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + virtual ReturnCode_t delete_content_filter( + const char* /*filter_class_name*/, + IContentFilter* filter_instance) override + { + if (this == filter_instance) + { + return ReturnCode_t::RETCODE_OK; + } + + return ReturnCode_t::RETCODE_BAD_PARAMETER; + } + + }; + + MockFilter test_filter; // Create the participant DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); From f3308f611ed03f00ff57e1024e2dbe4c08a07bbb Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:21:49 +0100 Subject: [PATCH 06/21] Refs 13290. Added negative tests for register_content_filter_factory. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 934fbd6b90b..cac423cd88a 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3066,6 +3066,9 @@ TEST(ParticipantTests, DeleteContainedEntities) */ TEST(ParticipantTests, ContentFilterInterfaces) { + static const char* TEST_FILTER_CLASS = "TESTFILTER"; + static const char* OTHER_FILTER_CLASS = "OTHERFILTER"; + struct MockFilter : public IContentFilter, public IContentFilterFactory { bool evaluate( @@ -3112,6 +3115,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) }; MockFilter test_filter; + std::string very_long_name(512, ' '); + // Create the participant DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); @@ -3142,6 +3147,18 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, participant->delete_contentfilteredtopic(nullptr)); } + // Negative tests for register_content_filter_factory + { + EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, + participant->register_content_filter_factory(nullptr, &test_filter)); + EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, + participant->register_content_filter_factory(very_long_name.c_str(), &test_filter)); + EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, + participant->register_content_filter_factory(TEST_FILTER_CLASS, nullptr)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->register_content_filter_factory(FASTDDS_SQLFILTER_NAME, &test_filter)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From dbe34f214317b6b76b02e8fc1caa042d667aaf96 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:24:44 +0100 Subject: [PATCH 07/21] Refs 13290. Check for nullptr on register_content_filter_factory. Signed-off-by: Miguel Company --- src/cpp/fastdds/domain/DomainParticipantImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp index b9c5c27ffa4..5d6a2d1661c 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp @@ -577,7 +577,7 @@ ReturnCode_t DomainParticipantImpl::register_content_filter_factory( const char* filter_class_name, IContentFilterFactory* const filter_factory) { - if (nullptr == filter_class_name || strlen(filter_class_name) > 255) + if (nullptr == filter_factory || nullptr == filter_class_name || strlen(filter_class_name) > 255) { return ReturnCode_t::RETCODE_BAD_PARAMETER; } From 182666f140e2a6a1e5011b8de3299867ac94f2c7 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:38:29 +0100 Subject: [PATCH 08/21] Refs 13290. Added negative tests for lookup_content_filter_factory. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index cac423cd88a..b6ee93811cc 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3159,6 +3159,13 @@ TEST(ParticipantTests, ContentFilterInterfaces) participant->register_content_filter_factory(FASTDDS_SQLFILTER_NAME, &test_filter)); } + // Negative tests for lookup_content_filter_factory + { + EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(nullptr)); + EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(FASTDDS_SQLFILTER_NAME)); + EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(TEST_FILTER_CLASS)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From dcf5ed79f530fdbe4f73f9cebf5cf2d15ca8212e Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:38:49 +0100 Subject: [PATCH 09/21] Refs 13290. Added negative tests for unregister_content_filter_factory. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index b6ee93811cc..7700fa0d15f 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3166,6 +3166,15 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(TEST_FILTER_CLASS)); } + // Negative tests for unregister_content_filter_factory + { + EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, participant->unregister_content_filter_factory(nullptr)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->unregister_content_filter_factory(FASTDDS_SQLFILTER_NAME)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From 1a2fe5888f18273b124410e4f64bf80d9ba4f8f0 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 13:39:39 +0100 Subject: [PATCH 10/21] Refs 13290. Added tests for custom filter factory registration. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 7700fa0d15f..b8239c443af 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3175,6 +3175,28 @@ TEST(ParticipantTests, ContentFilterInterfaces) participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); } + // Custom filter factory registration + { + // Register filter factory + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->register_content_filter_factory(TEST_FILTER_CLASS, &test_filter)); + // Lookup should return same pointer as the one registered + EXPECT_EQ(&test_filter, participant->lookup_content_filter_factory(TEST_FILTER_CLASS)); + // But not for other filter class name + EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(OTHER_FILTER_CLASS)); + // Should not be able to register twice + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->register_content_filter_factory(TEST_FILTER_CLASS, &test_filter)); + // Unregister filter factory + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + // Lookup should now return nullptr + EXPECT_EQ(nullptr, participant->lookup_content_filter_factory(TEST_FILTER_CLASS)); + // Unregister twice should fail + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From 7788306d8a9fd2bd39778bef17623aa7c9a1aa00 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 14:27:21 +0100 Subject: [PATCH 11/21] Refs 13290. Added tests for custom filtered topic creation. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index b8239c443af..9645fc06757 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3197,6 +3197,34 @@ TEST(ParticipantTests, ContentFilterInterfaces) participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); } + // Custom filter registration and creation of filtered topic + { + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS)); + + // Register filter factory + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->register_content_filter_factory(TEST_FILTER_CLASS, &test_filter)); + + // Negative tests for custom filtered topic creation + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", nullptr, "", {}, TEST_FILTER_CLASS)); + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {""}, TEST_FILTER_CLASS)); + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic, "%%", {""}, TEST_FILTER_CLASS)); + + ContentFilteredTopic* filtered_topic = participant->create_contentfilteredtopic("contentfilteredtopic", topic, + "", {}, TEST_FILTER_CLASS); + ASSERT_NE(nullptr, filtered_topic); + + EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); + + // Unregister filter factory + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + } + ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From 0dd1428a1ab972257874fc7df763fcdf40be4286 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Fri, 17 Dec 2021 14:28:11 +0100 Subject: [PATCH 12/21] Refs 13290. Fixed generation of filter_parameters. Signed-off-by: Miguel Company --- src/cpp/fastdds/domain/DomainParticipantImpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp index 5d6a2d1661c..c1805828df9 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp @@ -514,6 +514,7 @@ ContentFilteredTopic* DomainParticipantImpl::create_contentfilteredtopic( LoanableSequence::size_type n_params; n_params = static_cast::size_type>(expression_parameters.size()); LoanableSequence filter_parameters(n_params); + filter_parameters.length(n_params); while (n_params > 0) { n_params--; From 83bdf7f28d353324d46e3c0d5e55daff1f87af3d Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:11:36 +0100 Subject: [PATCH 13/21] Refs 13290. Added more checks on tests. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 9645fc06757..2f246ca6773 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3143,6 +3143,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) nullptr, "INVALID SQL EXPRESSION", std::vector({ "a", "b" }))); + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, nullptr)); EXPECT_EQ(ReturnCode_t::RETCODE_BAD_PARAMETER, participant->delete_contentfilteredtopic(nullptr)); } @@ -3207,6 +3209,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) participant->register_content_filter_factory(TEST_FILTER_CLASS, &test_filter)); // Negative tests for custom filtered topic creation + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic(topic->get_name(), topic, "", {}, TEST_FILTER_CLASS)); EXPECT_EQ(nullptr, participant->create_contentfilteredtopic("contentfilteredtopic", nullptr, "", {}, TEST_FILTER_CLASS)); EXPECT_EQ(nullptr, @@ -3214,11 +3218,37 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(nullptr, participant->create_contentfilteredtopic("contentfilteredtopic", topic, "%%", {""}, TEST_FILTER_CLASS)); + // Possitive test ContentFilteredTopic* filtered_topic = participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS); ASSERT_NE(nullptr, filtered_topic); + // Should fail to create same filter twice + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS)); + + // Should not be able to delete topic, since it is referenced by filtered_topic + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_topic(topic)); + + // Should not be able to unregister filter factory, since it is referenced by filtered_topic + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + + // Reference filtered_topic by creating a DataReader + auto subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT); + ASSERT_NE(nullptr, subscriber); + auto data_reader = subscriber->create_datareader(filtered_topic, DATAREADER_QOS_DEFAULT); + ASSERT_NE(nullptr, data_reader); + + // Should not be able to delete filtered_topic, since it is referenced by data_reader + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); + + EXPECT_EQ(ReturnCode_t::RETCODE_OK, subscriber->delete_datareader(data_reader)); + EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_subscriber(subscriber)); + + // Should be able to delete filtered_topic, but not twice EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); // Unregister filter factory EXPECT_EQ(ReturnCode_t::RETCODE_OK, From 251538823c7820d297f29755ffc1827c1143f8d0 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:16:39 +0100 Subject: [PATCH 14/21] Refs 13290. Added second participant and topic. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 2f246ca6773..a4e4f5ba9b2 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3117,17 +3117,23 @@ TEST(ParticipantTests, ContentFilterInterfaces) MockFilter test_filter; std::string very_long_name(512, ' '); - // Create the participant + // Create two participants DomainParticipant* participant = DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); ASSERT_NE(participant, nullptr); + DomainParticipant* participant2 = + DomainParticipantFactory::get_instance()->create_participant(0, PARTICIPANT_QOS_DEFAULT); + ASSERT_NE(participant2, nullptr); - // Create a type and a topic + // Create a type and a topics TypeSupport type(new TopicDataTypeMock()); ASSERT_EQ(type.register_type(participant), ReturnCode_t::RETCODE_OK); + ASSERT_EQ(type.register_type(participant2), ReturnCode_t::RETCODE_OK); Topic* topic = participant->create_topic("topic", type.get_type_name(), TOPIC_QOS_DEFAULT); ASSERT_NE(topic, nullptr); + Topic* topic2 = participant2->create_topic("topic", type.get_type_name(), TOPIC_QOS_DEFAULT); + ASSERT_NE(topic2, nullptr); // Negative tests for create_contentfilteredtopic and delete_contentfilteredtopic { @@ -3255,6 +3261,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); } + ASSERT_EQ(participant2->delete_topic(topic2), ReturnCode_t::RETCODE_OK); + ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant2), ReturnCode_t::RETCODE_OK); ASSERT_EQ(participant->delete_topic(topic), ReturnCode_t::RETCODE_OK); ASSERT_EQ(DomainParticipantFactory::get_instance()->delete_participant(participant), ReturnCode_t::RETCODE_OK); } From 5daaec6ece3dc3b07367296c5716d780e77fb660 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:26:16 +0100 Subject: [PATCH 15/21] Refs 13290. Added cross-participant checks. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index a4e4f5ba9b2..5c9a9b46718 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3217,6 +3217,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) // Negative tests for custom filtered topic creation EXPECT_EQ(nullptr, participant->create_contentfilteredtopic(topic->get_name(), topic, "", {}, TEST_FILTER_CLASS)); + EXPECT_EQ(nullptr, + participant->create_contentfilteredtopic("contentfilteredtopic", topic2, "", {}, TEST_FILTER_CLASS)); EXPECT_EQ(nullptr, participant->create_contentfilteredtopic("contentfilteredtopic", nullptr, "", {}, TEST_FILTER_CLASS)); EXPECT_EQ(nullptr, @@ -3252,7 +3254,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(ReturnCode_t::RETCODE_OK, subscriber->delete_datareader(data_reader)); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_subscriber(subscriber)); - // Should be able to delete filtered_topic, but not twice + // Should be able to delete filtered_topic, but only on correct participant and not twice + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant2->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); From bcfb07dd2696814f1b41e4e0bedc3b1fe95d123e Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:28:14 +0100 Subject: [PATCH 16/21] Refs 13290. Added lookup_topicdescription checks. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 5c9a9b46718..117668b05e2 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3230,6 +3230,7 @@ TEST(ParticipantTests, ContentFilterInterfaces) ContentFilteredTopic* filtered_topic = participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS); ASSERT_NE(nullptr, filtered_topic); + EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); // Should fail to create same filter twice EXPECT_EQ(nullptr, @@ -3250,13 +3251,16 @@ TEST(ParticipantTests, ContentFilterInterfaces) // Should not be able to delete filtered_topic, since it is referenced by data_reader EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); + EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_OK, subscriber->delete_datareader(data_reader)); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_subscriber(subscriber)); // Should be able to delete filtered_topic, but only on correct participant and not twice EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant2->delete_contentfilteredtopic(filtered_topic)); + EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); + EXPECT_EQ(nullptr, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); // Unregister filter factory From d73209bdf48e36a4a8a22e53e9d2a5235f2330a4 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:49:24 +0100 Subject: [PATCH 17/21] Refs 13290. Avoid deleting the same entity twice. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 117668b05e2..e919992fe7b 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3256,12 +3256,11 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(ReturnCode_t::RETCODE_OK, subscriber->delete_datareader(data_reader)); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_subscriber(subscriber)); - // Should be able to delete filtered_topic, but only on correct participant and not twice + // Should be able to delete filtered_topic, but only on correct participant EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant2->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(nullptr, participant->lookup_topicdescription("contentfilteredtopic")); - EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_contentfilteredtopic(filtered_topic)); // Unregister filter factory EXPECT_EQ(ReturnCode_t::RETCODE_OK, From ffc00bae1adcb6f7201a7d62dd30bbcde3babbb8 Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 12:50:32 +0100 Subject: [PATCH 18/21] Refs 13290. No need to check participant (implicit check when not found in collection) Signed-off-by: Miguel Company --- src/cpp/fastdds/domain/DomainParticipantImpl.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp index c1805828df9..ca08d0f893d 100644 --- a/src/cpp/fastdds/domain/DomainParticipantImpl.cpp +++ b/src/cpp/fastdds/domain/DomainParticipantImpl.cpp @@ -553,11 +553,6 @@ ReturnCode_t DomainParticipantImpl::delete_contentfilteredtopic( return ReturnCode_t::RETCODE_BAD_PARAMETER; } - if (participant_ != topic->get_participant()) - { - return ReturnCode_t::RETCODE_PRECONDITION_NOT_MET; - } - std::lock_guard lock(mtx_topics_); auto it = filtered_topics_.find(topic->get_name()); From cb95e9890e415c865dd90c427da4386104d7fd3f Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 14:24:45 +0100 Subject: [PATCH 19/21] Refs 13290. Fixed link issues on unit tests. Signed-off-by: Miguel Company --- test/unittest/dds/publisher/CMakeLists.txt | 1 + test/unittest/statistics/dds/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/test/unittest/dds/publisher/CMakeLists.txt b/test/unittest/dds/publisher/CMakeLists.txt index 384441c717e..17fcb776640 100644 --- a/test/unittest/dds/publisher/CMakeLists.txt +++ b/test/unittest/dds/publisher/CMakeLists.txt @@ -80,6 +80,7 @@ set(DATAWRITERTESTS_SOURCE DataWriterTests.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/Subscriber.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/SubscriberImpl.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/qos/SubscriberQos.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/ContentFilteredTopic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/Topic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/TopicImpl.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/TypeSupport.cpp diff --git a/test/unittest/statistics/dds/CMakeLists.txt b/test/unittest/statistics/dds/CMakeLists.txt index da809281b49..1eca0b887c0 100644 --- a/test/unittest/statistics/dds/CMakeLists.txt +++ b/test/unittest/statistics/dds/CMakeLists.txt @@ -134,6 +134,7 @@ if (SQLITE3_SUPPORT AND FASTDDS_STATISTICS) ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/Subscriber.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/SubscriberImpl.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/subscriber/qos/SubscriberQos.cpp + ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/ContentFilteredTopic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/Topic.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/TopicImpl.cpp ${PROJECT_SOURCE_DIR}/src/cpp/fastdds/topic/TypeSupport.cpp From 11a2add5fe6f7807618118bc4d27adc014574f3a Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 14:28:20 +0100 Subject: [PATCH 20/21] Refs 13290. Linters. Signed-off-by: Miguel Company --- test/unittest/dds/participant/ParticipantTests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index e919992fe7b..4cb2db0dc39 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3257,7 +3257,8 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_subscriber(subscriber)); // Should be able to delete filtered_topic, but only on correct participant - EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant2->delete_contentfilteredtopic(filtered_topic)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant2->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(nullptr, participant->lookup_topicdescription("contentfilteredtopic")); From 2502a7851c5c2793ad65727f9e479f5b6a34dd1f Mon Sep 17 00:00:00 2001 From: Miguel Company Date: Mon, 20 Dec 2021 15:59:21 +0100 Subject: [PATCH 21/21] Refs 13290. Additional checks to ensure collection traversal. Signed-off-by: Miguel Company --- .../dds/participant/ParticipantTests.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/unittest/dds/participant/ParticipantTests.cpp b/test/unittest/dds/participant/ParticipantTests.cpp index 4cb2db0dc39..3fbc30f2149 100644 --- a/test/unittest/dds/participant/ParticipantTests.cpp +++ b/test/unittest/dds/participant/ParticipantTests.cpp @@ -3210,9 +3210,11 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(nullptr, participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS)); - // Register filter factory + // Register two filter factories to ensure traversal of collections EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->register_content_filter_factory(TEST_FILTER_CLASS, &test_filter)); + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->register_content_filter_factory(OTHER_FILTER_CLASS, &test_filter)); // Negative tests for custom filtered topic creation EXPECT_EQ(nullptr, @@ -3236,12 +3238,20 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(nullptr, participant->create_contentfilteredtopic("contentfilteredtopic", topic, "", {}, TEST_FILTER_CLASS)); + // Create on the other filter class to ensure traversal of collections + ContentFilteredTopic* filtered_topic2 = participant->create_contentfilteredtopic("contentfilteredtopic2", + topic, "", {}, OTHER_FILTER_CLASS); + ASSERT_NE(nullptr, filtered_topic2); + EXPECT_EQ(filtered_topic2, participant->lookup_topicdescription("contentfilteredtopic2")); + // Should not be able to delete topic, since it is referenced by filtered_topic EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->delete_topic(topic)); // Should not be able to unregister filter factory, since it is referenced by filtered_topic EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + EXPECT_EQ(ReturnCode_t::RETCODE_PRECONDITION_NOT_MET, + participant->unregister_content_filter_factory(OTHER_FILTER_CLASS)); // Reference filtered_topic by creating a DataReader auto subscriber = participant->create_subscriber(SUBSCRIBER_QOS_DEFAULT); @@ -3262,10 +3272,13 @@ TEST(ParticipantTests, ContentFilterInterfaces) EXPECT_EQ(filtered_topic, participant->lookup_topicdescription("contentfilteredtopic")); EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic)); EXPECT_EQ(nullptr, participant->lookup_topicdescription("contentfilteredtopic")); + EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->delete_contentfilteredtopic(filtered_topic2)); - // Unregister filter factory + // Unregister filter factories EXPECT_EQ(ReturnCode_t::RETCODE_OK, participant->unregister_content_filter_factory(TEST_FILTER_CLASS)); + EXPECT_EQ(ReturnCode_t::RETCODE_OK, + participant->unregister_content_filter_factory(OTHER_FILTER_CLASS)); } ASSERT_EQ(participant2->delete_topic(topic2), ReturnCode_t::RETCODE_OK);