├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
└── org.eclipse.m2e.core.prefs
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── m2mci
│ │ └── mqttKafkaBridge
│ │ ├── Bridge.java
│ │ └── CommandLineParser.java
└── resources
│ ├── .gitkeep
│ └── log4j.properties
└── test
├── java
└── .gitkeep
└── resources
└── .gitkeep
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | mqttKafkaBridge
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.m2e.core.maven2Nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding//src/main/resources=UTF-8
4 | encoding//src/test/java=UTF-8
5 | encoding//src/test/resources=UTF-8
6 | encoding/=UTF-8
7 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
3 | org.eclipse.jdt.core.compiler.compliance=1.7
4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
5 | org.eclipse.jdt.core.compiler.source=1.7
6 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=false
4 | version=1
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mqttKafkaBridge
2 |
3 | Bridge which consumes MQTT messages and republishes them on Kafka on the same topic.
4 |
5 | ## Usage
6 |
7 | $ java -jar mqttKafkaBridge.jar [options...]
8 |
9 | Where `options` are:
10 |
11 | --help (-h) : Show help
12 | --id VAL : MQTT Client ID
13 | --topics VAL : MQTT topic filters (comma-separated)
14 | --uri VAL : MQTT Server URI
15 | --zk VAL : Zookeeper connect string
16 |
17 | If you don't specify any command-line options, it uses the following defaults:
18 |
19 | id: mqttKafkaBridge
20 | topics: '#' (all topics)
21 | uri: tcp://localhost:1883
22 | zk: localhost:2181
23 |
24 | ***Note***: you can't run more than one bridge using the default settings, since two clients cannot connect to the same MQTT server with the same client ID. Additionally, you will get multiple messages published to Kafka for each message published to MQTT. If you wish to run multiple instances, you'll need to divide up the topics among the instances, and make sure to give them different IDs.
25 |
26 | ## Logging
27 | `mqttKafkaBridge` uses [log4j](http://logging.apache.org/log4j/2.x/) for logging, as do the [Paho](http://www.eclipse.org/paho/) and [Kafka](http://kafka.apache.org/) libraries it uses. There is a default `log4j.properties` file packaged with the jar which simply prints all messages of level `INFO` or greater to the console. If you want to customize logging, simply create your own `log4j.properties` file, and start up `mqttKafkaBridge` as follows:
28 |
29 | $ java -Dlog4j.configuration=file:///path/to/log4j.properties -jar mqttKafkaBridge.jar [options...]
30 |
31 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 | com.m2mci
6 | mqttKafkaBridge
7 | 0.1.0
8 |
9 |
11 |
12 |
13 | scm:git:https://bitbucket.org/m2mconnections/mqttKafkaBridge.git
14 |
15 |
16 |
17 |
18 |
19 |
20 | maven-central
21 | Maven Central Repo
22 | https://repo.maven.apache.org/maven2
23 |
24 |
25 |
26 | eclipse-paho
27 | Eclipse Paho Repo
28 | https://repo.eclipse.org/content/repositories/paho-releases/
29 |
30 |
31 |
32 | apache-kafka
33 | Apache Kafka Repo
34 | http://clojars.org/repo/
35 |
36 |
37 | mvnrepository
38 | mvnrepository
39 | http://www.mvnrepository.com
40 |
41 |
42 |
43 |
44 |
45 |
46 | junit
47 | junit
48 | 4.11
49 |
50 |
51 | org.mockito
52 | mockito-core
53 | 1.9.5
54 |
55 |
56 | org.eclipse.paho
57 | mqtt-client
58 | 0.4.0
59 |
60 |
61 | org.apache.kafka
62 | kafka
63 | 0.7.2
64 |
65 |
66 | org.scala-lang
67 | scala-library
68 | 2.8.2
69 |
70 |
71 | args4j
72 | args4j
73 | 2.0.25
74 |
75 |
76 | com.101tec
77 | zkclient
78 | 0.3
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-compiler-plugin
89 | 3.1
90 |
91 | 1.7
92 | 1.7
93 |
94 |
95 |
96 |
97 | org.apache.maven.plugins
98 | maven-release-plugin
99 | 2.4.1
100 |
101 |
102 | org.apache.maven.plugins
103 | maven-javadoc-plugin
104 | 2.9
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-deploy-plugin
109 | 2.7
110 |
111 |
112 | org.apache.maven.plugins
113 | maven-source-plugin
114 | 2.2.1
115 |
116 |
117 |
118 | org.jacoco
119 | jacoco-maven-plugin
120 | 0.6.2.201302030002
121 |
122 |
123 | maven-assembly-plugin
124 |
125 |
126 |
127 | com.m2mci.mqttKafkaBridge.Bridge
128 |
129 |
130 |
131 | jar-with-dependencies
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | m2m-snapshot
142 | m2m-artifactory1-snapshots
143 | http://24.14.16.189:8081/artifactory/libs-snapshot-local
144 |
145 |
146 | m2m-release
147 | m2m-artifactory1-releases
148 | http://24.14.16.189:8081/artifactory/libs-release-local
149 |
150 |
151 |
152 |
153 |
154 |
156 | UTF-8
157 | UTF-8
158 |
159 |
160 |
--------------------------------------------------------------------------------
/src/main/java/com/m2mci/mqttKafkaBridge/Bridge.java:
--------------------------------------------------------------------------------
1 | package com.m2mci.mqttKafkaBridge;
2 |
3 | import java.util.Properties;
4 |
5 | import kafka.javaapi.producer.Producer;
6 | import kafka.javaapi.producer.ProducerData;
7 | import kafka.message.Message;
8 | import kafka.producer.ProducerConfig;
9 |
10 | import org.apache.log4j.Logger;
11 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
12 | import org.eclipse.paho.client.mqttv3.IMqttToken;
13 | import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
14 | import org.eclipse.paho.client.mqttv3.MqttCallback;
15 | import org.eclipse.paho.client.mqttv3.MqttException;
16 | import org.eclipse.paho.client.mqttv3.MqttMessage;
17 | import org.kohsuke.args4j.CmdLineException;
18 |
19 | public class Bridge implements MqttCallback {
20 | private Logger logger = Logger.getLogger(this.getClass().getName());
21 | private MqttAsyncClient mqtt;
22 | private Producer kafkaProducer;
23 |
24 | private void connect(String serverURI, String clientId, String zkConnect) throws MqttException {
25 | mqtt = new MqttAsyncClient(serverURI, clientId);
26 | mqtt.setCallback(this);
27 | IMqttToken token = mqtt.connect();
28 | Properties props = new Properties();
29 | props.put("zk.connect", zkConnect);
30 | props.put("serializer.class", "kafka.serializer.DefaultEncoder");
31 | ProducerConfig config = new ProducerConfig(props);
32 | kafkaProducer = new Producer(config);
33 | token.waitForCompletion();
34 | logger.info("Connected to MQTT and Kafka");
35 | }
36 |
37 | private void reconnect() throws MqttException {
38 | IMqttToken token = mqtt.connect();
39 | token.waitForCompletion();
40 | }
41 |
42 | private void subscribe(String[] mqttTopicFilters) throws MqttException {
43 | int[] qos = new int[mqttTopicFilters.length];
44 | for (int i = 0; i < qos.length; ++i) {
45 | qos[i] = 0;
46 | }
47 | mqtt.subscribe(mqttTopicFilters, qos);
48 | }
49 |
50 | @Override
51 | public void connectionLost(Throwable cause) {
52 | logger.warn("Lost connection to MQTT server", cause);
53 | while (true) {
54 | try {
55 | logger.info("Attempting to reconnect to MQTT server");
56 | reconnect();
57 | logger.info("Reconnected to MQTT server, resuming");
58 | return;
59 | } catch (MqttException e) {
60 | logger.warn("Reconnect failed, retrying in 10 seconds", e);
61 | }
62 | try {
63 | Thread.sleep(10000);
64 | } catch (InterruptedException e) {
65 | }
66 | }
67 | }
68 |
69 | @Override
70 | public void deliveryComplete(IMqttDeliveryToken token) {
71 | // TODO Auto-generated method stub
72 |
73 | }
74 |
75 | @Override
76 | public void messageArrived(String topic, MqttMessage message) throws Exception {
77 | byte[] payload = message.getPayload();
78 | ProducerData data = new ProducerData(topic, new Message(payload));
79 | kafkaProducer.send(data);
80 | }
81 |
82 | /**
83 | * @param args
84 | */
85 | public static void main(String[] args) {
86 | CommandLineParser parser = null;
87 | try {
88 | parser = new CommandLineParser();
89 | parser.parse(args);
90 | Bridge bridge = new Bridge();
91 | bridge.connect(parser.getServerURI(), parser.getClientId(), parser.getZkConnect());
92 | bridge.subscribe(parser.getMqttTopicFilters());
93 | } catch (MqttException e) {
94 | e.printStackTrace(System.err);
95 | } catch (CmdLineException e) {
96 | System.err.println(e.getMessage());
97 | parser.printUsage(System.err);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/m2mci/mqttKafkaBridge/CommandLineParser.java:
--------------------------------------------------------------------------------
1 | package com.m2mci.mqttKafkaBridge;
2 |
3 | import java.io.OutputStream;
4 | import java.io.PrintStream;
5 |
6 | import org.kohsuke.args4j.CmdLineException;
7 | import org.kohsuke.args4j.CmdLineParser;
8 | import org.kohsuke.args4j.Option;
9 |
10 | public class CommandLineParser {
11 | private static final String ALL_MQTT_TOPICS = "#";
12 | private static final String DEFAULT_ZOOKEEPER_CONNECT = "localhost:2181";
13 | private static final String DEFAULT_MQTT_SERVER_URI = "tcp://localhost:1883";
14 |
15 | @Option(name="--id", usage="MQTT Client ID")
16 | private String clientId = "mqttKafkaBridge";
17 |
18 | @Option(name="--uri", usage="MQTT Server URI")
19 | private String serverURI = DEFAULT_MQTT_SERVER_URI;
20 |
21 | @Option(name="--zk", usage="Zookeeper connect string")
22 | private String zkConnect = DEFAULT_ZOOKEEPER_CONNECT;
23 |
24 | @Option(name="--topics", usage="MQTT topic filters (comma-separated)")
25 | private String mqttTopicFilters = ALL_MQTT_TOPICS;
26 |
27 | @Option(name="--help", aliases="-h", usage="Show help")
28 | private boolean showHelp = false;
29 |
30 | private CmdLineParser parser = new CmdLineParser(this);
31 |
32 | public String getClientId() {
33 | return clientId;
34 | }
35 |
36 | public String getServerURI() {
37 | return serverURI;
38 | }
39 |
40 | public String getZkConnect() {
41 | return zkConnect;
42 | }
43 |
44 | public String[] getMqttTopicFilters() {
45 | return mqttTopicFilters.split(",");
46 | }
47 |
48 | public void parse(String[] args) throws CmdLineException {
49 | parser.parseArgument(args);
50 | if (showHelp) {
51 | printUsage(System.out);
52 | System.exit(0);
53 | }
54 | }
55 |
56 | public void printUsage(OutputStream out) {
57 | PrintStream stream = new PrintStream(out);
58 | stream.println("java " + Bridge.class.getName() + " [options...]");
59 | parser.printUsage(out);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/resources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacklund/mqttKafkaBridge/f11a33759c1bba27f95d2e6ec417d9a8867c6094/src/main/resources/.gitkeep
--------------------------------------------------------------------------------
/src/main/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | # Set root logger level to INFO and its only appender to A1.
2 | log4j.rootLogger=INFO, A1
3 |
4 | # A1 is set to be a ConsoleAppender.
5 | log4j.appender.A1=org.apache.log4j.ConsoleAppender
6 |
7 | # A1 uses PatternLayout.
8 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout
9 | log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
--------------------------------------------------------------------------------
/src/test/java/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacklund/mqttKafkaBridge/f11a33759c1bba27f95d2e6ec417d9a8867c6094/src/test/java/.gitkeep
--------------------------------------------------------------------------------
/src/test/resources/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jacklund/mqttKafkaBridge/f11a33759c1bba27f95d2e6ec417d9a8867c6094/src/test/resources/.gitkeep
--------------------------------------------------------------------------------