/* 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.graph_builder.impl; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.zip.ZipFile; import lombok.Setter; import org.onebusaway.csv_entities.CsvInputSource; import org.onebusaway.csv_entities.ZipFileCsvInputSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class DownloadableGtfsInputSource implements CsvInputSource { private static final Logger LOG = LoggerFactory.getLogger(DownloadableGtfsInputSource.class); private URL _url; private File _cacheDirectory; private String _defaultAgencyId; @Setter private boolean useCached = true; // Pattern: Decorator private ZipFileCsvInputSource _zip; public void setUrl(URL url) { _url = url; } public void setCacheDirectory(File cacheDirectory) { _cacheDirectory = cacheDirectory; } private void copyStreams(InputStream in, OutputStream out) throws IOException { byte[] buffer = new byte[1024]; while (true) { int rc = in.read(buffer); if (rc == -1) break; out.write(buffer, 0, rc); } in.close(); out.close(); } private File getTemporaryDirectory() { if (_cacheDirectory != null) { if (!_cacheDirectory.exists()) { if (!_cacheDirectory.mkdirs()) { throw new RuntimeException("Failed to create cache directory " + _cacheDirectory); } } return _cacheDirectory; } return new File(System.getProperty("java.io.tmpdir")); } private File getPathForGtfsBundle() throws IOException { if (_url != null) { File tmpDir = getTemporaryDirectory(); String cacheFile = _defaultAgencyId; if (cacheFile == null) { // Build a cache file based on URL cacheFile = (_url.getHost() + _url.getFile()).replace("/", "_"); } String fileName = cacheFile + "_gtfs.zip"; File gtfsFile = new File(tmpDir, fileName); if (gtfsFile.exists()) { if (useCached) { LOG.info("using already downloaded gtfs file: path=" + gtfsFile); return gtfsFile; } LOG.info("useCached=false; GTFS will be re-downloaded." + gtfsFile); } LOG.info("downloading gtfs: url=" + _url + " path=" + gtfsFile); BufferedInputStream in = new BufferedInputStream(_url.openStream()); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(gtfsFile)); try { copyStreams(in, out); } catch (RuntimeException e) { out.close(); if (!gtfsFile.delete()) { LOG.error("Failed to delete incomplete file " + gtfsFile); } throw e; } return gtfsFile; } throw new IllegalStateException("DownloadableGtfsInputSource did not include an url"); } private synchronized void checkIfDownloaded() throws IOException { if (_zip == null) { _zip = new ZipFileCsvInputSource(new ZipFile(getPathForGtfsBundle())); } } @Override public boolean hasResource(String name) throws IOException { checkIfDownloaded(); return _zip.hasResource(name); } @Override public InputStream getResource(String name) throws IOException { checkIfDownloaded(); return _zip.getResource(name); } @Override public void close() throws IOException { checkIfDownloaded(); _zip.close(); } }