thingsboard-memoizeit

Rule Chain and Rule Node DAO.

3/12/2018 7:28:16 AM

Changes

Details

diff --git a/application/src/main/data/upgrade/1.5.0/schema_update.cql b/application/src/main/data/upgrade/1.5.0/schema_update.cql
new file mode 100644
index 0000000..826373d
--- /dev/null
+++ b/application/src/main/data/upgrade/1.5.0/schema_update.cql
@@ -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.
+--
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+    ts              bigint,
+    msg             blob,
+	PRIMARY KEY ((node_id, clustered_hash, partition), ts))
+WITH CLUSTERING ORDER BY (ts DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+    msg_id              timeuuid,
+	PRIMARY KEY ((node_id, clustered_hash, partition), msg_id))
+WITH CLUSTERING ORDER BY (msg_id DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+	PRIMARY KEY ((node_id, clustered_hash), partition))
+WITH CLUSTERING ORDER BY (partition DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS  thingsboard.rule_chain (
+    id uuid,
+    tenant_id uuid,
+    name text,
+    search_text text,
+    first_rule_node_id uuid,
+    is_root boolean,
+    configuration text,
+    additional_info text,
+    PRIMARY KEY (id, tenant_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
+    SELECT *
+    from thingsboard.rule_chain
+    WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, search_text, id )
+    WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
+
+CREATE TABLE IF NOT EXISTS  thingsboard.rule_node (
+    id uuid,
+    type text,
+    name text,
+    search_text text,
+    configuration text,
+    additional_info text,
+    PRIMARY KEY (id)
+);
+
diff --git a/application/src/main/data/upgrade/1.5.0/schema_update.sql b/application/src/main/data/upgrade/1.5.0/schema_update.sql
new file mode 100644
index 0000000..12b80b3
--- /dev/null
+++ b/application/src/main/data/upgrade/1.5.0/schema_update.sql
@@ -0,0 +1,35 @@
+--
+-- 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.
+--
+
+CREATE TABLE IF NOT EXISTS rule_chain (
+    id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
+    additional_info varchar,
+    configuration varchar(10000000),
+    name varchar(255),
+    first_rule_node_id varchar(31),
+    is_root boolean,
+    search_text varchar(255),
+    tenant_id varchar(31)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node (
+    id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
+    additional_info varchar,
+    configuration varchar(10000000),
+    type varchar(255),
+    name varchar(255),
+    search_text varchar(255)
+);
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index e765c40..1835a93 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -77,11 +77,16 @@ public class ThingsboardInstallService {
 
                         databaseUpgradeService.upgradeDatabase("1.3.0");
 
-                    case "1.3.1":
+                    case "1.3.1": //NOSONAR, Need to execute gradual upgrade starting from upgradeFromVersion
                         log.info("Upgrading ThingsBoard from version 1.3.1 to 1.4.0 ...");
 
                         databaseUpgradeService.upgradeDatabase("1.3.1");
 
+                    case "1.4.0":
+                        log.info("Upgrading ThingsBoard from version 1.4.0 to 1.5.0 ...");
+
+                        databaseUpgradeService.upgradeDatabase("1.4.0");
+
                         log.info("Updating system data...");
 
                         systemDataLoaderService.deleteSystemWidgetBundle("charts");
diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
index e6826ec..8dd9698 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
@@ -186,6 +186,14 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
                 }
                 log.info("Dashboards restored.");
                 break;
+            case "1.4.0":
+
+                log.info("Updating schema ...");
+                schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_CQL);
+                loadCql(schemaUpdateFile);
+                log.info("Schema updated.");
+
+                break;
             default:
                 throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion);
         }
diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
index cdd3103..412ea8b 100644
--- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
+++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
@@ -97,6 +97,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
                     log.info("Dashboards restored.");
                 }
                 break;
+            case "1.4.0":
+                try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
+                    log.info("Updating schema ...");
+                    schemaUpdateFile = Paths.get(this.dataDir, "upgrade", "1.5.0", SCHEMA_UPDATE_SQL);
+                    String sql = new String(Files.readAllBytes(schemaUpdateFile), Charset.forName("UTF-8"));
+                    conn.createStatement().execute(sql); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
+                    log.info("Schema updated.");
+                }
+                break;
             default:
                 throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
         }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
index 05b558e..d28a53f 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
@@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
  * @author Andrew Shvayka
  */
 public enum EntityType {
-    TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM
+    TENANT, CUSTOMER, USER, RULE, PLUGIN, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE;
 }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
index e45cb91..76b3e33 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
@@ -53,6 +53,10 @@ public class EntityIdFactory {
                 return new AssetId(uuid);
             case ALARM:
                 return new AlarmId(uuid);
+            case RULE_CHAIN:
+                return new RuleChainId(uuid);
+            case RULE_NODE:
+                return new RuleNodeId(uuid);
         }
         throw new IllegalArgumentException("EntityType " + type + " is not supported!");
     }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java
new file mode 100644
index 0000000..a1a5075
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleChainId.java
@@ -0,0 +1,37 @@
+/**
+ * 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.common.data.id;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
+
+import java.util.UUID;
+
+public class RuleChainId extends UUIDBased implements EntityId {
+
+    @JsonCreator
+    public RuleChainId(@JsonProperty("id") UUID id) {
+        super(id);
+    }
+
+    @JsonIgnore
+    @Override
+    public EntityType getEntityType() {
+        return EntityType.RULE_CHAIN;
+    }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java
new file mode 100644
index 0000000..ec5ecce
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/RuleNodeId.java
@@ -0,0 +1,37 @@
+/**
+ * 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.common.data.id;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import org.thingsboard.server.common.data.EntityType;
+
+import java.util.UUID;
+
+public class RuleNodeId extends UUIDBased implements EntityId {
+
+    @JsonCreator
+    public RuleNodeId(@JsonProperty("id") UUID id) {
+        super(id);
+    }
+
+    @JsonIgnore
+    @Override
+    public EntityType getEntityType() {
+        return EntityType.RULE_NODE;
+    }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
new file mode 100644
index 0000000..48bc5de
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java
@@ -0,0 +1,79 @@
+/**
+ * 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.common.data.rule;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.HasName;
+import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Slf4j
+public class RuleChain extends SearchTextBasedWithAdditionalInfo<RuleChainId> implements HasName {
+
+    private static final long serialVersionUID = -5656679015121935465L;
+
+    private TenantId tenantId;
+    private String name;
+    private RuleNodeId firstRuleNodeId;
+    private boolean isRoot;
+    private transient JsonNode configuration;
+    @JsonIgnore
+    private byte[] configurationBytes;
+
+    public RuleChain() {
+        super();
+    }
+
+    public RuleChain(RuleChainId id) {
+        super(id);
+    }
+
+    public RuleChain(RuleChain ruleChain) {
+        super(ruleChain);
+        this.tenantId = ruleChain.getTenantId();
+        this.name = ruleChain.getName();
+        this.firstRuleNodeId = ruleChain.getFirstRuleNodeId();
+        this.isRoot = ruleChain.isRoot();
+        this.setConfiguration(ruleChain.getConfiguration());
+    }
+
+    @Override
+    public String getSearchText() {
+        return getName();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public JsonNode getConfiguration() {
+        return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes);
+    }
+
+    public void setConfiguration(JsonNode data) {
+        setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
+    }
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
new file mode 100644
index 0000000..d044000
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleNode.java
@@ -0,0 +1,74 @@
+/**
+ * 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.common.data.rule;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+import org.thingsboard.server.common.data.HasName;
+import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Slf4j
+public class RuleNode extends SearchTextBasedWithAdditionalInfo<RuleNodeId> implements HasName {
+
+    private static final long serialVersionUID = -5656679015121235465L;
+
+    private String type;
+    private String name;
+    private transient JsonNode configuration;
+    @JsonIgnore
+    private byte[] configurationBytes;
+
+    public RuleNode() {
+        super();
+    }
+
+    public RuleNode(RuleNodeId id) {
+        super(id);
+    }
+
+    public RuleNode(RuleNode ruleNode) {
+        super(ruleNode);
+        this.type = ruleNode.getType();
+        this.name = ruleNode.getName();
+        this.setConfiguration(ruleNode.getConfiguration());
+    }
+
+    @Override
+    public String getSearchText() {
+        return getName();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    public JsonNode getConfiguration() {
+        return SearchTextBasedWithAdditionalInfo.getJson(() -> configuration, () -> configurationBytes);
+    }
+
+    public void setConfiguration(JsonNode data) {
+        setJson(data, json -> this.configuration = json, bytes -> this.configurationBytes = bytes);
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
index c2b55c9..c68c23d 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
@@ -333,6 +333,26 @@ public class ModelConstants {
     public static final String EVENT_BY_ID_VIEW_NAME = "event_by_id";
 
     /**
+     * Cassandra rule chain constants.
+     */
+    public static final String RULE_CHAIN_COLUMN_FAMILY_NAME = "rule_chain";
+    public static final String RULE_CHAIN_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
+    public static final String RULE_CHAIN_NAME_PROPERTY = "name";
+    public static final String RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY = "first_rule_node_id";
+    public static final String RULE_CHAIN_IS_ROOT_PROPERTY = "is_root";
+    public static final String RULE_CHAIN_CONFIGURATION_PROPERTY = "configuration";
+
+    public static final String RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "rule_chain_by_tenant_and_search_text";
+
+    /**
+     * Cassandra rule node constants.
+     */
+    public static final String RULE_NODE_COLUMN_FAMILY_NAME = "rule_node";
+    public static final String RULE_NODE_TYPE_PROPERTY = "type";
+    public static final String RULE_NODE_NAME_PROPERTY = "name";
+    public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration";
+
+    /**
      * Cassandra attributes and timeseries constants.
      */
     public static final String ATTRIBUTES_KV_CF = "attributes_kv_cf";
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java
new file mode 100644
index 0000000..962da0a
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleChainEntity.java
@@ -0,0 +1,165 @@
+/**
+ * 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.dao.model.nosql;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.ClusteringColumn;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.DaoUtil;
+import org.thingsboard.server.dao.model.SearchTextEntity;
+import org.thingsboard.server.dao.model.type.JsonCodec;
+
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Table(name = RULE_CHAIN_COLUMN_FAMILY_NAME)
+@EqualsAndHashCode
+@ToString
+public class RuleChainEntity implements SearchTextEntity<RuleChain> {
+
+    @PartitionKey
+    @Column(name = ID_PROPERTY)
+    private UUID id;
+    @ClusteringColumn
+    @Column(name = RULE_CHAIN_TENANT_ID_PROPERTY)
+    private UUID tenantId;
+    @Column(name = RULE_CHAIN_NAME_PROPERTY)
+    private String name;
+    @Column(name = SEARCH_TEXT_PROPERTY)
+    private String searchText;
+    @Column(name = RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY)
+    private UUID firstRuleNodeId;
+    @Column(name = RULE_CHAIN_IS_ROOT_PROPERTY)
+    private boolean isRoot;
+    @Column(name = RULE_CHAIN_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
+    private JsonNode configuration;
+    @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
+    private JsonNode additionalInfo;
+
+    public RuleChainEntity() {
+    }
+
+    public RuleChainEntity(RuleChain ruleChain) {
+        if (ruleChain.getId() != null) {
+            this.id = ruleChain.getUuidId();
+        }
+        this.tenantId = DaoUtil.getId(ruleChain.getTenantId());
+        this.name = ruleChain.getName();
+        this.searchText = ruleChain.getName();
+        this.firstRuleNodeId = DaoUtil.getId(ruleChain.getFirstRuleNodeId());
+        this.isRoot = ruleChain.isRoot();
+        this.configuration = ruleChain.getConfiguration();
+        this.additionalInfo = ruleChain.getAdditionalInfo();
+    }
+
+    @Override
+    public String getSearchTextSource() {
+        return getSearchText();
+    }
+
+    @Override
+    public void setSearchText(String searchText) {
+        this.searchText = searchText;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public void setId(UUID id) {
+        this.id = id;
+    }
+
+    public UUID getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(UUID tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public UUID getFirstRuleNodeId() {
+        return firstRuleNodeId;
+    }
+
+    public void setFirstRuleNodeId(UUID firstRuleNodeId) {
+        this.firstRuleNodeId = firstRuleNodeId;
+    }
+
+    public boolean isRoot() {
+        return isRoot;
+    }
+
+    public void setRoot(boolean isRoot) {
+        this.isRoot = isRoot;
+    }
+
+    public String getSearchText() {
+        return searchText;
+    }
+
+    public JsonNode getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(JsonNode configuration) {
+        this.configuration = configuration;
+    }
+
+    public JsonNode getAdditionalInfo() {
+        return additionalInfo;
+    }
+
+    public void setAdditionalInfo(JsonNode additionalInfo) {
+        this.additionalInfo = additionalInfo;
+    }
+
+    @Override
+    public RuleChain toData() {
+        RuleChain ruleChain = new RuleChain(new RuleChainId(id));
+        ruleChain.setCreatedTime(UUIDs.unixTimestamp(id));
+        ruleChain.setTenantId(new TenantId(tenantId));
+        ruleChain.setName(name);
+        if (this.firstRuleNodeId != null) {
+            ruleChain.setFirstRuleNodeId(new RuleNodeId(this.firstRuleNodeId));
+        }
+        ruleChain.setRoot(this.isRoot);
+        ruleChain.setConfiguration(this.configuration);
+        ruleChain.setAdditionalInfo(this.additionalInfo);
+        return ruleChain;
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java
new file mode 100644
index 0000000..ba96e4b
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/nosql/RuleNodeEntity.java
@@ -0,0 +1,134 @@
+/**
+ * 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.dao.model.nosql;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.dao.model.SearchTextEntity;
+import org.thingsboard.server.dao.model.type.JsonCodec;
+
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Table(name = RULE_NODE_COLUMN_FAMILY_NAME)
+@EqualsAndHashCode
+@ToString
+public class RuleNodeEntity implements SearchTextEntity<RuleNode> {
+
+    @PartitionKey
+    @Column(name = ID_PROPERTY)
+    private UUID id;
+    @Column(name = RULE_NODE_TYPE_PROPERTY)
+    private String type;
+    @Column(name = RULE_NODE_NAME_PROPERTY)
+    private String name;
+    @Column(name = SEARCH_TEXT_PROPERTY)
+    private String searchText;
+    @Column(name = RULE_NODE_CONFIGURATION_PROPERTY, codec = JsonCodec.class)
+    private JsonNode configuration;
+    @Column(name = ADDITIONAL_INFO_PROPERTY, codec = JsonCodec.class)
+    private JsonNode additionalInfo;
+
+    public RuleNodeEntity() {
+    }
+
+    public RuleNodeEntity(RuleNode ruleNode) {
+        if (ruleNode.getId() != null) {
+            this.id = ruleNode.getUuidId();
+        }
+        this.type = ruleNode.getType();
+        this.name = ruleNode.getName();
+        this.searchText = ruleNode.getName();
+        this.configuration = ruleNode.getConfiguration();
+        this.additionalInfo = ruleNode.getAdditionalInfo();
+    }
+
+    @Override
+    public String getSearchTextSource() {
+        return getSearchText();
+    }
+
+    @Override
+    public void setSearchText(String searchText) {
+        this.searchText = searchText;
+    }
+
+    @Override
+    public UUID getId() {
+        return id;
+    }
+
+    @Override
+    public void setId(UUID id) {
+        this.id = id;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getSearchText() {
+        return searchText;
+    }
+
+    public JsonNode getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(JsonNode configuration) {
+        this.configuration = configuration;
+    }
+
+    public JsonNode getAdditionalInfo() {
+        return additionalInfo;
+    }
+
+    public void setAdditionalInfo(JsonNode additionalInfo) {
+        this.additionalInfo = additionalInfo;
+    }
+
+    @Override
+    public RuleNode toData() {
+        RuleNode ruleNode = new RuleNode(new RuleNodeId(id));
+        ruleNode.setCreatedTime(UUIDs.unixTimestamp(id));
+        ruleNode.setType(this.type);
+        ruleNode.setName(this.name);
+        ruleNode.setConfiguration(this.configuration);
+        ruleNode.setAdditionalInfo(this.additionalInfo);
+        return ruleNode;
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
new file mode 100644
index 0000000..518eda7
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java
@@ -0,0 +1,111 @@
+/**
+ * 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.dao.model.sql;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+import org.thingsboard.server.common.data.UUIDConverter;
+import org.thingsboard.server.common.data.id.RuleChainId;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.id.TenantId;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.DaoUtil;
+import org.thingsboard.server.dao.model.BaseSqlEntity;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.dao.model.SearchTextEntity;
+import org.thingsboard.server.dao.util.mapping.JsonStringType;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@TypeDef(name = "json", typeClass = JsonStringType.class)
+@Table(name = ModelConstants.RULE_CHAIN_COLUMN_FAMILY_NAME)
+public class RuleChainEntity extends BaseSqlEntity<RuleChain> implements SearchTextEntity<RuleChain> {
+
+    @Column(name = ModelConstants.RULE_CHAIN_TENANT_ID_PROPERTY)
+    private String tenantId;
+
+    @Column(name = ModelConstants.RULE_CHAIN_NAME_PROPERTY)
+    private String name;
+
+    @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
+    private String searchText;
+
+    @Column(name = ModelConstants.RULE_CHAIN_FIRST_RULE_NODE_ID_PROPERTY)
+    private String firstRuleNodeId;
+
+    @Column(name = ModelConstants.RULE_CHAIN_IS_ROOT_PROPERTY)
+    private boolean isRoot;
+
+    @Type(type = "json")
+    @Column(name = ModelConstants.RULE_CHAIN_CONFIGURATION_PROPERTY)
+    private JsonNode configuration;
+
+    @Type(type = "json")
+    @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
+    private JsonNode additionalInfo;
+
+    public RuleChainEntity() {
+    }
+
+    public RuleChainEntity(RuleChain ruleChain) {
+        if (ruleChain.getId() != null) {
+            this.setId(ruleChain.getUuidId());
+        }
+        this.tenantId = toString(DaoUtil.getId(ruleChain.getTenantId()));
+        this.name = ruleChain.getName();
+        this.searchText = ruleChain.getName();
+        if (ruleChain.getFirstRuleNodeId() != null) {
+            this.firstRuleNodeId = UUIDConverter.fromTimeUUID(ruleChain.getFirstRuleNodeId().getId());
+        }
+        this.isRoot = ruleChain.isRoot();
+        this.configuration = ruleChain.getConfiguration();
+        this.additionalInfo = ruleChain.getAdditionalInfo();
+    }
+
+    @Override
+    public String getSearchTextSource() {
+        return searchText;
+    }
+
+    @Override
+    public void setSearchText(String searchText) {
+        this.searchText = searchText;
+    }
+
+    @Override
+    public RuleChain toData() {
+        RuleChain ruleChain = new RuleChain(new RuleChainId(getId()));
+        ruleChain.setCreatedTime(UUIDs.unixTimestamp(getId()));
+        ruleChain.setTenantId(new TenantId(toUUID(tenantId)));
+        ruleChain.setName(name);
+        if (firstRuleNodeId != null) {
+            ruleChain.setFirstRuleNodeId(new RuleNodeId(UUIDConverter.fromString(firstRuleNodeId)));
+        }
+        ruleChain.setRoot(isRoot);
+        ruleChain.setConfiguration(configuration);
+        ruleChain.setAdditionalInfo(additionalInfo);
+        return ruleChain;
+    }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.java
new file mode 100644
index 0000000..d960487
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleNodeEntity.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.dao.model.sql;
+
+import com.datastax.driver.core.utils.UUIDs;
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.hibernate.annotations.Type;
+import org.hibernate.annotations.TypeDef;
+import org.thingsboard.server.common.data.id.RuleNodeId;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.dao.model.BaseSqlEntity;
+import org.thingsboard.server.dao.model.ModelConstants;
+import org.thingsboard.server.dao.model.SearchTextEntity;
+import org.thingsboard.server.dao.util.mapping.JsonStringType;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Entity
+@TypeDef(name = "json", typeClass = JsonStringType.class)
+@Table(name = ModelConstants.RULE_NODE_COLUMN_FAMILY_NAME)
+public class RuleNodeEntity extends BaseSqlEntity<RuleNode> implements SearchTextEntity<RuleNode> {
+
+    @Column(name = ModelConstants.RULE_NODE_TYPE_PROPERTY)
+    private String type;
+
+    @Column(name = ModelConstants.RULE_NODE_NAME_PROPERTY)
+    private String name;
+
+    @Column(name = ModelConstants.SEARCH_TEXT_PROPERTY)
+    private String searchText;
+
+    @Type(type = "json")
+    @Column(name = ModelConstants.RULE_NODE_CONFIGURATION_PROPERTY)
+    private JsonNode configuration;
+
+    @Type(type = "json")
+    @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY)
+    private JsonNode additionalInfo;
+
+    public RuleNodeEntity() {
+    }
+
+    public RuleNodeEntity(RuleNode ruleNode) {
+        if (ruleNode.getId() != null) {
+            this.setId(ruleNode.getUuidId());
+        }
+        this.type = ruleNode.getType();
+        this.name = ruleNode.getName();
+        this.searchText = ruleNode.getName();
+        this.configuration = ruleNode.getConfiguration();
+        this.additionalInfo = ruleNode.getAdditionalInfo();
+    }
+
+    @Override
+    public String getSearchTextSource() {
+        return searchText;
+    }
+
+    @Override
+    public void setSearchText(String searchText) {
+        this.searchText = searchText;
+    }
+
+    @Override
+    public RuleNode toData() {
+        RuleNode ruleNode = new RuleNode(new RuleNodeId(getId()));
+        ruleNode.setCreatedTime(UUIDs.unixTimestamp(getId()));
+        ruleNode.setType(type);
+        ruleNode.setName(name);
+        ruleNode.setConfiguration(configuration);
+        ruleNode.setAdditionalInfo(additionalInfo);
+        return ruleNode;
+    }
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java
new file mode 100644
index 0000000..863904b
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleChainDao.java
@@ -0,0 +1,60 @@
+/**
+ * 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.dao.rule;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.DaoUtil;
+import org.thingsboard.server.dao.model.nosql.RuleChainEntity;
+import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
+import org.thingsboard.server.dao.util.NoSqlDao;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static org.thingsboard.server.dao.model.ModelConstants.*;
+
+@Component
+@Slf4j
+@NoSqlDao
+public class CassandraRuleChainDao extends CassandraAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao {
+
+    @Override
+    protected Class<RuleChainEntity> getColumnFamilyClass() {
+        return RuleChainEntity.class;
+    }
+
+    @Override
+    protected String getColumnFamilyName() {
+        return RULE_CHAIN_COLUMN_FAMILY_NAME;
+    }
+
+    @Override
+    public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) {
+        log.debug("Try to find rule chains by tenantId [{}] and pageLink [{}]", tenantId, pageLink);
+        List<RuleChainEntity> ruleChainEntities = findPageWithTextSearch(RULE_CHAIN_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME,
+                Collections.singletonList(eq(RULE_CHAIN_TENANT_ID_PROPERTY, tenantId)),
+                pageLink);
+
+        log.trace("Found rule chains [{}] by tenantId [{}] and pageLink [{}]", ruleChainEntities, tenantId, pageLink);
+        return DaoUtil.convertDataList(ruleChainEntities);
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java
new file mode 100644
index 0000000..9e34531
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/rule/CassandraRuleNodeDao.java
@@ -0,0 +1,42 @@
+/**
+ * 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.dao.rule;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.dao.model.nosql.RuleNodeEntity;
+import org.thingsboard.server.dao.nosql.CassandraAbstractSearchTextDao;
+import org.thingsboard.server.dao.util.NoSqlDao;
+
+import static org.thingsboard.server.dao.model.ModelConstants.RULE_NODE_COLUMN_FAMILY_NAME;
+
+@Component
+@Slf4j
+@NoSqlDao
+public class CassandraRuleNodeDao extends CassandraAbstractSearchTextDao<RuleNodeEntity, RuleNode> implements RuleNodeDao {
+
+    @Override
+    protected Class<RuleNodeEntity> getColumnFamilyClass() {
+        return RuleNodeEntity.class;
+    }
+
+    @Override
+    protected String getColumnFamilyName() {
+        return RULE_NODE_COLUMN_FAMILY_NAME;
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
new file mode 100644
index 0000000..4a7cfae
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java
@@ -0,0 +1,40 @@
+/**
+ * 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.dao.rule;
+
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.Dao;
+
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Created by igor on 3/12/18.
+ */
+public interface RuleChainDao extends Dao<RuleChain> {
+
+    /**
+     * Find rule chains by tenantId and page link.
+     *
+     * @param tenantId the tenantId
+     * @param pageLink the page link
+     * @return the list of rule chain objects
+     */
+    List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink);
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
new file mode 100644
index 0000000..c6c947b
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java
@@ -0,0 +1,28 @@
+/**
+ * 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.dao.rule;
+
+import org.thingsboard.server.common.data.rule.RuleChain;
+
+/**
+ * Created by igor on 3/12/18.
+ */
+public interface RuleChainService {
+
+    RuleChain saveRuleChain(RuleChain ruleChain);
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java
new file mode 100644
index 0000000..7ef4b3e
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeDao.java
@@ -0,0 +1,27 @@
+/**
+ * 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.dao.rule;
+
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.dao.Dao;
+
+/**
+ * Created by igor on 3/12/18.
+ */
+public interface RuleNodeDao extends Dao<RuleNode> {
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
new file mode 100644
index 0000000..fb5d20a
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java
@@ -0,0 +1,66 @@
+/**
+ * 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.dao.sql.rule;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.UUIDConverter;
+import org.thingsboard.server.common.data.page.TextPageLink;
+import org.thingsboard.server.common.data.rule.RuleChain;
+import org.thingsboard.server.dao.DaoUtil;
+import org.thingsboard.server.dao.model.sql.RuleChainEntity;
+import org.thingsboard.server.dao.rule.RuleChainDao;
+import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
+import org.thingsboard.server.dao.util.SqlDao;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+
+import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID_STR;
+
+@Slf4j
+@Component
+@SqlDao
+public class JpaRuleChainDao extends JpaAbstractSearchTextDao<RuleChainEntity, RuleChain> implements RuleChainDao {
+
+    @Autowired
+    private RuleChainRepository ruleChainRepository;
+
+    @Override
+    protected Class getEntityClass() {
+        return RuleChainEntity.class;
+    }
+
+    @Override
+    protected CrudRepository getCrudRepository() {
+        return ruleChainRepository;
+    }
+
+    @Override
+    public List<RuleChain> findRuleChainsByTenantId(UUID tenantId, TextPageLink pageLink) {
+        return DaoUtil.convertDataList(ruleChainRepository
+                .findByTenantId(
+                        UUIDConverter.fromTimeUUID(tenantId),
+                        Objects.toString(pageLink.getTextSearch(), ""),
+                        pageLink.getIdOffset() == null ? NULL_UUID_STR : UUIDConverter.fromTimeUUID(pageLink.getIdOffset()),
+                        new PageRequest(0, pageLink.getLimit())));
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java
new file mode 100644
index 0000000..eec2894
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java
@@ -0,0 +1,46 @@
+/**
+ * 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.dao.sql.rule;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Component;
+import org.thingsboard.server.common.data.rule.RuleNode;
+import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
+import org.thingsboard.server.dao.rule.RuleNodeDao;
+import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao;
+import org.thingsboard.server.dao.util.SqlDao;
+
+@Slf4j
+@Component
+@SqlDao
+public class JpaRuleNodeDao extends JpaAbstractSearchTextDao<RuleNodeEntity, RuleNode> implements RuleNodeDao {
+
+    @Autowired
+    private RuleNodeRepository ruleNodeRepository;
+
+    @Override
+    protected Class getEntityClass() {
+        return RuleNodeEntity.class;
+    }
+
+    @Override
+    protected CrudRepository getCrudRepository() {
+        return ruleNodeRepository;
+    }
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java
new file mode 100644
index 0000000..58d4aac
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java
@@ -0,0 +1,38 @@
+/**
+ * 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.dao.sql.rule;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.data.repository.query.Param;
+import org.thingsboard.server.dao.model.sql.RuleChainEntity;
+import org.thingsboard.server.dao.util.SqlDao;
+
+import java.util.List;
+
+@SqlDao
+public interface RuleChainRepository extends CrudRepository<RuleChainEntity, String> {
+
+    @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " +
+            "AND LOWER(rc.searchText) LIKE LOWER(CONCAT(:searchText, '%')) " +
+            "AND rc.id > :idOffset ORDER BY rc.id")
+    List<RuleChainEntity> findByTenantId(@Param("tenantId") String tenantId,
+                                         @Param("searchText") String searchText,
+                                         @Param("idOffset") String idOffset,
+                                         Pageable pageable);
+
+}
diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java
new file mode 100644
index 0000000..574aedf
--- /dev/null
+++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleNodeRepository.java
@@ -0,0 +1,25 @@
+/**
+ * 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.dao.sql.rule;
+
+import org.springframework.data.repository.CrudRepository;
+import org.thingsboard.server.dao.model.sql.RuleNodeEntity;
+import org.thingsboard.server.dao.util.SqlDao;
+
+@SqlDao
+public interface RuleNodeRepository extends CrudRepository<RuleNodeEntity, String> {
+
+}
diff --git a/dao/src/main/resources/cassandra/schema.cql b/dao/src/main/resources/cassandra/schema.cql
index 0c8a644..6c62c89 100644
--- a/dao/src/main/resources/cassandra/schema.cql
+++ b/dao/src/main/resources/cassandra/schema.cql
@@ -542,55 +542,6 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.event_by_id AS
     PRIMARY KEY ((tenant_id, entity_type, entity_id), id, event_type, event_uid)
     WITH CLUSTERING ORDER BY (id ASC, event_type ASC, event_uid ASC);
 
-CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
-    node_id         timeuuid,
-    clustered_hash    bigint,
-    partition       bigint,
-    ts              bigint,
-    msg             blob,
-	PRIMARY KEY ((node_id, clustered_hash, partition), ts))
-WITH CLUSTERING ORDER BY (ts DESC)
-AND compaction = {
-    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
-    'min_threshold': '5',
-    'base_time_seconds': '43200',
-    'max_window_size_seconds': '43200',
-    'tombstone_threshold': '0.9',
-    'unchecked_tombstone_compaction': 'true'
-};
-
-
-CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
-    node_id         timeuuid,
-    clustered_hash    bigint,
-    partition       bigint,
-    msg_id              timeuuid,
-	PRIMARY KEY ((node_id, clustered_hash, partition), msg_id))
-WITH CLUSTERING ORDER BY (msg_id DESC)
-AND compaction = {
-    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
-    'min_threshold': '5',
-    'base_time_seconds': '43200',
-    'max_window_size_seconds': '43200',
-    'tombstone_threshold': '0.9',
-    'unchecked_tombstone_compaction': 'true'
-};
-
-CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
-    node_id         timeuuid,
-    clustered_hash    bigint,
-    partition       bigint,
-	PRIMARY KEY ((node_id, clustered_hash), partition))
-WITH CLUSTERING ORDER BY (partition DESC)
-AND compaction = {
-    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
-    'min_threshold': '5',
-    'base_time_seconds': '43200',
-    'max_window_size_seconds': '43200',
-    'tombstone_threshold': '0.9',
-    'unchecked_tombstone_compaction': 'true'
-};
-
 CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_entity_id (
   tenant_id timeuuid,
   id timeuuid,
@@ -639,8 +590,6 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_user_id (
   PRIMARY KEY ((tenant_id, user_id), id)
 );
 
-
-
 CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id (
   tenant_id timeuuid,
   id timeuuid,
@@ -664,3 +613,80 @@ CREATE TABLE IF NOT EXISTS thingsboard.audit_log_by_tenant_id_partitions (
   PRIMARY KEY (( tenant_id ), partition)
 ) WITH CLUSTERING ORDER BY ( partition ASC )
 AND compaction = { 'class' :  'LeveledCompactionStrategy'  };
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_queue (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+    ts              bigint,
+    msg             blob,
+	PRIMARY KEY ((node_id, clustered_hash, partition), ts))
+WITH CLUSTERING ORDER BY (ts DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.msg_ack_queue (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+    msg_id              timeuuid,
+	PRIMARY KEY ((node_id, clustered_hash, partition), msg_id))
+WITH CLUSTERING ORDER BY (msg_id DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS thingsboard.processed_msg_partitions (
+    node_id         timeuuid,
+    clustered_hash    bigint,
+    partition       bigint,
+	PRIMARY KEY ((node_id, clustered_hash), partition))
+WITH CLUSTERING ORDER BY (partition DESC)
+AND compaction = {
+    'class': 'org.apache.cassandra.db.compaction.DateTieredCompactionStrategy',
+    'min_threshold': '5',
+    'base_time_seconds': '43200',
+    'max_window_size_seconds': '43200',
+    'tombstone_threshold': '0.9',
+    'unchecked_tombstone_compaction': 'true'
+};
+
+CREATE TABLE IF NOT EXISTS  thingsboard.rule_chain (
+    id uuid,
+    tenant_id uuid,
+    name text,
+    search_text text,
+    first_rule_node_id uuid,
+    is_root boolean,
+    configuration text,
+    additional_info text,
+    PRIMARY KEY (id, tenant_id)
+);
+
+CREATE MATERIALIZED VIEW IF NOT EXISTS thingsboard.rule_chain_by_tenant_and_search_text AS
+    SELECT *
+    from thingsboard.rule_chain
+    WHERE tenant_id IS NOT NULL AND search_text IS NOT NULL AND id IS NOT NULL
+    PRIMARY KEY ( tenant_id, search_text, id )
+    WITH CLUSTERING ORDER BY ( search_text ASC, id DESC );
+
+CREATE TABLE IF NOT EXISTS  thingsboard.rule_node (
+    id uuid,
+    type text,
+    name text,
+    search_text text,
+    configuration text,
+    additional_info text,
+    PRIMARY KEY (id)
+);
\ No newline at end of file
diff --git a/dao/src/main/resources/sql/schema.sql b/dao/src/main/resources/sql/schema.sql
index 9f03dc8..e525007 100644
--- a/dao/src/main/resources/sql/schema.sql
+++ b/dao/src/main/resources/sql/schema.sql
@@ -254,4 +254,24 @@ CREATE TABLE IF NOT EXISTS widgets_bundle (
     search_text varchar(255),
     tenant_id varchar(31),
     title varchar(255)
-);
\ No newline at end of file
+);
+
+CREATE TABLE IF NOT EXISTS rule_chain (
+    id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY,
+    additional_info varchar,
+    configuration varchar(10000000),
+    name varchar(255),
+    first_rule_node_id varchar(31),
+    is_root boolean,
+    search_text varchar(255),
+    tenant_id varchar(31)
+);
+
+CREATE TABLE IF NOT EXISTS rule_node (
+    id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY,
+    additional_info varchar,
+    configuration varchar(10000000),
+    type varchar(255),
+    name varchar(255),
+    search_text varchar(255)
+);