/*
This file is part of RouteConverter.
RouteConverter is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
RouteConverter 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 RouteConverter; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Christian Pesch. All Rights Reserved.
*/
package slash.navigation.datasources;
import slash.navigation.datasources.helpers.DataSourceService;
import slash.navigation.download.Action;
import slash.navigation.download.Download;
import slash.navigation.download.DownloadManager;
import slash.navigation.download.FileAndChecksum;
import javax.xml.bind.JAXBException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import static java.lang.String.format;
import static java.util.Collections.singletonList;
import static slash.common.io.Directories.ensureDirectory;
import static slash.common.io.Directories.getApplicationDirectory;
import static slash.navigation.download.Action.Copy;
import static slash.navigation.download.Action.Extract;
import static slash.navigation.download.Action.Flatten;
/**
* Encapsulates access to the download of the DataSource XMLs.
*
* @author Christian Pesch
*/
public class DataSourceManager {
private static final Logger log = Logger.getLogger(DataSourceManager.class.getName());
private static final String EDITIONS = "editions";
public static final String V1 = "v1/";
private static final String EDITIONS_URI = V1 + EDITIONS + "/";
public static final String DATASOURCES_URI = V1 + "datasources/";
public static final String FORMAT_XML = "?format=xml";
private static final String DOT_XML = ".xml";
public static final String DOT_ZIP = ".zip";
private final DownloadManager downloadManager;
private DataSourceService dataSourceService = new DataSourceService();
public DataSourceManager(DownloadManager downloadManager) {
this.downloadManager = downloadManager;
}
public DataSourceService getDataSourceService() {
return dataSourceService;
}
public DownloadManager getDownloadManager() {
return downloadManager;
}
public void dispose() {
dataSourceService.clear();
getDownloadManager().dispose();
}
public void initialize(String edition, java.io.File directory) throws IOException, JAXBException {
java.io.File file = new File(directory, edition + DOT_XML);
log.info(format("Initializing edition '%s' from %s", edition, file));
Edition anEdition = loadEdition(file);
if(anEdition == null)
return;
this.dataSourceService = loadDataSources(anEdition.getDataSources(), directory);
}
private Edition loadEdition(java.io.File file) throws IOException, JAXBException {
if (!file.exists()) {
log.warning(format("Cannot find edition file %s", file));
return null;
}
DataSourceService service = loadDataSource(file);
List<Edition> editions = service.getEditions();
return editions.size() > 0 ? editions.get(0) : null;
}
private DataSourceService loadDataSources(List<DataSource> dataSources, java.io.File directory) throws IOException, JAXBException {
DataSourceService result = new DataSourceService();
for (DataSource dataSource : new ArrayList<>(dataSources)) {
java.io.File file = new java.io.File(directory, dataSource.getId() + DOT_XML);
log.info(format("Initializing data source from %s", file));
if (!file.exists()) {
log.warning(format("Cannot find data source file %s", file));
continue;
}
try (InputStream inputStream = new FileInputStream(file)) {
result.load(inputStream);
}
}
return result;
}
public void update(String edition, String url, java.io.File directory) throws IOException, JAXBException {
log.info(format("Updating edition '%s' from %s to %s", edition, url, directory));
java.io.File file = new java.io.File(directory, edition + DOT_XML);
downloadEdition(edition, url, file);
Edition anEdition = loadEdition(file);
if(anEdition == null)
return;
downloadDataSources(anEdition.getDataSources(), directory);
this.dataSourceService = loadDataSources(anEdition.getDataSources(), directory);
updateQueueFromDataSources();
}
public void downloadRoot(String url, java.io.File directory) {
String editionsUrl = url + EDITIONS_URI + FORMAT_XML;
java.io.File file = new java.io.File(directory, EDITIONS + DOT_XML);
Download download = downloadManager.queueForDownload("RouteConverter Editions", editionsUrl, Copy,
new FileAndChecksum(file, null), null);
downloadManager.waitForCompletion(singletonList(download));
}
private void downloadEdition(String edition, String url, java.io.File file) {
String editionUrl = url + EDITIONS_URI + edition + "/" + FORMAT_XML;
Download download = downloadManager.queueForDownload("RouteConverter Edition: " + edition, editionUrl, Copy,
new FileAndChecksum(file, null), null);
downloadManager.waitForCompletion(singletonList(download));
}
public void downloadEditions(List<Edition> editions, java.io.File directory) throws JAXBException, FileNotFoundException {
List<Download> downloads = new ArrayList<>();
for (Edition edition : editions) {
String editionUrl = edition.getHref() + FORMAT_XML;
java.io.File file = new java.io.File(directory, edition.getId() + DOT_XML);
Download download = downloadManager.queueForDownload("RouteConverter Edition: " + edition.getId(),
editionUrl, Copy, new FileAndChecksum(file, null), null);
downloads.add(download);
}
downloadManager.waitForCompletion(downloads);
}
public void downloadDataSources(List<DataSource> dataSources, java.io.File directory) throws JAXBException, FileNotFoundException {
List<Download> downloads = new ArrayList<>();
for (DataSource dataSource : dataSources) {
String datasourceUrl = dataSource.getHref() + FORMAT_XML;
java.io.File file = new java.io.File(directory, dataSource.getId() + DOT_XML);
Download download = downloadManager.queueForDownload("Datasource: " + dataSource.getId(),
datasourceUrl, Copy, new FileAndChecksum(file, null), null);
downloads.add(download);
}
downloadManager.waitForCompletion(downloads);
}
private static DataSourceService loadDataSource(java.io.File file) throws IOException, JAXBException {
DataSourceService result = new DataSourceService();
try (InputStream inputStream = new FileInputStream(file)) {
result.load(inputStream);
}
return result;
}
public static DataSourceService loadAllDataSources(java.io.File directory) throws IOException, JAXBException {
DataSourceService result = new DataSourceService();
java.io.File[] files = directory.listFiles(new FilenameFilter() {
public boolean accept(java.io.File dir, String name) {
return name.endsWith(DOT_XML);
}
});
if (files != null) {
for (java.io.File file : files) {
try (InputStream inputStream = new FileInputStream(file)) {
result.load(inputStream);
}
}
}
return result;
}
private void scanForFilesMissingInQueue(File directory) throws IOException {
java.io.File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
Downloadable downloadable = dataSourceService.getDownloadable(file);
if(downloadable != null) {
DataSource dataSource = downloadable.getDataSource();
addOrUpdateInQueue(dataSource, downloadable);
} else
log.fine("Cannot find downloadable for " + file);
} else if (file.isDirectory())
scanForFilesMissingInQueue(file);
}
}
}
public void scanForFilesMissingInQueue() throws IOException {
scanForFilesMissingInQueue(getApplicationDirectory());
}
private void addOrUpdateInQueue(DataSource dataSource, Downloadable downloadable) {
Action action = Action.valueOf(dataSource.getAction());
File directory = getApplicationDirectory(dataSource.getDirectory());
File target = directory; // Flatten
if (action.equals(Copy))
target = new File(directory, downloadable.getUri());
else if (action.equals(Extract))
target = target.getParentFile();
downloadManager.addOrUpdateInQueue(dataSource.getName() + ": " + downloadable.getUri(),
dataSource.getBaseUrl() + downloadable.getUri(), action,
new FileAndChecksum(target, downloadable.getLatestChecksum()),
asFragments(directory, downloadable.getFragments(), action.equals(Extract)));
}
public Download queueForDownload(DataSource dataSource, Downloadable downloadable) {
Action action = Action.valueOf(dataSource.getAction());
File directory = getApplicationDirectory(dataSource.getDirectory());
File target = new File(directory, downloadable.getUri());
if (action.equals(Extract) || action.equals(Flatten))
target = ensureDirectory(target.getParentFile());
return downloadManager.queueForDownload(dataSource.getName() + ": " + downloadable.getUri(),
dataSource.getBaseUrl() + downloadable.getUri(), action,
new FileAndChecksum(target, downloadable.getLatestChecksum()),
asFragments(target, downloadable.getFragments(), false));
}
private List<FileAndChecksum> asFragments(File directory, List<Fragment<Downloadable>> fragments,
boolean useDirectoryParent) {
if(fragments == null)
return null;
List<FileAndChecksum> result = new ArrayList<>();
for (Fragment<Downloadable> fragment : fragments) {
String key = fragment.getKey();
File target = new File(useDirectoryParent ? directory.getParentFile() : directory, key);
result.add(new FileAndChecksum(target, fragment.getLatestChecksum()));
}
return result;
}
private void updateQueueFromDataSources() {
for(Download download : downloadManager.getModel().getDownloads()) {
Downloadable downloadable = dataSourceService.getDownloadable(download.getUrl());
if(downloadable != null) {
DataSource dataSource = downloadable.getDataSource();
addOrUpdateInQueue(dataSource, downloadable);
} else
log.fine("Cannot find downloadable for " + download.getUrl());
}
}
}