Commit 19d3afa650f6796cfd261bab03c67331e0a601ca
1 parent
0c98ec78
Kafka, RabbitMQ and REST API call extensions + Docker image
Showing
50 changed files
with
2587 additions
and
0 deletions
dao/src/main/java/org/thingsboard/server/dao/cache/PreviousDeviceCredentialsIdKeyGenerator.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.cache; | |
17 | + | |
18 | +import org.springframework.cache.interceptor.KeyGenerator; | |
19 | +import org.thingsboard.server.common.data.security.DeviceCredentials; | |
20 | +import org.thingsboard.server.dao.device.DeviceCredentialsService; | |
21 | + | |
22 | +import java.lang.reflect.Method; | |
23 | + | |
24 | +public class PreviousDeviceCredentialsIdKeyGenerator implements KeyGenerator { | |
25 | + | |
26 | + @Override | |
27 | + public Object generate(Object o, Method method, Object... objects) { | |
28 | + DeviceCredentialsService deviceCredentialsService = (DeviceCredentialsService) o; | |
29 | + DeviceCredentials deviceCredentials = (DeviceCredentials) objects[0]; | |
30 | + if (deviceCredentials.getDeviceId() != null) { | |
31 | + DeviceCredentials oldDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(deviceCredentials.getDeviceId()); | |
32 | + if (oldDeviceCredentials != null) { | |
33 | + return oldDeviceCredentials.getCredentialsId(); | |
34 | + } | |
35 | + } | |
36 | + return null; | |
37 | + } | |
38 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.dao.cache; | |
17 | + | |
18 | +import com.hazelcast.config.Config; | |
19 | +import com.hazelcast.config.DiscoveryStrategyConfig; | |
20 | +import com.hazelcast.config.MapConfig; | |
21 | +import com.hazelcast.config.MaxSizeConfig; | |
22 | +import com.hazelcast.core.Hazelcast; | |
23 | +import com.hazelcast.core.HazelcastInstance; | |
24 | +import com.hazelcast.instance.GroupProperty; | |
25 | +import com.hazelcast.spring.cache.HazelcastCacheManager; | |
26 | +import com.hazelcast.zookeeper.ZookeeperDiscoveryProperties; | |
27 | +import com.hazelcast.zookeeper.ZookeeperDiscoveryStrategyFactory; | |
28 | +import org.springframework.beans.factory.annotation.Value; | |
29 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
30 | +import org.springframework.cache.CacheManager; | |
31 | +import org.springframework.cache.annotation.EnableCaching; | |
32 | +import org.springframework.cache.interceptor.KeyGenerator; | |
33 | +import org.springframework.context.annotation.Bean; | |
34 | +import org.springframework.context.annotation.Configuration; | |
35 | +import org.thingsboard.server.common.data.CacheConstants; | |
36 | + | |
37 | +@Configuration | |
38 | +@EnableCaching | |
39 | +@ConditionalOnProperty(prefix = "cache", value = "enabled", havingValue = "true") | |
40 | +public class ServiceCacheConfiguration { | |
41 | + | |
42 | + private static final String HAZELCAST_CLUSTER_NAME = "hazelcast"; | |
43 | + | |
44 | + @Value("${cache.device_credentials.max_size}") | |
45 | + private Integer deviceCredentialsCacheMaxSize; | |
46 | + @Value("${cache.device_credentials.time_to_live}") | |
47 | + private Integer deviceCredentialsCacheTTL; | |
48 | + | |
49 | + @Value("${zk.enabled}") | |
50 | + private boolean zkEnabled; | |
51 | + @Value("${zk.url}") | |
52 | + private String zkUrl; | |
53 | + @Value("${zk.zk_dir}") | |
54 | + private String zkDir; | |
55 | + | |
56 | + @Bean | |
57 | + public HazelcastInstance hazelcastInstance() { | |
58 | + Config config = new Config(); | |
59 | + | |
60 | + if (zkEnabled) { | |
61 | + config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false); | |
62 | + | |
63 | + config.setProperty(GroupProperty.DISCOVERY_SPI_ENABLED.getName(), Boolean.TRUE.toString()); | |
64 | + DiscoveryStrategyConfig discoveryStrategyConfig = new DiscoveryStrategyConfig(new ZookeeperDiscoveryStrategyFactory()); | |
65 | + discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.ZOOKEEPER_URL.key(), zkUrl); | |
66 | + discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.ZOOKEEPER_PATH.key(), zkDir); | |
67 | + discoveryStrategyConfig.addProperty(ZookeeperDiscoveryProperties.GROUP.key(), HAZELCAST_CLUSTER_NAME); | |
68 | + config.getNetworkConfig().getJoin().getDiscoveryConfig().addDiscoveryStrategyConfig(discoveryStrategyConfig); | |
69 | + } | |
70 | + | |
71 | + MapConfig deviceCredentialsCacheConfig = new MapConfig(CacheConstants.DEVICE_CREDENTIALS_CACHE); | |
72 | + deviceCredentialsCacheConfig.setTimeToLiveSeconds(deviceCredentialsCacheTTL); | |
73 | + deviceCredentialsCacheConfig.setMaxSizeConfig(new MaxSizeConfig(deviceCredentialsCacheMaxSize, MaxSizeConfig.MaxSizePolicy.PER_NODE)); | |
74 | + config.addMapConfig(deviceCredentialsCacheConfig); | |
75 | + | |
76 | + return Hazelcast.newHazelcastInstance(config); | |
77 | + } | |
78 | + | |
79 | + @Bean | |
80 | + public KeyGenerator previousDeviceCredentialsId() { | |
81 | + return new PreviousDeviceCredentialsIdKeyGenerator(); | |
82 | + } | |
83 | + | |
84 | + @Bean | |
85 | + public CacheManager cacheManager() { | |
86 | + return new HazelcastCacheManager(hazelcastInstance()); | |
87 | + } | |
88 | +} | ... | ... |
docker/db-schema.env
0 → 100644
docker/db-schema/Dockerfile
0 → 100644
1 | +# | |
2 | +# Copyright © 2016 The Thingsboard Authors | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | +# Unless required by applicable law or agreed to in writing, software | |
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +# See the License for the specific language governing permissions and | |
14 | +# limitations under the License. | |
15 | +# | |
16 | + | |
17 | +FROM cassandra:3.9 | |
18 | + | |
19 | +ADD install_schema.sh /root/install_schema.sh | |
20 | + | |
21 | +RUN apt-get update \ | |
22 | + && apt-get install -y nmap | |
23 | + | |
24 | +RUN chmod +x /root/install_schema.sh | |
25 | + | |
26 | +WORKDIR /root | ... | ... |
docker/db-schema/install_schema.sh
0 → 100644
1 | +#!/bin/bash | |
2 | +# | |
3 | +# Copyright © 2016 The Thingsboard Authors | |
4 | +# | |
5 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | +# you may not use this file except in compliance with the License. | |
7 | +# You may obtain a copy of the License at | |
8 | +# | |
9 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
10 | +# | |
11 | +# Unless required by applicable law or agreed to in writing, software | |
12 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | +# See the License for the specific language governing permissions and | |
15 | +# limitations under the License. | |
16 | +# | |
17 | + | |
18 | + | |
19 | +until nmap db -p 9042 | grep "9042/tcp open" | |
20 | +do | |
21 | + echo "Wait for Cassandra..." | |
22 | + sleep 10 | |
23 | +done | |
24 | + | |
25 | +if [ "$SKIP_SCHEMA_CREATION" == "false" ]; then | |
26 | + echo "Creating 'Thingsboard' keyspace..." | |
27 | + cqlsh db -f /root/schema.cql | |
28 | + if [ "$?" -eq 0 ]; then | |
29 | + echo "'Thingsboard' keyspace was successfully created!" | |
30 | + else | |
31 | + echo "There were issues while creating 'Thingsboard' keyspace!" | |
32 | + fi | |
33 | +fi | |
34 | + | |
35 | +if [ "$SKIP_SYSTEM_DATA" == "false" ]; then | |
36 | + echo "Adding system data..." | |
37 | + cqlsh db -f /root/system-data.cql | |
38 | + if [ "$?" -eq 0 ]; then | |
39 | + echo "System data was successfully added!" | |
40 | + else | |
41 | + echo "There were issues while adding System data!" | |
42 | + fi | |
43 | +fi | |
44 | + | |
45 | +if [ "$SKIP_DEMO_DATA" == "false" ]; then | |
46 | + echo "Adding demo data..." | |
47 | + cqlsh db -f /root/demo-data.cql | |
48 | + if [ "$?" -eq 0 ]; then | |
49 | + echo "Demo data was successfully added!" | |
50 | + else | |
51 | + echo "There were issues while adding Demo data!" | |
52 | + fi | |
53 | +fi | ... | ... |
docker/deploy.sh
0 → 100755
1 | +#!/bin/bash | |
2 | +# | |
3 | +# Copyright © 2016 The Thingsboard Authors | |
4 | +# | |
5 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | +# you may not use this file except in compliance with the License. | |
7 | +# You may obtain a copy of the License at | |
8 | +# | |
9 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
10 | +# | |
11 | +# Unless required by applicable law or agreed to in writing, software | |
12 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | +# See the License for the specific language governing permissions and | |
15 | +# limitations under the License. | |
16 | +# | |
17 | + | |
18 | + | |
19 | +command='docker-compose -f docker-compose.yml -f docker-compose.random.yml' | |
20 | + | |
21 | +echo "stopping images.." | |
22 | +$command stop | |
23 | + | |
24 | +echo "removing stopped images.." | |
25 | +$command rm -f | |
26 | + | |
27 | +echo "building images.." | |
28 | +$command build | |
29 | + | |
30 | +echo "starting images..." | |
31 | +$command up -d | ... | ... |
docker/deploy_cassandra_zookeeper.sh
0 → 100755
1 | +#!/bin/bash | |
2 | +# | |
3 | +# Copyright © 2016 The Thingsboard Authors | |
4 | +# | |
5 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | +# you may not use this file except in compliance with the License. | |
7 | +# You may obtain a copy of the License at | |
8 | +# | |
9 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
10 | +# | |
11 | +# Unless required by applicable law or agreed to in writing, software | |
12 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | +# See the License for the specific language governing permissions and | |
15 | +# limitations under the License. | |
16 | +# | |
17 | + | |
18 | + | |
19 | +command='docker-compose -f docker-compose.yml -f docker-compose.static.yml' | |
20 | + | |
21 | +echo "stopping images.." | |
22 | +$command stop | |
23 | + | |
24 | +echo "removing stopped images.." | |
25 | +$command rm -f | |
26 | + | |
27 | +echo "building images.." | |
28 | +$command build | |
29 | + | |
30 | +echo "starting cassandra, zookeeper, db-schema images..." | |
31 | +$command up -d cassandra zookeeper db-schema | ... | ... |
docker/docker-compose.random.yml
0 → 100644
1 | +# | |
2 | +# Copyright © 2016 The Thingsboard Authors | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | +# Unless required by applicable law or agreed to in writing, software | |
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +# See the License for the specific language governing permissions and | |
14 | +# limitations under the License. | |
15 | +# | |
16 | + | |
17 | +version: '2' | |
18 | + | |
19 | +services: | |
20 | + cassandra: | |
21 | + ports: | |
22 | + - "9042" | |
23 | + - "9160" | |
24 | + zookeeper: | |
25 | + ports: | |
26 | + - "2181" | ... | ... |
docker/docker-compose.static.yml
0 → 100644
1 | +# | |
2 | +# Copyright © 2016 The Thingsboard Authors | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | +# Unless required by applicable law or agreed to in writing, software | |
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +# See the License for the specific language governing permissions and | |
14 | +# limitations under the License. | |
15 | +# | |
16 | + | |
17 | +version: '2' | |
18 | + | |
19 | +services: | |
20 | + cassandra: | |
21 | + ports: | |
22 | + - "9042:9042" | |
23 | + - "9160:9160" | |
24 | + zookeeper: | |
25 | + ports: | |
26 | + - "2181:2181" | ... | ... |
docker/docker-compose.yml
0 → 100644
1 | +# | |
2 | +# Copyright © 2016 The Thingsboard Authors | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | +# Unless required by applicable law or agreed to in writing, software | |
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +# See the License for the specific language governing permissions and | |
14 | +# limitations under the License. | |
15 | +# | |
16 | + | |
17 | +version: '2' | |
18 | + | |
19 | +services: | |
20 | + thingsboard: | |
21 | + build: thingsboard | |
22 | + ports: | |
23 | + - "8080:8080" | |
24 | + - "1883:1883" | |
25 | + - "5683:5683" | |
26 | + links: | |
27 | + - cassandra:db | |
28 | + - zookeeper:zk | |
29 | + - db-schema:db-schema | |
30 | + volumes: | |
31 | + - "../application/target/thingsboard.deb:/root/thingsboard.deb" | |
32 | + env_file: | |
33 | + - thingsboard.env | |
34 | + entrypoint: ./run_web_app.sh | |
35 | + db-schema: | |
36 | + build: db-schema | |
37 | + links: | |
38 | + - cassandra:db | |
39 | + env_file: | |
40 | + - db-schema.env | |
41 | + volumes: | |
42 | + - "../dao/src/main/resources/schema.cql:/root/schema.cql" | |
43 | + - "../dao/src/main/resources/demo-data.cql:/root/demo-data.cql" | |
44 | + - "../dao/src/main/resources/system-data.cql:/root/system-data.cql" | |
45 | + entrypoint: ./install_schema.sh | |
46 | + cassandra: | |
47 | + image: "cassandra:3.9" | |
48 | + volumes: | |
49 | + - "${CASSANDRA_DATA_DIR}:/var/lib/cassandra" | |
50 | + zookeeper: | |
51 | + image: "zookeeper:3.4.9" | |
52 | + restart: always | ... | ... |
docker/thingsboard.env
0 → 100644
docker/thingsboard/Dockerfile
0 → 100644
1 | +# | |
2 | +# Copyright © 2016 The Thingsboard Authors | |
3 | +# | |
4 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +# you may not use this file except in compliance with the License. | |
6 | +# You may obtain a copy of the License at | |
7 | +# | |
8 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +# | |
10 | +# Unless required by applicable law or agreed to in writing, software | |
11 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +# See the License for the specific language governing permissions and | |
14 | +# limitations under the License. | |
15 | +# | |
16 | + | |
17 | +FROM java:8-jre | |
18 | + | |
19 | +ADD run_web_app.sh /root/run_web_app.sh | |
20 | + | |
21 | +RUN chmod +x /root/run_web_app.sh | |
22 | + | |
23 | +WORKDIR /root | ... | ... |
docker/thingsboard/run_web_app.sh
0 → 100755
1 | +#!/bin/bash | |
2 | +# | |
3 | +# Copyright © 2016 The Thingsboard Authors | |
4 | +# | |
5 | +# Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | +# you may not use this file except in compliance with the License. | |
7 | +# You may obtain a copy of the License at | |
8 | +# | |
9 | +# http://www.apache.org/licenses/LICENSE-2.0 | |
10 | +# | |
11 | +# Unless required by applicable law or agreed to in writing, software | |
12 | +# distributed under the License is distributed on an "AS IS" BASIS, | |
13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | +# See the License for the specific language governing permissions and | |
15 | +# limitations under the License. | |
16 | +# | |
17 | + | |
18 | + | |
19 | +dpkg -i /root/thingsboard.deb | |
20 | + | |
21 | +reachable=0 | |
22 | +while [ $reachable -eq 0 ]; | |
23 | +do | |
24 | + echo "db-schema container is still in progress. waiting until it completed..." | |
25 | + sleep 3 | |
26 | + ping -q -c 1 db-schema > /dev/null 2>&1 | |
27 | + if [ "$?" -ne 0 ]; | |
28 | + then | |
29 | + echo "db-schema container completed!" | |
30 | + reachable=1 | |
31 | + fi | |
32 | +done | |
33 | + | |
34 | +echo "Starting 'Thingsboard' service..." | |
35 | +thingsboard start | |
36 | + | ... | ... |
extensions/extension-kafka/pom.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<!-- | |
3 | + | |
4 | + Copyright © 2016 The Thingsboard Authors | |
5 | + | |
6 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | + you may not use this file except in compliance with the License. | |
8 | + You may obtain a copy of the License at | |
9 | + | |
10 | + http://www.apache.org/licenses/LICENSE-2.0 | |
11 | + | |
12 | + Unless required by applicable law or agreed to in writing, software | |
13 | + distributed under the License is distributed on an "AS IS" BASIS, | |
14 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | + See the License for the specific language governing permissions and | |
16 | + limitations under the License. | |
17 | + | |
18 | +--> | |
19 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
20 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
21 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
22 | + <modelVersion>4.0.0</modelVersion> | |
23 | + <parent> | |
24 | + <groupId>org.thingsboard.server</groupId> | |
25 | + <version>0.0.1-SNAPSHOT</version> | |
26 | + <artifactId>extensions</artifactId> | |
27 | + </parent> | |
28 | + <groupId>org.thingsboard.server.extensions</groupId> | |
29 | + <artifactId>extension-kafka</artifactId> | |
30 | + <packaging>jar</packaging> | |
31 | + | |
32 | + <name>Thingsboard Server Kafka Extension</name> | |
33 | + <url>http://thingsboard.org</url> | |
34 | + | |
35 | + <properties> | |
36 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
37 | + <main.dir>${basedir}/../..</main.dir> | |
38 | + </properties> | |
39 | + | |
40 | + <dependencies> | |
41 | + <dependency> | |
42 | + <groupId>ch.qos.logback</groupId> | |
43 | + <artifactId>logback-core</artifactId> | |
44 | + <scope>provided</scope> | |
45 | + </dependency> | |
46 | + <dependency> | |
47 | + <groupId>ch.qos.logback</groupId> | |
48 | + <artifactId>logback-classic</artifactId> | |
49 | + <scope>provided</scope> | |
50 | + </dependency> | |
51 | + <dependency> | |
52 | + <groupId>org.thingsboard.server</groupId> | |
53 | + <artifactId>extensions-api</artifactId> | |
54 | + <scope>provided</scope> | |
55 | + </dependency> | |
56 | + <dependency> | |
57 | + <groupId>org.thingsboard.server</groupId> | |
58 | + <artifactId>extensions-core</artifactId> | |
59 | + <scope>provided</scope> | |
60 | + </dependency> | |
61 | + <dependency> | |
62 | + <groupId>org.apache.velocity</groupId> | |
63 | + <artifactId>velocity</artifactId> | |
64 | + <scope>provided</scope> | |
65 | + </dependency> | |
66 | + <dependency> | |
67 | + <groupId>org.apache.velocity</groupId> | |
68 | + <artifactId>velocity-tools</artifactId> | |
69 | + <scope>provided</scope> | |
70 | + </dependency> | |
71 | + <dependency> | |
72 | + <groupId>org.apache.kafka</groupId> | |
73 | + <artifactId>kafka_2.10</artifactId> | |
74 | + </dependency> | |
75 | + </dependencies> | |
76 | + <build> | |
77 | + <plugins> | |
78 | + <plugin> | |
79 | + <artifactId>maven-assembly-plugin</artifactId> | |
80 | + <configuration> | |
81 | + <descriptors> | |
82 | + <descriptor>src/assembly/extension.xml</descriptor> | |
83 | + </descriptors> | |
84 | + </configuration> | |
85 | + <executions> | |
86 | + <execution> | |
87 | + <id>make-assembly</id> | |
88 | + <phase>package</phase> | |
89 | + <goals> | |
90 | + <goal>single</goal> | |
91 | + </goals> | |
92 | + </execution> | |
93 | + </executions> | |
94 | + </plugin> | |
95 | + </plugins> | |
96 | + </build> | |
97 | +</project> | |
\ No newline at end of file | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" | |
19 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
20 | + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> | |
21 | + <id>extension</id> | |
22 | + <formats> | |
23 | + <format>jar</format> | |
24 | + </formats> | |
25 | + <includeBaseDirectory>false</includeBaseDirectory> | |
26 | + <dependencySets> | |
27 | + <dependencySet> | |
28 | + <outputDirectory>/</outputDirectory> | |
29 | + <useProjectArtifact>true</useProjectArtifact> | |
30 | + <unpack>true</unpack> | |
31 | + <scope>runtime</scope> | |
32 | + <excludes> | |
33 | + <exclude>org.apache.zookeeper:zookeeper</exclude> | |
34 | + <exclude>org.scala-lang:scala-library</exclude> | |
35 | + <exclude>io.netty:netty</exclude> | |
36 | + </excludes> | |
37 | + </dependencySet> | |
38 | + </dependencySets> | |
39 | +</assembly> | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.action; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.CustomerId; | |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg; | |
22 | + | |
23 | +public class KafkaActionMsg extends AbstractRuleToPluginMsg<KafkaActionPayload> { | |
24 | + | |
25 | + public KafkaActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, KafkaActionPayload payload) { | |
26 | + super(tenantId, customerId, deviceId, payload); | |
27 | + } | |
28 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.action; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.msg.session.MsgType; | |
21 | + | |
22 | +import java.io.Serializable; | |
23 | + | |
24 | +@Data | |
25 | +@Builder | |
26 | +public class KafkaActionPayload implements Serializable { | |
27 | + | |
28 | + private final String topic; | |
29 | + private final String msgBody; | |
30 | + private final boolean sync; | |
31 | + | |
32 | + private final Integer requestId; | |
33 | + private final MsgType msgType; | |
34 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.action; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
20 | +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; | |
21 | +import org.thingsboard.server.extensions.api.component.Action; | |
22 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
23 | +import org.thingsboard.server.extensions.api.rules.RuleContext; | |
24 | +import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction; | |
25 | + | |
26 | +import java.util.Optional; | |
27 | + | |
28 | +@Action(name = "Kafka Plugin Action", descriptor = "KafkaActionDescriptor.json", configuration = KafkaPluginActionConfiguration.class) | |
29 | +@Slf4j | |
30 | +public class KafkaPluginAction extends AbstractTemplatePluginAction<KafkaPluginActionConfiguration> { | |
31 | + | |
32 | + @Override | |
33 | + protected Optional<RuleToPluginMsg<?>> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) { | |
34 | + KafkaActionPayload.KafkaActionPayloadBuilder builder = KafkaActionPayload.builder(); | |
35 | + builder.msgType(payload.getMsgType()); | |
36 | + builder.requestId(payload.getRequestId()); | |
37 | + builder.sync(configuration.isSync()); | |
38 | + builder.topic(configuration.getTopic()); | |
39 | + builder.msgBody(getMsgBody(ctx, msg)); | |
40 | + return Optional.of(new KafkaActionMsg(msg.getTenantId(), | |
41 | + msg.getCustomerId(), | |
42 | + msg.getDeviceId(), | |
43 | + builder.build())); | |
44 | + } | |
45 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.action; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration; | |
20 | + | |
21 | +@Data | |
22 | +public class KafkaPluginActionConfiguration implements TemplateActionConfiguration { | |
23 | + private boolean sync; | |
24 | + private String topic; | |
25 | + private String template; | |
26 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.plugin; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.apache.kafka.clients.producer.Producer; | |
20 | +import org.apache.kafka.clients.producer.ProducerRecord; | |
21 | +import org.thingsboard.server.common.data.id.RuleId; | |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
23 | +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; | |
24 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
25 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
26 | +import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; | |
27 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
28 | +import org.thingsboard.server.extensions.api.rules.RuleException; | |
29 | +import org.thingsboard.server.extensions.kafka.action.KafkaActionMsg; | |
30 | +import org.thingsboard.server.extensions.kafka.action.KafkaActionPayload; | |
31 | + | |
32 | +@RequiredArgsConstructor | |
33 | +public class KafkaMsgHandler implements RuleMsgHandler { | |
34 | + | |
35 | + private final Producer<?, String> producer; | |
36 | + | |
37 | + @Override | |
38 | + public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException { | |
39 | + if (!(msg instanceof KafkaActionMsg)) { | |
40 | + throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!"); | |
41 | + } | |
42 | + KafkaActionPayload payload = ((KafkaActionMsg) msg).getPayload(); | |
43 | + | |
44 | + try { | |
45 | + producer.send(new ProducerRecord<>(payload.getTopic(), payload.getMsgBody()), | |
46 | + (metadata, e) -> { | |
47 | + if (payload.isSync()) { | |
48 | + if (metadata != null) { | |
49 | + ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, | |
50 | + BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId()))); | |
51 | + } else { | |
52 | + ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, | |
53 | + BasicStatusCodeResponse.onError(payload.getMsgType(), payload.getRequestId(), e))); | |
54 | + } | |
55 | + } | |
56 | + }); | |
57 | + } catch (Exception e) { | |
58 | + throw new RuleException(e.getMessage(), e); | |
59 | + } | |
60 | + } | |
61 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.plugin; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.apache.kafka.clients.producer.KafkaProducer; | |
20 | +import org.apache.kafka.clients.producer.Producer; | |
21 | +import org.thingsboard.server.extensions.api.component.Plugin; | |
22 | +import org.thingsboard.server.extensions.api.plugins.AbstractPlugin; | |
23 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
24 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
25 | +import org.thingsboard.server.extensions.kafka.action.KafkaPluginAction; | |
26 | + | |
27 | +import java.util.Properties; | |
28 | + | |
29 | +@Plugin(name = "Kafka Plugin", actions = {KafkaPluginAction.class}, | |
30 | + descriptor = "KafkaPluginDescriptor.json", configuration = KafkaPluginConfiguration.class) | |
31 | +@Slf4j | |
32 | +public class KafkaPlugin extends AbstractPlugin<KafkaPluginConfiguration> { | |
33 | + | |
34 | + private KafkaMsgHandler handler; | |
35 | + private Producer<?, String> producer; | |
36 | + private final Properties properties = new Properties(); | |
37 | + | |
38 | + @Override | |
39 | + public void init(KafkaPluginConfiguration configuration) { | |
40 | + properties.put("bootstrap.servers", configuration.getBootstrapServers()); | |
41 | + properties.put("value.serializer", configuration.getValueSerializer()); | |
42 | + properties.put("key.serializer", configuration.getKeySerializer()); | |
43 | + properties.put("acks", String.valueOf(configuration.getAcks())); | |
44 | + properties.put("retries", configuration.getRetries()); | |
45 | + properties.put("batch.size", configuration.getBatchSize()); | |
46 | + properties.put("linger.ms", configuration.getLinger()); | |
47 | + properties.put("buffer.memory", configuration.getBufferMemory()); | |
48 | + if (configuration.getOtherProperties() != null) { | |
49 | + configuration.getOtherProperties() | |
50 | + .stream().forEach(p -> properties.put(p.getKey(), p.getValue())); | |
51 | + } | |
52 | + init(); | |
53 | + } | |
54 | + | |
55 | + private void init() { | |
56 | + try { | |
57 | + this.producer = new KafkaProducer<>(properties); | |
58 | + this.handler = new KafkaMsgHandler(producer); | |
59 | + } catch (Exception e) { | |
60 | + log.error("Failed to start kafka producer", e); | |
61 | + throw new RuntimeException(e); | |
62 | + } | |
63 | + } | |
64 | + | |
65 | + private void destroy() { | |
66 | + try { | |
67 | + this.handler = null; | |
68 | + this.producer.close(); | |
69 | + } catch (Exception e) { | |
70 | + log.error("Failed to close producer during destroy()", e); | |
71 | + } | |
72 | + } | |
73 | + | |
74 | + @Override | |
75 | + protected RuleMsgHandler getRuleMsgHandler() { | |
76 | + return handler; | |
77 | + } | |
78 | + | |
79 | + @Override | |
80 | + public void resume(PluginContext ctx) { | |
81 | + init(); | |
82 | + } | |
83 | + | |
84 | + @Override | |
85 | + public void suspend(PluginContext ctx) { | |
86 | + destroy(); | |
87 | + } | |
88 | + | |
89 | + @Override | |
90 | + public void stop(PluginContext ctx) { | |
91 | + destroy(); | |
92 | + } | |
93 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka.plugin; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.extensions.core.plugin.KeyValuePluginProperties; | |
20 | + | |
21 | +import java.util.List; | |
22 | + | |
23 | +@Data | |
24 | +public class KafkaPluginConfiguration { | |
25 | + private String bootstrapServers; | |
26 | + private int retries; | |
27 | + private int batchSize; | |
28 | + private int linger; | |
29 | + private int bufferMemory; | |
30 | + private int acks; | |
31 | + private String keySerializer; | |
32 | + private String valueSerializer; | |
33 | + private List<KeyValuePluginProperties> otherProperties; | |
34 | +} | |
\ No newline at end of file | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "Kafka Action Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "sync": { | |
7 | + "title": "Requires delivery confirmation", | |
8 | + "type": "boolean" | |
9 | + }, | |
10 | + "topic": { | |
11 | + "title": "Topic Name", | |
12 | + "type": "string" | |
13 | + }, | |
14 | + "template": { | |
15 | + "title": "Body Template", | |
16 | + "type": "string" | |
17 | + } | |
18 | + }, | |
19 | + "required": [ | |
20 | + "sync", | |
21 | + "topic", | |
22 | + "template" | |
23 | + ] | |
24 | + }, | |
25 | + "form": [ | |
26 | + "sync", | |
27 | + "topic", | |
28 | + { | |
29 | + "key": "template", | |
30 | + "type": "textarea", | |
31 | + "rows": 5 | |
32 | + } | |
33 | + ] | |
34 | +} | |
\ No newline at end of file | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "Kafka Plugin Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "bootstrapServers": { | |
7 | + "title": "Bootstrap Servers", | |
8 | + "type": "string", | |
9 | + "default": "localhost:9092" | |
10 | + }, | |
11 | + "retries": { | |
12 | + "title": "Automatically Retry Times If Fails", | |
13 | + "type": "integer", | |
14 | + "default": 0 | |
15 | + }, | |
16 | + "batchSize": { | |
17 | + "title": "Producer Batch Size On Client", | |
18 | + "type": "integer", | |
19 | + "default": 16384 | |
20 | + }, | |
21 | + "linger": { | |
22 | + "title": "Time To Buffer Locally Before Sending To Kafka Broker (in ms)", | |
23 | + "type": "integer", | |
24 | + "default": 0 | |
25 | + }, | |
26 | + "bufferMemory": { | |
27 | + "title": "Buffer Max Size On Client", | |
28 | + "type": "integer", | |
29 | + "default": 33554432 | |
30 | + }, | |
31 | + "acks": { | |
32 | + "title": "Minimum Number Of Replicas That Must Acknowledge A Write (-1 for 'all')", | |
33 | + "type": "integer", | |
34 | + "default": -1 | |
35 | + }, | |
36 | + "keySerializer": { | |
37 | + "title": "Key Serializer", | |
38 | + "type": "string", | |
39 | + "default": "org.apache.kafka.common.serialization.StringSerializer" | |
40 | + }, | |
41 | + "valueSerializer": { | |
42 | + "title": "Value Serializer", | |
43 | + "type": "string", | |
44 | + "default": "org.apache.kafka.common.serialization.StringSerializer" | |
45 | + }, | |
46 | + "otherProperties": { | |
47 | + "title": "Other Kafka properties", | |
48 | + "type": "array", | |
49 | + "items": { | |
50 | + "title": "Kafka property", | |
51 | + "type": "object", | |
52 | + "properties": { | |
53 | + "key": { | |
54 | + "title": "Key", | |
55 | + "type": "string" | |
56 | + }, | |
57 | + "value": { | |
58 | + "title": "Value", | |
59 | + "type": "string" | |
60 | + } | |
61 | + } | |
62 | + } | |
63 | + } | |
64 | + }, | |
65 | + "required": [ | |
66 | + "bootstrapServers" | |
67 | + ] | |
68 | + }, | |
69 | + "form": [ | |
70 | + "bootstrapServers", | |
71 | + "retries", | |
72 | + "batchSize", | |
73 | + "linger", | |
74 | + "bufferMemory", | |
75 | + "acks", | |
76 | + "keySerializer", | |
77 | + "valueSerializer", | |
78 | + "otherProperties" | |
79 | + ] | |
80 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.kafka; | |
17 | + | |
18 | +import kafka.server.KafkaConfig; | |
19 | +import kafka.server.KafkaServerStartable; | |
20 | +import org.apache.zookeeper.server.ServerConfig; | |
21 | +import org.apache.zookeeper.server.ZooKeeperServerMain; | |
22 | +import org.apache.zookeeper.server.quorum.QuorumPeerConfig; | |
23 | + | |
24 | +import java.io.File; | |
25 | +import java.io.IOException; | |
26 | +import java.util.Properties; | |
27 | + | |
28 | +public class KafkaDemoClient { | |
29 | + | |
30 | + private static final int ZK_PORT = 2222; | |
31 | + private static final String HOSTNAME = "localhost"; | |
32 | + private static final String ZOOKEEPER_CONNECT = HOSTNAME + ":" + ZK_PORT; | |
33 | + private static final int KAFKA_PORT = 9092; | |
34 | + private static final int BROKER_ID = 1; | |
35 | + | |
36 | + public static void main(String[] args) { | |
37 | + try { | |
38 | + startZkLocal(); | |
39 | + startKafkaLocal(); | |
40 | + } catch (Exception e) { | |
41 | + System.out.println("Error running local Kafka broker"); | |
42 | + e.printStackTrace(System.out); | |
43 | + } | |
44 | + } | |
45 | + | |
46 | + private static void startZkLocal() throws Exception { | |
47 | + final File zkTmpDir = File.createTempFile("zookeeper", "test"); | |
48 | + if (zkTmpDir.delete() && zkTmpDir.mkdir()) { | |
49 | + Properties zkProperties = new Properties(); | |
50 | + zkProperties.setProperty("dataDir", zkTmpDir.getAbsolutePath()); | |
51 | + zkProperties.setProperty("clientPort", String.valueOf(ZK_PORT)); | |
52 | + | |
53 | + ServerConfig configuration = new ServerConfig(); | |
54 | + QuorumPeerConfig quorumConfiguration = new QuorumPeerConfig(); | |
55 | + quorumConfiguration.parseProperties(zkProperties); | |
56 | + configuration.readFrom(quorumConfiguration); | |
57 | + | |
58 | + new Thread() { | |
59 | + public void run() { | |
60 | + try { | |
61 | + new ZooKeeperServerMain().runFromConfig(configuration); | |
62 | + } catch (IOException e) { | |
63 | + System.out.println("Start of Local ZooKeeper Failed"); | |
64 | + e.printStackTrace(System.err); | |
65 | + } | |
66 | + } | |
67 | + }.start(); | |
68 | + } else { | |
69 | + System.out.println("Failed to delete or create data dir for Zookeeper"); | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + private static void startKafkaLocal() { | |
74 | + Properties kafkaProperties = new Properties(); | |
75 | + kafkaProperties.setProperty("host.name", HOSTNAME); | |
76 | + kafkaProperties.setProperty("port", String.valueOf(KAFKA_PORT)); | |
77 | + kafkaProperties.setProperty("broker.id", String.valueOf(BROKER_ID)); | |
78 | + kafkaProperties.setProperty("zookeeper.connect", ZOOKEEPER_CONNECT); | |
79 | + KafkaConfig kafkaConfig = new KafkaConfig(kafkaProperties); | |
80 | + KafkaServerStartable kafka = new KafkaServerStartable(kafkaConfig); | |
81 | + kafka.startup(); | |
82 | + } | |
83 | +} | |
\ No newline at end of file | ... | ... |
extensions/extension-rabbitmq/pom.xml
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
19 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
20 | + <modelVersion>4.0.0</modelVersion> | |
21 | + <parent> | |
22 | + <groupId>org.thingsboard.server</groupId> | |
23 | + <version>0.0.1-SNAPSHOT</version> | |
24 | + <artifactId>extensions</artifactId> | |
25 | + </parent> | |
26 | + <groupId>org.thingsboard.server.extensions</groupId> | |
27 | + <artifactId>extension-rabbitmq</artifactId> | |
28 | + <packaging>jar</packaging> | |
29 | + | |
30 | + <name>Thingsboard Server RabbitMQ Extension</name> | |
31 | + <url>http://thingsboard.org</url> | |
32 | + | |
33 | + <properties> | |
34 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
35 | + <main.dir>${basedir}/../..</main.dir> | |
36 | + </properties> | |
37 | + | |
38 | + <dependencies> | |
39 | + <dependency> | |
40 | + <groupId>com.rabbitmq</groupId> | |
41 | + <artifactId>amqp-client</artifactId> | |
42 | + </dependency> | |
43 | + <dependency> | |
44 | + <groupId>org.thingsboard.server</groupId> | |
45 | + <artifactId>extensions-api</artifactId> | |
46 | + <scope>provided</scope> | |
47 | + </dependency> | |
48 | + <dependency> | |
49 | + <groupId>org.thingsboard.server</groupId> | |
50 | + <artifactId>extensions-core</artifactId> | |
51 | + <scope>provided</scope> | |
52 | + </dependency> | |
53 | + <dependency> | |
54 | + <groupId>org.apache.velocity</groupId> | |
55 | + <artifactId>velocity</artifactId> | |
56 | + <scope>provided</scope> | |
57 | + </dependency> | |
58 | + <dependency> | |
59 | + <groupId>org.apache.velocity</groupId> | |
60 | + <artifactId>velocity-tools</artifactId> | |
61 | + <scope>provided</scope> | |
62 | + </dependency> | |
63 | + <dependency> | |
64 | + <groupId>org.springframework.boot</groupId> | |
65 | + <artifactId>spring-boot-starter-web</artifactId> | |
66 | + <scope>provided</scope> | |
67 | + </dependency> | |
68 | + <dependency> | |
69 | + <groupId>org.springframework</groupId> | |
70 | + <artifactId>spring-context-support</artifactId> | |
71 | + <scope>provided</scope> | |
72 | + </dependency> | |
73 | + <dependency> | |
74 | + <groupId>org.slf4j</groupId> | |
75 | + <artifactId>slf4j-api</artifactId> | |
76 | + <scope>provided</scope> | |
77 | + </dependency> | |
78 | + <dependency> | |
79 | + <groupId>org.slf4j</groupId> | |
80 | + <artifactId>log4j-over-slf4j</artifactId> | |
81 | + <scope>provided</scope> | |
82 | + </dependency> | |
83 | + <dependency> | |
84 | + <groupId>ch.qos.logback</groupId> | |
85 | + <artifactId>logback-core</artifactId> | |
86 | + <scope>provided</scope> | |
87 | + </dependency> | |
88 | + <dependency> | |
89 | + <groupId>ch.qos.logback</groupId> | |
90 | + <artifactId>logback-classic</artifactId> | |
91 | + <scope>provided</scope> | |
92 | + </dependency> | |
93 | + <dependency> | |
94 | + <groupId>junit</groupId> | |
95 | + <artifactId>junit</artifactId> | |
96 | + <scope>test</scope> | |
97 | + </dependency> | |
98 | + <dependency> | |
99 | + <groupId>org.mockito</groupId> | |
100 | + <artifactId>mockito-all</artifactId> | |
101 | + <scope>test</scope> | |
102 | + </dependency> | |
103 | + </dependencies> | |
104 | + | |
105 | + <build> | |
106 | + <plugins> | |
107 | + <plugin> | |
108 | + <groupId>org.apache.maven.plugins</groupId> | |
109 | + <artifactId>maven-dependency-plugin</artifactId> | |
110 | + </plugin> | |
111 | + <plugin> | |
112 | + <groupId>org.xolstice.maven.plugins</groupId> | |
113 | + <artifactId>protobuf-maven-plugin</artifactId> | |
114 | + </plugin> | |
115 | + <plugin> | |
116 | + <groupId>org.codehaus.mojo</groupId> | |
117 | + <artifactId>build-helper-maven-plugin</artifactId> | |
118 | + </plugin> | |
119 | + <plugin> | |
120 | + <artifactId>maven-assembly-plugin</artifactId> | |
121 | + <configuration> | |
122 | + <descriptors> | |
123 | + <descriptor>src/assembly/extension.xml</descriptor> | |
124 | + </descriptors> | |
125 | + </configuration> | |
126 | + <executions> | |
127 | + <execution> | |
128 | + <id>make-assembly</id> | |
129 | + <phase>package</phase> | |
130 | + <goals> | |
131 | + <goal>single</goal> | |
132 | + </goals> | |
133 | + </execution> | |
134 | + </executions> | |
135 | + </plugin> | |
136 | + </plugins> | |
137 | + </build> | |
138 | + | |
139 | +</project> | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" | |
19 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
20 | + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> | |
21 | + <id>extension</id> | |
22 | + <formats> | |
23 | + <format>jar</format> | |
24 | + </formats> | |
25 | + <includeBaseDirectory>false</includeBaseDirectory> | |
26 | + <dependencySets> | |
27 | + <dependencySet> | |
28 | + <outputDirectory>/</outputDirectory> | |
29 | + <useProjectArtifact>true</useProjectArtifact> | |
30 | + <unpack>true</unpack> | |
31 | + <scope>runtime</scope> | |
32 | + </dependencySet> | |
33 | + </dependencySets> | |
34 | +</assembly> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.action; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.CustomerId; | |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg; | |
22 | + | |
23 | +/** | |
24 | + * @author Andrew Shvayka | |
25 | + */ | |
26 | +public class RabbitMqActionMsg extends AbstractRuleToPluginMsg<RabbitMqActionPayload> { | |
27 | + | |
28 | + public RabbitMqActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, RabbitMqActionPayload payload) { | |
29 | + super(tenantId, customerId, deviceId, payload); | |
30 | + } | |
31 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.action; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Data; | |
20 | +import org.thingsboard.server.common.msg.session.MsgType; | |
21 | + | |
22 | +import java.io.Serializable; | |
23 | + | |
24 | +/** | |
25 | + * @author Andrew Shvayka | |
26 | + */ | |
27 | +@Data | |
28 | +@Builder | |
29 | +public class RabbitMqActionPayload implements Serializable { | |
30 | + | |
31 | + private final String exchange; | |
32 | + private final String queueName; | |
33 | + private final String messageProperties; | |
34 | + private final String payload; | |
35 | + | |
36 | + private final boolean sync; | |
37 | + private final Integer requestId; | |
38 | + private final MsgType msgType; | |
39 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.action; | |
17 | + | |
18 | +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
19 | +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; | |
20 | +import org.thingsboard.server.extensions.api.component.Action; | |
21 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
22 | +import org.thingsboard.server.extensions.api.rules.RuleContext; | |
23 | +import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction; | |
24 | + | |
25 | +import java.util.Optional; | |
26 | + | |
27 | +/** | |
28 | + * @author Andrew Shvayka | |
29 | + */ | |
30 | +@Action(name = "RabbitMQ Plugin Action", | |
31 | + descriptor = "RabbitMqActionDescriptor.json", configuration = RabbitMqPluginActionConfiguration.class) | |
32 | +public class RabbitMqPluginAction extends AbstractTemplatePluginAction<RabbitMqPluginActionConfiguration> { | |
33 | + | |
34 | + @Override | |
35 | + protected Optional<RuleToPluginMsg<?>> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) { | |
36 | + RabbitMqActionPayload.RabbitMqActionPayloadBuilder builder = RabbitMqActionPayload.builder(); | |
37 | + builder.sync(configuration.isSync()); | |
38 | + builder.exchange(configuration.getExchange()); | |
39 | + builder.queueName(configuration.getQueueName()); | |
40 | + builder.messageProperties(configuration.getMessageProperties()[0]); | |
41 | + builder.msgType(payload.getMsgType()); | |
42 | + builder.requestId(payload.getRequestId()); | |
43 | + builder.payload(getMsgBody(ctx, msg)); | |
44 | + return Optional.of(new RabbitMqActionMsg(msg.getTenantId(), | |
45 | + msg.getCustomerId(), | |
46 | + msg.getDeviceId(), | |
47 | + builder.build())); | |
48 | + } | |
49 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.action; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration; | |
20 | + | |
21 | +/** | |
22 | + * @author Andrew Shvayka | |
23 | + */ | |
24 | +@Data | |
25 | +public class RabbitMqPluginActionConfiguration implements TemplateActionConfiguration{ | |
26 | + | |
27 | + private boolean sync; | |
28 | + private String exchange; | |
29 | + private String queueName; | |
30 | + private String[] messageProperties; | |
31 | + private String template; | |
32 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.plugin; | |
17 | + | |
18 | +import com.rabbitmq.client.AMQP; | |
19 | +import com.rabbitmq.client.Channel; | |
20 | +import com.rabbitmq.client.MessageProperties; | |
21 | +import lombok.RequiredArgsConstructor; | |
22 | +import org.thingsboard.server.common.data.id.RuleId; | |
23 | +import org.thingsboard.server.common.data.id.TenantId; | |
24 | +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; | |
25 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
26 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
27 | +import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; | |
28 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
29 | +import org.thingsboard.server.extensions.api.rules.RuleException; | |
30 | +import org.thingsboard.server.extensions.rabbitmq.action.RabbitMqActionMsg; | |
31 | +import org.thingsboard.server.extensions.rabbitmq.action.RabbitMqActionPayload; | |
32 | + | |
33 | +import java.io.IOException; | |
34 | +import java.nio.charset.Charset; | |
35 | + | |
36 | +/** | |
37 | + * @author Andrew Shvayka | |
38 | + */ | |
39 | +@RequiredArgsConstructor | |
40 | +public class RabbitMqMsgHandler implements RuleMsgHandler { | |
41 | + private static final Charset UTF8 = Charset.forName("UTF-8"); | |
42 | + | |
43 | + private final Channel channel; | |
44 | + | |
45 | + @Override | |
46 | + public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException { | |
47 | + if (!(msg instanceof RabbitMqActionMsg)) { | |
48 | + throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!"); | |
49 | + } | |
50 | + RabbitMqActionPayload payload = ((RabbitMqActionMsg) msg).getPayload(); | |
51 | + AMQP.BasicProperties properties = convert(payload.getMessageProperties()); | |
52 | + try { | |
53 | + channel.basicPublish( | |
54 | + payload.getExchange() != null ? payload.getExchange() : "", | |
55 | + payload.getQueueName(), | |
56 | + properties, | |
57 | + payload.getPayload().getBytes(UTF8)); | |
58 | + if (payload.isSync()) { | |
59 | + ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, | |
60 | + BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId()))); | |
61 | + } | |
62 | + } catch (IOException e) { | |
63 | + throw new RuleException(e.getMessage(), e); | |
64 | + } | |
65 | + } | |
66 | + | |
67 | + private static AMQP.BasicProperties convert(String name) throws RuleException { | |
68 | + switch (name) { | |
69 | + case "BASIC": | |
70 | + return MessageProperties.BASIC; | |
71 | + case "TEXT_PLAIN": | |
72 | + return MessageProperties.TEXT_PLAIN; | |
73 | + case "MINIMAL_BASIC": | |
74 | + return MessageProperties.MINIMAL_BASIC; | |
75 | + case "MINIMAL_PERSISTENT_BASIC": | |
76 | + return MessageProperties.MINIMAL_PERSISTENT_BASIC; | |
77 | + case "PERSISTENT_BASIC": | |
78 | + return MessageProperties.PERSISTENT_BASIC; | |
79 | + case "PERSISTENT_TEXT_PLAIN": | |
80 | + return MessageProperties.PERSISTENT_TEXT_PLAIN; | |
81 | + default: | |
82 | + throw new RuleException("Message Properties: '" + name + "' is undefined!"); | |
83 | + } | |
84 | + } | |
85 | + | |
86 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.plugin; | |
17 | + | |
18 | +import com.rabbitmq.client.Connection; | |
19 | +import com.rabbitmq.client.ConnectionFactory; | |
20 | +import lombok.extern.slf4j.Slf4j; | |
21 | +import org.apache.commons.lang.StringUtils; | |
22 | +import org.thingsboard.server.extensions.api.component.Plugin; | |
23 | +import org.thingsboard.server.extensions.api.plugins.AbstractPlugin; | |
24 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
25 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
26 | +import org.thingsboard.server.extensions.rabbitmq.action.RabbitMqPluginAction; | |
27 | + | |
28 | +import java.io.IOException; | |
29 | +import java.util.concurrent.TimeoutException; | |
30 | +import java.util.function.Consumer; | |
31 | +import java.util.stream.Collectors; | |
32 | + | |
33 | +/** | |
34 | + * @author Andrew Shvayka | |
35 | + */ | |
36 | +@Plugin(name = "RabbitMQ Plugin", actions = {RabbitMqPluginAction.class}, | |
37 | +descriptor = "RabbitMqPluginDescriptor.json", configuration = RabbitMqPluginConfiguration.class) | |
38 | +@Slf4j | |
39 | +public class RabbitMqPlugin extends AbstractPlugin<RabbitMqPluginConfiguration> { | |
40 | + | |
41 | + private ConnectionFactory factory; | |
42 | + private Connection connection; | |
43 | + private RabbitMqMsgHandler handler; | |
44 | + | |
45 | + @Override | |
46 | + public void init(RabbitMqPluginConfiguration configuration) { | |
47 | + factory = new ConnectionFactory(); | |
48 | + factory.setHost(configuration.getHost()); | |
49 | + factory.setPort(configuration.getPort()); | |
50 | + set(configuration.getVirtualHost(), factory::setVirtualHost); | |
51 | + set(configuration.getUserName(), factory::setUsername); | |
52 | + set(configuration.getPassword(), factory::setPassword); | |
53 | + set(configuration.getAutomaticRecoveryEnabled(), factory::setAutomaticRecoveryEnabled); | |
54 | + set(configuration.getConnectionTimeout(), factory::setConnectionTimeout); | |
55 | + set(configuration.getHandshakeTimeout(), factory::setHandshakeTimeout); | |
56 | + set(configuration.getClientProperties(), props -> { | |
57 | + factory.setClientProperties(props.stream().collect(Collectors.toMap( | |
58 | + RabbitMqPluginConfiguration.RabbitMqPluginProperties::getKey, | |
59 | + RabbitMqPluginConfiguration.RabbitMqPluginProperties::getValue))); | |
60 | + }); | |
61 | + | |
62 | + init(); | |
63 | + } | |
64 | + | |
65 | + private <T> void set(T source, Consumer<T> setter) { | |
66 | + if (source != null && !StringUtils.isEmpty(source.toString())) { | |
67 | + setter.accept(source); | |
68 | + } | |
69 | + } | |
70 | + | |
71 | + private void init() { | |
72 | + try { | |
73 | + this.connection = factory.newConnection(); | |
74 | + this.handler = new RabbitMqMsgHandler(connection.createChannel()); | |
75 | + } catch (IOException | TimeoutException e) { | |
76 | + throw new RuntimeException(e); | |
77 | + } | |
78 | + } | |
79 | + | |
80 | + private void destroy() { | |
81 | + try { | |
82 | + this.handler = null; | |
83 | + this.connection.close(); | |
84 | + } catch (Exception e) { | |
85 | + log.info("Failed to close connection during destroy()", e); | |
86 | + } | |
87 | + } | |
88 | + | |
89 | + @Override | |
90 | + protected RuleMsgHandler getRuleMsgHandler() { | |
91 | + return handler; | |
92 | + } | |
93 | + | |
94 | + @Override | |
95 | + public void resume(PluginContext ctx) { | |
96 | + init(); | |
97 | + } | |
98 | + | |
99 | + @Override | |
100 | + public void suspend(PluginContext ctx) { | |
101 | + destroy(); | |
102 | + } | |
103 | + | |
104 | + @Override | |
105 | + public void stop(PluginContext ctx) { | |
106 | + destroy(); | |
107 | + } | |
108 | + | |
109 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq.plugin; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.List; | |
21 | + | |
22 | +/** | |
23 | + * @author Andrew Shvayka | |
24 | + */ | |
25 | +@Data | |
26 | +public class RabbitMqPluginConfiguration { | |
27 | + private String host; | |
28 | + private int port; | |
29 | + private String virtualHost; | |
30 | + | |
31 | + private String userName; | |
32 | + private String password; | |
33 | + | |
34 | + private Boolean automaticRecoveryEnabled; | |
35 | + | |
36 | + private Integer connectionTimeout; | |
37 | + private Integer handshakeTimeout; | |
38 | + | |
39 | + private List<RabbitMqPluginProperties> clientProperties; | |
40 | + | |
41 | + @Data | |
42 | + public static class RabbitMqPluginProperties { | |
43 | + private String key; | |
44 | + private String value; | |
45 | + } | |
46 | + | |
47 | +} | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "RabbitMQ Action Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "sync": { | |
7 | + "title": "Requires delivery confirmation", | |
8 | + "type": "boolean" | |
9 | + }, | |
10 | + "exchange": { | |
11 | + "title": "Exchange", | |
12 | + "type": "string", | |
13 | + "default": "" | |
14 | + }, | |
15 | + "queueName": { | |
16 | + "title": "Queue Name", | |
17 | + "type": "string" | |
18 | + }, | |
19 | + "messageProperties": { | |
20 | + "title": "Message properties", | |
21 | + "type": "array", | |
22 | + "minItems" : 1, | |
23 | + "items": [ | |
24 | + { | |
25 | + "value": "BASIC", | |
26 | + "label": "BASIC" | |
27 | + }, | |
28 | + { | |
29 | + "value": "MINIMAL_BASIC", | |
30 | + "label": "MINIMAL_BASIC" | |
31 | + }, | |
32 | + { | |
33 | + "value": "MINIMAL_PERSISTENT_BASIC", | |
34 | + "label": "MINIMAL_PERSISTENT_BASIC" | |
35 | + }, | |
36 | + { | |
37 | + "value": "PERSISTENT_BASIC", | |
38 | + "label": "PERSISTENT_BASIC" | |
39 | + }, | |
40 | + { | |
41 | + "value": "PERSISTENT_TEXT_PLAIN", | |
42 | + "label": "PERSISTENT_TEXT_PLAIN" | |
43 | + }, | |
44 | + { | |
45 | + "value": "TEXT_PLAIN", | |
46 | + "label": "TEXT_PLAIN" | |
47 | + } | |
48 | + ], | |
49 | + "uniqueItems": true | |
50 | + }, | |
51 | + "template": { | |
52 | + "title": "Body Template", | |
53 | + "type": "string" | |
54 | + } | |
55 | + }, | |
56 | + "required": [ | |
57 | + "sync", | |
58 | + "queueName", | |
59 | + "messageProperties", | |
60 | + "template" | |
61 | + ] | |
62 | + }, | |
63 | + "form": [ | |
64 | + "sync", | |
65 | + "exchange", | |
66 | + "queueName", | |
67 | + { | |
68 | + "key": "messageProperties", | |
69 | + "type": "rc-select", | |
70 | + "multiple": false | |
71 | + }, | |
72 | + { | |
73 | + "key": "template", | |
74 | + "type": "textarea", | |
75 | + "rows": 5 | |
76 | + } | |
77 | + ] | |
78 | +} | |
\ No newline at end of file | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "RabbitMQ Plugin Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "host": { | |
7 | + "title": "Host", | |
8 | + "type": "string" | |
9 | + }, | |
10 | + "port": { | |
11 | + "title": "Port", | |
12 | + "type": "integer", | |
13 | + "default": 5672, | |
14 | + "minimum": 0, | |
15 | + "maximum": 65536 | |
16 | + }, | |
17 | + "virtualHost": { | |
18 | + "title": "Virtual Host", | |
19 | + "type": "string" | |
20 | + }, | |
21 | + "userName": { | |
22 | + "title": "Username", | |
23 | + "type": "string" | |
24 | + }, | |
25 | + "password": { | |
26 | + "title": "Password", | |
27 | + "type": "string" | |
28 | + }, | |
29 | + "automaticRecoveryEnabled": { | |
30 | + "title": "Automatic Recovery Enabled", | |
31 | + "type": "boolean" | |
32 | + }, | |
33 | + "connectionTimeout": { | |
34 | + "title": "Connection Timeout", | |
35 | + "type": "integer" | |
36 | + }, | |
37 | + "handshakeTimeout": { | |
38 | + "title": "Handshake Timeout", | |
39 | + "type": "integer" | |
40 | + }, | |
41 | + "clientProperties": { | |
42 | + "title": "Client properties", | |
43 | + "type": "array", | |
44 | + "items": { | |
45 | + "title": "Client property", | |
46 | + "type": "object", | |
47 | + "properties": { | |
48 | + "key": { | |
49 | + "title": "Key", | |
50 | + "type": "string" | |
51 | + }, | |
52 | + "value": { | |
53 | + "title": "Value", | |
54 | + "type": "string" | |
55 | + } | |
56 | + } | |
57 | + } | |
58 | + } | |
59 | + }, | |
60 | + "required": [ | |
61 | + "host", | |
62 | + "port" | |
63 | + ] | |
64 | + }, | |
65 | + "form": [ | |
66 | + "host", | |
67 | + "port", | |
68 | + "virtualHost", | |
69 | + "userName", | |
70 | + { | |
71 | + "key": "password", | |
72 | + "type": "password" | |
73 | + }, | |
74 | + "automaticRecoveryEnabled", | |
75 | + "connectionTimeout", | |
76 | + "handshakeTimeout", | |
77 | + "clientProperties" | |
78 | + ] | |
79 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rabbitmq; | |
17 | + | |
18 | +import com.rabbitmq.client.*; | |
19 | + | |
20 | +import java.io.IOException; | |
21 | + | |
22 | +/** | |
23 | + * @author Andrew Shvayka | |
24 | + */ | |
25 | +public class DemoClient { | |
26 | + | |
27 | + private static final String HOST = "localhost"; | |
28 | + private static final String USERNAME = "guest"; | |
29 | + private static final String PASSWORD = "guest"; | |
30 | + private static final String QUEUE_NAME = "queue"; | |
31 | + | |
32 | + | |
33 | + public static void main(String[] argv) throws Exception { | |
34 | + | |
35 | + ConnectionFactory factory = new ConnectionFactory(); | |
36 | + factory.setHost(HOST); | |
37 | + factory.setUsername(USERNAME); | |
38 | + factory.setPassword(PASSWORD); | |
39 | + | |
40 | + Connection connection = factory.newConnection(); | |
41 | + Channel channel = connection.createChannel(); | |
42 | + | |
43 | + channel.queueDeclare(QUEUE_NAME, false, false, false, null); | |
44 | + System.out.println(" [*] Waiting for messages."); | |
45 | + Consumer consumer = new DefaultConsumer(channel) { | |
46 | + @Override | |
47 | + public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) | |
48 | + throws IOException { | |
49 | + String message = new String(body, "UTF-8"); | |
50 | + System.out.println(" [x] Received '" + message + "'"); | |
51 | + } | |
52 | + }; | |
53 | + channel.basicConsume(QUEUE_NAME, true, consumer); | |
54 | + | |
55 | + } | |
56 | +} | ... | ... |
extensions/extension-rest-api-call/pom.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | |
2 | +<!-- | |
3 | + | |
4 | + Copyright © 2016 The Thingsboard Authors | |
5 | + | |
6 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
7 | + you may not use this file except in compliance with the License. | |
8 | + You may obtain a copy of the License at | |
9 | + | |
10 | + http://www.apache.org/licenses/LICENSE-2.0 | |
11 | + | |
12 | + Unless required by applicable law or agreed to in writing, software | |
13 | + distributed under the License is distributed on an "AS IS" BASIS, | |
14 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
15 | + See the License for the specific language governing permissions and | |
16 | + limitations under the License. | |
17 | + | |
18 | +--> | |
19 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | |
20 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
21 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
22 | + <modelVersion>4.0.0</modelVersion> | |
23 | + <parent> | |
24 | + <groupId>org.thingsboard.server</groupId> | |
25 | + <version>0.0.1-SNAPSHOT</version> | |
26 | + <artifactId>extensions</artifactId> | |
27 | + </parent> | |
28 | + <groupId>org.thingsboard.server.extensions</groupId> | |
29 | + <artifactId>extension-rest-api-call</artifactId> | |
30 | + <packaging>jar</packaging> | |
31 | + | |
32 | + <name>Thingsboard Server REST API Call Extension</name> | |
33 | + <url>http://thingsboard.org</url> | |
34 | + | |
35 | + <properties> | |
36 | + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
37 | + <main.dir>${basedir}/../..</main.dir> | |
38 | + </properties> | |
39 | + | |
40 | + <dependencies> | |
41 | + <dependency> | |
42 | + <groupId>ch.qos.logback</groupId> | |
43 | + <artifactId>logback-core</artifactId> | |
44 | + <scope>provided</scope> | |
45 | + </dependency> | |
46 | + <dependency> | |
47 | + <groupId>ch.qos.logback</groupId> | |
48 | + <artifactId>logback-classic</artifactId> | |
49 | + <scope>provided</scope> | |
50 | + </dependency> | |
51 | + <dependency> | |
52 | + <groupId>org.thingsboard.server</groupId> | |
53 | + <artifactId>extensions-api</artifactId> | |
54 | + <scope>provided</scope> | |
55 | + </dependency> | |
56 | + <dependency> | |
57 | + <groupId>org.springframework</groupId> | |
58 | + <artifactId>spring-web</artifactId> | |
59 | + <scope>provided</scope> | |
60 | + </dependency> | |
61 | + <dependency> | |
62 | + <groupId>org.thingsboard.server</groupId> | |
63 | + <artifactId>extensions-core</artifactId> | |
64 | + <scope>provided</scope> | |
65 | + </dependency> | |
66 | + <dependency> | |
67 | + <groupId>org.apache.velocity</groupId> | |
68 | + <artifactId>velocity</artifactId> | |
69 | + <scope>provided</scope> | |
70 | + </dependency> | |
71 | + <dependency> | |
72 | + <groupId>org.apache.velocity</groupId> | |
73 | + <artifactId>velocity-tools</artifactId> | |
74 | + <scope>provided</scope> | |
75 | + </dependency> | |
76 | + </dependencies> | |
77 | + <build> | |
78 | + <plugins> | |
79 | + <plugin> | |
80 | + <artifactId>maven-assembly-plugin</artifactId> | |
81 | + <configuration> | |
82 | + <descriptors> | |
83 | + <descriptor>src/assembly/extension.xml</descriptor> | |
84 | + </descriptors> | |
85 | + </configuration> | |
86 | + <executions> | |
87 | + <execution> | |
88 | + <id>make-assembly</id> | |
89 | + <phase>package</phase> | |
90 | + <goals> | |
91 | + <goal>single</goal> | |
92 | + </goals> | |
93 | + </execution> | |
94 | + </executions> | |
95 | + </plugin> | |
96 | + </plugins> | |
97 | + </build> | |
98 | +</project> | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" | |
19 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
20 | + xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> | |
21 | + <id>extension</id> | |
22 | + <formats> | |
23 | + <format>jar</format> | |
24 | + </formats> | |
25 | + <includeBaseDirectory>false</includeBaseDirectory> | |
26 | + <dependencySets> | |
27 | + <dependencySet> | |
28 | + <outputDirectory>/</outputDirectory> | |
29 | + <useProjectArtifact>true</useProjectArtifact> | |
30 | + <unpack>true</unpack> | |
31 | + <scope>runtime</scope> | |
32 | + </dependencySet> | |
33 | + </dependencySets> | |
34 | +</assembly> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.action; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.CustomerId; | |
19 | +import org.thingsboard.server.common.data.id.DeviceId; | |
20 | +import org.thingsboard.server.common.data.id.TenantId; | |
21 | +import org.thingsboard.server.extensions.api.plugins.msg.AbstractRuleToPluginMsg; | |
22 | + | |
23 | +public class RestApiCallActionMsg extends AbstractRuleToPluginMsg<RestApiCallActionPayload> { | |
24 | + | |
25 | + public RestApiCallActionMsg(TenantId tenantId, CustomerId customerId, DeviceId deviceId, RestApiCallActionPayload payload) { | |
26 | + super(tenantId, customerId, deviceId, payload); | |
27 | + } | |
28 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.action; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Data; | |
20 | +import org.springframework.http.HttpMethod; | |
21 | +import org.springframework.http.HttpStatus; | |
22 | +import org.thingsboard.server.common.msg.session.MsgType; | |
23 | + | |
24 | +import java.io.Serializable; | |
25 | + | |
26 | +@Data | |
27 | +@Builder | |
28 | +public class RestApiCallActionPayload implements Serializable { | |
29 | + private final String actionPath; | |
30 | + private final String msgBody; | |
31 | + private final HttpMethod httpMethod; | |
32 | + private final HttpStatus expectedResultCode; | |
33 | + private final boolean sync; | |
34 | + | |
35 | + private final Integer requestId; | |
36 | + private final MsgType msgType; | |
37 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.action; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.apache.velocity.Template; | |
20 | +import org.apache.velocity.VelocityContext; | |
21 | +import org.apache.velocity.runtime.parser.ParseException; | |
22 | +import org.springframework.http.HttpMethod; | |
23 | +import org.springframework.http.HttpStatus; | |
24 | +import org.thingsboard.server.common.msg.device.ToDeviceActorMsg; | |
25 | +import org.thingsboard.server.common.msg.session.FromDeviceRequestMsg; | |
26 | +import org.thingsboard.server.common.msg.session.ToDeviceMsg; | |
27 | +import org.thingsboard.server.extensions.api.component.Action; | |
28 | +import org.thingsboard.server.extensions.api.plugins.PluginAction; | |
29 | +import org.thingsboard.server.extensions.api.plugins.msg.PluginToRuleMsg; | |
30 | +import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; | |
31 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
32 | +import org.thingsboard.server.extensions.api.rules.RuleContext; | |
33 | +import org.thingsboard.server.extensions.api.rules.RuleProcessingMetaData; | |
34 | +import org.thingsboard.server.extensions.core.action.template.AbstractTemplatePluginAction; | |
35 | +import org.thingsboard.server.extensions.core.utils.VelocityUtils; | |
36 | + | |
37 | +import java.util.Optional; | |
38 | + | |
39 | +@Action(name = "REST API Call Plugin Action", | |
40 | + descriptor = "RestApiCallActionDescriptor.json", configuration = RestApiCallPluginActionConfiguration.class) | |
41 | +@Slf4j | |
42 | +public class RestApiCallPluginAction extends AbstractTemplatePluginAction<RestApiCallPluginActionConfiguration> { | |
43 | + | |
44 | + @Override | |
45 | + protected Optional<RuleToPluginMsg<?>> buildRuleToPluginMsg(RuleContext ctx, ToDeviceActorMsg msg, FromDeviceRequestMsg payload) { | |
46 | + RestApiCallActionPayload.RestApiCallActionPayloadBuilder builder = RestApiCallActionPayload.builder(); | |
47 | + builder.msgType(payload.getMsgType()); | |
48 | + builder.requestId(payload.getRequestId()); | |
49 | + builder.sync(configuration.isSync()); | |
50 | + builder.actionPath(configuration.getActionPath()); | |
51 | + builder.httpMethod(HttpMethod.valueOf(configuration.getRequestMethod()[0])); | |
52 | + builder.expectedResultCode(HttpStatus.valueOf(configuration.getExpectedResultCode())); | |
53 | + builder.msgBody(getMsgBody(ctx, msg)); | |
54 | + return Optional.of(new RestApiCallActionMsg(msg.getTenantId(), | |
55 | + msg.getCustomerId(), | |
56 | + msg.getDeviceId(), | |
57 | + builder.build())); | |
58 | + } | |
59 | + | |
60 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.action; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.extensions.core.action.template.TemplateActionConfiguration; | |
20 | + | |
21 | +@Data | |
22 | +public class RestApiCallPluginActionConfiguration implements TemplateActionConfiguration { | |
23 | + private boolean sync; | |
24 | + private String template; | |
25 | + private String actionPath; | |
26 | + private int expectedResultCode; | |
27 | + private String[] requestMethod; | |
28 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.plugin; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.springframework.http.HttpEntity; | |
20 | +import org.springframework.http.HttpHeaders; | |
21 | +import org.springframework.http.ResponseEntity; | |
22 | +import org.springframework.web.client.RestClientException; | |
23 | +import org.springframework.web.client.RestTemplate; | |
24 | +import org.thingsboard.server.common.data.id.RuleId; | |
25 | +import org.thingsboard.server.common.data.id.TenantId; | |
26 | +import org.thingsboard.server.common.msg.core.BasicStatusCodeResponse; | |
27 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
28 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
29 | +import org.thingsboard.server.extensions.api.plugins.msg.ResponsePluginToRuleMsg; | |
30 | +import org.thingsboard.server.extensions.api.plugins.msg.RuleToPluginMsg; | |
31 | +import org.thingsboard.server.extensions.api.rules.RuleException; | |
32 | +import org.thingsboard.server.extensions.rest.action.RestApiCallActionMsg; | |
33 | +import org.thingsboard.server.extensions.rest.action.RestApiCallActionPayload; | |
34 | + | |
35 | +@RequiredArgsConstructor | |
36 | +public class RestApiCallMsgHandler implements RuleMsgHandler { | |
37 | + | |
38 | + private final String baseUrl; | |
39 | + private final HttpHeaders headers; | |
40 | + | |
41 | + @Override | |
42 | + public void process(PluginContext ctx, TenantId tenantId, RuleId ruleId, RuleToPluginMsg<?> msg) throws RuleException { | |
43 | + if (!(msg instanceof RestApiCallActionMsg)) { | |
44 | + throw new RuleException("Unsupported message type " + msg.getClass().getName() + "!"); | |
45 | + } | |
46 | + RestApiCallActionPayload payload = ((RestApiCallActionMsg)msg).getPayload(); | |
47 | + try { | |
48 | + ResponseEntity<String> exchangeResponse = new RestTemplate().exchange( | |
49 | + baseUrl + payload.getActionPath(), | |
50 | + payload.getHttpMethod(), | |
51 | + new HttpEntity<>(payload.getMsgBody(), headers), | |
52 | + String.class); | |
53 | + if (exchangeResponse.getStatusCode().equals(payload.getExpectedResultCode()) && payload.isSync()) { | |
54 | + ctx.reply(new ResponsePluginToRuleMsg(msg.getUid(), tenantId, ruleId, | |
55 | + BasicStatusCodeResponse.onSuccess(payload.getMsgType(), payload.getRequestId()))); | |
56 | + } else if(!exchangeResponse.getStatusCode().equals(payload.getExpectedResultCode())) { | |
57 | + throw new RuntimeException("Response Status Code '" | |
58 | + + exchangeResponse.getStatusCode() | |
59 | + + "' doesn't equals to Expected Status Code '" | |
60 | + + payload.getExpectedResultCode() + "'"); | |
61 | + } | |
62 | + | |
63 | + } catch (RestClientException e) { | |
64 | + throw new RuleException(e.getMessage(), e); | |
65 | + } | |
66 | + } | |
67 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.plugin; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.http.HttpHeaders; | |
20 | +import org.thingsboard.server.extensions.api.component.Plugin; | |
21 | +import org.thingsboard.server.extensions.api.plugins.AbstractPlugin; | |
22 | +import org.thingsboard.server.extensions.api.plugins.PluginContext; | |
23 | +import org.thingsboard.server.extensions.api.plugins.handlers.RuleMsgHandler; | |
24 | +import org.thingsboard.server.extensions.rest.action.RestApiCallPluginAction; | |
25 | + | |
26 | +import java.util.Base64; | |
27 | + | |
28 | +@Plugin(name = "REST API Call Plugin", actions = {RestApiCallPluginAction.class}, | |
29 | + descriptor = "RestApiCallPluginDescriptor.json", configuration = RestApiCallPluginConfiguration.class) | |
30 | +@Slf4j | |
31 | +public class RestApiCallPlugin extends AbstractPlugin<RestApiCallPluginConfiguration> { | |
32 | + | |
33 | + private static final String BASIC_AUTH_METHOD = "BASIC_AUTH"; | |
34 | + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; | |
35 | + private static final String AUTHORIZATION_HEADER_FORMAT = "Basic %s"; | |
36 | + private static final String CREDENTIALS_TEMPLATE = "%s:%s"; | |
37 | + private static final String BASE_URL_TEMPLATE = "http://%s:%d%s"; | |
38 | + private RestApiCallMsgHandler handler; | |
39 | + private String baseUrl; | |
40 | + private HttpHeaders headers = new HttpHeaders(); | |
41 | + | |
42 | + @Override | |
43 | + public void init(RestApiCallPluginConfiguration configuration) { | |
44 | + this.baseUrl = String.format( | |
45 | + BASE_URL_TEMPLATE, | |
46 | + configuration.getHost(), | |
47 | + configuration.getPort(), | |
48 | + configuration.getBasePath()); | |
49 | + | |
50 | + if (configuration.getAuthMethod()[0].equals(BASIC_AUTH_METHOD)) { | |
51 | + String userName = configuration.getUserName(); | |
52 | + String password = configuration.getPassword(); | |
53 | + String credentials = String.format(CREDENTIALS_TEMPLATE, userName, password); | |
54 | + byte[] token = Base64.getEncoder().encode(credentials.getBytes()); | |
55 | + this.headers.add(AUTHORIZATION_HEADER_NAME, String.format(AUTHORIZATION_HEADER_FORMAT, new String(token))); | |
56 | + } | |
57 | + | |
58 | + init(); | |
59 | + } | |
60 | + | |
61 | + private void init() { | |
62 | + this.handler = new RestApiCallMsgHandler(baseUrl, headers); | |
63 | + } | |
64 | + | |
65 | + @Override | |
66 | + protected RuleMsgHandler getRuleMsgHandler() { | |
67 | + return handler; | |
68 | + } | |
69 | + | |
70 | + @Override | |
71 | + public void resume(PluginContext ctx) { | |
72 | + init(); | |
73 | + } | |
74 | + | |
75 | + @Override | |
76 | + public void suspend(PluginContext ctx) { | |
77 | + log.debug("Suspend method was called, but no impl provided!"); | |
78 | + } | |
79 | + | |
80 | + @Override | |
81 | + public void stop(PluginContext ctx) { | |
82 | + log.debug("Stop method was called, but no impl provided!"); | |
83 | + } | |
84 | +} | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest.plugin; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class RestApiCallPluginConfiguration { | |
22 | + private String host; | |
23 | + private int port; | |
24 | + private String basePath; | |
25 | + | |
26 | + private String[] authMethod; | |
27 | + | |
28 | + private String userName; | |
29 | + private String password; | |
30 | +} | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "REST API Call Action Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "sync": { | |
7 | + "title": "Requires delivery confirmation", | |
8 | + "type": "boolean" | |
9 | + }, | |
10 | + "template": { | |
11 | + "title": "Body Template", | |
12 | + "type": "string" | |
13 | + }, | |
14 | + "actionPath": { | |
15 | + "title": "Action Path", | |
16 | + "type": "string", | |
17 | + "default": "/" | |
18 | + }, | |
19 | + "requestMethod": { | |
20 | + "title": "Request method", | |
21 | + "type": "array", | |
22 | + "minItems" : 1, | |
23 | + "items": [ | |
24 | + { | |
25 | + "value": "POST", | |
26 | + "label": "POST" | |
27 | + }, | |
28 | + { | |
29 | + "value": "PUT", | |
30 | + "label": "PUT" | |
31 | + } | |
32 | + ], | |
33 | + "uniqueItems": true | |
34 | + }, | |
35 | + "expectedResultCode": { | |
36 | + "title": "Expected Result Code", | |
37 | + "type": "integer" | |
38 | + } | |
39 | + }, | |
40 | + "required": [ | |
41 | + "sync", | |
42 | + "template", | |
43 | + "actionPath", | |
44 | + "expectedResultCode", | |
45 | + "requestMethod" | |
46 | + ] | |
47 | + }, | |
48 | + "form": [ | |
49 | + "sync", | |
50 | + { | |
51 | + "key": "template", | |
52 | + "type": "textarea", | |
53 | + "rows": 5 | |
54 | + }, | |
55 | + "actionPath", | |
56 | + { | |
57 | + "key": "requestMethod", | |
58 | + "type": "rc-select", | |
59 | + "multiple": false | |
60 | + }, | |
61 | + "expectedResultCode" | |
62 | + ] | |
63 | +} | |
\ No newline at end of file | ... | ... |
1 | +{ | |
2 | + "schema": { | |
3 | + "title": "REST API Call Plugin Configuration", | |
4 | + "type": "object", | |
5 | + "properties": { | |
6 | + "host": { | |
7 | + "title": "Host", | |
8 | + "type": "string" | |
9 | + }, | |
10 | + "port": { | |
11 | + "title": "Port", | |
12 | + "type": "integer", | |
13 | + "default": 8080, | |
14 | + "minimum": 0, | |
15 | + "maximum": 65536 | |
16 | + }, | |
17 | + "basePath": { | |
18 | + "title": "Base Path", | |
19 | + "type": "string", | |
20 | + "default": "/" | |
21 | + }, | |
22 | + "authMethod": { | |
23 | + "title": "Authentication method", | |
24 | + "type": "array", | |
25 | + "minItems" : 1, | |
26 | + "items": [ | |
27 | + { | |
28 | + "value": "NO_AUTH", | |
29 | + "label": "No authentication" | |
30 | + }, | |
31 | + { | |
32 | + "value": "BASIC_AUTH", | |
33 | + "label": "Basic authentication" | |
34 | + } | |
35 | + ], | |
36 | + "uniqueItems": true | |
37 | + }, | |
38 | + "userName": { | |
39 | + "title": "Username", | |
40 | + "type": "string" | |
41 | + }, | |
42 | + "password": { | |
43 | + "title": "Password", | |
44 | + "type": "string" | |
45 | + } | |
46 | + }, | |
47 | + "required": [ | |
48 | + "host", | |
49 | + "port", | |
50 | + "basePath", | |
51 | + "authMethod" | |
52 | + ] | |
53 | + }, | |
54 | + "form": [ | |
55 | + "host", | |
56 | + "port", | |
57 | + "basePath", | |
58 | + { | |
59 | + "key": "authMethod", | |
60 | + "type": "rc-select", | |
61 | + "multiple": false | |
62 | + }, | |
63 | + "userName", | |
64 | + { | |
65 | + "key": "password", | |
66 | + "type": "password" | |
67 | + } | |
68 | + ] | |
69 | +} | |
\ No newline at end of file | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.extensions.rest; | |
17 | + | |
18 | +import com.sun.net.httpserver.*; | |
19 | + | |
20 | +import java.io.BufferedReader; | |
21 | +import java.io.IOException; | |
22 | +import java.io.InputStreamReader; | |
23 | +import java.io.OutputStream; | |
24 | +import java.net.InetSocketAddress; | |
25 | +import java.util.stream.Collectors; | |
26 | + | |
27 | +public class RestApiCallDemoClient { | |
28 | + | |
29 | + private static final String DEMO_REST_BASIC_AUTH = "/demo-rest-basic-auth"; | |
30 | + private static final String DEMO_REST_NO_AUTH = "/demo-rest-no-auth"; | |
31 | + private static final String USERNAME = "demo"; | |
32 | + private static final String PASSWORD = "demo"; | |
33 | + private static final int HTTP_SERVER_PORT = 8888; | |
34 | + | |
35 | + public static void main(String[] args) throws IOException { | |
36 | + HttpServer server = HttpServer.create(new InetSocketAddress(HTTP_SERVER_PORT), 0); | |
37 | + | |
38 | + HttpContext secureContext = server.createContext(DEMO_REST_BASIC_AUTH, new RestDemoHandler()); | |
39 | + secureContext.setAuthenticator(new BasicAuthenticator("demo-auth") { | |
40 | + @Override | |
41 | + public boolean checkCredentials(String user, String pwd) { | |
42 | + return user.equals(USERNAME) && pwd.equals(PASSWORD); | |
43 | + } | |
44 | + }); | |
45 | + | |
46 | + server.createContext(DEMO_REST_NO_AUTH, new RestDemoHandler()); | |
47 | + server.setExecutor(null); | |
48 | + System.out.println("[*] Waiting for messages."); | |
49 | + server.start(); | |
50 | + } | |
51 | + | |
52 | + private static class RestDemoHandler implements HttpHandler { | |
53 | + @Override | |
54 | + public void handle(HttpExchange exchange) throws IOException { | |
55 | + String requestBody; | |
56 | + try (BufferedReader br = new BufferedReader(new InputStreamReader(exchange.getRequestBody(), "utf-8"))) { | |
57 | + requestBody = br.lines().collect(Collectors.joining(System.lineSeparator())); | |
58 | + } | |
59 | + System.out.println("[x] Received body: \n" + requestBody); | |
60 | + | |
61 | + String response = "Hello from demo client!"; | |
62 | + exchange.sendResponseHeaders(200, response.length()); | |
63 | + System.out.println("[x] Sending response: \n" + response); | |
64 | + | |
65 | + OutputStream os = exchange.getResponseBody(); | |
66 | + os.write(response.getBytes()); | |
67 | + os.close(); | |
68 | + } | |
69 | + } | |
70 | +} | |
\ No newline at end of file | ... | ... |
extensions/pom.xml
0 → 100644
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
19 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |
20 | + <modelVersion>4.0.0</modelVersion> | |
21 | + <parent> | |
22 | + <groupId>org.thingsboard</groupId> | |
23 | + <version>0.0.1-SNAPSHOT</version> | |
24 | + <artifactId>server</artifactId> | |
25 | + </parent> | |
26 | + <groupId>org.thingsboard.server</groupId> | |
27 | + <artifactId>extensions</artifactId> | |
28 | + <packaging>pom</packaging> | |
29 | + | |
30 | + <name>Thingsboard Extensions</name> | |
31 | + <url>http://thingsboard.org</url> | |
32 | + | |
33 | + <properties> | |
34 | + <main.dir>${basedir}/..</main.dir> | |
35 | + </properties> | |
36 | + | |
37 | + <modules> | |
38 | + <module>extension-rabbitmq</module> | |
39 | + <module>extension-rest-api-call</module> | |
40 | + <module>extension-kafka</module> | |
41 | + </modules> | |
42 | + | |
43 | +</project> | ... | ... |