annotate data/fileio/CachedFile.cpp @ 489:82ab61fa9223

* Reorganise our sparql queries on the basis that Redland must be available, not only optional. So for anything querying the pool of data about plugins, we use a single datastore and model which is initialised at the outset by PluginRDFIndexer and then queried directly; for anything that "reads from a file" (e.g. loading annotations) we query directly using Rasqal, going to the datastore when we need additional plugin-related information. This may improve performance, but mostly it simplifies the code and fixes a serious issue with RDF import in the previous versions (namely that multiple sequential RDF imports would end up sharing the same RDF data pool!)
author Chris Cannam
date Fri, 21 Nov 2008 16:12:29 +0000
parents a82645e788fc
children e340b2fb9471
rev   line source
Chris@465 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@465 2
Chris@465 3 /*
Chris@465 4 Sonic Visualiser
Chris@465 5 An audio file viewer and annotation editor.
Chris@465 6 Centre for Digital Music, Queen Mary, University of London.
Chris@465 7 This file copyright 2008 QMUL.
Chris@465 8
Chris@465 9 This program is free software; you can redistribute it and/or
Chris@465 10 modify it under the terms of the GNU General Public License as
Chris@465 11 published by the Free Software Foundation; either version 2 of the
Chris@465 12 License, or (at your option) any later version. See the file
Chris@465 13 COPYING included with this distribution for more information.
Chris@465 14 */
Chris@465 15
Chris@465 16 #include "CachedFile.h"
Chris@465 17
Chris@465 18 #include "base/TempDirectory.h"
Chris@465 19 #include "base/ProgressReporter.h"
Chris@465 20 #include "base/Exceptions.h"
Chris@465 21
Chris@466 22 #include "FileSource.h"
Chris@466 23
Chris@465 24 #include <QFileInfo>
Chris@465 25 #include <QSettings>
Chris@465 26 #include <QVariant>
Chris@465 27 #include <QMap>
Chris@465 28 #include <QDir>
Chris@465 29 #include <QCryptographicHash>
Chris@465 30
Chris@481 31 #include "base/Profiler.h"
Chris@481 32
Chris@466 33 #include <iostream>
Chris@466 34
Chris@481 35 CachedFile::OriginLocalFilenameMap
Chris@481 36 CachedFile::m_knownGoodCaches;
Chris@481 37
Chris@465 38 QString
Chris@465 39 CachedFile::getLocalFilenameFor(QUrl url)
Chris@465 40 {
Chris@481 41 Profiler p("CachedFile::getLocalFilenameFor");
Chris@481 42
Chris@465 43 QDir dir(getCacheDirectory());
Chris@465 44
Chris@465 45 QString filename =
Chris@465 46 QString::fromLocal8Bit
Chris@465 47 (QCryptographicHash::hash(url.toString().toLocal8Bit(),
Chris@465 48 QCryptographicHash::Sha1).toHex());
Chris@465 49
Chris@465 50 return dir.filePath(filename);
Chris@465 51 }
Chris@465 52
Chris@465 53 QString
Chris@465 54 CachedFile::getCacheDirectory()
Chris@465 55 {
Chris@465 56 QDir dir = TempDirectory::getInstance()->getContainingPath();
Chris@465 57
Chris@465 58 QString cacheDirName("cache");
Chris@465 59
Chris@465 60 QFileInfo fi(dir.filePath(cacheDirName));
Chris@465 61
Chris@465 62 if ((fi.exists() && !fi.isDir()) ||
Chris@465 63 (!fi.exists() && !dir.mkdir(cacheDirName))) {
Chris@465 64
Chris@465 65 throw DirectoryCreationFailed(fi.filePath());
Chris@465 66 }
Chris@465 67
Chris@465 68 return fi.filePath();
Chris@465 69 }
Chris@465 70
Chris@468 71 CachedFile::CachedFile(QString origin, ProgressReporter *reporter) :
Chris@468 72 m_origin(origin),
Chris@466 73 m_reporter(reporter),
Chris@465 74 m_ok(false)
Chris@465 75 {
Chris@481 76 Profiler p("CachedFile::CachedFile[1]");
Chris@481 77
Chris@468 78 std::cerr << "CachedFile::CachedFile: origin is \""
Chris@468 79 << origin.toStdString() << "\"" << std::endl;
Chris@467 80 check();
Chris@467 81 }
Chris@467 82
Chris@467 83 CachedFile::CachedFile(QUrl url, ProgressReporter *reporter) :
Chris@468 84 m_origin(url.toString()),
Chris@467 85 m_reporter(reporter),
Chris@467 86 m_ok(false)
Chris@467 87 {
Chris@481 88 Profiler p("CachedFile::CachedFile[2]");
Chris@481 89
Chris@467 90 std::cerr << "CachedFile::CachedFile: url is \""
Chris@467 91 << url.toString().toStdString() << "\"" << std::endl;
Chris@467 92 check();
Chris@467 93 }
Chris@467 94
Chris@467 95 CachedFile::~CachedFile()
Chris@467 96 {
Chris@465 97 }
Chris@465 98
Chris@465 99 bool
Chris@465 100 CachedFile::isOK() const
Chris@465 101 {
Chris@465 102 return m_ok;
Chris@465 103 }
Chris@465 104
Chris@465 105 QString
Chris@465 106 CachedFile::getLocalFilename() const
Chris@465 107 {
Chris@465 108 return m_localFilename;
Chris@465 109 }
Chris@465 110
Chris@465 111 void
Chris@467 112 CachedFile::check()
Chris@465 113 {
Chris@465 114 //!!! n.b. obvious race condition here if different CachedFile
Chris@465 115 // objects for same url used in more than one thread -- need to
Chris@465 116 // lock appropriately. also consider race condition between
Chris@481 117 // separate instances of the program!
Chris@481 118
Chris@481 119 OriginLocalFilenameMap::const_iterator i = m_knownGoodCaches.find(m_origin);
Chris@481 120 if (i != m_knownGoodCaches.end()) {
Chris@481 121 m_ok = true;
Chris@481 122 m_localFilename = i->second;
Chris@481 123 return;
Chris@481 124 }
Chris@481 125
Chris@481 126 m_localFilename = getLocalFilenameFor(m_origin);
Chris@465 127
Chris@465 128 if (!QFileInfo(m_localFilename).exists()) {
Chris@467 129 std::cerr << "CachedFile::check: Local file does not exist, making a note that it hasn't been retrieved" << std::endl;
Chris@465 130 updateLastRetrieval(false); // empirically!
Chris@465 131 }
Chris@465 132
Chris@465 133 QDateTime lastRetrieval = getLastRetrieval();
Chris@465 134
Chris@465 135 if (lastRetrieval.isValid()) {
Chris@467 136 std::cerr << "CachedFile::check: Valid last retrieval at "
Chris@467 137 << lastRetrieval.toString().toStdString() << std::endl;
Chris@465 138 // this will not be the case if the file is missing, after
Chris@465 139 // updateLastRetrieval(false) was called above
Chris@465 140 m_ok = true;
Chris@465 141 if (lastRetrieval.addDays(2) < QDateTime::currentDateTime()) { //!!!
Chris@467 142 std::cerr << "CachedFile::check: Out of date; trying to retrieve again" << std::endl;
Chris@465 143 // doesn't matter if retrieval fails -- we just don't
Chris@465 144 // update the last retrieval time
Chris@465 145
Chris@465 146 //!!! but we do want an additional last-attempted
Chris@465 147 // timestamp so as to ensure we aren't retrying the
Chris@465 148 // retrieval every single time if it isn't working
Chris@465 149
Chris@465 150 if (retrieve()) {
Chris@467 151 std::cerr << "CachedFile::check: Retrieval succeeded" << std::endl;
Chris@465 152 updateLastRetrieval(true);
Chris@467 153 } else {
Chris@467 154 std::cerr << "CachedFile::check: Retrieval failed, will try again later (using existing file for now)" << std::endl;
Chris@467 155 }
Chris@465 156 }
Chris@465 157 } else {
Chris@467 158 std::cerr << "CachedFile::check: No valid last retrieval" << std::endl;
Chris@465 159 // there is no acceptable file
Chris@465 160 if (retrieve()) {
Chris@467 161 std::cerr << "CachedFile::check: Retrieval succeeded" << std::endl;
Chris@465 162 m_ok = true;
Chris@465 163 updateLastRetrieval(true);
Chris@465 164 } else {
Chris@467 165 std::cerr << "CachedFile::check: Retrieval failed, remaining in invalid state" << std::endl;
Chris@465 166 // again, we don't need to do anything here -- the last
Chris@465 167 // retrieval timestamp is already invalid
Chris@465 168 }
Chris@465 169 }
Chris@481 170
Chris@481 171 if (m_ok) {
Chris@481 172 m_knownGoodCaches[m_origin] = m_localFilename;
Chris@481 173 }
Chris@465 174 }
Chris@465 175
Chris@465 176 bool
Chris@465 177 CachedFile::retrieve()
Chris@465 178 {
Chris@465 179 //!!! need to work by retrieving the file to another name, and
Chris@465 180 //!!! then "atomically" moving it to its proper place (I'm not
Chris@465 181 //!!! sure we can do an atomic move to replace an existing file
Chris@465 182 //!!! using Qt classes, but a plain delete then copy is probably
Chris@465 183 //!!! good enough)
Chris@465 184
Chris@468 185 FileSource fs(m_origin, m_reporter);
Chris@465 186
Chris@466 187 if (!fs.isOK() || !fs.isAvailable()) {
Chris@468 188 std::cerr << "CachedFile::retrieve: ERROR: FileSource reported unavailable or failure" << std::endl;
Chris@466 189 return false;
Chris@466 190 }
Chris@465 191
Chris@466 192 fs.waitForData();
Chris@465 193
Chris@466 194 if (!fs.isOK()) {
Chris@468 195 std::cerr << "CachedFile::retrieve: ERROR: FileSource reported failure during receive" << std::endl;
Chris@466 196 return false;
Chris@466 197 }
Chris@466 198
Chris@466 199 QString tempName = fs.getLocalFilename();
Chris@466 200 QFile tempFile(tempName);
Chris@466 201 if (!tempFile.exists()) {
Chris@466 202 std::cerr << "CachedFile::retrieve: ERROR: FileSource reported success, but local temporary file \"" << tempName.toStdString() << "\" does not exist" << std::endl;
Chris@466 203 return false;
Chris@466 204 }
Chris@466 205
Chris@466 206 QFile previous(m_localFilename);
Chris@466 207 if (previous.exists()) {
Chris@466 208 if (!previous.remove()) {
Chris@466 209 std::cerr << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@466 210 return false;
Chris@466 211 }
Chris@466 212 }
Chris@466 213
Chris@466 214 //!!! This is not ideal, could leave us with nothing (old file
Chris@466 215 //!!! removed, new file not able to be copied in because e.g. no
Chris@466 216 //!!! disk space left)
Chris@466 217
Chris@466 218 if (!tempFile.copy(m_localFilename)) {
Chris@466 219 std::cerr << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName.toStdString() << "\" to \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@466 220 return false;
Chris@466 221 }
Chris@466 222
Chris@468 223 std::cerr << "CachedFile::retrieve: Successfully copied newly retrieved file \"" << tempName.toStdString() << "\" to its home at \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@468 224
Chris@466 225 return true;
Chris@465 226 }
Chris@465 227
Chris@465 228 QDateTime
Chris@465 229 CachedFile::getLastRetrieval()
Chris@465 230 {
Chris@465 231 QSettings settings;
Chris@465 232 settings.beginGroup("FileCache");
Chris@465 233
Chris@465 234 QString key("last-retrieval-times");
Chris@465 235
Chris@465 236 QMap<QString, QVariant> timeMap = settings.value(key).toMap();
Chris@465 237 QDateTime lastTime = timeMap[m_localFilename].toDateTime();
Chris@465 238
Chris@465 239 settings.endGroup();
Chris@465 240 return lastTime;
Chris@465 241 }
Chris@465 242
Chris@465 243 void
Chris@465 244 CachedFile::updateLastRetrieval(bool successful)
Chris@465 245 {
Chris@465 246 //!!! note !successful does not mean "we failed to update the
Chris@465 247 //!!! file" (and so it remains the same as before); it means "the
Chris@465 248 //!!! file is not there at all"
Chris@465 249
Chris@465 250 QSettings settings;
Chris@465 251 settings.beginGroup("FileCache");
Chris@465 252
Chris@465 253 QString key("last-retrieval-times");
Chris@465 254
Chris@465 255 QMap<QString, QVariant> timeMap = settings.value(key).toMap();
Chris@465 256
Chris@465 257 QDateTime dt;
Chris@465 258 if (successful) dt = QDateTime::currentDateTime();
Chris@465 259
Chris@465 260 timeMap[m_localFilename] = dt;
Chris@465 261 settings.setValue(key, timeMap);
Chris@465 262
Chris@465 263 settings.endGroup();
Chris@465 264 }
Chris@465 265
Chris@465 266