You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pe...@apache.org on 2023/05/18 01:39:45 UTC
[celix] 01/03: Add celix_bundleCache_destroyArchive to support OSGi uninstall.
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 <ho...@gmail.com>
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);