This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/cap_req_model_lib in repository https://gitbox.apache.org/repos/asf/celix.git
commit 52b95af3157b678c6cf21f0929a2de6f11e909be Author: Pepijn Noltes <[email protected]> AuthorDate: Thu Apr 6 00:02:44 2023 +0200 Add rcm lib with base requirement-capability-model implementation --- CMakeLists.txt | 2 +- libs/CMakeLists.txt | 1 + libs/{ => rcm}/CMakeLists.txt | 38 ++-- libs/rcm/README.md | 44 +++++ libs/rcm/diagrams/logical-req-cap-model.png | Bin 0 -> 4927 bytes libs/rcm/diagrams/logical-req-cap-model.puml | 23 +++ libs/{ => rcm/gtest}/CMakeLists.txt | 34 ++-- .../src/RequirementCapabilityModelTestSuite.cc | 217 +++++++++++++++++++++ libs/rcm/include/celix_capability.h | 73 +++++++ libs/rcm/include/celix_rcm_err.h | 62 ++++++ libs/rcm/include/celix_rcm_types.h | 42 ++++ libs/rcm/include/celix_requirement.h | 81 ++++++++ libs/rcm/include/celix_resource.h | 62 ++++++ libs/rcm/src/celix_capability.c | 171 ++++++++++++++++ libs/rcm/src/celix_rcm_err.c | 105 ++++++++++ libs/rcm/src/celix_rcm_err_private.h | 42 ++++ libs/rcm/src/celix_requirement.c | 182 +++++++++++++++++ libs/rcm/src/celix_resource.c | 183 +++++++++++++++++ 18 files changed, 1318 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 184b90d2..4512800e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,7 +48,7 @@ endif () if (NOT TARGET CURL::libcurl) #Note more recent curl will create CURL::libcurl target message("Note CURL::libcurl target not created by find_package(CURL). Creating CURL::libcurl Target.") - add_library(CURL::libcurl SHARED IMPORTED) + add_library(CURL::libcurl SHARED IMPORTED libs/rcm/include/celix_requirement.h libs/rcm/include/celix_capability.h) set_target_properties(CURL::libcurl PROPERTIES IMPORTED_LOCATION "${CURL_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}" diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index feee84b7..20b51203 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -22,6 +22,7 @@ add_subdirectory(dfi) add_subdirectory(etcdlib) add_subdirectory(promises) add_subdirectory(pushstreams) +add_subdirectory(rcm) add_subdirectory(framework) diff --git a/libs/CMakeLists.txt b/libs/rcm/CMakeLists.txt similarity index 59% copy from libs/CMakeLists.txt copy to libs/rcm/CMakeLists.txt index feee84b7..bc812dc1 100644 --- a/libs/CMakeLists.txt +++ b/libs/rcm/CMakeLists.txt @@ -5,9 +5,9 @@ # to you 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 @@ -15,27 +15,19 @@ # specific language governing permissions and limitations # under the License. -#utils, dfi and etcdlib are standalone -#(e.g. no dependency on celix framework -add_subdirectory(utils) -add_subdirectory(dfi) -add_subdirectory(etcdlib) -add_subdirectory(promises) -add_subdirectory(pushstreams) +add_library(rcm STATIC + src/celix_resource.c + src/celix_capability.c + src/celix_requirement.c + src/celix_rcm_err.c +) +set_target_properties(rcm PROPERTIES OUTPUT_NAME "celix_rcm") +target_include_directories(rcm PRIVATE src) +target_include_directories(rcm PUBLIC include) +target_link_libraries(rcm PUBLIC Celix::utils) -add_subdirectory(framework) +add_library(Celix::rcm ALIAS rcm) -#launcher -add_subdirectory(launcher) - -#add_subdirectory(event_admin)# event_admin is unstable -add_subdirectory(dependency_manager) -if (CELIX_CXX14) - add_subdirectory(dependency_manager_cxx) -endif () - -# Error Injectors -if (ENABLE_TESTING AND LINKER_WRAP_SUPPORTED) - add_subdirectory(error_injector) +if (ENABLE_TESTING) + add_subdirectory(gtest) endif () - diff --git a/libs/rcm/README.md b/libs/rcm/README.md new file mode 100644 index 00000000..f77602b6 --- /dev/null +++ b/libs/rcm/README.md @@ -0,0 +1,44 @@ +--- +title: RCM library +--- + +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. +--> + +## Requirement-Capability-Model (RCM) library +C library based on the OSGi Requirement-Capability-Model, part of +[Chapter 3.3](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.module.html#framework.module.dependencies) +, [Chapter 6](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.resource.html) +and [Chapter 7](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.wiring.html) +in the OSGi Core Specification 8. + +## Base Requirement-Capability-Model + +The Requirement-Capability-Model (RCM) is a model for describing the capabilities and requirements for a resource. +This model can be used in bundle wiring to resolve the dependencies between bundles. + +The following diagram shows the RCM model: + + + +## Requirement-Capability-Model and (Bundle) Wiring + +TODO + +## Requirement-Capability-Model Resolver + +TODO diff --git a/libs/rcm/diagrams/logical-req-cap-model.png b/libs/rcm/diagrams/logical-req-cap-model.png new file mode 100644 index 00000000..d7a14d69 Binary files /dev/null and b/libs/rcm/diagrams/logical-req-cap-model.png differ diff --git a/libs/rcm/diagrams/logical-req-cap-model.puml b/libs/rcm/diagrams/logical-req-cap-model.puml new file mode 100644 index 00000000..e65d3169 --- /dev/null +++ b/libs/rcm/diagrams/logical-req-cap-model.puml @@ -0,0 +1,23 @@ +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You 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. + +@startuml +rectangle "Resource" +rectangle "Capability" +rectangle "Requirement" + +Resource *-up-> "0..n" Requirement +Resource *-down-> "0..n" Capability +@enduml diff --git a/libs/CMakeLists.txt b/libs/rcm/gtest/CMakeLists.txt similarity index 55% copy from libs/CMakeLists.txt copy to libs/rcm/gtest/CMakeLists.txt index feee84b7..bfe6ef45 100644 --- a/libs/CMakeLists.txt +++ b/libs/rcm/gtest/CMakeLists.txt @@ -15,27 +15,21 @@ # specific language governing permissions and limitations # under the License. -#utils, dfi and etcdlib are standalone -#(e.g. no dependency on celix framework -add_subdirectory(utils) -add_subdirectory(dfi) -add_subdirectory(etcdlib) -add_subdirectory(promises) -add_subdirectory(pushstreams) +set(CMAKE_CXX_STANDARD 17) -add_subdirectory(framework) +add_executable(test_rcm + src/RequirementCapabilityModelTestSuite.cc +) -#launcher -add_subdirectory(launcher) +target_link_libraries(test_rcm PRIVATE Celix::rcm GTest::gtest GTest::gtest_main) +add_test(NAME test_rcm COMMAND test_rcm) +setup_target_for_coverage(test_rcm SCAN_DIR ..) -#add_subdirectory(event_admin)# event_admin is unstable -add_subdirectory(dependency_manager) -if (CELIX_CXX14) - add_subdirectory(dependency_manager_cxx) -endif () - -# Error Injectors -if (ENABLE_TESTING AND LINKER_WRAP_SUPPORTED) - add_subdirectory(error_injector) -endif () +#TODO +#add_executable(test_rcm_with_error_injection +# src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc +#) +#target_link_libraries(test_rcm PRIVATE Celix::rcm GTest::gtest GTest::gtest_main) +#add_test(NAME test_rcm_with_error_injection COMMAND test_rcm_with_error_injection) +#setup_target_for_coverage(test_rcm_with_error_injection SCAN_DIR ..) diff --git a/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc new file mode 100644 index 00000000..e90b0aa5 --- /dev/null +++ b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <gtest/gtest.h> + +#include "celix/Properties.h" +#include "celix_capability.h" +#include "celix_requirement.h" +#include "celix_resource.h" +#include "celix_rcm_err.h" + +class RequirementCapabilityModelTestSuite : public ::testing::Test {}; + +TEST_F(RequirementCapabilityModelTestSuite, TestRequirement) { + celix_requirement_t* req = celix_requirement_create(nullptr, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))"); + ASSERT_TRUE(req != nullptr); + EXPECT_STREQ("test-namespace", celix_requirement_getNamespace(req)); + EXPECT_STREQ("(&(capability.attribute1=foo)(capability.attribute2=bar))", celix_requirement_getFilter(req)); + + celix_requirement_t* req2 = celix_requirement_create(nullptr, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))"); + ASSERT_TRUE(req2 != nullptr); + EXPECT_EQ(celix_requirement_hashCode(req), celix_requirement_hashCode(req2)); + EXPECT_TRUE(celix_requirement_equals(req, req2)); + + celix_requirement_t* req3 = celix_requirement_create(nullptr, "test-namespace2", "(&(capability.attribute1=foo)(capability.attribute2=bar2))"); //note different filter + ASSERT_TRUE(req3 != nullptr); + EXPECT_NE(celix_requirement_hashCode(req), celix_requirement_hashCode(req3)); + EXPECT_FALSE(celix_requirement_equals(req, req3)); + + //test attributes/directives + EXPECT_EQ(0, celix_properties_size(celix_requirement_getAttributes(req))); + EXPECT_EQ(1, celix_properties_size(celix_requirement_getDirectives(req))); //1 filter directive + celix_requirement_addAttribute(req, "test-attribute", "test-attribute-value"); + celix_requirement_addDirective(req, "test-directive", "test-directive-value"); + EXPECT_EQ(1, celix_properties_size(celix_requirement_getAttributes(req))); + EXPECT_EQ(2, celix_properties_size(celix_requirement_getDirectives(req))); + + celix::Properties attr {{"test-attribute1", "test-attribute-value1"}, {"test-attribute2", "test-attribute-value2"}}; + celix_requirement_addAttributes(req, attr.getCProperties()); + celix::Properties dir {{"test-directive1", "test-directive-value1"}, {"test-directive2", "test-directive-value2"}}; + celix_requirement_addDirectives(req, dir.getCProperties()); + EXPECT_EQ(3, celix_properties_size(celix_requirement_getAttributes(req))); + EXPECT_EQ(4, celix_properties_size(celix_requirement_getDirectives(req))); + + //overwrite filter directive + celix_requirement_addDirective(req, "filter", "(&(capability.attribute1=foo)(capability.attribute2=bar3))"); + EXPECT_EQ(4, celix_properties_size(celix_requirement_getDirectives(req))); + EXPECT_STREQ("(&(capability.attribute1=foo)(capability.attribute2=bar3))", celix_requirement_getFilter(req)); + + EXPECT_FALSE(celix_requirement_equals(req, req2)); //note req 1 changed + EXPECT_STREQ(celix_requirement_getDirective(req, "test-directive"), "test-directive-value"); + EXPECT_EQ(nullptr, celix_requirement_getDirective(req, "test-directive-non-existing")); + EXPECT_STREQ(celix_requirement_getAttribute(req, "test-attribute"), "test-attribute-value"); + EXPECT_EQ(nullptr, celix_requirement_getAttribute(req, "test-attribute-non-existing")); + + celix_requirement_destroy(req); + celix_requirement_destroy(req2); + celix_requirement_destroy(req3); +} + +TEST_F(RequirementCapabilityModelTestSuite, TestCapability) { + celix_capability_t* cap = celix_capability_create(nullptr, "test-namespace"); + ASSERT_TRUE(cap != nullptr); + EXPECT_STREQ("test-namespace", celix_capability_getNamespace(cap)); + + celix_capability_t* cap2 = celix_capability_create(nullptr, "test-namespace"); + ASSERT_TRUE(cap2 != nullptr); + EXPECT_EQ(celix_capability_hashCode(cap), celix_capability_hashCode(cap2)); + EXPECT_TRUE(celix_capability_equals(cap, cap2)); + + celix_capability_t* cap3 = celix_capability_create(nullptr, "test-namespace2"); + ASSERT_TRUE(cap3 != nullptr); + EXPECT_NE(celix_capability_hashCode(cap), celix_capability_hashCode(cap3)); + EXPECT_FALSE(celix_capability_equals(cap, cap3)); + + //test attributes/directives + EXPECT_EQ(0, celix_properties_size(celix_capability_getAttributes(cap))); + EXPECT_EQ(0, celix_properties_size(celix_capability_getDirectives(cap))); + celix_capability_addAttribute(cap, "test-attribute", "test-attribute-value"); + celix_capability_addDirective(cap, "test-directive", "test-directive-value"); + EXPECT_EQ(1, celix_properties_size(celix_capability_getAttributes(cap))); + EXPECT_EQ(1, celix_properties_size(celix_capability_getDirectives(cap))); + + celix::Properties attr {{"test-attribute1", "test-attribute-value1"}, {"test-attribute2", "test-attribute-value2"}}; + celix_capability_addAttributes(cap, attr.getCProperties()); + celix::Properties dir {{"test-directive1", "test-directive-value1"}, {"test-directive2", "test-directive-value2"}}; + celix_capability_addDirectives(cap, attr.getCProperties()); + EXPECT_EQ(3, celix_properties_size(celix_capability_getAttributes(cap))); + EXPECT_EQ(3, celix_properties_size(celix_capability_getDirectives(cap))); + + EXPECT_FALSE(celix_capability_equals(cap, cap2)); //note cap 1 changed + EXPECT_STREQ(celix_capability_getDirective(cap, "test-directive"), "test-directive-value"); + EXPECT_EQ(nullptr, celix_capability_getDirective(cap, "test-directive-non-existing")); + EXPECT_STREQ(celix_capability_getAttribute(cap, "test-attribute"), "test-attribute-value"); + EXPECT_EQ(nullptr, celix_capability_getAttribute(cap, "test-attribute-non-existing")); + + celix_capability_destroy(cap); + celix_capability_destroy(cap2); + celix_capability_destroy(cap3); +} + +TEST_F(RequirementCapabilityModelTestSuite, TestNoNamespaceForCapabilityAndRequirement) { + celix_rcmErr_resetErrors(); + + celix_capability_t* cap = celix_capability_create(nullptr, nullptr); + EXPECT_TRUE(cap == nullptr); + char* err = celix_rcmErr_popLastError(); + EXPECT_TRUE(err != nullptr); + EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message should contain 'namespace' but was: " << err; + free(err); + + celix_requirement_t* req = celix_requirement_create(nullptr, nullptr, nullptr); + EXPECT_TRUE(req == nullptr); + err = celix_rcmErr_popLastError(); + EXPECT_TRUE(err != nullptr); + EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message should contain 'namespace' but was: " << err; + free(err); + + //test reset + cap = celix_capability_create(nullptr, nullptr); + EXPECT_TRUE(cap == nullptr); + req = celix_requirement_create(nullptr, nullptr, nullptr); + EXPECT_TRUE(req == nullptr); + EXPECT_EQ(2, celix_rcmErr_getErrorCount()); + celix_rcmErr_resetErrors(); //should clear 2 error messages + EXPECT_EQ(0, celix_rcmErr_getErrorCount()); +} + +TEST_F(RequirementCapabilityModelTestSuite, TestResource) { + celix_resource_t* res = celix_resource_create(); + ASSERT_TRUE(res != nullptr); + EXPECT_EQ(0, celix_arrayList_size(celix_resource_getRequirements(res, nullptr))); + EXPECT_EQ(0, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr))); + + celix_resource_t* res2 = celix_resource_create(); + ASSERT_TRUE(res2 != nullptr); + EXPECT_EQ(0, celix_arrayList_size(celix_resource_getRequirements(res, nullptr))); + EXPECT_EQ(0, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr))); + + EXPECT_EQ(celix_resource_hashCode(res), celix_resource_hashCode(res2)); + EXPECT_TRUE(celix_resource_equals(res, res2)); + + celix_requirement_t* req = celix_requirement_create(res, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))"); + celix_resource_addRequirement(res, req); + EXPECT_EQ(res, celix_requirement_getResource(req)); + EXPECT_EQ(1, celix_arrayList_size(celix_resource_getRequirements(res, nullptr))); + + celix_capability_t *cap = celix_capability_create(res, "test-namespace"); + celix_resource_addCapability(res, cap); + EXPECT_EQ(res, celix_capability_getResource(cap)); + EXPECT_EQ(1, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr))); + + EXPECT_FALSE(celix_resource_equals(res, res2)); //note res 1 changed + EXPECT_NE(celix_resource_hashCode(res), celix_resource_hashCode(res2)); + + req = celix_requirement_create(res2, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))"); + cap = celix_capability_create(res2, "test-namespace"); + EXPECT_TRUE(celix_resource_addRequirement(res2, req)); + EXPECT_TRUE(celix_resource_addCapability(res2, cap)); + EXPECT_TRUE(celix_resource_equals(res, res2)); //note res and res2 are the same again + EXPECT_EQ(celix_resource_hashCode(res), celix_resource_hashCode(res2)); + + //test get capabilities/requirements by namespace + req = celix_requirement_create(res, "test-namespace2", nullptr); + cap = celix_capability_create(res, "test-namespace2"); + EXPECT_TRUE(celix_resource_addRequirement(res, req)); + EXPECT_TRUE(celix_resource_addCapability(res, cap)); + EXPECT_EQ(2, celix_arrayList_size(celix_resource_getRequirements(res, nullptr))); + EXPECT_EQ(2, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr))); + const celix_array_list_t* caps = celix_resource_getCapabilities(res, "test-namespace"); + EXPECT_EQ(1, celix_arrayList_size(caps)); + const celix_array_list_t* reqs = celix_resource_getRequirements(res, "test-namespace"); + EXPECT_EQ(1, celix_arrayList_size(reqs)); + + celix_resource_destroy(res); + celix_resource_destroy(res2); +} + +TEST_F(RequirementCapabilityModelTestSuite, TestCapabilityAndRequirementWithWrongResource) { + celix_resource_t* res = celix_resource_create(); + ASSERT_TRUE(res != nullptr); + + celix_capability_t* cap = celix_capability_create(nullptr, "test-namespace"); + celix_requirement_t* req = celix_requirement_create(nullptr, "test-namespace", nullptr); + + celix_rcmErr_resetErrors(); + EXPECT_FALSE(celix_resource_addCapability(res, cap)); + EXPECT_FALSE(celix_resource_addRequirement(res, req)); + EXPECT_EQ(2, celix_rcmErr_getErrorCount()); + celix_requirement_destroy(req); + celix_capability_destroy(cap); + + char* err = celix_rcmErr_popLastError(); + EXPECT_TRUE(strcasestr(err, "capability") != nullptr) << "Error message should contain 'capability' but was: " << err; + free(err); + err = celix_rcmErr_popLastError(); + EXPECT_TRUE(strcasestr(err, "requirement") != nullptr) << "Error message should contain 'requirement' but was: " << err; + free(err); + + celix_resource_destroy(res); +} diff --git a/libs/rcm/include/celix_capability.h b/libs/rcm/include/celix_capability.h new file mode 100644 index 00000000..06ec7b41 --- /dev/null +++ b/libs/rcm/include/celix_capability.h @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_CAPABILITY_H +#define CELIX_CELIX_CAPABILITY_H + +#include <stdbool.h> + +#include "celix_rcm_types.h" +#include "celix_properties.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file celix_capability.h +* @brief The celix_capability_t is a struct which represents a capability. +* +* @thread_safety none +*/ + +celix_capability_t* celix_capability_create( + const celix_resource_t* resource, + const char* ns); + +void celix_capability_destroy(celix_capability_t* capability); + +bool celix_capability_equals(const celix_capability_t* cap1, const celix_capability_t* cap2); + +unsigned int celix_capability_hashCode(const celix_capability_t* cap); + +const celix_resource_t* celix_capability_getResource(const celix_capability_t* cap); + +const char* celix_capability_getNamespace(const celix_capability_t* cap); + +const celix_properties_t* celix_capability_getAttributes(const celix_capability_t* cap); + +const celix_properties_t* celix_capability_getDirectives(const celix_capability_t* cap); + +const char* celix_capability_getAttribute(const celix_capability_t* cap, const char* key); + +const char* celix_capability_getDirective(const celix_capability_t* cap, const char* key); + +void celix_capability_addAttribute(celix_capability_t* cap, const char* key, const char* value); + +void celix_capability_addAttributes(celix_capability_t* cap, const celix_properties_t* attributes); + +void celix_capability_addDirective(celix_capability_t* cap, const char* key, const char* value); + +void celix_capability_addDirectives(celix_capability_t* cap, const celix_properties_t* directives); + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_CAPABILITY_H diff --git a/libs/rcm/include/celix_rcm_err.h b/libs/rcm/include/celix_rcm_err.h new file mode 100644 index 00000000..4ee07e78 --- /dev/null +++ b/libs/rcm/include/celix_rcm_err.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_RCM_ERR_H +#define CELIX_CELIX_RCM_ERR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file celix_rcm_err.h + * @brief The rcm error handling functions. + * @thread_safety Thread safe. + */ + +/** + * @brief Returns the last error message from the current thread. + * The error message must be freed by the caller. + * @returnval NULL if no error message is available. + */ +char* celix_rcmErr_popLastError(); + +//TBD, maybe remove a error struct instead of a single error message, something like: +//typedef struct celix_rcm_error { +// celix_status_t status; +// char* msg; +//} celix_rcm_error_t; +// celix_rcm_error_t* celix_rcmErr_popLastError(); +// celix_rcmErr_destroyError(celix_rcm_error_t* err); + +/** + * @brief Returns the number of error messages from the current thread. + */ +int celix_rcmErr_getErrorCount(); + +/** + * @brief Resets the error message for the current thread. + */ +void celix_rcmErr_resetErrors(); + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_RCM_ERR_H diff --git a/libs/rcm/include/celix_rcm_types.h b/libs/rcm/include/celix_rcm_types.h new file mode 100644 index 00000000..278e1c10 --- /dev/null +++ b/libs/rcm/include/celix_rcm_types.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_RCM_TYPES_H +#define CELIX_CELIX_RCM_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file celix_rcm_types.h +* @brief The celix_rcm_types.h header file contains the forward declarations of the celix_resource_t, celix_capability_t and celix_requirement_t types. +*/ + +//forward declarations of opaque types +typedef struct celix_resource celix_resource_t; +typedef struct celix_capability celix_capability_t; +typedef struct celix_requirement celix_requirement_t; + + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_RCM_TYPES_H diff --git a/libs/rcm/include/celix_requirement.h b/libs/rcm/include/celix_requirement.h new file mode 100644 index 00000000..6c463bbf --- /dev/null +++ b/libs/rcm/include/celix_requirement.h @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_REQUIREMENT_H +#define CELIX_CELIX_REQUIREMENT_H + +#include <stdbool.h> + +#include "celix_rcm_types.h" +#include "celix_properties.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file celix_requirement.h +* @brief The celix_requirement_t is a requirement for a capability. +* +* A requirement is a requirement for a capability. A requirement has a namespace, a filter and a set of directives and attributes. +* The namespace is used to identify the type of requirement. The filter is used to filter the capabilities. +* The directives and attributes are used to configure the requirement. +* +* @thread_safety none +*/ + +celix_requirement_t* celix_requirement_create( + celix_resource_t* resource, + const char* ns, + const char* filter); + + +void celix_requirement_destroy(celix_requirement_t* requirement); + +bool celix_requirement_equals(const celix_requirement_t* req1, const celix_requirement_t* req2); + +unsigned int celix_requirement_hashCode(const celix_requirement_t* req); + +const celix_resource_t* celix_requirement_getResource(const celix_requirement_t* req); + +const char* celix_requirement_getNamespace(const celix_requirement_t* req); + +const char* celix_requirement_getFilter(const celix_requirement_t* req); + +const celix_properties_t* celix_requirement_getDirectives(const celix_requirement_t* req); + +const char* celix_requirement_getDirective(const celix_requirement_t* req, const char* key); + +const celix_properties_t* celix_requirement_getAttributes(const celix_requirement_t* req); + +const char* celix_requirement_getAttribute(const celix_requirement_t* req, const char* key); + +void celix_requirement_addDirective(celix_requirement_t* req, const char* key, const char* value); + +void celix_requirement_addDirectives(celix_requirement_t* req, const celix_properties_t* directives); + +void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, const char* value); + +void celix_requirement_addAttributes(celix_requirement_t* req, const celix_properties_t* attributes); + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_REQUIREMENT_H diff --git a/libs/rcm/include/celix_resource.h b/libs/rcm/include/celix_resource.h new file mode 100644 index 00000000..59b31cf1 --- /dev/null +++ b/libs/rcm/include/celix_resource.h @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_RESOURCE_H +#define CELIX_CELIX_RESOURCE_H + +#include <stdbool.h> + +#include "celix_rcm_types.h" +#include "celix_array_list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @file celix_resource.h +* @brief The celix_resource_t is a resource in the RCM. +* +* A resource is a collection of capabilities and requirements. +* +* @thread_safety none +*/ + +celix_resource_t* celix_resource_create(); + +void celix_resource_destroy(celix_resource_t* resource); + +bool celix_resource_equals(const celix_resource_t* res1, const celix_resource_t* res2); + +unsigned int celix_resource_hashCode(const celix_resource_t* res); + +const celix_array_list_t* celix_resource_getCapabilities(const celix_resource_t* res, const char* ns); + +const celix_array_list_t* celix_resource_getRequirements(const celix_resource_t* res, const char* ns); + +bool celix_resource_addCapability(celix_resource_t* res, celix_capability_t* cap); + +bool celix_resource_addRequirement(celix_resource_t* res, celix_requirement_t* req); + + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_RESOURCE_H diff --git a/libs/rcm/src/celix_capability.c b/libs/rcm/src/celix_capability.c new file mode 100644 index 00000000..7f11f95c --- /dev/null +++ b/libs/rcm/src/celix_capability.c @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stdlib.h> + +#include "celix_utils.h" +#include "celix_capability.h" +#include "celix_rcm_err_private.h" + +struct celix_capability { + const celix_resource_t* resource; //weak ref + char* ns; + celix_properties_t* attributes; + celix_properties_t* directives; +}; + +celix_capability_t* celix_capability_create( + const celix_resource_t* resource, + const char* ns) { + + if (celix_utils_isStringNullOrEmpty(ns)) { + celix_rcmErr_push("Failed to create celix_capability_t. Namespace is NULL or empty."); + return NULL; + } + + celix_capability_t* cap = malloc(sizeof(*cap)); + if (cap != NULL) { + cap->resource = resource; + cap->ns = celix_utils_strdup(ns); + cap->attributes = celix_properties_create(); + cap->directives = celix_properties_create(); + if (cap->ns == NULL || cap->attributes == NULL || cap->directives == NULL) { + celix_rcmErr_push("Failed to allocate celix_capability_t fields. Out of memory."); + celix_capability_destroy(cap); + cap = NULL; + } + } else { + celix_rcmErr_push("Failed to allocate celix_capability_t. Out of memory."); + } + return cap; +} + +void celix_capability_destroy(celix_capability_t* capability) { + if (capability != NULL) { + free(capability->ns); + celix_properties_destroy(capability->attributes); + celix_properties_destroy(capability->directives); + free(capability); + } +} + +bool celix_capability_equals(const celix_capability_t* cap1, const celix_capability_t* cap2) { + if (cap1 == cap2) { + return true; + } + + if (celix_properties_size(cap1->attributes) != celix_properties_size(cap2->attributes) || + celix_properties_size(cap1->directives) != celix_properties_size(cap2->directives)) { + return false; + } + + if (!celix_utils_stringEquals(cap1->ns, cap2->ns)) { + return false; + } + + //compare attributes + bool equals = true; + const char* visit; + CELIX_PROPERTIES_FOR_EACH(cap1->attributes, visit) { + const char* value1 = celix_properties_get(cap1->attributes, visit, NULL); + const char* value2 = celix_properties_get(cap2->attributes, visit, NULL); + if (!celix_utils_stringEquals(value1, value2)) { + equals = false; + break; + } + } + if (!equals) { + return false; + } + CELIX_PROPERTIES_FOR_EACH(cap1->directives, visit) { + const char* value1 = celix_properties_get(cap1->directives, visit, NULL); + const char* value2 = celix_properties_get(cap2->directives, visit, NULL); + if (!celix_utils_stringEquals(value1, value2)) { + equals = false; + break; + } + } + return equals; +} + +unsigned int celix_capability_hashCode(const celix_capability_t* cap) { + unsigned int hash = celix_utils_stringHash(cap->ns); + const char* visit; + + //FIXME order of attributes/directives is not taken into account + CELIX_PROPERTIES_FOR_EACH(cap->attributes, visit) { + const char* value = celix_properties_get(cap->attributes, visit, NULL); + hash += celix_utils_stringHash(visit); + hash += celix_utils_stringHash(value); + } + CELIX_PROPERTIES_FOR_EACH(cap->directives, visit) { + const char* value = celix_properties_get(cap->directives, visit, NULL); + hash += celix_utils_stringHash(visit); + hash += celix_utils_stringHash(value); + } + return hash; +} + +const celix_resource_t* celix_capability_getResource(const celix_capability_t* cap) { + return cap->resource; +} + +const char* celix_capability_getNamespace(const celix_capability_t* cap) { + return cap->ns; +} + +const celix_properties_t* celix_capability_getAttributes(const celix_capability_t* cap) { + return cap->attributes; +} + +const celix_properties_t* celix_capability_getDirectives(const celix_capability_t* cap) { + return cap->directives; +} + +const char* celix_capability_getAttribute(const celix_capability_t* cap, const char* key) { + return celix_properties_get(cap->attributes, key, NULL); +} + +const char* celix_capability_getDirective(const celix_capability_t* cap, const char* key) { + return celix_properties_get(cap->directives, key, NULL); +} + +void celix_capability_addAttribute(celix_capability_t* cap, const char* key, const char* value) { + celix_properties_set(cap->attributes, key, value); +} + +void celix_capability_addAttributes(celix_capability_t* cap, const celix_properties_t* attributes) { + const char* visit; + CELIX_PROPERTIES_FOR_EACH(attributes, visit) { + const char* value = celix_properties_get(attributes, visit, NULL); + celix_properties_set(cap->attributes, visit, value); + } +} + +void celix_capability_addDirective(celix_capability_t* cap, const char* key, const char* value) { + celix_properties_set(cap->directives, key, value); +} + +void celix_capability_addDirectives(celix_capability_t* cap, const celix_properties_t* directives) { + const char* visit; + CELIX_PROPERTIES_FOR_EACH(directives, visit) { + const char* value = celix_properties_get(directives, visit, NULL); + celix_properties_set(cap->directives, visit, value); + } +} diff --git a/libs/rcm/src/celix_rcm_err.c b/libs/rcm/src/celix_rcm_err.c new file mode 100644 index 00000000..d98fdbbf --- /dev/null +++ b/libs/rcm/src/celix_rcm_err.c @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stdlib.h> +#include <threads.h> +#include <stdarg.h> +#include <stdio.h> + +#include "celix_array_list.h" +#include "celix_rcm_err_private.h" +#include "celix_rcm_err.h" +#include "celix_utils.h" + +static tss_t celix_rcm_tssErrorsKey; + +static void celix_rcm_destroyTssErrors(void* data) { + celix_array_list_t* errors = data; + if (errors != NULL) { + for (int i = 0; i < celix_arrayList_size(errors); ++i) { + char* msg = celix_arrayList_get(errors, i); + free(msg); + } + } + celix_arrayList_destroy(errors); +} + +__attribute__((constructor)) static void celix_rcm_initThreadSpecificStorageKey() { + tss_create(&celix_rcm_tssErrorsKey, celix_rcm_destroyTssErrors); +} + +__attribute__((destructor)) static void celix_rcm_deinitThreadSpecificStorageKey() { + tss_delete(celix_rcm_tssErrorsKey); +} + +char* celix_rcmErr_popLastError() { + char* result = NULL; + celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey); + if (errors != NULL && celix_arrayList_size(errors) > 0) { + result = celix_arrayList_get(errors, 0); + celix_arrayList_removeAt(errors, 0); + } + return result; +} + +int celix_rcmErr_getErrorCount() { + int result = 0; + celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey); + if (errors != NULL) { + result = celix_arrayList_size(errors); + } + return result; +} + +void celix_rcmErr_resetErrors() { + celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey); + if (errors != NULL) { + for (int i = 0; i < celix_arrayList_size(errors); ++i) { + char* msg = celix_arrayList_get(errors, i); + free(msg); + } + celix_arrayList_clear(errors); + } +} + +static void celix_rcm_pushMsg(char* msg) { + celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey); + if (errors == NULL) { + errors = celix_arrayList_create(); + tss_set(celix_rcm_tssErrorsKey, errors); + } + if (errors != NULL) { + celix_arrayList_add(errors, msg); + } +} + +void celix_rcmErr_push(const char* msg) { + celix_rcm_pushMsg(celix_utils_strdup(msg)); +} + +void celix_rcmErr_pushf(char* format, ...) { + va_list args; + va_start(args, format); + char* msg = NULL; + int rc = vasprintf(&msg, format, args); + if (rc >= 0) { + celix_rcm_pushMsg(msg); + } + va_end(args); +} diff --git a/libs/rcm/src/celix_rcm_err_private.h b/libs/rcm/src/celix_rcm_err_private.h new file mode 100644 index 00000000..83b353be --- /dev/null +++ b/libs/rcm/src/celix_rcm_err_private.h @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +#ifndef CELIX_CELIX_RCM_ERR_PRIVATE_H +#define CELIX_CELIX_RCM_ERR_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Push an formatted error message to the thread specific storage rcm errors. + */ +void celix_rcmErr_pushf(char* format, ...) __attribute__((format(printf, 1, 2))); + +/** + * @brief Push an error message to the thread specific storage rcm errors. + */ +void celix_rcmErr_push(const char* msg); + + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CELIX_RCM_ERR_PRIVATE_H diff --git a/libs/rcm/src/celix_requirement.c b/libs/rcm/src/celix_requirement.c new file mode 100644 index 00000000..38b940cc --- /dev/null +++ b/libs/rcm/src/celix_requirement.c @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stdlib.h> + +#include "celix_utils.h" +#include "celix_requirement.h" +#include "celix_rcm_err_private.h" + +#define CELIX_REQUIREMENT_DIRECTIVE_FILTER "filter" + +struct celix_requirement { + const celix_resource_t* resource; //weak ref + char* ns; + celix_properties_t* attributes; + celix_properties_t* directives; +}; + +celix_requirement_t* celix_requirement_create( + celix_resource_t* resource, + const char* ns, + const char* filter) { + + if (celix_utils_isStringNullOrEmpty(ns)) { + celix_rcmErr_push("Failed to create celix_requirement_t. Namespace is NULL or empty."); + return NULL; + } + + celix_requirement_t* req = malloc(sizeof(*req)); + if (req != NULL) { + req->resource = resource; + req->ns = celix_utils_strdup(ns); + req->attributes = celix_properties_create(); + req->directives = celix_properties_create(); + if (req->ns == NULL || req->attributes == NULL || req->directives == NULL) { + celix_rcmErr_push("Failed to allocate celix_requirement_t fields. Out of memory."); + celix_requirement_destroy(req); + req = NULL; + } else if (filter != NULL) { + celix_properties_set(req->directives, CELIX_REQUIREMENT_DIRECTIVE_FILTER, filter); + } + } else { + celix_rcmErr_push("Failed to allocate celix_requirement_t. Out of memory."); + } + return req; +} + +void celix_requirement_destroy(celix_requirement_t* requirement) { + if (requirement != NULL) { + free(requirement->ns); + celix_properties_destroy(requirement->attributes); + celix_properties_destroy(requirement->directives); + free(requirement); + } +} + +bool celix_requirement_equals(const celix_requirement_t* req1, const celix_requirement_t* req2) { + if (req1 == req2) { + return true; + } + + if (celix_properties_size(req1->attributes) != celix_properties_size(req2->attributes) || + celix_properties_size(req1->directives) != celix_properties_size(req2->directives)) { + return false; + } + + if (!celix_utils_stringEquals(req1->ns, req2->ns)) { + return false; + } + + //compare attributes + bool equals = true; + const char* visit; + CELIX_PROPERTIES_FOR_EACH(req1->attributes, visit) { + const char* val1 = celix_properties_get(req1->attributes, visit, NULL); + const char* val2 = celix_properties_get(req2->attributes, visit, NULL); + if (!celix_utils_stringEquals(val1, val2)) { + equals = false; + break; + } + } + if (!equals) { + return false; + } + + //compare directives + CELIX_PROPERTIES_FOR_EACH(req1->directives, visit) { + const char* val1 = celix_properties_get(req1->directives, visit, NULL); + const char* val2 = celix_properties_get(req2->directives, visit, NULL); + if (!celix_utils_stringEquals(val1, val2)) { + equals = false; + break; + } + } + return equals; +} + +unsigned int celix_requirement_hashCode(const celix_requirement_t* req) { + unsigned int hash = celix_utils_stringHash(req->ns); + const char* visit; + + //FIXME order of attributes/directives is not taken into account + CELIX_PROPERTIES_FOR_EACH(req->attributes, visit) { + const char* val = celix_properties_get(req->attributes, visit, NULL); + hash += celix_utils_stringHash(visit); + hash += celix_utils_stringHash(val); + } + CELIX_PROPERTIES_FOR_EACH(req->directives, visit) { + const char* val = celix_properties_get(req->directives, visit, NULL); + hash += celix_utils_stringHash(visit); + hash += celix_utils_stringHash(val); + } + return hash; +} + +const celix_resource_t* celix_requirement_getResource(const celix_requirement_t* req) { + return req->resource; +} + +const char* celix_requirement_getNamespace(const celix_requirement_t* req) { + return req->ns; +} + +const char* celix_requirement_getFilter(const celix_requirement_t* req) { + return celix_properties_get(req->directives, CELIX_REQUIREMENT_DIRECTIVE_FILTER, NULL); +} + +const celix_properties_t* celix_requirement_getDirectives(const celix_requirement_t* req) { + return req->directives; +} + +const char* celix_requirement_getDirective(const celix_requirement_t* req, const char* key) { + return celix_properties_get(req->directives, key, NULL); +} + +const celix_properties_t* celix_requirement_getAttributes(const celix_requirement_t* req) { + return req->attributes; +} + +const char* celix_requirement_getAttribute(const celix_requirement_t* req, const char* key) { + return celix_properties_get(req->attributes, key, NULL); +} + +void celix_requirement_addDirective(celix_requirement_t* req, const char* key, const char* value) { + celix_properties_set(req->directives, key, value); +} + +void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, const char* value) { + celix_properties_set(req->attributes, key, value); +} + +void celix_requirement_addDirectives(celix_requirement_t* req, const celix_properties_t* directives) { + const char* visit; + CELIX_PROPERTIES_FOR_EACH(directives, visit) { + const char* val = celix_properties_get(directives, visit, NULL); + celix_requirement_addDirective(req, visit, val); + } +} + +void celix_requirement_addAttributes(celix_requirement_t* req, const celix_properties_t* attributes) { + const char* visit; + CELIX_PROPERTIES_FOR_EACH(attributes, visit) { + const char* val = celix_properties_get(attributes, visit, NULL); + celix_requirement_addAttribute(req, visit, val); + } +} diff --git a/libs/rcm/src/celix_resource.c b/libs/rcm/src/celix_resource.c new file mode 100644 index 00000000..6b41f457 --- /dev/null +++ b/libs/rcm/src/celix_resource.c @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include <stdlib.h> + +#include "celix_string_hash_map.h" +#include "celix_rcm_err_private.h" +#include "celix_resource.h" +#include "celix_capability.h" +#include "celix_requirement.h" + +struct celix_resource { + celix_array_list_t* allCapabilities; + celix_array_list_t* allRequirements; + celix_string_hash_map_t* capabilitiesByNamespace; //note weak ref to capabilities in allCapabilities + celix_string_hash_map_t* requirementsByNamespace; //note weak ref to requirements in allRequirements +}; + +static void celix_resource_destroyCapability(void* capability) { + celix_capability_destroy((celix_capability_t*)capability); +} + +static void celix_resource_destroyRequirement(void* requirement) { + celix_requirement_destroy((celix_requirement_t*)requirement); +} + +static void celix_resource_freeReqOrCapList(void* list) { + celix_arrayList_destroy((celix_array_list_t*)list); +} + +celix_resource_t* celix_resource_create() { + celix_resource_t* res = malloc(sizeof(*res)); + if (res != NULL) { + celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS; + opts.simpleRemovedCallback = celix_resource_destroyCapability; + res->allCapabilities = celix_arrayList_createWithOptions(&opts); + + opts.simpleRemovedCallback = celix_resource_destroyRequirement; + res->allRequirements = celix_arrayList_createWithOptions(&opts); + + celix_string_hash_map_create_options_t mapOpts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS; + mapOpts.simpleRemovedCallback = celix_resource_freeReqOrCapList; + res->capabilitiesByNamespace = celix_stringHashMap_createWithOptions(&mapOpts); + res->requirementsByNamespace = celix_stringHashMap_createWithOptions(&mapOpts); + + if ( res->allCapabilities == NULL || + res->allRequirements == NULL || + res->capabilitiesByNamespace == NULL || + res->requirementsByNamespace == NULL) { + celix_rcmErr_push("Failed to allocate celix_resource_t fields. Out of memory."); + celix_resource_destroy(res); + res = NULL; + } + } else { + celix_rcmErr_push("Failed to allocate celix_resource_t. Out of memory."); + } + return res; +} + +void celix_resource_destroy(celix_resource_t* resource) { + if (resource != NULL) { + celix_arrayList_destroy(resource->allCapabilities); + celix_arrayList_destroy(resource->allRequirements); + celix_stringHashMap_destroy(resource->capabilitiesByNamespace); + celix_stringHashMap_destroy(resource->requirementsByNamespace); + free(resource); + } +} + +bool celix_resource_equals(const celix_resource_t* res1, const celix_resource_t* res2) { + if (res1 == res2) { + return true; + } + + if (celix_arrayList_size(res1->allCapabilities) != celix_arrayList_size(res2->allCapabilities) || + celix_arrayList_size(res1->allRequirements) != celix_arrayList_size(res2->allRequirements)) { + return false; + } + + //FIXME this is not correct, as the order of the capabilities and requirements is not important + for (int i = 0; i < celix_arrayList_size(res1->allCapabilities); ++i) { + celix_capability_t* cap1 = celix_arrayList_get(res1->allCapabilities, i); + celix_capability_t* cap2 = celix_arrayList_get(res2->allCapabilities, i); + if (!celix_capability_equals(cap1, cap2)) { + return false; + } + } + + //FIXME this is not correct, as the order of the capabilities and requirements is not important + for (int i = 0; i < celix_arrayList_size(res1->allCapabilities); ++i) { + celix_requirement_t* req1 = celix_arrayList_get(res1->allCapabilities, i); + celix_requirement_t* req2 = celix_arrayList_get(res2->allCapabilities, i); + if (!celix_requirement_equals(req1, req2)) { + return false; + } + } + return true; +} + +unsigned int celix_resource_hashCode(const celix_resource_t* res) { + unsigned int hash = 0; + + //FIXME this is not correct, as the order of the capabilities and requirements is not important + for (int i = 0; i < celix_arrayList_size(res->allCapabilities); ++i) { + celix_capability_t* cap = celix_arrayList_get(res->allCapabilities, i); + hash += celix_capability_hashCode(cap); + } + + //FIXME this is not correct, as the order of the capabilities and requirements is not important + for (int i = 0; i < celix_arrayList_size(res->allRequirements); ++i) { + celix_requirement_t* req = celix_arrayList_get(res->allRequirements, i); + hash += celix_requirement_hashCode(req); + } + return hash; +} + +const celix_array_list_t* celix_resource_getCapabilities(const celix_resource_t* res, const char* ns) { + if (ns != NULL) { + return celix_stringHashMap_get(res->capabilitiesByNamespace, ns); + } + return res->allCapabilities; +} + +const celix_array_list_t* celix_resource_getRequirements(const celix_resource_t* res, const char* ns) { + if (ns != NULL) { + return celix_stringHashMap_get(res->requirementsByNamespace, ns); + } + return res->allRequirements; +} + +bool celix_resource_addCapability(celix_resource_t* res, celix_capability_t* cap) { + if (celix_capability_getResource(cap) != res) { + celix_rcmErr_pushf( + "Capability is not added to the correct resource. (Capability is added to resource %p, but resource %p is expected.)", + celix_capability_getResource(cap), res); + return false; + } + + celix_arrayList_add(res->allCapabilities, cap); + const char* ns = celix_capability_getNamespace(cap); + celix_array_list_t* caps = celix_stringHashMap_get(res->capabilitiesByNamespace, ns); + if (caps == NULL) { + caps = celix_arrayList_create(); + celix_stringHashMap_put(res->capabilitiesByNamespace, ns, caps); + } + celix_arrayList_add(caps, cap); + return true; +} + +bool celix_resource_addRequirement(celix_resource_t* res, celix_requirement_t* req) { + if (celix_requirement_getResource(req) != res) { + celix_rcmErr_pushf( + "Requirement is not added to the correct resource. (Requirement is added to resource %p, but resource %p is expected.)", + celix_requirement_getResource(req), res); + return false; + } + + celix_arrayList_add(res->allRequirements, req); + const char* ns = celix_requirement_getNamespace(req); + celix_array_list_t* reqs = celix_stringHashMap_get(res->requirementsByNamespace, ns); + if (reqs == NULL) { + reqs = celix_arrayList_create(); + celix_stringHashMap_put(res->requirementsByNamespace, ns, reqs); + } + celix_arrayList_add(reqs, req); + return true; +}
