org.opentripplanner.routing.impl.GraphScanner Maven / Gradle / Ivy
package org.opentripplanner.routing.impl;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.opentripplanner.routing.error.GraphNotFoundException;
import org.opentripplanner.routing.services.GraphService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Scan for graphs under the base directory and auto-register them.
*/
public class GraphScanner {
private static final Logger LOG = LoggerFactory.getLogger(GraphScanner.class);
/** Auto-scan for new graphs every n secs. */
private static final int AUTOSCAN_PERIOD_SEC = 10;
/** Where to look for graphs. Defaults to 'graphs' under the OTP server base path. */
public File basePath = null;
/** A list of routerIds to automatically register and load at startup */
public List autoRegister;
/** The default router, none by default */
public String defaultRouterId = null;
/** The GraphService where register graphs to */
private GraphService graphService;
private ScheduledExecutorService scanExecutor;
public GraphScanner(GraphService graphService, File basePath, boolean autoScan) {
this.graphService = graphService;
this.basePath = basePath;
if (autoScan) {
scanExecutor = Executors.newSingleThreadScheduledExecutor();
}
}
/**
* Based on the autoRegister list, automatically register all routerIds for which we can find a
* graph file in a subdirectory of the resourceBase path. Also register and load the graph for
* the defaultRouterId and warn if no routerIds are registered.
*/
public void startup() {
Set routerIds = new HashSet();
if (autoRegister != null)
routerIds.addAll(autoRegister);
if (defaultRouterId != null) {
graphService.setDefaultRouterId(defaultRouterId);
routerIds.add(defaultRouterId);
}
if (!routerIds.isEmpty()) {
LOG.info("Attempting to automatically register routerIds {}", autoRegister);
LOG.info("Graph files will be sought in paths relative to {}", basePath);
for (String routerId : routerIds) {
InputStreamGraphSource graphSource = InputStreamGraphSource.newFileGraphSource(
routerId, getBasePath(routerId));
graphService.registerGraph(routerId, graphSource);
}
} else {
LOG.info("No list of routerIds was provided for automatic registration.");
}
if (scanExecutor != null) {
LOG.info("Auto-scan mode activated, looking in {}", basePath);
autoScan();
scanExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
autoScan();
}
}, AUTOSCAN_PERIOD_SEC, AUTOSCAN_PERIOD_SEC, TimeUnit.SECONDS);
}
}
private void autoScan() {
LOG.debug("Auto discovering graphs under {}", basePath);
/*
* There is no need to synchronize scan and registration here. If a graph file is removed
* between scan and register, registering will fail but it's safe. It a graph file is
* created, we'll wait for the next scan to register it.
*/
Set graphOnDisk = new HashSet();
/* First check for a root graph */
File rootGraphFile = new File(basePath, InputStreamGraphSource.GRAPH_FILENAME);
if (rootGraphFile.exists() && rootGraphFile.canRead()) {
graphOnDisk.add("");
}
/* Then graph in sub-directories */
for (String sub : basePath.list()) {
File subPath = new File(basePath, sub);
if (subPath.isDirectory()) {
File graphFile = new File(subPath, InputStreamGraphSource.GRAPH_FILENAME);
if (graphFile.exists() && graphFile.canRead()) {
graphOnDisk.add(sub);
}
}
}
Set graphRegistered = new HashSet<>(graphService.getRouterIds());
Set graphToRegister = new HashSet<>(graphOnDisk);
graphToRegister.removeAll(graphRegistered);
if (!graphToRegister.isEmpty()) {
LOG.info("Found new routers to register: {}",
Arrays.toString(graphToRegister.toArray()));
for (String routerId : graphToRegister) {
InputStreamGraphSource graphSource = InputStreamGraphSource.newFileGraphSource(
routerId, getBasePath(routerId));
// Can be null here if the file has been removed in the meantime.
graphService.registerGraph(routerId, graphSource);
}
}
/*
* Note: We do not automatically evict removed graph. They will be evicted only in
* auto-reload mode, and that's the behavior we want.
*/
Collection routerIds = graphService.getRouterIds();
if (routerIds.isEmpty()) {
LOG.warn("No graphs have been loaded/registered. "
+ "You must place one or more graphs before routing.");
} else {
try {
// Check if we still have a default graph.
graphService.getRouter();
} catch (GraphNotFoundException e) {
// Let's see which one we want to take by default
if (routerIds.contains("")) {
// If we have a root graph, this should be a good default
LOG.info("Setting default routerId to root graph ''");
graphService.setDefaultRouterId("");
} else {
// Otherwise take first one present
String defRouterId = routerIds.iterator().next();
if (routerIds.size() > 1)
LOG.warn("Setting default routerId to arbitrary one '{}'", defRouterId);
else
LOG.info("Setting default routerId to '{}'", defRouterId);
graphService.setDefaultRouterId(defRouterId);
}
}
}
}
private File getBasePath(String routerId) {
return new File(basePath, routerId);
}
}