All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.updater.stoptime.WebsocketGtfsRealtimeUpdater Maven / Gradle / Ivy

package org.opentripplanner.updater.stoptime;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import com.fasterxml.jackson.databind.JsonNode;

import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.updater.GraphUpdater;
import org.opentripplanner.updater.GraphUpdaterManager;
import org.opentripplanner.updater.GraphWriterRunnable;
import org.opentripplanner.updater.stoptime.TimetableSnapshotSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.transit.realtime.GtfsRealtime;
import com.google.transit.realtime.GtfsRealtime.FeedEntity;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;
import com.google.transit.realtime.GtfsRealtime.TripUpdate;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.websocket.DefaultWebSocketListener;
import com.ning.http.client.websocket.WebSocket;
import com.ning.http.client.websocket.WebSocketListener;
import com.ning.http.client.websocket.WebSocketUpgradeHandler;

/**
 * This class starts an HTTP client which opens a websocket connection to a GTFS-RT data source. A
 * callback is registered which handles incoming GTFS-RT messages as they stream in by placing a
 * GTFS-RT decoder Runnable task in the single-threaded executor for handling.
 *
 * Usage example ('websocket' name is an example) in the file 'Graph.properties':
 *
 * 
 * websocket.type = websocket-gtfs-rt-updater
 * websocket.defaultAgencyId = agency
 * websocket.url = ws://localhost:8088/tripUpdates
 * 
* */ public class WebsocketGtfsRealtimeUpdater implements GraphUpdater { /** * Number of seconds to wait before checking again whether we are still connected */ private static final int CHECK_CONNECTION_PERIOD_SEC = 1; private static final int DEFAULT_RECONNECT_PERIOD_SEC = 300; // Five minutes private static Logger LOG = LoggerFactory.getLogger(WebsocketGtfsRealtimeUpdater.class); /** * Parent update manager. Is used to execute graph writer runnables. */ private GraphUpdaterManager updaterManager; /** * Url of the websocket server */ private String url; /** * The ID for the static feed to which these TripUpdates are applied */ private String feedId; /** * The number of seconds to wait before reconnecting after a failed connection. */ private int reconnectPeriodSec; @Override public void setGraphUpdaterManager(GraphUpdaterManager updaterManager) { this.updaterManager = updaterManager; } @Override public void configure(Graph graph, JsonNode config) throws Exception { url = config.path("url").asText(); feedId = config.path("feedId").asText(""); reconnectPeriodSec = config.path("reconnectPeriodSec").asInt(DEFAULT_RECONNECT_PERIOD_SEC); } @Override public void setup(Graph graph) throws InterruptedException, ExecutionException { // Only create a realtime data snapshot source if none exists already if (graph.timetableSnapshotSource == null) { TimetableSnapshotSource snapshotSource = new TimetableSnapshotSource(graph); // Add snapshot source to graph graph.timetableSnapshotSource = (snapshotSource); } } @Override public void run() throws InterruptedException { // The AsyncHttpClient library uses Netty by default (it has a dependency on Netty). // It can also make use of Grizzly for the HTTP layer, but the Jersey-Grizzly integration // forces us to use a version of Grizzly that is too old to be compatible with the current // AsyncHttpClient. This would be done as follows: // AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().build(); // AsyncHttpClient client = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), // config); // Using Netty by default: while (true) { AsyncHttpClient client = new AsyncHttpClient(); WebSocketListener listener = new Listener(); WebSocketUpgradeHandler handler = new WebSocketUpgradeHandler.Builder() .addWebSocketListener(listener).build(); WebSocket socket = null; boolean connectionSuccessful = true; // Try to create a websocket connection try { socket = client.prepareGet(url).execute(handler).get(); LOG.info("Successfully connected to {}.", url); } catch (ExecutionException e) { LOG.error("Could not connect to {}: {}", url, e.getCause().getMessage()); connectionSuccessful = false; } catch (Exception e) { LOG.error("Unknown exception when trying to connect to {}:", url, e); connectionSuccessful = false; } // If connection was unsuccessful, wait some time before trying again if (!connectionSuccessful) { Thread.sleep(reconnectPeriodSec * 1000); } // Keep checking whether connection is still open while (true) { if (socket == null || !socket.isOpen()) { // The connection is closed somehow, try to reconnect if (connectionSuccessful) { LOG.warn("Connection to {} was lost. Trying to reconnect...", url); } break; } Thread.sleep(CHECK_CONNECTION_PERIOD_SEC * 1000); } client.close(); } } @Override public void teardown() { } /** * Auxiliary class to handle incoming messages via the websocket connection */ private class Listener extends DefaultWebSocketListener { @Override public void onMessage(byte[] message) { FeedMessage feedMessage = null; List feedEntityList = null; List updates = null; boolean fullDataset = true; try { // Decode message feedMessage = FeedMessage.PARSER.parseFrom(message); feedEntityList = feedMessage.getEntityList(); // Change fullDataset value if this is an incremental update if (feedMessage.hasHeader() && feedMessage.getHeader().hasIncrementality() && feedMessage.getHeader().getIncrementality() .equals(GtfsRealtime.FeedHeader.Incrementality.DIFFERENTIAL)) { fullDataset = false; } // Create List of TripUpdates updates = new ArrayList(feedEntityList.size()); for (FeedEntity feedEntity : feedEntityList) { if (feedEntity.hasTripUpdate()) { updates.add(feedEntity.getTripUpdate()); } } } catch (InvalidProtocolBufferException e) { LOG.error("Could not decode gtfs-rt message:", e); } if (updates != null) { // Handle trip updates via graph writer runnable TripUpdateGraphWriterRunnable runnable = new TripUpdateGraphWriterRunnable( fullDataset, updates, feedId); updaterManager.execute(runnable); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy