org.transitime.gtfs.GtfsUpdatedModule.java Source code

Java tutorial

Introduction

Here is the source code for org.transitime.gtfs.GtfsUpdatedModule.java

Source

/*
 * This file is part of Transitime.org
 * 
 * Transitime.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPL) as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * Transitime.org 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 Transitime.org .  If not, see <http://www.gnu.org/licenses/>.
 */

package org.transitime.gtfs;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Date;

import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.transitime.config.LongConfigValue;
import org.transitime.config.StringConfigValue;
import org.transitime.configData.AgencyConfig;
import org.transitime.logging.Markers;
import org.transitime.modules.Module;
import org.transitime.monitoring.MonitorBase;
import org.transitime.utils.EmailSender;
import org.transitime.utils.HttpGetFile;
import org.transitime.utils.Time;

/**
 * Downloads GTFS file from web server if it has been updated and notifies
 * users. Useful for automatically determining when GTFS data has been updated
 * by an agency.
 * <p>
 * When a GTFS file is downloaded then this module also e-mails recipients
 * specified by the parameter transitime.monitoring.emailRecipients
 * 
 * @author SkiBu Smith
 *
 */
public class GtfsUpdatedModule extends Module {

    // Configuration parameters
    private static StringConfigValue url = new StringConfigValue("transitime.gtfs.url",
            "URL where to retrieve the GTFS file.");

    private static StringConfigValue dirName = new StringConfigValue("transitime.gtfs.dirName",
            "Directory on agency server where to place the GTFS file.");

    private static LongConfigValue intervalMsec = new LongConfigValue("transitime.gtfs.intervalMsec",
            // Low cost unless file actually downloaded so do pretty 
            // frequently so get updates as soon as possible
            4 * Time.MS_PER_HOUR, "How long to wait before checking if GTFS file has changed " + "on web");

    private static EmailSender emailSender = new EmailSender();

    private static final Logger logger = LoggerFactory.getLogger(GtfsUpdatedModule.class);

    /********************** Member Functions **************************/

    /**
     * Constructor
     * 
     * @param agencyId
     */
    public GtfsUpdatedModule(String agencyId) {
        super(agencyId);
    }

    /**
     * Gets the GTFS file via http from the configured URL urlStr and stores it
     * in the configured directory dirName.
     * <p>
     * If file on web server last modified time is not newer than the local file
     * then the file will not actually be downloaded.
     * <p>
     * If file is downloaded then users and e-mailed.
     */
    public static void get() {
        logger.info("Checking to see if GTFS should be downloaded " + "because it was modified. {}",
                url.getValue());

        // Construct the getter
        HttpGetFile httpGetFile = new HttpGetFile(url.getValue(), dirName.getValue());

        // If file hasn't been modified then don't want to download it
        // since it can be large. Therefore determine age of previously 
        // downloaded file and use If-Modified-Since to only actually
        // get the file if it is newer on the web server
        File file = new File(httpGetFile.getFullFileName());
        boolean fileAlreadyExists = file.exists();
        if (fileAlreadyExists) {
            // Get the last modified time of the local file. Add 10 minutes
            // since the web server might be load balanced and the files
            // on the different servers might have slightly different last 
            // modified times. To make sure that don't keep downloading 
            // from the different servers until get the file with most 
            // recent modified time add 10 minutes to the time to indicate
            // that as long as the local file is within 10 minutes of the
            // remote file that it is ok.
            long lastModified = file.lastModified() + 10 * Time.MS_PER_MIN;

            httpGetFile.addRequestHeader("If-Modified-Since", Time.httpDate(lastModified));

            logger.debug("The file {} already exists so using " + "If-Modified-Since header of \"{}\" or {} msec.",
                    httpGetFile.getFullFileName(), Time.httpDate(lastModified), lastModified);
        }

        try {
            // Actually get the file from web server
            int httpResponseCode = httpGetFile.getFile();

            // If got a new file (instead of getting a NOT MODIFIED
            // response) then send message to those monitoring so that
            // the GTFS file can be processed.
            if (httpResponseCode == HttpStatus.SC_OK) {
                if (fileAlreadyExists)
                    logger.info("Got remote file because version on web server " + "is newer. Url={} dir={}",
                            httpGetFile.getFullFileName(), dirName.getValue());
                else
                    logger.info("Got remote file because didn't have a local " + "copy of it. Url={} dir={}",
                            httpGetFile.getFullFileName(), dirName.getValue());

                // Email message
                String subject = "GTFS file was updated for " + AgencyConfig.getAgencyId();
                String message = "For " + AgencyConfig.getAgencyId() + " the GTFS file " + url.getValue()
                        + " was updated so was downloaded to " + httpGetFile.getFullFileName();
                emailSender.send(MonitorBase.recipientsGlobal(), subject, message);

                // Make copy of GTFS zip file in separate directory for archival
                archive(httpGetFile.getFullFileName());
            } else if (httpResponseCode == HttpStatus.SC_NOT_MODIFIED) {
                // If not modified then don't need to do anything
                logger.info(
                        "Remote GTFS file {} not updated (got " + "HTTP NOT_MODIFIED status 304) since the local "
                                + "one  at {} has last modified date of {}",
                        url.getValue(), httpGetFile.getFullFileName(), new Date(file.lastModified()));
            } else {
                // Got unexpected response so log issue
                logger.error("Error retrieving remote GTFS file {} . Http " + "response code={}", url.getValue(),
                        httpResponseCode);
            }
        } catch (IOException e) {
            logger.error("Error retrieving {} . {}", url.getValue(), e.getMessage());
        }
    }

    /**
     * Copies the specified file to a directory at the same directory level but
     * with the directory name that is the last modified date of the file (e.g.
     * 03-28-2015).
     * 
     * @param fullFileName
     *            The full name of the file to be copied
     */
    private static void archive(String fullFileName) {
        // Determine name of directory to archive file into. Use date of
        // lastModified time of file e.g. MM-dd-yyyy.
        File file = new File(fullFileName);
        Date lastModified = new Date(file.lastModified());
        String dirName = Time.dateStr(lastModified);

        // Copy the file to the sibling directory with the name that is the
        // last modified date (e.g. 03-28-2015)
        Path source = Paths.get(fullFileName);
        Path target = source.getParent().getParent().resolve(dirName).resolve(source.getFileName());

        logger.info("Archiving file {} to {}", source.toString(), target.toString());

        try {
            // Create the directory where file is to go
            String fullDirName = target.getParent().toString();
            new File(fullDirName).mkdir();

            // Copy the file to the directory
            Files.copy(source, target, StandardCopyOption.COPY_ATTRIBUTES);
        } catch (IOException e) {
            logger.error("Was not able to archive GTFS file {} to {}", source.toString(), target);
        }
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        // Continue running module forever
        while (true) {
            // Wait until appropriate time
            Time.sleep(intervalMsec.getValue());

            // Get the GTFS file if it has been updated. Catch and
            // handle all exceptions to make sure module continues
            // to run even if there is an unexpected problem.
            try {
                get();
            } catch (Exception e) {
                logger.error(Markers.email(), "Exception in GtfsUpdatedModule", e);
            }
        }
    }

    /**
     * For debugging
     * 
     * @param args
     */
    public static void main(String[] args) {
        // Start the module
        start("org.transitime.gtfs.GtfsUpdatedModule");
    }

}