55
66import com .google .protobuf .InvalidProtocolBufferException ;
77import com .google .transit .realtime .GtfsRealtime ;
8+ import com .hivemq .client .mqtt .datatypes .MqttQos ;
9+ import com .hivemq .client .mqtt .lifecycle .MqttClientDisconnectedContext ;
10+ import com .hivemq .client .mqtt .mqtt5 .Mqtt5AsyncClient ;
11+ import com .hivemq .client .mqtt .mqtt5 .Mqtt5Client ;
12+ import com .hivemq .client .mqtt .mqtt5 .message .auth .Mqtt5SimpleAuth ;
13+ import com .hivemq .client .mqtt .mqtt5 .message .publish .Mqtt5Publish ;
814import java .net .URI ;
15+ import java .net .URISyntaxException ;
16+ import java .nio .charset .StandardCharsets ;
917import java .util .ArrayList ;
1018import java .util .List ;
19+ import java .util .Optional ;
20+ import java .util .UUID ;
1121import java .util .function .Consumer ;
12- import org .eclipse .paho .client .mqttv3 .IMqttDeliveryToken ;
13- import org .eclipse .paho .client .mqttv3 .MqttCallbackExtended ;
14- import org .eclipse .paho .client .mqttv3 .MqttClient ;
15- import org .eclipse .paho .client .mqttv3 .MqttConnectOptions ;
16- import org .eclipse .paho .client .mqttv3 .MqttException ;
17- import org .eclipse .paho .client .mqttv3 .MqttMessage ;
18- import org .eclipse .paho .client .mqttv3 .persist .MemoryPersistence ;
1922import org .opentripplanner .updater .spi .GraphUpdater ;
2023import org .opentripplanner .updater .spi .UpdateResult ;
2124import org .opentripplanner .updater .spi .WriteToGraphCallback ;
3033import org .slf4j .LoggerFactory ;
3134
3235/**
33- * This class starts an Paho MQTT client which opens a connection to a GTFS-RT data source. A
36+ * This class starts a hive MQTT client which opens a connection to a GTFS-RT data source. A
3437 * callback is registered which handles incoming GTFS-RT messages as they stream in by placing a
3538 * GTFS-RT decoder Runnable task in the single-threaded executor for handling.
3639 * <p>
@@ -56,16 +59,14 @@ public class MqttGtfsRealtimeUpdater implements GraphUpdater {
5659 private final int qos ;
5760 private final ForwardsDelayPropagationType forwardsDelayPropagationType ;
5861 private final BackwardsDelayPropagationType backwardsDelayPropagationType ;
59- private final String clientId = "OpenTripPlanner-" + MqttClient .generateClientId ();
6062 private final String configRef ;
61- private final MemoryPersistence persistence = new MemoryPersistence ();
6263 private final GtfsRealTimeTripUpdateAdapter adapter ;
6364 private final Consumer <UpdateResult > recordMetrics ;
6465 private WriteToGraphCallback saveResultOnGraph ;
6566
6667 private final boolean fuzzyTripMatching ;
6768
68- private MqttClient client ;
69+ private Mqtt5AsyncClient client ;
6970
7071 public MqttGtfsRealtimeUpdater (
7172 MqttGtfsRealtimeUpdaterParameters parameters ,
@@ -92,106 +93,112 @@ public void setup(WriteToGraphCallback writeToGraphCallback) {
9293
9394 @ Override
9495 public void run () throws Exception {
95- client = new MqttClient (url , clientId , persistence );
96- MqttConnectOptions connOpts = new MqttConnectOptions ();
97- connOpts .setCleanSession (true );
98- connOpts .setAutomaticReconnect (true );
96+ client = connectAndSubscribeToClient ();
97+ }
98+
99+ private Mqtt5AsyncClient connectAndSubscribeToClient () throws URISyntaxException {
100+ Mqtt5SimpleAuth auth ;
101+
99102 URI parsedUrl = new URI (url );
100103 if (parsedUrl .getUserInfo () != null ) {
101104 String [] userinfo = parsedUrl .getUserInfo ().split (":" );
102- connOpts .setUserName (userinfo [0 ]);
103- connOpts .setPassword (userinfo [1 ].toCharArray ());
105+ auth = Mqtt5SimpleAuth .builder ()
106+ .username (userinfo [0 ])
107+ .password (userinfo [1 ].getBytes (StandardCharsets .UTF_8 ))
108+ .build ();
109+ } else {
110+ auth = null ;
104111 }
105- client .setCallback (new Callback ());
106112
107- LOG .debug ("Connecting to broker: {}" , url );
108- client .connect (connOpts );
113+ Mqtt5AsyncClient asyncClient = Mqtt5Client .builder ()
114+ .identifier ("OpenTripPlanner-" + UUID .randomUUID ())
115+ .serverHost (parsedUrl .getHost ())
116+ .serverPort (parsedUrl .getPort ())
117+ .simpleAuth (auth )
118+ .automaticReconnectWithDefaultConfig ()
119+ .addConnectedListener (ctx -> onConnect ())
120+ .addDisconnectedListener (this ::onDisconnect )
121+ .buildAsync ();
122+
123+ asyncClient .connectWith ().keepAlive (30 ).cleanStart (false ).send ().join ();
124+
125+ asyncClient
126+ .subscribeWith ()
127+ .topicFilter (topic )
128+ .qos (Optional .ofNullable (MqttQos .fromCode (qos )).orElse (MqttQos .AT_MOST_ONCE ))
129+ .callback (this ::onMessage )
130+ .send ()
131+ .join ();
132+
133+ return asyncClient ;
134+ }
135+
136+ private void onDisconnect (MqttClientDisconnectedContext ctx ) {
137+ LOG .info ("Disconnected client from MQTT broker: {}" , url , ctx .getCause ());
138+ }
139+
140+ private void onConnect () {
141+ LOG .info ("Connected client to MQTT broker: {} with qos: {}" , url , qos );
109142 }
110143
111144 @ Override
112145 public void teardown () {
113- try {
114- client .disconnect ();
115- } catch (MqttException e ) {
116- LOG .error ("Error disconnecting" , e );
117- }
146+ client .disconnect ();
118147 }
119148
120149 @ Override
121150 public String getConfigRef () {
122151 return configRef ;
123152 }
124153
125- private class Callback implements MqttCallbackExtended {
126-
127- @ Override
128- public void connectComplete (boolean reconnect , String serverURI ) {
129- try {
130- LOG .debug ("Connected" );
131- client .subscribe (topic , qos );
132- } catch (MqttException e ) {
133- LOG .warn ("Could not subscribe to: {}" , topic );
154+ private void onMessage (Mqtt5Publish message ) {
155+ List <GtfsRealtime .TripUpdate > updates = null ;
156+ UpdateIncrementality updateIncrementality = FULL_DATASET ;
157+ try {
158+ // Decode message
159+ GtfsRealtime .FeedMessage feedMessage = GtfsRealtime .FeedMessage .parseFrom (
160+ message .getPayloadAsBytes ()
161+ );
162+ List <GtfsRealtime .FeedEntity > feedEntityList = feedMessage .getEntityList ();
163+
164+ // Change fullDataset value if this is an incremental update
165+ if (
166+ feedMessage .hasHeader () &&
167+ feedMessage .getHeader ().hasIncrementality () &&
168+ feedMessage
169+ .getHeader ()
170+ .getIncrementality ()
171+ .equals (GtfsRealtime .FeedHeader .Incrementality .DIFFERENTIAL )
172+ ) {
173+ updateIncrementality = DIFFERENTIAL ;
134174 }
135- }
136175
137- @ Override
138- public void connectionLost (Throwable cause ) {
139- LOG .debug ("Disconnected" );
140- }
141-
142- @ Override
143- public void messageArrived (String topic , MqttMessage message ) {
144- List <GtfsRealtime .TripUpdate > updates = null ;
145- UpdateIncrementality updateIncrementality = FULL_DATASET ;
146- try {
147- // Decode message
148- GtfsRealtime .FeedMessage feedMessage = GtfsRealtime .FeedMessage .parseFrom (
149- message .getPayload ()
150- );
151- List <GtfsRealtime .FeedEntity > feedEntityList = feedMessage .getEntityList ();
152-
153- // Change fullDataset value if this is an incremental update
154- if (
155- feedMessage .hasHeader () &&
156- feedMessage .getHeader ().hasIncrementality () &&
157- feedMessage
158- .getHeader ()
159- .getIncrementality ()
160- .equals (GtfsRealtime .FeedHeader .Incrementality .DIFFERENTIAL )
161- ) {
162- updateIncrementality = DIFFERENTIAL ;
176+ // Create List of TripUpdates
177+ updates = new ArrayList <>(feedEntityList .size ());
178+ for (GtfsRealtime .FeedEntity feedEntity : feedEntityList ) {
179+ if (feedEntity .hasTripUpdate ()) {
180+ updates .add (feedEntity .getTripUpdate ());
163181 }
164-
165- // Create List of TripUpdates
166- updates = new ArrayList <>(feedEntityList .size ());
167- for (GtfsRealtime .FeedEntity feedEntity : feedEntityList ) {
168- if (feedEntity .hasTripUpdate ()) {
169- updates .add (feedEntity .getTripUpdate ());
170- }
171- }
172- } catch (InvalidProtocolBufferException e ) {
173- LOG .error ("Could not decode gtfs-rt message:" , e );
174- }
175-
176- if (updates != null ) {
177- // Handle trip updates via graph writer runnable
178- saveResultOnGraph .execute (
179- new TripUpdateGraphWriterRunnable (
180- adapter ,
181- fuzzyTripMatching ,
182- forwardsDelayPropagationType ,
183- backwardsDelayPropagationType ,
184- updateIncrementality ,
185- updates ,
186- feedId ,
187- recordMetrics
188- )
189- );
190182 }
183+ } catch (InvalidProtocolBufferException e ) {
184+ LOG .error ("Could not decode gtfs-rt message:" , e );
191185 }
192186
193- @ Override
194- public void deliveryComplete (IMqttDeliveryToken token ) {}
187+ if (updates != null ) {
188+ // Handle trip updates via graph writer runnable
189+ saveResultOnGraph .execute (
190+ new TripUpdateGraphWriterRunnable (
191+ adapter ,
192+ fuzzyTripMatching ,
193+ forwardsDelayPropagationType ,
194+ backwardsDelayPropagationType ,
195+ updateIncrementality ,
196+ updates ,
197+ feedId ,
198+ recordMetrics
199+ )
200+ );
201+ }
195202 }
196203
197204 @ Override
0 commit comments