This is an automated email from the ASF dual-hosted git repository. pengzheng pushed a commit to branch feature/556-osgi-uninstall in repository https://gitbox.apache.org/repos/asf/celix.git
commit e62f269fa946f09aa54500d83e654d6f3698ca08 Author: PengZheng <[email protected]> AuthorDate: Tue May 16 22:02:48 2023 +0800 Add celix_bundleCache_destroyArchive to support OSGi uninstall. And some unit tests for bundle cache are added. --- libs/error_injector/CMakeLists.txt | 1 + libs/error_injector/celix_hash_map/CMakeLists.txt | 25 ++++++++ .../celix_hash_map/include/celix_hash_map_ei.h | 34 ++++++++++ .../celix_hash_map/src/celix_hash_map_ei.cc | 29 +++++++++ libs/framework/gtest/CMakeLists.txt | 2 + .../src/CelixBundleCacheErrorInjectionTestSuite.cc | 60 ++++++++++++++++++ libs/framework/src/bundle_archive.c | 16 ++--- libs/framework/src/celix_bundle_cache.c | 74 ++++++++++++++-------- libs/framework/src/celix_bundle_cache.h | 11 ++++ libs/framework/src/framework.c | 9 +-- 10 files changed, 216 insertions(+), 45 deletions(-) diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index 1607671e..bdf096fe 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -44,6 +44,7 @@ add_subdirectory(celix_log_helper) add_subdirectory(celix_bundle) add_subdirectory(celix_version) add_subdirectory(dfi) +add_subdirectory(celix_hash_map) celix_subproject(ERROR_INJECTOR_MDNSRESPONDER "Option to enable building the mdnsresponder error injector" OFF) if (ERROR_INJECTOR_MDNSRESPONDER) diff --git a/libs/error_injector/celix_hash_map/CMakeLists.txt b/libs/error_injector/celix_hash_map/CMakeLists.txt new file mode 100644 index 00000000..dbef2bb6 --- /dev/null +++ b/libs/error_injector/celix_hash_map/CMakeLists.txt @@ -0,0 +1,25 @@ +# 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. + +add_library(hash_map_ei STATIC src/celix_hash_map_ei.cc) + +target_include_directories(hash_map_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(hash_map_ei PUBLIC Celix::error_injector Celix::utils) +target_link_options(hash_map_ei INTERFACE + LINKER:--wrap,celix_stringHashMap_create +) +add_library(Celix::hash_map_ei ALIAS hash_map_ei) diff --git a/libs/error_injector/celix_hash_map/include/celix_hash_map_ei.h b/libs/error_injector/celix_hash_map/include/celix_hash_map_ei.h new file mode 100644 index 00000000..3c38ad41 --- /dev/null +++ b/libs/error_injector/celix_hash_map/include/celix_hash_map_ei.h @@ -0,0 +1,34 @@ +/* + 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_HASH_MAP_EI_H +#define CELIX_CELIX_HASH_MAP_EI_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "celix_string_hash_map.h" +#include "celix_error_injector.h" + +CELIX_EI_DECLARE(celix_stringHashMap_create, celix_string_hash_map_t*); + +#ifdef __cplusplus +} +#endif +#endif //CELIX_CELIX_HASH_MAP_EI_H diff --git a/libs/error_injector/celix_hash_map/src/celix_hash_map_ei.cc b/libs/error_injector/celix_hash_map/src/celix_hash_map_ei.cc new file mode 100644 index 00000000..4edbc381 --- /dev/null +++ b/libs/error_injector/celix_hash_map/src/celix_hash_map_ei.cc @@ -0,0 +1,29 @@ +/* + 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 "celix_hash_map_ei.h" + +extern "C" { +celix_string_hash_map_t* __real_celix_stringHashMap_create(); +CELIX_EI_DEFINE(celix_stringHashMap_create, celix_string_hash_map_t*); +celix_string_hash_map_t* __wrap_celix_stringHashMap_create() { + CELIX_EI_IMPL(celix_stringHashMap_create); + return __real_celix_stringHashMap_create(); +} +} diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt index c05fe4f5..e71fd953 100644 --- a/libs/framework/gtest/CMakeLists.txt +++ b/libs/framework/gtest/CMakeLists.txt @@ -127,6 +127,7 @@ if (LINKER_WRAP_SUPPORTED) src/BundleArchiveWithErrorInjectionTestSuite.cc src/CelixFrameworkUtilsErrorInjectionTestSuite.cc src/CelixBundleContextBundlesWithErrorTestSuite.cc + src/CelixBundleCacheErrorInjectionTestSuite.cc ) target_compile_definitions(test_framework_with_ei PRIVATE SIMPLE_TEST_BUNDLE1_LOCATION="${SIMPLE_TEST_BUNDLE1}" @@ -142,6 +143,7 @@ if (LINKER_WRAP_SUPPORTED) Celix::utils_ei Celix::asprintf_ei Celix::dlfcn_ei + Celix::hash_map_ei GTest::gtest GTest::gtest_main ) diff --git a/libs/framework/gtest/src/CelixBundleCacheErrorInjectionTestSuite.cc b/libs/framework/gtest/src/CelixBundleCacheErrorInjectionTestSuite.cc new file mode 100644 index 00000000..dd181972 --- /dev/null +++ b/libs/framework/gtest/src/CelixBundleCacheErrorInjectionTestSuite.cc @@ -0,0 +1,60 @@ +/* + 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 "asprintf_ei.h" +#include "celix_constants.h" +#include "celix_bundle_cache.h" +#include "celix_properties.h" +#include "celix_log.h" +#include "framework_private.h" +#include "gtest/gtest.h" +#include "celix_hash_map_ei.h" +#include "malloc_ei.h" + +class CelixBundleCacheErrorInjectionTestSuite : public ::testing::Test { +public: + CelixBundleCacheErrorInjectionTestSuite() { + fw.configurationMap = celix_properties_create(); + fw.logger = celix_frameworkLogger_create(CELIX_LOG_LEVEL_TRACE); + } + + ~CelixBundleCacheErrorInjectionTestSuite() override { + celix_ei_expect_asprintf(nullptr, 0, -1); + celix_ei_expect_celix_stringHashMap_create(nullptr, 0, nullptr); + celix_ei_expect_calloc(nullptr, 0, nullptr); + celix_frameworkLogger_destroy(fw.logger); + celix_properties_destroy(fw.configurationMap); + } + struct celix_framework fw{}; +}; + +TEST_F(CelixBundleCacheErrorInjectionTestSuite, CacheCreateErrorTest) { + celix_bundle_cache_t *cache = nullptr; + celix_ei_expect_calloc((void *) celix_bundleCache_create, 0, nullptr); + EXPECT_EQ(CELIX_ENOMEM, celix_bundleCache_create(&fw, &cache)); + EXPECT_EQ(nullptr, cache); + celix_ei_expect_celix_stringHashMap_create((void *) celix_bundleCache_create, 0, nullptr); + EXPECT_EQ(CELIX_ENOMEM, celix_bundleCache_create(&fw, &cache)); + EXPECT_EQ(nullptr, cache); + celix_properties_setBool(fw.configurationMap, CELIX_FRAMEWORK_CACHE_USE_TMP_DIR, true); + celix_ei_expect_asprintf((void *) celix_bundleCache_create, 0, -1); + EXPECT_EQ(CELIX_ENOMEM, celix_bundleCache_create(&fw, &cache)); + EXPECT_EQ(nullptr, cache); + celix_properties_setBool(fw.configurationMap, CELIX_FRAMEWORK_CACHE_USE_TMP_DIR, false); +} \ No newline at end of file diff --git a/libs/framework/src/bundle_archive.c b/libs/framework/src/bundle_archive.c index b06300b8..2fa5ab7e 100644 --- a/libs/framework/src/bundle_archive.c +++ b/libs/framework/src/bundle_archive.c @@ -461,18 +461,12 @@ celix_status_t bundleArchive_close(bundle_archive_pt archive) { } celix_status_t bundleArchive_closeAndDelete(bundle_archive_pt archive) { - celix_status_t status = CELIX_SUCCESS; - - status = bundleArchive_close(archive); - if (status == CELIX_SUCCESS) { - const char* err = NULL; - status = celix_utils_deleteDirectory(archive->archiveRoot, &err); - framework_logIfError(archive->fw->logger, status, NULL, "Failed to delete archive root '%s': %s", archive->archiveRoot, err); - } - - framework_logIfError(archive->fw->logger, status, NULL, "Failed to close and delete archive"); + celix_status_t status = CELIX_SUCCESS; - return status; + const char* err = NULL; + status = celix_utils_deleteDirectory(archive->archiveRoot, &err); + framework_logIfError(archive->fw->logger, status, NULL, "Failed to delete archive root '%s': %s", archive->archiveRoot, err); + return status; } const char* celix_bundleArchive_getPersistentStoreRoot(bundle_archive_t* archive) { diff --git a/libs/framework/src/celix_bundle_cache.c b/libs/framework/src/celix_bundle_cache.c index c315164a..1b52a00f 100644 --- a/libs/framework/src/celix_bundle_cache.c +++ b/libs/framework/src/celix_bundle_cache.c @@ -54,6 +54,8 @@ struct celix_bundle_cache { celix_string_hash_map_t* locationToBundleIdLookupMap; //key = location, value = bundle id. }; +static void celix_bundleCache_updateIdForLocationLookupMap(celix_bundle_cache_t* cache); + static const char* bundleCache_progamName() { #if defined(__APPLE__) || defined(__FreeBSD__) return getprogname(); @@ -118,14 +120,18 @@ celix_status_t celix_bundleCache_create(celix_framework_t* fw, celix_bundle_cach celix_bundle_cache_t *cache = calloc(1, sizeof(*cache)); if (!cache) { status = CELIX_ENOMEM; - fw_logCode(fw->logger, CELIX_LOG_LEVEL_ERROR, status, "Cannot create bundle cache, out of memory"); - return status; + goto cache_calloc_failure; } + cache->fw = fw; bool useTmpDir = celix_bundleCache_useTmpDir(fw); cache->deleteOnCreate = celix_bundleCache_cleanOnCreate(fw); cache->deleteOnDestroy = useTmpDir; //if tmp dir is used, delete on destroy cache->locationToBundleIdLookupMap = celix_stringHashMap_create(); + if (NULL == cache->locationToBundleIdLookupMap) { + status = CELIX_ENOMEM; + goto cache_map_failure; + } celixThreadMutex_create(&cache->mutex, NULL); if (useTmpDir) { @@ -135,12 +141,15 @@ celix_status_t celix_bundleCache_create(celix_framework_t* fw, celix_bundle_cach if (pg == NULL) { pg = ""; } - asprintf(&cache->cacheDir, "/tmp/celix-cache-%s-%s", pg, celix_framework_getUUID(fw)); } else { const char* cacheDir = celix_bundleCache_cacheDirPath(fw); cache->cacheDir = celix_utils_strdup(cacheDir); } + if (NULL == cache->cacheDir) { + status = CELIX_ENOMEM; + goto cache_dir_failure; + } if (cache->deleteOnCreate) { status = celix_bundleCache_deleteCacheDir(cache); @@ -157,9 +166,16 @@ celix_status_t celix_bundleCache_create(celix_framework_t* fw, celix_bundle_cach celix_bundleCache_destroy(cache); return status; } - + celix_bundleCache_updateIdForLocationLookupMap(cache); *out = cache; - return status; + return CELIX_SUCCESS; +cache_dir_failure: + celixThreadMutex_destroy(&cache->mutex); + celix_stringHashMap_destroy(cache->locationToBundleIdLookupMap); +cache_map_failure: + free(cache); +cache_calloc_failure: + return status; } celix_status_t celix_bundleCache_destroy(celix_bundle_cache_t* cache) { @@ -178,6 +194,9 @@ celix_status_t celix_bundleCache_deleteCacheDir(celix_bundle_cache_t* cache) { const char* err = NULL; celixThreadMutex_lock(&cache->mutex); celix_status_t status = celix_utils_deleteDirectory(cache->cacheDir, &err); + if (status == CELIX_SUCCESS) { + celix_stringHashMap_clear(cache->locationToBundleIdLookupMap); + } celixThreadMutex_unlock(&cache->mutex); if (status != CELIX_SUCCESS) { fw_logCode(cache->fw->logger, CELIX_LOG_LEVEL_ERROR, status, "Cannot delete bundle cache directory %s: %s", cache->cacheDir, err); @@ -194,6 +213,9 @@ celix_status_t celix_bundleCache_createArchive(celix_framework_t* fw, long id, c if (archiveRoot) { celixThreadMutex_lock(&fw->cache->mutex); status = celix_bundleArchive_create(fw, archiveRoot, id, location, &archive); + if (status == CELIX_SUCCESS) { + celix_stringHashMap_put(fw->cache->locationToBundleIdLookupMap, location, (void*)id); + } celixThreadMutex_unlock(&fw->cache->mutex); celix_utils_freeStringIfNotEqual(archiveRootBuffer, archiveRoot); } else { @@ -203,12 +225,6 @@ celix_status_t celix_bundleCache_createArchive(celix_framework_t* fw, long id, c fw_logCode(fw->logger, CELIX_LOG_LEVEL_ERROR, status, "Failed to create archive."); return status; } - - //add bundle id and location to lookup maps - celixThreadMutex_lock(&fw->cache->mutex); - celix_stringHashMap_put(fw->cache->locationToBundleIdLookupMap, location, (void*)id); - celixThreadMutex_unlock(&fw->cache->mutex); - *archiveOut = archive; return status; } @@ -217,16 +233,28 @@ celix_status_t celix_bundleCache_createSystemArchive(celix_framework_t* fw, bund return celix_bundleCache_createArchive(fw, CELIX_FRAMEWORK_BUNDLE_ID, NULL, archive); } +celix_status_t celix_bundleCache_destroyArchive(celix_bundle_cache_t *cache, bundle_archive_pt archive) { + celix_status_t status = CELIX_SUCCESS; + const char* loc = NULL; + celixThreadMutex_lock(&cache->mutex); + (void)bundleArchive_getLocation(archive, &loc); + (void)celix_stringHashMap_remove(cache->locationToBundleIdLookupMap, loc); + status = bundleArchive_closeAndDelete(archive); + celixThreadMutex_unlock(&cache->mutex); + (void)bundleArchive_destroy(archive); + return status; +} + /** * Update location->bundle id lookup map. * Assumes that bundle cache dir are not removed, so only adding not removing entries. */ -static void celix_bundleCache_updateIdForLocationLookupMap(celix_framework_t* fw) { - celixThreadMutex_lock(&fw->cache->mutex); - DIR* dir = opendir(fw->cache->cacheDir); +static void celix_bundleCache_updateIdForLocationLookupMap(celix_bundle_cache_t* cache) { + celixThreadMutex_lock(&cache->mutex); + DIR* dir = opendir(cache->cacheDir); if (dir == NULL) { - fw_logCode(fw->logger, CELIX_LOG_LEVEL_ERROR, CELIX_BUNDLE_EXCEPTION, "Cannot open bundle cache directory %s", fw->cache->cacheDir); - celixThreadMutex_unlock(&fw->cache->mutex); + fw_logCode(cache->fw->logger, CELIX_LOG_LEVEL_ERROR, CELIX_BUNDLE_EXCEPTION, "Cannot open bundle cache directory %s", cache->cacheDir); + celixThreadMutex_unlock(&cache->mutex); return; } char archiveRootBuffer[CELIX_DEFAULT_STRING_CREATE_BUFFER_SIZE]; @@ -236,22 +264,22 @@ static void celix_bundleCache_updateIdForLocationLookupMap(celix_framework_t* fw continue; } char *bundleStateProperties = celix_utils_writeOrCreateString(archiveRootBuffer, sizeof(archiveRootBuffer), - "%s/%s/%s", fw->cache->cacheDir, dent->d_name, + "%s/%s/%s", cache->cacheDir, dent->d_name, CELIX_BUNDLE_ARCHIVE_STATE_PROPERTIES_FILE_NAME); if (celix_utils_fileExists(bundleStateProperties)) { celix_properties_t *props = celix_properties_load(bundleStateProperties); const char *visitLoc = celix_properties_get(props, CELIX_BUNDLE_ARCHIVE_LOCATION_PROPERTY_NAME, NULL); long bndId = celix_properties_getAsLong(props, CELIX_BUNDLE_ARCHIVE_BUNDLE_ID_PROPERTY_NAME, -1); if (visitLoc != NULL && bndId >= 0) { - fw_log(fw->logger, CELIX_LOG_LEVEL_TRACE, "Adding location %s -> bnd id %li to lookup map", + fw_log(cache->fw->logger, CELIX_LOG_LEVEL_TRACE, "Adding location %s -> bnd id %li to lookup map", visitLoc, bndId); - celix_stringHashMap_putLong(fw->cache->locationToBundleIdLookupMap, visitLoc, bndId); + celix_stringHashMap_putLong(cache->locationToBundleIdLookupMap, visitLoc, bndId); } celix_properties_destroy(props); } } closedir(dir); - celixThreadMutex_unlock(&fw->cache->mutex); + celixThreadMutex_unlock(&cache->mutex); } long celix_bundleCache_findBundleIdForLocation(celix_framework_t *fw, const char *location) { @@ -261,12 +289,6 @@ long celix_bundleCache_findBundleIdForLocation(celix_framework_t *fw, const char bndId = celix_stringHashMap_getLong(fw->cache->locationToBundleIdLookupMap, location, -1); } celixThreadMutex_unlock(&fw->cache->mutex); - if (bndId == -1) { - celix_bundleCache_updateIdForLocationLookupMap(fw); - celixThreadMutex_lock(&fw->cache->mutex); - bndId = celix_stringHashMap_getLong(fw->cache->locationToBundleIdLookupMap, location, -1); - celixThreadMutex_unlock(&fw->cache->mutex); - } return bndId; } diff --git a/libs/framework/src/celix_bundle_cache.h b/libs/framework/src/celix_bundle_cache.h index e1829044..06810b54 100644 --- a/libs/framework/src/celix_bundle_cache.h +++ b/libs/framework/src/celix_bundle_cache.h @@ -87,6 +87,17 @@ celix_bundleCache_createArchive(celix_framework_t *fw, long id, const char *loca */ celix_status_t celix_bundleCache_createSystemArchive(celix_framework_t* fw, bundle_archive_pt *archive); +/** + * @brief Destroy the archive from the cache. + * It releases all resources allocated in celix_bundleCache_createArchive and deletes the archive directory. + * @param [in] cache The bundle cache to destroy archive from. + * @param [in] archive The archive to destroy. + * @return Status code indication failure or success: + * - CELIX_SUCCESS when no errors are encountered. + * - CELIX_FILE_IO_EXCEPTION when root of the archive is not a directory. + * - errno when the directory cannot be deleted for other reasons, check error codes of fts_open/fts_read/remove. + */ +celix_status_t celix_bundleCache_destroyArchive(celix_bundle_cache_t *cache, bundle_archive_pt archive); /** * @brief Deletes the entire bundle cache. diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c index 44662850..8e9dd886 100644 --- a/libs/framework/src/framework.c +++ b/libs/framework/src/framework.c @@ -654,14 +654,7 @@ celix_status_t celix_framework_installBundleInternal(celix_framework_t *framewor long id = alreadyExistingBndId == -1 ? framework_getNextBundleId(framework) : alreadyExistingBndId; bundle_archive_t* archive = NULL; status = CELIX_DO_IF(status, celix_bundleCache_createArchive(framework, id, bndLoc, &archive)); - if (status != CELIX_SUCCESS) { - bundleArchive_destroy(archive); - } - - if (status == CELIX_SUCCESS) { - status = celix_bundle_createFromArchive(framework, archive, &bundle); - } - + status = CELIX_DO_IF(status, celix_bundle_createFromArchive(framework, archive, &bundle)); if (status == CELIX_SUCCESS) { celix_framework_bundle_entry_t *bEntry = fw_bundleEntry_create(bundle); celix_framework_bundleEntry_increaseUseCount(bEntry);
