You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2022/05/08 20:46:09 UTC

[celix] branch feature/additional_component_states created (now 2eca5a79)

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a change to branch feature/additional_component_states
in repository https://gitbox.apache.org/repos/asf/celix.git


      at 2eca5a79 Refactors celix cmp to include intermediate states

This branch includes the following new commits:

     new 2eca5a79 Refactors celix cmp to include intermediate states

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[celix] 01/01: Refactors celix cmp to include intermediate states

Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/additional_component_states
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 2eca5a7944a68e427fcbcd7b5a64d11ec6c52679
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun May 8 22:45:47 2022 +0200

    Refactors celix cmp to include intermediate states
---
 documents/diagrams/component_lifecycle.puml        |  47 +++++
 .../gtest/src/DependencyManagerTestSuite.cc        |  37 +++-
 libs/framework/include/celix/dm/Component.h        |  39 +++-
 libs/framework/include/celix_dm_component.h        |  15 +-
 libs/framework/src/dm_component_impl.c             | 197 ++++++++++++++-------
 5 files changed, 252 insertions(+), 83 deletions(-)

diff --git a/documents/diagrams/component_lifecycle.puml b/documents/diagrams/component_lifecycle.puml
new file mode 100644
index 00000000..c27fa33e
--- /dev/null
+++ b/documents/diagrams/component_lifecycle.puml
@@ -0,0 +1,47 @@
+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
+state "Waiting for Required" as WaitingForRequired
+state "Initialized And Waiting For Required" as Initialized
+
+[*] -down-> Inactive
+Inactive -left-> [*]
+Inactive  -right-> WaitingForRequired
+WaitingForRequired -down-> Initializing
+Initializing -down-> Initialized
+
+state Active {
+    state "Tracking Optional Dependencies" as TrackingOptional
+    [*] -> Starting
+    Starting -down-> TrackingOptional
+    TrackingOptional -> Suspending
+    Suspending -> Suspended
+    Suspended -> Resuming
+    Resuming --> TrackingOptional
+    TrackingOptional --> Stopping
+    Stopping -left-> [*]
+}
+
+Initialized -> Active
+Active -> Initialized
+
+Initialized -down-> Deinitializing
+Deinitializing -down-> Inactive
+
+
+
+
+@enduml
\ No newline at end of file
diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index e55efaee..a07cbde9 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -123,7 +123,7 @@ TEST_F(DependencyManagerTestSuite, DmComponentRemoveAllAsync) {
     EXPECT_EQ(1, count.load());
 }
 
-TEST_F(DependencyManagerTestSuite, CDmGetInfo) {
+TEST_F(DependencyManagerTestSuite, DmGetInfo) {
     auto* mng = celix_bundleContext_getDependencyManager(ctx);
     auto* cmp = celix_dmComponent_create(ctx, "test1");
 
@@ -207,7 +207,7 @@ TEST_F(DependencyManagerTestSuite, CxxDmGetInfo) {
     auto& cmpInfo = info.components[0];
     EXPECT_TRUE(!cmpInfo.uuid.empty());
     EXPECT_EQ(cmpInfo.name, "Cmp1");
-    EXPECT_EQ(cmpInfo.state, "WAITING_FOR_REQUIRED");
+    EXPECT_EQ(cmpInfo.state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED");
     EXPECT_FALSE(cmpInfo.isActive);
     EXPECT_EQ(cmpInfo.nrOfTimesStarted, 0);
     EXPECT_EQ(cmpInfo.nrOfTimesResumed, 0);
@@ -641,13 +641,13 @@ TEST_F(DependencyManagerTestSuite, UnneededSuspendIsPrevented) {
 
     celix::dm::DependencyManager dm{ctx};
     //cmp1 has lifecycle callbacks, but not set or add/rem callbacks for the service dependency -> should not trigger suspend
-    auto& cmp1 = dm.createComponent<CounterComponent>()
+    auto& cmp1 = dm.createComponent<CounterComponent>("CounterCmp1")
             .setCallbacks(nullptr, &CounterComponent::start, &CounterComponent::stop, nullptr);
     cmp1.createServiceDependency<TestService>();
     cmp1.build();
 
     //cmp2 has lifecycle callbacks and set, add/rem callbacks for the service dependency -> should trigger suspend 2x
-    auto& cmp2 = dm.createComponent<CounterComponent>()
+    auto& cmp2 = dm.createComponent<CounterComponent>("CounterCmp2")
             .setCallbacks(nullptr, &CounterComponent::start, &CounterComponent::stop, nullptr);
     cmp2.createServiceDependency<TestService>()
             .setCallbacks(&CounterComponent::setService)
@@ -788,6 +788,35 @@ TEST_F(DependencyManagerTestSuite, installBundleWithDepManActivator) {
     celix_arrayList_destroy(list);
 }
 
+TEST_F(DependencyManagerTestSuite, testStateToString) {
+    const char* state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INACTIVE);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INITIALIZING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_DEINITIALIZING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_DEINITIALIZING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STARTING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STARTING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_STOPPING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_STOPPING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_TRACKING_OPTIONAL);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_TRACKING_OPTIONAL");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDING");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_SUSPENDED);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_SUSPENDED");
+    state = celix_dmComponent_stateToString(CELIX_DM_CMP_STATE_RESUMING);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_RESUMING");
+    state = celix_dmComponent_stateToString((celix_dm_component_state_enum)0);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+    state = celix_dmComponent_stateToString((celix_dm_component_state_enum)16);
+    EXPECT_STREQ(state, "CELIX_DM_CMP_STATE_INACTIVE");
+}
+
 #if __cplusplus >= 201703L //C++17 or higher
 
 class TestInterfaceWithStaticInfo {
diff --git a/libs/framework/include/celix/dm/Component.h b/libs/framework/include/celix/dm/Component.h
index 6c9004db..d101b35f 100644
--- a/libs/framework/include/celix/dm/Component.h
+++ b/libs/framework/include/celix/dm/Component.h
@@ -41,10 +41,17 @@
 namespace celix { namespace dm {
 
     enum class ComponentState {
-        INACTIVE = 1,
-        WAITING_FOR_REQUIRED = 2,
-        INSTANTIATED_AND_WAITING_FOR_REQUIRED = 3,
-        TRACKING_OPTIONAL = 4,
+        INACTIVE =                              1,
+        WAITING_FOR_REQUIRED =                  2,
+        INITIALIZING =                          3,
+        DEINITIALIZING =                        4,
+        INSTANTIATED_AND_WAITING_FOR_REQUIRED = 5,
+        STARTING =                              6,
+        STOPPING =                              7,
+        TRACKING_OPTIONAL =                     8,
+        SUSPENDING =                            9,
+        SUSPENDED =                             10,
+        RESUMING =                              11,
     };
 
     class BaseComponent {
@@ -90,14 +97,28 @@ namespace celix { namespace dm {
         ComponentState getState() const {
              auto cState = celix_dmComponent_currentState(cCmp);
              switch (cState) {
-                 case DM_CMP_STATE_INACTIVE:
-                     return ComponentState::INACTIVE;
-                 case DM_CMP_STATE_WAITING_FOR_REQUIRED:
+                 case CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED:
                      return ComponentState::WAITING_FOR_REQUIRED;
-                 case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED:
+                 case CELIX_DM_CMP_STATE_INITIALIZING:
+                     return ComponentState::INITIALIZING;
+                 case CELIX_DM_CMP_STATE_DEINITIALIZING:
+                     return ComponentState::DEINITIALIZING;
+                 case CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED:
                      return ComponentState::INSTANTIATED_AND_WAITING_FOR_REQUIRED;
-                 default:
+                 case CELIX_DM_CMP_STATE_STARTING:
+                     return ComponentState::STARTING;
+                 case CELIX_DM_CMP_STATE_STOPPING:
+                     return ComponentState::STOPPING;
+                 case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
                      return ComponentState::TRACKING_OPTIONAL;
+                 case CELIX_DM_CMP_STATE_SUSPENDING:
+                     return ComponentState::SUSPENDING;
+                 case CELIX_DM_CMP_STATE_SUSPENDED:
+                     return ComponentState::SUSPENDED;
+                 case CELIX_DM_CMP_STATE_RESUMING:
+                     return ComponentState::RESUMING;
+                 default:
+                     return ComponentState::INACTIVE;
              }
          }
 
diff --git a/libs/framework/include/celix_dm_component.h b/libs/framework/include/celix_dm_component.h
index 29d3cb3d..885a46b2 100644
--- a/libs/framework/include/celix_dm_component.h
+++ b/libs/framework/include/celix_dm_component.h
@@ -35,10 +35,17 @@ extern "C" {
 #define CELIX_DM_COMPONENT_UUID     "component.uuid"
 
 typedef enum celix_dm_component_state_enum {
-    DM_CMP_STATE_INACTIVE = 1,
-    DM_CMP_STATE_WAITING_FOR_REQUIRED = 2,
-    DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED = 3,
-    DM_CMP_STATE_TRACKING_OPTIONAL = 4,
+    CELIX_DM_CMP_STATE_INACTIVE =                               1,
+    CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED =                   2,
+    CELIX_DM_CMP_STATE_INITIALIZING =                           3,
+    CELIX_DM_CMP_STATE_DEINITIALIZING =                         4,
+    CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED =   5,
+    CELIX_DM_CMP_STATE_STARTING =                               6,
+    CELIX_DM_CMP_STATE_STOPPING =                               7,
+    CELIX_DM_CMP_STATE_TRACKING_OPTIONAL =                      8,
+    CELIX_DM_CMP_STATE_SUSPENDING =                             9,
+    CELIX_DM_CMP_STATE_SUSPENDED =                              10,
+    CELIX_DM_CMP_STATE_RESUMING =                               11,
 } celix_dm_component_state_t;
 
 #define CELIX_DM_COMPONENT_MAX_ID_LENGTH 64
diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c
index c456487d..c4544f75 100644
--- a/libs/framework/src/dm_component_impl.c
+++ b/libs/framework/src/dm_component_impl.c
@@ -57,6 +57,12 @@ struct celix_dm_component_struct {
     size_t nrOfTimesResumed;
 
     bool isEnabled;
+
+    /**
+     * Whether the component is an a transition (active performTransition call).
+     * Should only be used inside the Celix event Thread -> no locking needed.
+     */
+    bool inTransition;
 };
 
 typedef struct dm_interface_struct {
@@ -83,6 +89,7 @@ static void celix_dmComponent_disableDirectly(celix_dm_component_t *component);
 static celix_status_t celix_dmComponent_disableDependencies(celix_dm_component_t *component);
 static bool celix_dmComponent_isDisabled(celix_dm_component_t *component);
 static void celix_dmComponent_cleanupRemovedDependencies(celix_dm_component_t* component);
+static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component);
 
 
 celix_dm_component_t* celix_dmComponent_create(bundle_context_t *context, const char* name) {
@@ -123,7 +130,7 @@ celix_dm_component_t* celix_dmComponent_createWithUUID(bundle_context_t *context
     component->callbackStart = NULL;
     component->callbackStop = NULL;
     component->callbackDeinit = NULL;
-    component->state = DM_CMP_STATE_INACTIVE;
+    component->state = CELIX_DM_CMP_STATE_INACTIVE;
     component->setCLanguageProperty = false;
 
     component->providedInterfaces = celix_arrayList_create();
@@ -251,7 +258,7 @@ celix_status_t celix_dmComponent_addServiceDependency(celix_dm_component_t *comp
 
     celixThreadMutex_lock(&component->mutex);
     arrayList_add(component->dependencies, dep);
-    bool startDep = component->state != DM_CMP_STATE_INACTIVE;
+    bool startDep = component->state != CELIX_DM_CMP_STATE_INACTIVE;
     if (startDep) {
         celix_dmServiceDependency_enable(dep);
     }
@@ -266,7 +273,7 @@ celix_status_t celix_dmComponent_removeServiceDependency(celix_dm_component_t *c
 
     celixThreadMutex_lock(&component->mutex);
     celix_arrayList_remove(component->dependencies, dep);
-    bool disableDependency = component->state != DM_CMP_STATE_INACTIVE;
+    bool disableDependency = component->state != CELIX_DM_CMP_STATE_INACTIVE;
     if (disableDependency) {
         celix_dmServiceDependency_disable(dep);
     }
@@ -344,7 +351,7 @@ static celix_status_t celix_dmComponent_disable(celix_dm_component_t *component)
  */
 static void celix_dmComponent_disableDirectly(celix_dm_component_t *component) {
     component->isEnabled = false;
-    component->state = DM_CMP_STATE_INACTIVE;
+    component->state = CELIX_DM_CMP_STATE_INACTIVE;
     celix_dmComponent_unregisterServices(component, false);
     celix_dmComponent_disableDependencies(component);
 }
@@ -389,7 +396,7 @@ static bool celix_dmComponent_isDisabled(celix_dm_component_t *component) {
     celixThreadMutex_lock(&component->mutex);
     isStopped =
             !component->isEnabled &&
-            component->state == DM_CMP_STATE_INACTIVE &&
+            component->state == CELIX_DM_CMP_STATE_INACTIVE &&
             celix_dmComponent_areAllDependenciesDisabled(component);
     celixThreadMutex_unlock(&component->mutex);
     return isStopped;
@@ -432,7 +439,7 @@ celix_status_t celix_dmComponent_addInterface(celix_dm_component_t *component, c
         interface->properties = properties;
         interface->svcId= -1L;
         celix_arrayList_add(component->providedInterfaces, interface);
-        if (component->state == DM_CMP_STATE_TRACKING_OPTIONAL) {
+        if (component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
             celix_dmComponent_registerServices(component, false);
         }
         celixThreadMutex_unlock(&component->mutex);
@@ -517,49 +524,69 @@ celix_status_t celix_private_dmComponent_handleEvent(celix_dm_component_t *compo
 static celix_status_t celix_dmComponent_suspend(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) {
 	celix_status_t status = CELIX_SUCCESS;
 	if (component->callbackStop != NULL) {
+        celixThreadMutex_lock(&component->mutex);
+        component->state = CELIX_DM_CMP_STATE_SUSPENDING;
         celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
                "Suspending component %s (uuid=%s)",
                component->name,
                component->uuid);
-        celix_dmComponent_unregisterServices(component, true);
+        celix_dmComponent_unregisterServices(component, false);
 		status = component->callbackStop(component->implementation);
-		if (status != CELIX_SUCCESS) {
+        if (status == CELIX_SUCCESS) {
+            component->state = CELIX_DM_CMP_STATE_SUSPENDED;
+            celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
+                                    "Suspended component %s (uuid=%s)",
+                                    component->name,
+                                    component->uuid);
+        } else {
             celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
-                                    "Error stopping component %s (uuid=%s) using the stop callback",
+                                    "Error stopping component %s (uuid=%s) using the stop callback. Disabling component.",
                                     component->name,
                                     component->uuid);
+            celix_dmComponent_disableDirectly(component);
         }
+        celixThreadMutex_unlock(&component->mutex);
 	}
 	return status;
 }
 
 static celix_status_t celix_dmComponent_resume(celix_dm_component_t *component, celix_dm_service_dependency_t *dependency) {
 	celix_status_t status = CELIX_SUCCESS;
-	if (component->callbackStart != NULL) {
-	    //ensure that the current state is still TRACKING_OPTION. Else during a add/rem/set call a required svc dep has been added.
+    celixThreadMutex_lock(&component->mutex);
+	if (component->state == CELIX_DM_CMP_STATE_SUSPENDED) {
+        component->state = CELIX_DM_CMP_STATE_RESUMING;
         celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
                                 "Resuming component %s (uuid=%s)",
                                 component->name,
                                 component->uuid);
         status = component->callbackStart(component->implementation);
         if (status == CELIX_SUCCESS) {
-            celix_dmComponent_registerServices(component, true);
+            celix_dmComponent_registerServices(component, false);
             component->nrOfTimesResumed += 1;
+            component->state = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL;
+            celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_TRACE,
+                                    "Resumed component %s (uuid=%s)",
+                                    component->name,
+                                    component->uuid);
         } else {
             celix_bundleContext_log(component->context, CELIX_LOG_LEVEL_ERROR,
-                                    "Error starting component %s (uuid=%s) using the start callback",
+                                    "Error starting component %s (uuid=%s) using the start callback. Disabling component.",
                                     component->name,
                                     component->uuid);
+            celixThreadMutex_lock(&component->mutex);
+            celix_dmComponent_disableDirectly(component);
+            celixThreadMutex_unlock(&component->mutex);
         }
-	}
-	return status;
+    }
+    celixThreadMutex_unlock(&component->mutex);
+    return status;
 }
 
 static bool celix_dmComponent_needsSuspend(celix_dm_component_t *component, const celix_dm_event_t* event) {
     bool cmpActive = false;
     celixThreadMutex_lock(&component->mutex);
     switch (component->state) {
-        case DM_CMP_STATE_TRACKING_OPTIONAL:
+        case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
             cmpActive = true;
             break;
         default: //DM_CMP_STATE_INACTIVE
@@ -586,6 +613,17 @@ static celix_status_t celix_dmComponent_handleEvent(celix_dm_component_t *compon
                             component->uuid,
                             event->dep->serviceName);
 
+
+    if (component->inTransition) {
+        /* Note if the component is already in transition (stopping, starting, etc) then only remove the svc
+         * function pointers. This can happen when with dependency loops or if a component also depends on a
+         * services it provides. (e.g. during stopping the component a handle event will be created to remove the
+         * service dependency).
+         */
+        setAddOrRemFp(event->dep, event->svc, event->props);
+        return CELIX_SUCCESS;
+    }
+
     bool eventHandled = false;
     if (event->eventType == CELIX_DM_EVENT_SVC_ADD || (event->eventType == CELIX_DM_EVENT_SVC_SET && event->svc != NULL)) {
         //note adding service or setting new service, so cmp will not be stopped in handleChange -> use suspend / resume now
@@ -705,41 +743,49 @@ static celix_status_t celix_dmComponent_calculateNewState(celix_dm_component_t *
     celix_status_t status = CELIX_SUCCESS;
 
     bool allResolved = celix_dmComponent_areAllRequiredServiceDependenciesResolved(component);
-    if (currentState == DM_CMP_STATE_INACTIVE) {
+    if (currentState == CELIX_DM_CMP_STATE_INACTIVE) {
         if (component->isEnabled) {
-            *newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED;
         } else {
             *newState = currentState;
         }
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
         if (!component->isEnabled) {
-            *newState = DM_CMP_STATE_INACTIVE;
+            *newState = CELIX_DM_CMP_STATE_INACTIVE;
         } else {
             if (allResolved) {
-                *newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+                *newState = CELIX_DM_CMP_STATE_INITIALIZING;
             } else {
                 *newState = currentState;
             }
         }
-    } else if (currentState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING) {
+        *newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
+    } else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING) {
+        *newState = CELIX_DM_CMP_STATE_INACTIVE;
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         if (!component->isEnabled) {
-            *newState = DM_CMP_STATE_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_DEINITIALIZING;
         } else {
             if (allResolved) {
-                *newState = DM_CMP_STATE_TRACKING_OPTIONAL;
+                *newState = CELIX_DM_CMP_STATE_STARTING;
             } else {
                 *newState = currentState;
             }
         }
-    } else if (currentState == DM_CMP_STATE_TRACKING_OPTIONAL) {
+    } else if (currentState == CELIX_DM_CMP_STATE_STARTING) {
+        *newState = CELIX_DM_CMP_STATE_TRACKING_OPTIONAL;
+    } else if (currentState == CELIX_DM_CMP_STATE_STOPPING) {
+        *newState = CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED;
+    } else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
         if (component->isEnabled && allResolved) {
             *newState = currentState;
         } else {
-            *newState = DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED;
+            *newState = CELIX_DM_CMP_STATE_STOPPING;
         }
     } else {
         //should not reach
-        *newState = DM_CMP_STATE_INACTIVE;
+        *newState = CELIX_DM_CMP_STATE_INACTIVE;
         status = CELIX_BUNDLE_EXCEPTION;
     }
 
@@ -756,6 +802,7 @@ static bool celix_dmComponent_performTransition(celix_dm_component_t *component,
     if (currentState == desiredState) {
         return false;
     }
+    component->inTransition = true;
 
     celix_bundleContext_log(component->context,
            CELIX_LOG_LEVEL_TRACE,
@@ -766,13 +813,24 @@ static bool celix_dmComponent_performTransition(celix_dm_component_t *component,
            celix_dmComponent_stateToString(desiredState));
 
     celix_status_t status = CELIX_SUCCESS;
-    if (currentState == DM_CMP_STATE_INACTIVE && desiredState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
+    if (currentState == CELIX_DM_CMP_STATE_INACTIVE && desiredState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED) {
         celix_dmComponent_enableDependencies(component);
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_INITIALIZING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZING && desiredState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         if (component->callbackInit) {
-        	status = component->callbackInit(component->implementation);
+            status = component->callbackInit(component->implementation);
         }
-    } else if (currentState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && desiredState == DM_CMP_STATE_TRACKING_OPTIONAL) {
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_DEINITIALIZING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_DEINITIALIZING && desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
+        if (component->callbackDeinit) {
+            status = component->callbackDeinit(component->implementation);
+        }
+        celix_dmComponent_disableDependencies(component);
+    } else if (currentState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_STARTING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_STARTING && desiredState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL) {
         if (component->callbackStart) {
         	status = component->callbackStart(component->implementation);
         }
@@ -780,16 +838,14 @@ static bool celix_dmComponent_performTransition(celix_dm_component_t *component,
             celix_dmComponent_registerServices(component, false);
             component->nrOfTimesStarted += 1;
         }
-    } else if (currentState == DM_CMP_STATE_TRACKING_OPTIONAL && desiredState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED) {
+    } else if (currentState == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL && desiredState == CELIX_DM_CMP_STATE_STOPPING) {
+        //nop
+    } else if (currentState == CELIX_DM_CMP_STATE_STOPPING && desiredState == CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED) {
         celix_dmComponent_unregisterServices(component, false);
         if (component->callbackStop) {
         	status = component->callbackStop(component->implementation);
         }
-    } else if (currentState == DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED && desiredState == DM_CMP_STATE_WAITING_FOR_REQUIRED) {
-        if (component->callbackDeinit) {
-            status = component->callbackDeinit(component->implementation);
-        }
-    } else if (currentState == DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == DM_CMP_STATE_INACTIVE) {
+    } else if (currentState == CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED && desiredState == CELIX_DM_CMP_STATE_INACTIVE) {
         celix_dmComponent_disableDependencies(component);
     } else {
         assert(false); //should not be reached.
@@ -809,6 +865,7 @@ static bool celix_dmComponent_performTransition(celix_dm_component_t *component,
         celix_dmComponent_disableDirectly(component);
     }
 
+    component->inTransition = false;
     return transition;
 }
 
@@ -964,25 +1021,8 @@ celix_status_t celix_dmComponent_getComponentInfo(celix_dm_component_t *componen
     memcpy(info->name, component->name, DM_COMPONENT_MAX_NAME_LENGTH);
     info->nrOfTimesStarted = component->nrOfTimesStarted;
     info->nrOfTimesResumed = component->nrOfTimesResumed;
-
-    switch (component->state) {
-        case DM_CMP_STATE_INACTIVE :
-            info->state = strdup("INACTIVE");
-            break;
-        case DM_CMP_STATE_WAITING_FOR_REQUIRED :
-            info->state = strdup("WAITING_FOR_REQUIRED");
-            break;
-        case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED :
-            info->state = strdup("INSTANTIATED_AND_WAITING_FOR_REQUIRED");
-            break;
-        case DM_CMP_STATE_TRACKING_OPTIONAL :
-            info->state = strdup("TRACKING_OPTIONAL");
-            info->active = true;
-            break;
-        default :
-            info->state = strdup("UNKNOWN");
-            break;
-    }
+    info->state = celix_utils_strdup(celix_dmComponent_stateToString(component->state));
+    info->active = celix_dmComponent_isActiveInternal(component);
 
     for (int i = 0; i < celix_arrayList_size(component->dependencies); i += 1) {
         celix_dm_service_dependency_t *dep = celix_arrayList_get(component->dependencies, i);
@@ -1027,22 +1067,47 @@ void celix_dmComponent_destroyComponentInfo(dm_component_info_pt info) {
     free(info);
 }
 
+static bool celix_dmComponent_isActiveInternal(celix_dm_component_t *component) {
+    //precondition: mutex component->mutex taken.
+    return
+            component->state == CELIX_DM_CMP_STATE_STARTING ||
+            component->state == CELIX_DM_CMP_STATE_TRACKING_OPTIONAL ||
+            component->state == CELIX_DM_CMP_STATE_SUSPENDING ||
+            component->state == CELIX_DM_CMP_STATE_SUSPENDED ||
+            component->state == CELIX_DM_CMP_STATE_RESUMING ||
+            component->state == CELIX_DM_CMP_STATE_STOPPING;
+}
+
 bool celix_dmComponent_isActive(celix_dm_component_t *component) {
     celixThreadMutex_lock(&component->mutex);
-    bool active = component->state == DM_CMP_STATE_TRACKING_OPTIONAL;
+    bool isActivate = celix_dmComponent_isActiveInternal(component);
     celixThreadMutex_unlock(&component->mutex);
-    return active;
+    return isActivate;
 }
 
 const char* celix_dmComponent_stateToString(celix_dm_component_state_t state) {
     switch(state) {
-        case DM_CMP_STATE_INACTIVE:
-            return "DM_CMP_STATE_INACTIVE";
-        case DM_CMP_STATE_WAITING_FOR_REQUIRED:
-            return "DM_CMP_STATE_WAITING_FOR_REQUIRED";
-        case DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED:
-            return "DM_CMP_STATE_INSTANTIATED_AND_WAITING_FOR_REQUIRED";
-        default: //only DM_CMP_STATE_TRACKING_OPTIONAL left
-            return "DM_CMP_STATE_TRACKING_OPTIONAL";
+        case CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED:
+            return "CELIX_DM_CMP_STATE_WAITING_FOR_REQUIRED";
+        case CELIX_DM_CMP_STATE_INITIALIZING:
+            return "CELIX_DM_CMP_STATE_INITIALIZING";
+        case CELIX_DM_CMP_STATE_DEINITIALIZING:
+            return "CELIX_DM_CMP_STATE_DEINITIALIZING";
+        case CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED:
+            return "CELIX_DM_CMP_STATE_INITIALIZED_AND_WAITING_FOR_REQUIRED";
+        case CELIX_DM_CMP_STATE_STARTING:
+            return "CELIX_DM_CMP_STATE_STARTING";
+        case CELIX_DM_CMP_STATE_STOPPING:
+            return "CELIX_DM_CMP_STATE_STOPPING";
+        case CELIX_DM_CMP_STATE_TRACKING_OPTIONAL:
+            return "CELIX_DM_CMP_STATE_TRACKING_OPTIONAL";
+        case CELIX_DM_CMP_STATE_SUSPENDING:
+            return "CELIX_DM_CMP_STATE_SUSPENDING";
+        case CELIX_DM_CMP_STATE_SUSPENDED:
+            return "CELIX_DM_CMP_STATE_SUSPENDED";
+        case CELIX_DM_CMP_STATE_RESUMING:
+            return "CELIX_DM_CMP_STATE_RESUMING";
+        default: //only CELIX_DM_CMP_STATE_INACTIVE left
+            return "CELIX_DM_CMP_STATE_INACTIVE";
     }
 }