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> |
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> |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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 | +} |
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> |