Package org.opentripplanner.routing.impl

Source Code of org.opentripplanner.routing.impl.GraphServiceImpl

/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (props, at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>. */

package org.opentripplanner.routing.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.PreDestroy;

import org.geotools.referencing.factory.DeferredAuthorityFactory;
import org.geotools.util.WeakCollectionCleaner;
import org.opentripplanner.routing.error.GraphNotFoundException;
import org.opentripplanner.routing.graph.Graph;
import org.opentripplanner.routing.services.GraphService;
import org.opentripplanner.routing.services.GraphSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The primary implementation of the GraphService interface. It can handle multiple graphs, each
* with its own routerId.
*
* Delegate the graph creation/loading details to the GraphSource implementations.
*
* @see GraphSource
*/
public class GraphServiceImpl implements GraphService {

    private static final Logger LOG = LoggerFactory.getLogger(GraphServiceImpl.class);

    /** Poll period for auto-reload scan. */
    private static final int AUTORELOAD_PERIOD_SEC = 10;

    /**
     * Should we pre-evict in auto-reload mode? False is more memory consuming but safer in case of
     * problems.
     */
    private static final boolean AUTORELOAD_PREEVICT = false;

    private Map<String, GraphSource> graphSources = new HashMap<>();

    /**
     * Router IDs may contain alphanumeric characters, underscores, and dashes only. This prevents
     * any confusion caused by the presence of special characters that might have a meaning for the
     * filesystem.
     */
    public static final Pattern routerIdPattern = Pattern.compile("[\\p{Alnum}_-]*");

    private String defaultRouterId = "";

    public GraphSource.Factory graphSourceFactory;

    private ScheduledExecutorService scanExecutor;

    public GraphServiceImpl() {
        this(false);
    }

    public GraphServiceImpl(boolean autoReload) {
        if (autoReload) {
            scanExecutor = Executors.newSingleThreadScheduledExecutor();
            scanExecutor.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    autoReloadScan();
                }
            }, AUTORELOAD_PERIOD_SEC, AUTORELOAD_PERIOD_SEC, TimeUnit.SECONDS);
        }
    }

    /**
     * @param defaultRouterId
     */
    @Override
    public void setDefaultRouterId(String defaultRouterId) {
        this.defaultRouterId = defaultRouterId;
    }

    /**
     * This is called when the bean gets deleted, that is mainly in case of webapp container
     * application stop or reload. We teardown all loaded graph to stop their background real-time
     * data updater thread.
     */
    @PreDestroy
    private void teardown() {
        LOG.info("Cleaning-up graphs...");
        evictAll();
        cleanupWebapp();
    }

    @Override
    public Graph getGraph() throws GraphNotFoundException {
        return getGraph(null);
    }

    @Override
    public Graph getGraph(String routerId) throws GraphNotFoundException {
        if (routerId == null || routerId.isEmpty() || routerId.equalsIgnoreCase("default")) {
            routerId = defaultRouterId;
            LOG.debug("routerId not specified, set to default of '{}'", routerId);
        }
        /*
         * Here we should not synchronize on graphSource as it may block for a while (during
         * reload/autoreload). For normal operations a simple get do not need to be synchronized so
         * we should be safe.
         */
        GraphSource graphSource = graphSources.get(routerId);
        if (graphSource == null) {
            LOG.error("no graph registered with the routerId '{}'", routerId);
            throw new GraphNotFoundException();
        }
        Graph graph = graphSource.getGraph();
        if (graph == null) {
            evictGraph(routerId);
            throw new GraphNotFoundException();
        }
        return graph;
    }

    @Override
    public boolean reloadGraphs(boolean preEvict) {
        boolean allSucceeded = true;
        synchronized (graphSources) {
            Collection<String> routerIds = getRouterIds();
            for (String routerId : routerIds) {
                GraphSource graphSource = graphSources.get(routerId);
                boolean success = graphSource.reload(true, preEvict);
                if (!success) {
                    evictGraph(routerId);
                }
                allSucceeded &= success;
            }
        }
        return allSucceeded;
    }

    @Override
    public Collection<String> getRouterIds() {
        return new ArrayList<String>(graphSources.keySet());
    }

    @Override
    public boolean registerGraph(String routerId, GraphSource graphSource) {
        LOG.info("Registering new graph '{}'", routerId);
        if (!routerIdLegal(routerId)) {
            LOG.error(
                    "routerId '{}' contains characters other than alphanumeric, underscore, and dash.",
                    routerId);
            return false;
        }
        if (graphSource.getGraph() == null) {
            LOG.warn("Can't register router ID '{}', null graph.", routerId);
            return false;
        }
        synchronized (graphSources) {
            GraphSource oldSource = graphSources.get(routerId);
            if (oldSource != null) {
                LOG.info("Graph '{}' already registered. Nothing to do.", routerId);
                return false;
            }
            graphSources.put(routerId, graphSource);
            return true;
        }
    }

    @Override
    public boolean evictGraph(String routerId) {
        LOG.info("Evicting graph '{}'", routerId);
        synchronized (graphSources) {
            GraphSource graphSource = graphSources.get(routerId);
            graphSources.remove(routerId);
            if (graphSource != null) {
                graphSource.evict();
                return true;
            } else {
                return false;
            }
        }
    }

    @Override
    public int evictAll() {
        LOG.info("Evincting all graphs.");
        synchronized (graphSources) {
            int n = 0;
            Collection<String> routerIds = new ArrayList<String>(getRouterIds());
            for (String routerId : routerIds) {
                if (evictGraph(routerId)) {
                    n++;
                }
            }
            return n;
        }
    }

    @Override
    public GraphSource.Factory getGraphSourceFactory() {
        return graphSourceFactory;
    }

    /**
     * Hook to cleanup various stuff of some used libraries (org.geotools), which depend on the
     * external client to call them for cleaning-up.
     */
    private void cleanupWebapp() {
        LOG.info("Web application shutdown: cleaning various stuff");
        WeakCollectionCleaner.DEFAULT.exit();
        DeferredAuthorityFactory.exit();
    }

    /**
     * Check whether a router ID is legal or not.
     */
    private boolean routerIdLegal(String routerId) {
        Matcher m = routerIdPattern.matcher(routerId);
        return m.matches();
    }

    private void autoReloadScan() {
        synchronized (graphSources) {
            Collection<String> routerIds = getRouterIds();
            for (String routerId : routerIds) {
                GraphSource graphSource = graphSources.get(routerId);
                boolean success = graphSource.reload(false, AUTORELOAD_PREEVICT);
                if (!success) {
                    evictGraph(routerId);
                }
            }
        }
    }
}
TOP

Related Classes of org.opentripplanner.routing.impl.GraphServiceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.