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 6008b3d734a204711b98c4b588bcd51dfa0b2384
Author: PengZheng <[email protected]>
AuthorDate: Thu Jun 1 21:40:26 2023 +0800

    Add support for bundle unload.
    
    Unloading a bundle from a framework instance will release all its resources 
but leave the bundle in cache so that it can be reloaded using `install`.
---
 bundles/shell/shell/CMakeLists.txt                 |  1 +
 bundles/shell/shell/gtest/src/ShellTestSuite.cc    |  3 +
 bundles/shell/shell/src/std_commands.c             |  9 ++-
 bundles/shell/shell/src/std_commands.h             |  2 +
 bundles/shell/shell/src/unload_command.c           | 24 +++++++
 .../src/CelixBundleContextBundlesTestSuite.cc      | 82 +++++++++++++++++++++-
 .../framework/gtest/src/CelixFrameworkTestSuite.cc |  9 +++
 libs/framework/include/celix_bundle_context.h      | 16 +++++
 libs/framework/include/celix_framework.h           | 22 ++++++
 libs/framework/src/bundle.c                        |  4 +-
 libs/framework/src/bundle_context.c                |  4 ++
 libs/framework/src/framework.c                     | 16 +++--
 .../src/framework_bundle_lifecycle_handler.c       | 14 ++--
 libs/framework/src/framework_private.h             |  6 +-
 14 files changed, 195 insertions(+), 17 deletions(-)

diff --git a/bundles/shell/shell/CMakeLists.txt 
b/bundles/shell/shell/CMakeLists.txt
index a07c76b6..34914da8 100644
--- a/bundles/shell/shell/CMakeLists.txt
+++ b/bundles/shell/shell/CMakeLists.txt
@@ -41,6 +41,7 @@ if (SHELL)
                        src/install_command.c
                        src/update_command.c
                        src/uninstall_command.c
+                       src/unload_command.c
                        src/help_command.c
                        src/dm_shell_list_command.c
                        src/query_command.c
diff --git a/bundles/shell/shell/gtest/src/ShellTestSuite.cc 
b/bundles/shell/shell/gtest/src/ShellTestSuite.cc
index 36d1f9c2..4117f0ad 100644
--- a/bundles/shell/shell/gtest/src/ShellTestSuite.cc
+++ b/bundles/shell/shell/gtest/src/ShellTestSuite.cc
@@ -104,11 +104,14 @@ TEST_F(ShellTestSuite, testAllCommandsAreCallable) {
     callCommand(ctx, "start", false); // incorrect number of arguments
     callCommand(ctx, "uninstall not-a-number", false);
     callCommand(ctx, "uninstall", false); // incorrect number of arguments
+    callCommand(ctx, "unload not-a-number", false);
+    callCommand(ctx, "unload", false); // incorrect number of arguments
     callCommand(ctx, "update not-a-number", false);
     callCommand(ctx, "update", false); // incorrect number of arguments
     callCommand(ctx, "stop 15", false); //non existing bundle id
     callCommand(ctx, "start 15", false); //non existing bundle id
     callCommand(ctx, "uninstall 15", false); //non existing bundle id
+    callCommand(ctx, "unload 15", false); //non existing bundle id
     callCommand(ctx, "update 15", false); //non existing bundle id
 }
 
diff --git a/bundles/shell/shell/src/std_commands.c 
b/bundles/shell/shell/src/std_commands.c
index dd3934ad..8edbf092 100644
--- a/bundles/shell/shell/src/std_commands.c
+++ b/bundles/shell/shell/src/std_commands.c
@@ -82,7 +82,7 @@ celix_std_commands_t* 
celix_stdCommands_create(celix_bundle_context_t* ctx) {
                     .exec = uninstallCommand_execute,
                     .name = "celix::uninstall",
                     .description = "uninstall bundle(s).",
-                    .usage = "uninstall <file> [<file> ...]"
+                    .usage = "uninstall <id> [<id> ...]"
             };
     commands->std_commands[5] =
             (struct celix_shell_command_register_entry) {
@@ -133,6 +133,13 @@ celix_std_commands_t* 
celix_stdCommands_create(celix_bundle_context_t* ctx) {
                     .usage = "quit"
             };
     commands->std_commands[11] =
+        (struct celix_shell_command_register_entry) {
+            .exec = unloadCommand_execute,
+            .name = "celix::unload",
+            .description = "unload bundle(s).",
+            .usage = "unload <id> [<id> ...]"
+        };
+    commands->std_commands[12] =
             (struct celix_shell_command_register_entry) {
                     .exec = NULL
             };
diff --git a/bundles/shell/shell/src/std_commands.h 
b/bundles/shell/shell/src/std_commands.h
index 6c0a8d06..a2be1070 100644
--- a/bundles/shell/shell/src/std_commands.h
+++ b/bundles/shell/shell/src/std_commands.h
@@ -48,6 +48,8 @@ bool installCommand_execute(void *handle, const char 
*commandLine, FILE *outStre
 
 bool uninstallCommand_execute(void *handle, const char *commandLine, FILE 
*outStream, FILE *errStream);
 
+bool unloadCommand_execute(void *handle, const char *commandLine, FILE 
*outStream, FILE *errStream);
+
 bool updateCommand_execute(void *handle, const char *commandLine, FILE 
*outStream, FILE *errStream);
 
 bool helpCommand_execute(void *handle, const char *commandLine, FILE 
*outStream, FILE *errStream);
diff --git a/bundles/shell/shell/src/unload_command.c 
b/bundles/shell/shell/src/unload_command.c
new file mode 100644
index 00000000..b27a37e1
--- /dev/null
+++ b/bundles/shell/shell/src/unload_command.c
@@ -0,0 +1,24 @@
+/*
+ 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 "bundle_command.h"
+
+bool unloadCommand_execute(void *handle, const char* constCommandLine, FILE 
*outStream, FILE *errStream) {
+    return bundleCommand_execute(handle, constCommandLine, outStream, 
errStream, celix_framework_unloadBundleAsync);
+}
diff --git a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc 
b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc
index e6213766..f7672087 100644
--- a/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc
+++ b/libs/framework/gtest/src/CelixBundleContextBundlesTestSuite.cc
@@ -203,11 +203,87 @@ TEST_F(CelixBundleContextBundlesTestSuite, 
InstallAndUninstallBundlesTest) {
     long bndId6 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
 
     ASSERT_TRUE(bndId4 >= 0L);
-    ASSERT_FALSE(bndId1 == bndId4); //bundle cache -> reuse of bundle id.
+    ASSERT_FALSE(bndId1 == bndId4);
     ASSERT_TRUE(bndId5 >= 0L);
-    ASSERT_FALSE(bndId2 == bndId5); //bundle cache -> reuse of bundle id.
+    ASSERT_FALSE(bndId2 == bndId5);
     ASSERT_TRUE(bndId6 >= 0L);
-    ASSERT_FALSE(bndId3 == bndId6); //bundle cache -> reuse of bundle id.
+    ASSERT_FALSE(bndId3 == bndId6);
+}
+
+TEST_F(CelixBundleContextBundlesTestSuite, InstallAndUnloadBundlesTest) {
+    //install bundles
+    long bndId1 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
+    long bndId2 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false);
+    long bndId3 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
+
+    ASSERT_TRUE(bndId1 >= 0L);
+    ASSERT_TRUE(bndId2 >= 0L);
+    ASSERT_TRUE(bndId3 >= 0L);
+
+    ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId1));
+    ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId2));
+    ASSERT_TRUE(celix_bundleContext_isBundleInstalled(ctx, bndId3));
+
+    ASSERT_TRUE(celix_bundleContext_isBundleActive(ctx, bndId1));
+    ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2)); //not auto 
started
+    ASSERT_TRUE(celix_bundleContext_isBundleActive(ctx, bndId3));
+
+    char *bndRoot1 = nullptr;
+    ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId1, &bndRoot1, [](void* 
handle, const celix_bundle_t* bnd) {
+      char **root = static_cast<char **>(handle);
+      *root = celix_bundle_getEntry(bnd, "/");
+    }));
+    ASSERT_TRUE(bndRoot1 != nullptr);
+    char* bndRoot2 = nullptr;
+    ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId2, &bndRoot2, [](void* 
handle, const celix_bundle_t* bnd) {
+      char **root = static_cast<char **>(handle);
+      *root = celix_bundle_getEntry(bnd, "/");
+    }));
+    ASSERT_TRUE(bndRoot2 != nullptr);
+    char* bndRoot3 = nullptr;
+    ASSERT_TRUE(celix_bundleContext_useBundle(ctx, bndId3, &bndRoot3, [](void* 
handle, const celix_bundle_t* bnd) {
+      char **root = static_cast<char **>(handle);
+      *root = celix_bundle_getEntry(bnd, "/");
+    }));
+    ASSERT_TRUE(bndRoot3 != nullptr);
+
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot1));
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot2));
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot3));
+
+    //unload bundles
+    ASSERT_TRUE(celix_bundleContext_unloadBundle(ctx, bndId1));
+    ASSERT_TRUE(celix_bundleContext_unloadBundle(ctx, bndId2));
+    ASSERT_TRUE(celix_bundleContext_unloadBundle(ctx, bndId3));
+
+    ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId1));
+    ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId2));
+    ASSERT_FALSE(celix_bundleContext_isBundleInstalled(ctx, bndId3));
+
+    ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId1)); //not 
uninstall -> not active
+    ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId2));
+    ASSERT_FALSE(celix_bundleContext_isBundleActive(ctx, bndId3));
+
+    // bundle cache is NOT cleaned up
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot1));
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot2));
+    ASSERT_TRUE(celix_utils_directoryExists(bndRoot3));
+
+    free(bndRoot1);
+    free(bndRoot2);
+    free(bndRoot3);
+
+    //reinstall bundles
+    long bndId4 = celix_bundleContext_installBundle(ctx, TEST_BND1_LOC, true);
+    long bndId5 = celix_bundleContext_installBundle(ctx, TEST_BND2_LOC, false);
+    long bndId6 = celix_bundleContext_installBundle(ctx, TEST_BND3_LOC, true);
+
+    ASSERT_TRUE(bndId4 >= 0L);
+    ASSERT_TRUE(bndId1 == bndId4); //bundle cache -> reuse of bundle id.
+    ASSERT_TRUE(bndId5 >= 0L);
+    ASSERT_TRUE(bndId2 == bndId5); //bundle cache -> reuse of bundle id.
+    ASSERT_TRUE(bndId6 >= 0L);
+    ASSERT_TRUE(bndId3 == bndId6); //bundle cache -> reuse of bundle id.
 }
 
 TEST_F(CelixBundleContextBundlesTestSuite, StartBundleWithException) {
diff --git a/libs/framework/gtest/src/CelixFrameworkTestSuite.cc 
b/libs/framework/gtest/src/CelixFrameworkTestSuite.cc
index 1a3562c1..89c20f61 100644
--- a/libs/framework/gtest/src/CelixFrameworkTestSuite.cc
+++ b/libs/framework/gtest/src/CelixFrameworkTestSuite.cc
@@ -93,6 +93,15 @@ TEST_F(CelixFrameworkTestSuite, 
AsyncInstallStartStopAndUninstallBundleTest) {
     std::this_thread::sleep_for(std::chrono::milliseconds{100});
     EXPECT_FALSE(celix_framework_isBundleActive(framework.get(), bndId));
 
+    celix_framework_unloadBundleAsync(framework.get(), bndId);
+    std::this_thread::sleep_for(std::chrono::milliseconds{100});
+    EXPECT_FALSE(celix_framework_isBundleInstalled(framework.get(), bndId));
+
+    // reloaded bundle should reuse the same bundle id
+    EXPECT_EQ(bndId, celix_framework_installBundleAsync(framework.get(), 
SIMPLE_TEST_BUNDLE1_LOCATION, false));
+    EXPECT_TRUE(celix_framework_isBundleInstalled(framework.get(), bndId));
+    EXPECT_FALSE(celix_framework_isBundleActive(framework.get(), bndId));
+
     celix_framework_uninstallBundleAsync(framework.get(), bndId);
     std::this_thread::sleep_for(std::chrono::milliseconds{100});
     EXPECT_FALSE(celix_framework_isBundleInstalled(framework.get(), bndId));
diff --git a/libs/framework/include/celix_bundle_context.h 
b/libs/framework/include/celix_bundle_context.h
index 8b1ef2bc..fc73b180 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -905,6 +905,22 @@ CELIX_FRAMEWORK_EXPORT long 
celix_bundleContext_installBundle(celix_bundle_conte
  */
 CELIX_FRAMEWORK_EXPORT bool 
celix_bundleContext_uninstallBundle(celix_bundle_context_t *ctx, long bndId);
 
+/**
+ * @brief Unload the bundle with the provided bundle id. If needed the bundle 
will be stopped first.
+ * Will silently ignore bundle ids < 0.
+ * Note that unloaded bundle is kept in bundle cache and can be reloaded with 
the celix_bundleContext_installBundle function.
+ *
+ * If this function is called on the Celix event thread, the actual stopping 
of the bundle will be done async and
+ * on a separate thread.
+ * If this function is called from a different thread than the Celix event 
thread, then the function will return after
+ * the bundle is stopped.
+ *
+ * @param ctx The bundle context
+ * @param bndId The bundle id to unload.
+ * @return true if the bundle is correctly unloaded. False if not.
+ */
+CELIX_FRAMEWORK_EXPORT bool 
celix_bundleContext_unloadBundle(celix_bundle_context_t *ctx, long bndId);
+
 /**
  * @brief Stop the bundle with the provided bundle id.
  * Will silently ignore bundle ids < 0.
diff --git a/libs/framework/include/celix_framework.h 
b/libs/framework/include/celix_framework.h
index e5beb0d2..2bfbd114 100644
--- a/libs/framework/include/celix_framework.h
+++ b/libs/framework/include/celix_framework.h
@@ -138,6 +138,17 @@ CELIX_FRAMEWORK_EXPORT long 
celix_framework_installBundle(celix_framework_t *fw,
  */
 CELIX_FRAMEWORK_EXPORT bool celix_framework_uninstallBundle(celix_framework_t 
*fw, long bndId);
 
+/**
+ * @brief Unload the bundle with the provided bundle id. If needed the bundle 
will be stopped first.
+ * Will silently ignore bundle ids < 0.
+ * Note that unloaded bundle is kept in bundle cache and can be reloaded with 
the celix_framework_installBundle function.
+ *
+ * @param fw The Celix framework
+ * @param bndId The bundle id to unload.
+ * @return true if the bundle is correctly unloaded. False if not.
+ */
+CELIX_FRAMEWORK_EXPORT bool celix_framework_unloadBundle(celix_framework_t 
*fw, long bndId);
+
 /**
  * @brief Update the bundle with the provided bundle id.
  *
@@ -221,6 +232,17 @@ CELIX_FRAMEWORK_EXPORT void 
celix_framework_updateBundleAsync(celix_framework_t
  */
 CELIX_FRAMEWORK_EXPORT void 
celix_framework_uninstallBundleAsync(celix_framework_t *fw, long bndId);
 
+/**
+ * @brief Unload the bundle with the provided bundle id async. If needed the 
bundle will be stopped first.
+ * Will silently ignore bundle ids < 0.
+ * Note that unloaded bundle is kept in bundle cache and can be reloaded with 
the celix_framework_installBundle function.
+ * The bundle will be unloaded on a separate spawned thread.
+ *
+ * @param fw The Celix framework
+ * @param bndId The bundle id to unload.
+ */
+CELIX_FRAMEWORK_EXPORT void 
celix_framework_unloadBundleAsync(celix_framework_t *fw, long bndId);
+
 /**
  * @brief Stop the bundle with the provided bundle id async.
  * Will silently ignore bundle ids < 0.
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 7f08f15e..64eaa241 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -238,8 +238,8 @@ celix_status_t bundle_update(bundle_pt bundle, const char* 
updatedBundleUrl) {
 }
 
 celix_status_t bundle_stop(bundle_pt bundle) {
-    //note deprecated call use celix_bundleContext_startBundle instead
-    return celix_framework_startBundle(bundle->framework, 
celix_bundle_getId(bundle));
+    //note deprecated call use celix_bundleContext_stopBundle instead
+    return celix_framework_stopBundle(bundle->framework, 
celix_bundle_getId(bundle));
 }
 
 celix_status_t bundle_uninstall(bundle_pt bundle) {
diff --git a/libs/framework/src/bundle_context.c 
b/libs/framework/src/bundle_context.c
index 8a802b7c..b3f70232 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -995,6 +995,10 @@ bool celix_bundleContext_uninstallBundle(bundle_context_t 
*ctx, long bndId) {
     return celix_framework_uninstallBundle(ctx->framework, bndId);
 }
 
+bool celix_bundleContext_unloadBundle(celix_bundle_context_t *ctx, long bndId) 
{
+    return celix_framework_unloadBundle(ctx->framework, bndId);
+}
+
 bool celix_bundleContext_useServiceWithId(
         bundle_context_t *ctx,
         long serviceId,
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index 823d3d19..8671abac 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -1872,11 +1872,11 @@ long 
celix_framework_installBundleAsync(celix_framework_t *fw, const char *bundl
     return celix_framework_installAndStartBundleInternal(fw, bundleLoc, 
autoStart, true);
 }
 
-static bool celix_framework_uninstallBundleInternal(celix_framework_t *fw, 
long bndId, bool forcedAsync) {
+static bool celix_framework_uninstallBundleInternal(celix_framework_t *fw, 
long bndId, bool forcedAsync, bool permanent) {
     bool uninstalled = false;
     celix_framework_bundle_entry_t *bndEntry = 
celix_framework_bundleEntry_getBundleEntryAndIncreaseUseCount(fw, bndId);
     if (bndEntry != NULL) {
-        celix_status_t status = 
celix_framework_uninstallBundleOnANonCelixEventThread(fw, bndEntry, 
forcedAsync);
+        celix_status_t status = 
celix_framework_uninstallBundleOnANonCelixEventThread(fw, bndEntry, 
forcedAsync, permanent);
         celix_framework_waitForBundleEvents(fw, bndId);
         //note not decreasing bndEntry, because this entry should now be 
deleted (uninstalled)
         uninstalled = status == CELIX_SUCCESS;
@@ -1885,11 +1885,19 @@ static bool 
celix_framework_uninstallBundleInternal(celix_framework_t *fw, long
 }
 
 bool celix_framework_uninstallBundle(celix_framework_t *fw, long bndId) {
-    return celix_framework_uninstallBundleInternal(fw, bndId, false);
+    return celix_framework_uninstallBundleInternal(fw, bndId, false, true);
 }
 
 void celix_framework_uninstallBundleAsync(celix_framework_t *fw, long bndId) {
-    celix_framework_uninstallBundleInternal(fw, bndId, true);
+    celix_framework_uninstallBundleInternal(fw, bndId, true, true);
+}
+
+bool celix_framework_unloadBundle(celix_framework_t *fw, long bndId) {
+    return celix_framework_uninstallBundleInternal(fw, bndId, false, false);
+}
+
+void celix_framework_unloadBundleAsync(celix_framework_t *fw, long bndId) {
+    celix_framework_uninstallBundleInternal(fw, bndId, true, false);
 }
 
 celix_status_t celix_framework_uninstallBundleEntry(celix_framework_t* 
framework, celix_framework_bundle_entry_t* bndEntry, bool permanent) {
diff --git a/libs/framework/src/framework_bundle_lifecycle_handler.c 
b/libs/framework/src/framework_bundle_lifecycle_handler.c
index 669778d8..6ffa9bb3 100644
--- a/libs/framework/src/framework_bundle_lifecycle_handler.c
+++ b/libs/framework/src/framework_bundle_lifecycle_handler.c
@@ -49,11 +49,15 @@ static void* 
celix_framework_BundleLifecycleHandlingThread(void *data) {
             celix_framework_bundleEntry_decreaseUseCount(handler->bndEntry);
             celix_framework_uninstallBundleEntry(handler->framework, 
handler->bndEntry, true);
             break;
+        case CELIX_BUNDLE_LIFECYCLE_UNLOAD:
+            celix_framework_bundleEntry_decreaseUseCount(handler->bndEntry);
+            celix_framework_uninstallBundleEntry(handler->framework, 
handler->bndEntry, false);
+            break;
         default: //update
             celix_framework_updateBundleEntry(handler->framework, 
handler->bndEntry, handler->updatedBundleUrl);
             break;
     }
-    if (handler->command != CELIX_BUNDLE_LIFECYCLE_UNINSTALL) {
+    if (handler->command != CELIX_BUNDLE_LIFECYCLE_UNINSTALL && 
handler->command != CELIX_BUNDLE_LIFECYCLE_UNLOAD) {
         celix_framework_bundleEntry_decreaseUseCount(handler->bndEntry);
     }
     celix_framework_cleanupBundleLifecycleHandler(handler->framework, handler);
@@ -132,18 +136,18 @@ celix_status_t 
celix_framework_stopBundleOnANonCelixEventThread(celix_framework_
     }
 }
 
-celix_status_t 
celix_framework_uninstallBundleOnANonCelixEventThread(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, bool forceSpawnThread) {
+celix_status_t 
celix_framework_uninstallBundleOnANonCelixEventThread(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, bool forceSpawnThread, bool 
permanent) {
     if (forceSpawnThread) {
         fw_log(fw->logger, CELIX_LOG_LEVEL_TRACE, "uninstall bundle from a 
separate thread");
-        celix_framework_createAndStartBundleLifecycleHandler(fw, bndEntry, 
CELIX_BUNDLE_LIFECYCLE_UNINSTALL, NULL);
+        celix_framework_createAndStartBundleLifecycleHandler(fw, bndEntry, 
permanent ? CELIX_BUNDLE_LIFECYCLE_UNINSTALL : CELIX_BUNDLE_LIFECYCLE_UNLOAD, 
NULL);
         return CELIX_SUCCESS;
     } else if (celix_framework_isCurrentThreadTheEventLoop(fw)) {
         fw_log(fw->logger, CELIX_LOG_LEVEL_DEBUG,
                "Cannot uninstall bundle from Celix event thread. Using a 
separate thread to uninstall bundle. See celix_bundleContext_uninstall Bundle 
for more info.");
-        celix_framework_createAndStartBundleLifecycleHandler(fw, bndEntry, 
CELIX_BUNDLE_LIFECYCLE_UNINSTALL, NULL);
+        celix_framework_createAndStartBundleLifecycleHandler(fw, bndEntry, 
permanent ? CELIX_BUNDLE_LIFECYCLE_UNINSTALL : CELIX_BUNDLE_LIFECYCLE_UNLOAD, 
NULL);
         return CELIX_SUCCESS;
     } else {
-        return celix_framework_uninstallBundleEntry(fw, bndEntry, true);
+        return celix_framework_uninstallBundleEntry(fw, bndEntry, permanent);
     }
 }
 
diff --git a/libs/framework/src/framework_private.h 
b/libs/framework/src/framework_private.h
index f2944cac..c506e94b 100644
--- a/libs/framework/src/framework_private.h
+++ b/libs/framework/src/framework_private.h
@@ -113,7 +113,8 @@ enum celix_bundle_lifecycle_command {
     CELIX_BUNDLE_LIFECYCLE_START,
     CELIX_BUNDLE_LIFECYCLE_STOP,
     CELIX_BUNDLE_LIFECYCLE_UNINSTALL,
-    CELIX_BUNDLE_LIFECYCLE_UPDATE
+    CELIX_BUNDLE_LIFECYCLE_UPDATE,
+    CELIX_BUNDLE_LIFECYCLE_UNLOAD
 };
 
 typedef struct celix_framework_bundle_lifecycle_handler {
@@ -396,9 +397,10 @@ celix_status_t 
celix_framework_stopBundleOnANonCelixEventThread(celix_framework_
  * @param fw The Celix framework
  * @param bndEntry A bnd entry
  * @param forceSpawnThread If the true, the start bundle will always be done 
on a spawn thread
+ * @param permanent If true, the bundle will be permanently uninstalled (e.g. 
the bundle archive will be removed).
  * @return CELIX_SUCCESS of the call went alright.
  */
-celix_status_t 
celix_framework_uninstallBundleOnANonCelixEventThread(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, bool forceSpawnThread);
+celix_status_t 
celix_framework_uninstallBundleOnANonCelixEventThread(celix_framework_t* fw, 
celix_framework_bundle_entry_t* bndEntry, bool forceSpawnThread, bool 
permanent);
 
 /**
  * Update (and if needed stop and start) a bundle and ensure that this is not 
done on the Celix event thread.

Reply via email to