thingsboard-memoizeit

LifeCycle Events for RuleChainActor

3/15/2018 10:17:32 AM

Changes

common/message/src/main/java/org/thingsboard/server/common/msg/RuleMsg.java 38(+0 -38)

Details

diff --git a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
index b475277..549d71d 100644
--- a/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/app/AppActor.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2018 The Thingsboard Authors
- *
+ * <p>
  * Licensed 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
@@ -22,18 +22,15 @@ import akka.event.LoggingAdapter;
 import akka.japi.Function;
 import org.thingsboard.server.actors.ActorSystemContext;
 import org.thingsboard.server.actors.plugin.PluginTerminationMsg;
-import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
 import org.thingsboard.server.actors.service.ContextBasedCreator;
 import org.thingsboard.server.actors.service.DefaultActorService;
-import org.thingsboard.server.actors.shared.plugin.PluginManager;
 import org.thingsboard.server.actors.shared.plugin.SystemPluginManager;
-import org.thingsboard.server.actors.shared.rule.RuleManager;
-import org.thingsboard.server.actors.shared.rule.SystemRuleManager;
-import org.thingsboard.server.actors.tenant.RuleChainDeviceMsg;
+import org.thingsboard.server.actors.shared.rulechain.SystemRuleChainManager;
 import org.thingsboard.server.actors.tenant.TenantActor;
 import org.thingsboard.server.common.data.Tenant;
 import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
+import org.thingsboard.server.common.data.id.RuleChainId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.page.PageDataIterable;
 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
@@ -43,27 +40,21 @@ import org.thingsboard.server.dao.model.ModelConstants;
 import org.thingsboard.server.dao.tenant.TenantService;
 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg;
 import scala.concurrent.duration.Duration;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
 
-public class AppActor extends ContextAwareActor {
+public class AppActor extends RuleChainManagerActor {
 
     private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
 
     public static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID);
-    private final RuleManager ruleManager;
-    private final PluginManager pluginManager;
     private final TenantService tenantService;
     private final Map<TenantId, ActorRef> tenantActors;
 
     private AppActor(ActorSystemContext systemContext) {
-        super(systemContext);
-        this.ruleManager = new SystemRuleManager(systemContext);
-        this.pluginManager = new SystemPluginManager(systemContext);
+        super(systemContext, new SystemRuleChainManager(systemContext), new SystemPluginManager(systemContext));
         this.tenantService = systemContext.getTenantService();
         this.tenantActors = new HashMap<>();
     }
@@ -77,8 +68,7 @@ public class AppActor extends ContextAwareActor {
     public void preStart() {
         logger.info("Starting main system actor.");
         try {
-            ruleManager.init(this.context());
-            pluginManager.init(this.context());
+            initRuleChains();
 
             if (systemContext.isTenantComponentsInitEnabled()) {
                 PageDataIterable<Tenant> tenantIterator = new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT);
@@ -102,8 +92,6 @@ public class AppActor extends ContextAwareActor {
             processDeviceMsg((ToDeviceActorMsg) msg);
         } else if (msg instanceof ToPluginActorMsg) {
             onToPluginMsg((ToPluginActorMsg) msg);
-        } else if (msg instanceof ToRuleActorMsg) {
-            onToRuleMsg((ToRuleActorMsg) msg);
         } else if (msg instanceof ToDeviceActorNotificationMsg) {
             onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);
         } else if (msg instanceof Terminated) {
@@ -128,20 +116,10 @@ public class AppActor extends ContextAwareActor {
         tenantActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
     }
 
-    private void onToRuleMsg(ToRuleActorMsg msg) {
-        ActorRef target;
-        if (SYSTEM_TENANT.equals(msg.getTenantId())) {
-            target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId());
-        } else {
-            target = getOrCreateTenantActor(msg.getTenantId());
-        }
-        target.tell(msg, ActorRef.noSender());
-    }
-
     private void onToPluginMsg(ToPluginActorMsg msg) {
         ActorRef target;
         if (SYSTEM_TENANT.equals(msg.getPluginTenantId())) {
-            target = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId());
+            target = pluginManager.getOrCreateActor(this.context(), msg.getPluginId());
         } else {
             target = getOrCreateTenantActor(msg.getPluginTenantId());
         }
@@ -149,26 +127,16 @@ public class AppActor extends ContextAwareActor {
     }
 
     private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
-        ActorRef target = null;
+        ActorRef target;
         if (SYSTEM_TENANT.equals(msg.getTenantId())) {
-            Optional<PluginId> pluginId = msg.getPluginId();
-            Optional<RuleId> ruleId = msg.getRuleId();
-            if (pluginId.isPresent()) {
-                target = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get());
-            } else if (ruleId.isPresent()) {
-                Optional<ActorRef> ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent());
-                if (ref.isPresent()) {
-                    target = ref.get();
-                } else {
-                    logger.debug("Failed to find actor for rule: [{}]", ruleId);
-                    return;
-                }
-            }
+            target = getEntityActorRef(msg.getEntityId());
         } else {
             target = getOrCreateTenantActor(msg.getTenantId());
         }
         if (target != null) {
             target.tell(msg, ActorRef.noSender());
+        } else {
+            logger.debug("Invalid component lifecycle msg: {}", msg);
         }
     }
 
@@ -180,7 +148,7 @@ public class AppActor extends ContextAwareActor {
         TenantId tenantId = toDeviceActorMsg.getTenantId();
         ActorRef tenantActor = getOrCreateTenantActor(tenantId);
         if (toDeviceActorMsg.getPayload().getMsgType().requiresRulesProcessing()) {
-            tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
+//            tenantActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, ruleManager.getRuleChain(this.context())), context().self());
         } else {
             tenantActor.tell(toDeviceActorMsg, context().self());
         }
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
new file mode 100644
index 0000000..83536f8
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActor.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ * <p>
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ComponentActor;
+import org.thingsboard.server.actors.service.ContextBasedCreator;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+public class RuleChainActor extends ComponentActor<RuleChainId, RuleChainActorMessageProcessor> {
+
+    private RuleChainActor(ActorSystemContext systemContext, TenantId tenantId, RuleChainId ruleChainId) {
+        super(systemContext, tenantId, ruleChainId);
+        setProcessor(new RuleChainActorMessageProcessor(tenantId, ruleChainId, systemContext,
+                logger, context().parent(), context().self()));
+    }
+
+    @Override
+    public void onReceive(Object msg) throws Exception {
+        logger.debug("[{}][{}] Unknown msg type.", tenantId, id, msg.getClass().getName());
+    }
+
+    public static class ActorCreator extends ContextBasedCreator<RuleChainActor> {
+        private static final long serialVersionUID = 1L;
+
+        private final TenantId tenantId;
+        private final RuleChainId ruleChainId;
+
+        public ActorCreator(ActorSystemContext context, TenantId tenantId, RuleChainId pluginId) {
+            super(context);
+            this.tenantId = tenantId;
+            this.ruleChainId = pluginId;
+        }
+
+        @Override
+        public RuleChainActor create() throws Exception {
+            return new RuleChainActor(context, tenantId, ruleChainId);
+        }
+    }
+
+    @Override
+    protected long getErrorPersistFrequency() {
+        return systemContext.getPluginErrorPersistFrequency();
+    }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
new file mode 100644
index 0000000..3075367
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainActorMessageProcessor.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed 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.
+ */
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.event.LoggingAdapter;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.plugin.*;
+import org.thingsboard.server.actors.shared.ComponentMsgProcessor;
+import org.thingsboard.server.common.data.id.PluginId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.plugin.ComponentType;
+import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
+import org.thingsboard.server.common.msg.cluster.ServerAddress;
+import org.thingsboard.server.extensions.api.plugins.Plugin;
+import org.thingsboard.server.extensions.api.plugins.PluginInitializationException;
+import org.thingsboard.server.extensions.api.plugins.msg.FromDeviceRpcResponse;
+import org.thingsboard.server.extensions.api.plugins.msg.TimeoutMsg;
+import org.thingsboard.server.extensions.api.plugins.rest.PluginRestMsg;
+import org.thingsboard.server.extensions.api.plugins.rpc.PluginRpcMsg;
+import org.thingsboard.server.extensions.api.plugins.ws.msg.PluginWebsocketMsg;
+import org.thingsboard.server.extensions.api.rules.RuleException;
+
+/**
+ * @author Andrew Shvayka
+ */
+public class RuleChainActorMessageProcessor extends ComponentMsgProcessor<RuleChainId> {
+
+    private ComponentLifecycleState state;
+
+    protected RuleChainActorMessageProcessor(TenantId tenantId, RuleChainId pluginId, ActorSystemContext systemContext
+            , LoggingAdapter logger, ActorRef parent, ActorRef self) {
+        super(systemContext, logger, tenantId, pluginId);
+    }
+
+    @Override
+    public void start() throws Exception {
+
+    }
+
+    @Override
+    public void stop() throws Exception {
+
+    }
+
+    @Override
+    public void onCreated(ActorContext context) throws Exception {
+
+    }
+
+    @Override
+    public void onUpdate(ActorContext context) throws Exception {
+
+    }
+
+    @Override
+    public void onActivate(ActorContext context) throws Exception {
+
+    }
+
+    @Override
+    public void onSuspend(ActorContext context) throws Exception {
+
+    }
+
+    @Override
+    public void onStop(ActorContext context) throws Exception {
+
+    }
+
+    @Override
+    public void onClusterEventMsg(ClusterEventMsg msg) throws Exception {
+
+    }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
new file mode 100644
index 0000000..2dca8dd
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/RuleChainManagerActor.java
@@ -0,0 +1,43 @@
+package org.thingsboard.server.actors.ruleChain;
+
+import akka.actor.ActorRef;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.shared.plugin.PluginManager;
+import org.thingsboard.server.actors.shared.rulechain.RuleChainManager;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.PluginId;
+import org.thingsboard.server.common.data.id.RuleChainId;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+public abstract class RuleChainManagerActor extends ContextAwareActor {
+
+    protected final RuleChainManager ruleChainManager;
+    protected final PluginManager pluginManager;
+
+    public RuleChainManagerActor(ActorSystemContext systemContext, RuleChainManager ruleChainManager, PluginManager pluginManager) {
+        super(systemContext);
+        this.ruleChainManager = ruleChainManager;
+        this.pluginManager = pluginManager;
+    }
+
+    protected void initRuleChains() {
+        pluginManager.init(this.context());
+        ruleChainManager.init(this.context());
+    }
+
+    protected ActorRef getEntityActorRef(EntityId entityId) {
+        ActorRef target = null;
+        switch (entityId.getEntityType()) {
+            case PLUGIN:
+                target = pluginManager.getOrCreateActor(this.context(), (PluginId) entityId);
+                break;
+            case RULE_CHAIN:
+                target = ruleChainManager.getOrCreateActor(this.context(), (RuleChainId) entityId);
+                break;
+        }
+        return target;
+    }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
index baae376..0f66d25 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/ActorService.java
@@ -15,10 +15,7 @@
  */
 package org.thingsboard.server.actors.service;
 
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
 import org.thingsboard.server.common.transport.SessionMsgProcessor;
 import org.thingsboard.server.service.cluster.discovery.DiscoveryServiceListener;
@@ -26,9 +23,7 @@ import org.thingsboard.server.service.cluster.rpc.RpcMsgListener;
 
 public interface ActorService extends SessionMsgProcessor, WebSocketMsgProcessor, RestMsgProcessor, RpcMsgListener, DiscoveryServiceListener {
 
-    void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state);
-
-    void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state);
+    void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state);
 
     void onCredentialsUpdate(TenantId tenantId, DeviceId deviceId);
 
diff --git a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
index bb84a30..7b62687 100644
--- a/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
+++ b/application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2018 The Thingsboard Authors
- *
+ * <p>
  * Licensed 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
@@ -30,10 +30,7 @@ import org.thingsboard.server.actors.rpc.RpcSessionCreateRequestMsg;
 import org.thingsboard.server.actors.rpc.RpcSessionTellMsg;
 import org.thingsboard.server.actors.session.SessionManagerActor;
 import org.thingsboard.server.actors.stats.StatsActor;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
 import org.thingsboard.server.common.msg.aware.SessionAwareMsg;
 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
@@ -212,15 +209,9 @@ public class DefaultActorService implements ActorService {
     }
 
     @Override
-    public void onPluginStateChange(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent state) {
-        log.trace("[{}] Processing onPluginStateChange event: {}", pluginId, state);
-        broadcast(ComponentLifecycleMsg.forPlugin(tenantId, pluginId, state));
-    }
-
-    @Override
-    public void onRuleStateChange(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent state) {
-        log.trace("[{}] Processing onRuleStateChange event: {}", ruleId, state);
-        broadcast(ComponentLifecycleMsg.forRule(tenantId, ruleId, state));
+    public void onEntityStateChange(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent state) {
+        log.trace("[{}] Processing {} state change event: {}", tenantId, entityId.getEntityType(), state);
+        broadcast(new ComponentLifecycleMsg(tenantId, entityId, state));
     }
 
     @Override
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
new file mode 100644
index 0000000..a98609c
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/EntityActorsManager.java
@@ -0,0 +1,68 @@
+package org.thingsboard.server.actors.shared;
+
+import akka.actor.ActorContext;
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.common.data.SearchTextBased;
+import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
+import org.thingsboard.server.common.data.id.EntityId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.id.UUIDBased;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.common.data.plugin.PluginMetaData;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Slf4j
+public abstract class EntityActorsManager<T extends EntityId, A extends UntypedActor, M extends SearchTextBased<? extends UUIDBased>> {
+
+    protected final ActorSystemContext systemContext;
+    protected final Map<T, ActorRef> actors;
+
+    public EntityActorsManager(ActorSystemContext systemContext) {
+        this.systemContext = systemContext;
+        this.actors = new HashMap<>();
+    }
+
+    protected abstract TenantId getTenantId();
+
+    protected abstract String getDispatcherName();
+
+    protected abstract Creator<A> creator(T entityId);
+
+    protected abstract PageDataIterable.FetchFunction<M> getFetchEntitiesFunction();
+
+    public void init(ActorContext context) {
+        for (M entity : new PageDataIterable<>(getFetchEntitiesFunction(), ContextAwareActor.ENTITY_PACK_LIMIT)) {
+            log.debug("[{}] Creating plugin actor", entity.getId());
+            //TODO: remove this cast making UUIDBased subclass of EntityId an interface and vice versa.
+            getOrCreateActor(context, (T) entity.getId());
+            log.debug("[{}] Plugin actor created.", entity.getId());
+        }
+    }
+
+
+    public ActorRef getOrCreateActor(ActorContext context, T entityId) {
+        return actors.computeIfAbsent(entityId, eId ->
+                context.actorOf(Props.create(creator(eId))
+                        .withDispatcher(getDispatcherName()), eId.toString()));
+    }
+
+    public void broadcast(Object msg) {
+        actors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
+    }
+
+    public void remove(T id) {
+        actors.remove(id);
+    }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
index 4f5871f..5142de2 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/PluginManager.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2018 The Thingsboard Authors
- *
+ * <p>
  * Licensed 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
@@ -15,63 +15,28 @@
  */
 package org.thingsboard.server.actors.shared.plugin;
 
-import akka.actor.ActorContext;
-import akka.actor.ActorRef;
-import akka.actor.Props;
+import akka.japi.Creator;
 import lombok.extern.slf4j.Slf4j;
 import org.thingsboard.server.actors.ActorSystemContext;
 import org.thingsboard.server.actors.plugin.PluginActor;
-import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.shared.EntityActorsManager;
 import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.TenantId;
-import org.thingsboard.server.common.data.page.PageDataIterable;
-import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
 import org.thingsboard.server.common.data.plugin.PluginMetaData;
 import org.thingsboard.server.dao.plugin.PluginService;
 
-import java.util.HashMap;
-import java.util.Map;
-
 @Slf4j
-public abstract class PluginManager {
+public abstract class PluginManager extends EntityActorsManager<PluginId, PluginActor, PluginMetaData> {
 
-    protected final ActorSystemContext systemContext;
     protected final PluginService pluginService;
-    protected final Map<PluginId, ActorRef> pluginActors;
 
     public PluginManager(ActorSystemContext systemContext) {
-        this.systemContext = systemContext;
+        super(systemContext);
         this.pluginService = systemContext.getPluginService();
-        this.pluginActors = new HashMap<>();
     }
 
-    public void init(ActorContext context) {
-        PageDataIterable<PluginMetaData> pluginIterator = new PageDataIterable<>(getFetchPluginsFunction(),
-                ContextAwareActor.ENTITY_PACK_LIMIT);
-        for (PluginMetaData plugin : pluginIterator) {
-            log.debug("[{}] Creating plugin actor", plugin.getId());
-            getOrCreatePluginActor(context, plugin.getId());
-            log.debug("Plugin actor created.");
-        }
+    @Override
+    public Creator<PluginActor> creator(PluginId entityId){
+        return new PluginActor.ActorCreator(systemContext, getTenantId(), entityId);
     }
 
-    abstract FetchFunction<PluginMetaData> getFetchPluginsFunction();
-
-    abstract TenantId getTenantId();
-
-    abstract String getDispatcherName();
-
-    public ActorRef getOrCreatePluginActor(ActorContext context, PluginId pluginId) {
-        return pluginActors.computeIfAbsent(pluginId, pId ->
-                context.actorOf(Props.create(new PluginActor.ActorCreator(systemContext, getTenantId(), pId))
-                        .withDispatcher(getDispatcherName()), pId.toString()));
-    }
-
-    public void broadcast(Object msg) {
-        pluginActors.values().forEach(actorRef -> actorRef.tell(msg, ActorRef.noSender()));
-    }
-
-    public void remove(PluginId id) {
-        pluginActors.remove(id);
-    }
 }
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java
index 0888e23..88c52a6 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/SystemPluginManager.java
@@ -29,12 +29,12 @@ public class SystemPluginManager extends PluginManager {
     }
 
     @Override
-    FetchFunction<PluginMetaData> getFetchPluginsFunction() {
+    protected FetchFunction<PluginMetaData> getFetchEntitiesFunction() {
         return pluginService::findSystemPlugins;
     }
 
     @Override
-    TenantId getTenantId() {
+    protected TenantId getTenantId() {
         return BasePluginService.SYSTEM_TENANT;
     }
 
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java
index 14ea2aa..09115f0 100644
--- a/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/plugin/TenantPluginManager.java
@@ -19,6 +19,7 @@ import akka.actor.ActorContext;
 import org.thingsboard.server.actors.ActorSystemContext;
 import org.thingsboard.server.actors.service.DefaultActorService;
 import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable;
 import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
 import org.thingsboard.server.common.data.plugin.PluginMetaData;
 
@@ -39,12 +40,12 @@ public class TenantPluginManager extends PluginManager {
     }
 
     @Override
-    FetchFunction<PluginMetaData> getFetchPluginsFunction() {
+    protected FetchFunction<PluginMetaData> getFetchEntitiesFunction() {
         return link -> pluginService.findTenantPlugins(tenantId, link);
     }
 
     @Override
-    TenantId getTenantId() {
+    protected TenantId getTenantId() {
         return tenantId;
     }
 
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
new file mode 100644
index 0000000..0939611
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/RuleChainManager.java
@@ -0,0 +1,30 @@
+package org.thingsboard.server.actors.shared.rulechain;
+
+import akka.japi.Creator;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.ruleChain.RuleChainActor;
+import org.thingsboard.server.actors.shared.EntityActorsManager;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.rule.RuleChainService;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+@Slf4j
+public abstract class RuleChainManager extends EntityActorsManager<RuleChainId, RuleChainActor, RuleChain> {
+
+    protected final RuleChainService service;
+
+    public RuleChainManager(ActorSystemContext systemContext) {
+        super(systemContext);
+        this.service = systemContext.getRuleChainService();
+    }
+
+    @Override
+    public Creator<RuleChainActor> creator(RuleChainId entityId) {
+        return new RuleChainActor.ActorCreator(systemContext, getTenantId(), entityId);
+    }
+
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java
new file mode 100644
index 0000000..a8bb069
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/SystemRuleChainManager.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ *
+ * Licensed 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.
+ */
+package org.thingsboard.server.actors.shared.rulechain;
+
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.actors.shared.plugin.PluginManager;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
+import org.thingsboard.server.common.data.plugin.PluginMetaData;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.plugin.BasePluginService;
+
+public class SystemRuleChainManager extends RuleChainManager {
+
+    public SystemRuleChainManager(ActorSystemContext systemContext) {
+        super(systemContext);
+    }
+
+    @Override
+    protected FetchFunction<RuleChain> getFetchEntitiesFunction() {
+        return service::findSystemRuleChains;
+    }
+
+    @Override
+    protected TenantId getTenantId() {
+        return BasePluginService.SYSTEM_TENANT;
+    }
+
+    @Override
+    protected String getDispatcherName() {
+        return DefaultActorService.SYSTEM_RULE_DISPATCHER_NAME;
+    }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
new file mode 100644
index 0000000..aa2a631
--- /dev/null
+++ b/application/src/main/java/org/thingsboard/server/actors/shared/rulechain/TenantRuleChainManager.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright © 2016-2018 The Thingsboard Authors
+ * <p>
+ * Licensed 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.
+ */
+package org.thingsboard.server.actors.shared.rulechain;
+
+import akka.actor.ActorContext;
+import org.thingsboard.server.actors.ActorSystemContext;
+import org.thingsboard.server.actors.service.DefaultActorService;
+import org.thingsboard.server.actors.shared.rule.RuleManager;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.page.PageDataIterable;
+import org.thingsboard.server.common.data.page.PageDataIterable.FetchFunction;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.common.data.rule.RuleMetaData;
+
+public class TenantRuleChainManager extends RuleChainManager {
+
+    private final TenantId tenantId;
+
+    public TenantRuleChainManager(ActorSystemContext systemContext, TenantId tenantId) {
+        super(systemContext);
+        this.tenantId = tenantId;
+    }
+
+    @Override
+    public void init(ActorContext context) {
+        if (systemContext.isTenantComponentsInitEnabled()) {
+            super.init(context);
+        }
+    }
+
+    @Override
+    protected TenantId getTenantId() {
+        return tenantId;
+    }
+
+    @Override
+    protected String getDispatcherName() {
+        return DefaultActorService.TENANT_RULE_DISPATCHER_NAME;
+    }
+
+    @Override
+    protected FetchFunction<RuleChain> getFetchEntitiesFunction() {
+        return link -> service.findTenantRuleChains(tenantId, link);
+    }
+}
diff --git a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
index b923fe1..8621179 100644
--- a/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2018 The Thingsboard Authors
- *
+ * <p>
  * Licensed 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
@@ -15,52 +15,38 @@
  */
 package org.thingsboard.server.actors.tenant;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.event.Logging;
+import akka.event.LoggingAdapter;
 import org.thingsboard.server.actors.ActorSystemContext;
 import org.thingsboard.server.actors.device.DeviceActor;
 import org.thingsboard.server.actors.plugin.PluginTerminationMsg;
-import org.thingsboard.server.actors.rule.ComplexRuleActorChain;
-import org.thingsboard.server.actors.rule.RuleActorChain;
-import org.thingsboard.server.actors.service.ContextAwareActor;
+import org.thingsboard.server.actors.ruleChain.RuleChainManagerActor;
 import org.thingsboard.server.actors.service.ContextBasedCreator;
 import org.thingsboard.server.actors.service.DefaultActorService;
-import org.thingsboard.server.actors.shared.plugin.PluginManager;
 import org.thingsboard.server.actors.shared.plugin.TenantPluginManager;
-import org.thingsboard.server.actors.shared.rule.RuleManager;
-import org.thingsboard.server.actors.shared.rule.TenantRuleManager;
-import org.thingsboard.server.common.data.id.DeviceId;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.actors.shared.rulechain.TenantRuleChainManager;
+import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.msg.cluster.ClusterEventMsg;
 import org.thingsboard.server.common.msg.device.ToDeviceActorMsg;
-
-import akka.actor.ActorRef;
-import akka.actor.Props;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
 import org.thingsboard.server.common.msg.plugin.ComponentLifecycleMsg;
 import org.thingsboard.server.extensions.api.device.ToDeviceActorNotificationMsg;
 import org.thingsboard.server.extensions.api.plugins.msg.ToPluginActorMsg;
-import org.thingsboard.server.extensions.api.rules.ToRuleActorMsg;
 
-public class TenantActor extends ContextAwareActor {
+import java.util.HashMap;
+import java.util.Map;
+
+public class TenantActor extends RuleChainManagerActor {
 
     private final LoggingAdapter logger = Logging.getLogger(getContext().system(), this);
 
     private final TenantId tenantId;
-    private final RuleManager ruleManager;
-    private final PluginManager pluginManager;
     private final Map<DeviceId, ActorRef> deviceActors;
 
     private TenantActor(ActorSystemContext systemContext, TenantId tenantId) {
-        super(systemContext);
+        super(systemContext, new TenantRuleChainManager(systemContext, tenantId), new TenantPluginManager(systemContext, tenantId));
         this.tenantId = tenantId;
-        this.ruleManager = new TenantRuleManager(systemContext, tenantId);
-        this.pluginManager = new TenantPluginManager(systemContext, tenantId);
         this.deviceActors = new HashMap<>();
     }
 
@@ -68,8 +54,7 @@ public class TenantActor extends ContextAwareActor {
     public void preStart() {
         logger.info("[{}] Starting tenant actor.", tenantId);
         try {
-            ruleManager.init(this.context());
-            pluginManager.init(this.context());
+            initRuleChains();
             logger.info("[{}] Tenant actor started.", tenantId);
         } catch (Exception e) {
             logger.error(e, "[{}] Unknown failure", tenantId);
@@ -79,14 +64,10 @@ public class TenantActor extends ContextAwareActor {
     @Override
     public void onReceive(Object msg) throws Exception {
         logger.debug("[{}] Received message: {}", tenantId, msg);
-        if (msg instanceof RuleChainDeviceMsg) {
-            process((RuleChainDeviceMsg) msg);
-        } else if (msg instanceof ToDeviceActorMsg) {
+        if (msg instanceof ToDeviceActorMsg) {
             onToDeviceActorMsg((ToDeviceActorMsg) msg);
         } else if (msg instanceof ToPluginActorMsg) {
             onToPluginMsg((ToPluginActorMsg) msg);
-        } else if (msg instanceof ToRuleActorMsg) {
-            onToRuleMsg((ToRuleActorMsg) msg);
         } else if (msg instanceof ToDeviceActorNotificationMsg) {
             onToDeviceActorMsg((ToDeviceActorNotificationMsg) msg);
         } else if (msg instanceof ClusterEventMsg) {
@@ -113,14 +94,9 @@ public class TenantActor extends ContextAwareActor {
         getOrCreateDeviceActor(msg.getDeviceId()).tell(msg, ActorRef.noSender());
     }
 
-    private void onToRuleMsg(ToRuleActorMsg msg) {
-        ActorRef target = ruleManager.getOrCreateRuleActor(this.context(), msg.getRuleId());
-        target.tell(msg, ActorRef.noSender());
-    }
-
     private void onToPluginMsg(ToPluginActorMsg msg) {
         if (msg.getPluginTenantId().equals(tenantId)) {
-            ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), msg.getPluginId());
+            ActorRef pluginActor = pluginManager.getOrCreateActor(this.context(), msg.getPluginId());
             pluginActor.tell(msg, ActorRef.noSender());
         } else {
             context().parent().tell(msg, ActorRef.noSender());
@@ -128,23 +104,11 @@ public class TenantActor extends ContextAwareActor {
     }
 
     private void onComponentLifecycleMsg(ComponentLifecycleMsg msg) {
-        Optional<PluginId> pluginId = msg.getPluginId();
-        Optional<RuleId> ruleId = msg.getRuleId();
-        if (pluginId.isPresent()) {
-            ActorRef pluginActor = pluginManager.getOrCreatePluginActor(this.context(), pluginId.get());
-            pluginActor.tell(msg, ActorRef.noSender());
-        } else if (ruleId.isPresent()) {
-            ActorRef target;
-            Optional<ActorRef> ref = ruleManager.update(this.context(), ruleId.get(), msg.getEvent());
-            if (ref.isPresent()) {
-                target = ref.get();
-            } else {
-                logger.debug("Failed to find actor for rule: [{}]", ruleId);
-                return;
-            }
+        ActorRef target = getEntityActorRef(msg.getEntityId());
+        if (target != null) {
             target.tell(msg, ActorRef.noSender());
         } else {
-            logger.debug("[{}] Invalid component lifecycle msg.", tenantId);
+            logger.debug("Invalid component lifecycle msg: {}", msg);
         }
     }
 
@@ -152,13 +116,6 @@ public class TenantActor extends ContextAwareActor {
         pluginManager.remove(msg.getId());
     }
 
-    private void process(RuleChainDeviceMsg msg) {
-        ToDeviceActorMsg toDeviceActorMsg = msg.getToDeviceActorMsg();
-        ActorRef deviceActor = getOrCreateDeviceActor(toDeviceActorMsg.getDeviceId());
-        RuleActorChain tenantChain = ruleManager.getRuleChain(this.context());
-        RuleActorChain chain = new ComplexRuleActorChain(msg.getRuleChain(), tenantChain);
-        deviceActor.tell(new RuleChainDeviceMsg(toDeviceActorMsg, chain), context().self());
-    }
 
     private ActorRef getOrCreateDeviceActor(DeviceId deviceId) {
         return deviceActors.computeIfAbsent(deviceId, k -> context().actorOf(Props.create(new DeviceActor.ActorCreator(systemContext, tenantId, deviceId))
diff --git a/application/src/main/java/org/thingsboard/server/controller/PluginController.java b/application/src/main/java/org/thingsboard/server/controller/PluginController.java
index 2c69248..ed17600 100644
--- a/application/src/main/java/org/thingsboard/server/controller/PluginController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/PluginController.java
@@ -71,7 +71,7 @@ public class PluginController extends BaseController {
             boolean created = source.getId() == null;
             source.setTenantId(getCurrentUser().getTenantId());
             PluginMetaData plugin = checkNotNull(pluginService.savePlugin(source));
-            actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(),
+            actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(),
                     created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
 
             logEntityAction(plugin.getId(), plugin,
@@ -97,7 +97,7 @@ public class PluginController extends BaseController {
             PluginId pluginId = new PluginId(toUUID(strPluginId));
             PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
             pluginService.activatePluginById(pluginId);
-            actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED);
+            actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.ACTIVATED);
 
             logEntityAction(plugin.getId(), plugin,
                     null,
@@ -123,7 +123,7 @@ public class PluginController extends BaseController {
             PluginId pluginId = new PluginId(toUUID(strPluginId));
             PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
             pluginService.suspendPluginById(pluginId);
-            actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED);
+            actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.SUSPENDED);
 
             logEntityAction(plugin.getId(), plugin,
                     null,
@@ -221,7 +221,7 @@ public class PluginController extends BaseController {
             PluginId pluginId = new PluginId(toUUID(strPluginId));
             PluginMetaData plugin = checkPlugin(pluginService.findPluginById(pluginId));
             pluginService.deletePluginById(pluginId);
-            actorService.onPluginStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED);
+            actorService.onEntityStateChange(plugin.getTenantId(), plugin.getId(), ComponentLifecycleEvent.DELETED);
 
             logEntityAction(pluginId, plugin,
                     null,
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index f24646b..93891ac 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -78,6 +78,9 @@ public class RuleChainController extends BaseController {
             ruleChain.setTenantId(getCurrentUser().getTenantId());
             RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain));
 
+            actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(),
+                    created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
+
             logEntityAction(savedRuleChain.getId(), savedRuleChain,
                     null,
                     created ? ActionType.ADDED : ActionType.UPDATED, null);
@@ -100,6 +103,8 @@ public class RuleChainController extends BaseController {
             RuleChain ruleChain = checkRuleChain(ruleChainMetaData.getRuleChainId());
             RuleChainMetaData savedRuleChainMetaData = checkNotNull(ruleChainService.saveRuleChainMetaData(ruleChainMetaData));
 
+            actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.UPDATED);
+
             logEntityAction(ruleChain.getId(), ruleChain,
                     null,
                     ActionType.UPDATED, null, ruleChainMetaData);
@@ -183,6 +188,8 @@ public class RuleChainController extends BaseController {
             RuleChain ruleChain = checkRuleChain(ruleChainId);
             ruleChainService.deleteRuleChainById(ruleChainId);
 
+            actorService.onEntityStateChange(ruleChain.getTenantId(), ruleChain.getId(), ComponentLifecycleEvent.DELETED);
+
             logEntityAction(ruleChainId, ruleChain,
                     null,
                     ActionType.DELETED, null, strRuleChainId);
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleController.java b/application/src/main/java/org/thingsboard/server/controller/RuleController.java
index e498c8f..9a26902 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleController.java
@@ -73,7 +73,7 @@ public class RuleController extends BaseController {
             boolean created = source.getId() == null;
             source.setTenantId(getCurrentUser().getTenantId());
             RuleMetaData rule = checkNotNull(ruleService.saveRule(source));
-            actorService.onRuleStateChange(rule.getTenantId(), rule.getId(),
+            actorService.onEntityStateChange(rule.getTenantId(), rule.getId(),
                     created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
 
             logEntityAction(rule.getId(), rule,
@@ -99,7 +99,7 @@ public class RuleController extends BaseController {
             RuleId ruleId = new RuleId(toUUID(strRuleId));
             RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
             ruleService.activateRuleById(ruleId);
-            actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED);
+            actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.ACTIVATED);
 
             logEntityAction(rule.getId(), rule,
                     null,
@@ -125,7 +125,7 @@ public class RuleController extends BaseController {
             RuleId ruleId = new RuleId(toUUID(strRuleId));
             RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
             ruleService.suspendRuleById(ruleId);
-            actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED);
+            actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.SUSPENDED);
 
             logEntityAction(rule.getId(), rule,
                     null,
@@ -219,7 +219,7 @@ public class RuleController extends BaseController {
             RuleId ruleId = new RuleId(toUUID(strRuleId));
             RuleMetaData rule = checkRule(ruleService.findRuleById(ruleId));
             ruleService.deleteRuleById(ruleId);
-            actorService.onRuleStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED);
+            actorService.onEntityStateChange(rule.getTenantId(), rule.getId(), ComponentLifecycleEvent.DELETED);
 
             logEntityAction(ruleId, rule,
                     null,
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java
index 34f8c3a..ffd7822 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageDataIterable.java
@@ -20,6 +20,7 @@ import java.util.List;
 import java.util.NoSuchElementException;
 
 import org.thingsboard.server.common.data.SearchTextBased;
+import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.UUIDBased;
 
 public class PageDataIterable<T extends SearchTextBased<? extends UUIDBased>> implements Iterable<T>, Iterator<T> {
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
new file mode 100644
index 0000000..55c131d
--- /dev/null
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/MsgType.java
@@ -0,0 +1,7 @@
+package org.thingsboard.server.common.msg;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+public enum MsgType {
+}
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java
index d48c3fe..b77722e 100644
--- a/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/plugin/ComponentLifecycleMsg.java
@@ -1,12 +1,12 @@
 /**
  * Copyright © 2016-2018 The Thingsboard Authors
- *
+ * <p>
  * Licensed 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
- *
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
  * 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.
@@ -15,14 +15,12 @@
  */
 package org.thingsboard.server.common.msg.plugin;
 
-import lombok.Data;
 import lombok.Getter;
 import lombok.ToString;
-import org.thingsboard.server.common.data.id.PluginId;
-import org.thingsboard.server.common.data.id.RuleId;
-import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.EntityType;
+import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
-import org.thingsboard.server.common.data.plugin.ComponentLifecycleState;
+import org.thingsboard.server.common.data.rule.RuleChain;
 import org.thingsboard.server.common.msg.aware.TenantAwareMsg;
 import org.thingsboard.server.common.msg.cluster.ToAllNodesMsg;
 
@@ -35,31 +33,27 @@ import java.util.Optional;
 public class ComponentLifecycleMsg implements TenantAwareMsg, ToAllNodesMsg {
     @Getter
     private final TenantId tenantId;
-    private final PluginId pluginId;
-    private final RuleId ruleId;
+    @Getter
+    private final EntityId entityId;
     @Getter
     private final ComponentLifecycleEvent event;
 
-    public static ComponentLifecycleMsg forPlugin(TenantId tenantId, PluginId pluginId, ComponentLifecycleEvent event) {
-        return new ComponentLifecycleMsg(tenantId, pluginId, null, event);
-    }
-
-    public static ComponentLifecycleMsg forRule(TenantId tenantId, RuleId ruleId, ComponentLifecycleEvent event) {
-        return new ComponentLifecycleMsg(tenantId, null, ruleId, event);
-    }
-
-    private ComponentLifecycleMsg(TenantId tenantId, PluginId pluginId, RuleId ruleId, ComponentLifecycleEvent event) {
+    public ComponentLifecycleMsg(TenantId tenantId, EntityId entityId, ComponentLifecycleEvent event) {
         this.tenantId = tenantId;
-        this.pluginId = pluginId;
-        this.ruleId = ruleId;
+        this.entityId = entityId;
         this.event = event;
     }
 
     public Optional<PluginId> getPluginId() {
-        return Optional.ofNullable(pluginId);
+        return entityId.getEntityType() == EntityType.PLUGIN ? Optional.of((PluginId) entityId) : Optional.empty();
     }
 
     public Optional<RuleId> getRuleId() {
-        return Optional.ofNullable(ruleId);
+        return entityId.getEntityType() == EntityType.RULE ? Optional.of((RuleId) entityId) : Optional.empty();
+    }
+
+    public Optional<RuleChainId> getRuleChainId() {
+        return entityId.getEntityType() == EntityType.RULE_CHAIN ? Optional.of((RuleChainId) entityId) : Optional.empty();
     }
+
 }
diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java
new file mode 100644
index 0000000..e7668ff
--- /dev/null
+++ b/common/message/src/main/java/org/thingsboard/server/common/msg/TbActorMsg.java
@@ -0,0 +1,10 @@
+package org.thingsboard.server.common.msg;
+
+/**
+ * Created by ashvayka on 15.03.18.
+ */
+public interface TbActorMsg {
+
+    MsgType getMsgType();
+
+}