CachedFile.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2008 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "CachedFile.h"
17 
18 #include "base/TempDirectory.h"
19 #include "base/ProgressReporter.h"
20 #include "base/Exceptions.h"
21 
22 #include "FileSource.h"
23 
24 #include <QFileInfo>
25 #include <QSettings>
26 #include <QVariant>
27 #include <QMap>
28 #include <QDir>
29 #include <QCryptographicHash>
30 
31 #include "base/Profiler.h"
32 
33 #include <iostream>
34 
37 
38 QString
40 {
41  Profiler p("CachedFile::getLocalFilenameFor");
42 
43  QDir dir(getCacheDirectory());
44 
45  QString filename =
46  QString::fromLocal8Bit
47  (QCryptographicHash::hash(url.toString().toLocal8Bit(),
48  QCryptographicHash::Sha1).toHex());
49 
50  return dir.filePath(filename);
51 }
52 
53 QString
55 {
57 
58  QString cacheDirName("cache");
59 
60  QFileInfo fi(dir.filePath(cacheDirName));
61 
62  if ((fi.exists() && !fi.isDir()) ||
63  (!fi.exists() && !dir.mkdir(cacheDirName))) {
64 
65  throw DirectoryCreationFailed(fi.filePath());
66  }
67 
68  return fi.filePath();
69 }
70 
71 CachedFile::CachedFile(QString origin,
72  ProgressReporter *reporter,
73  QString preferredContentType) :
74  m_origin(origin),
75  m_preferredContentType(preferredContentType),
76  m_reporter(reporter),
77  m_ok(false)
78 {
79  Profiler p("CachedFile::CachedFile[1]");
80 
81  SVDEBUG << "CachedFile::CachedFile: origin is \""
82  << origin << "\"" << endl;
83  checkFile();
84 }
85 
87  ProgressReporter *reporter,
88  QString preferredContentType) :
89  m_origin(url.toString()),
90  m_preferredContentType(preferredContentType),
91  m_reporter(reporter),
92  m_ok(false)
93 {
94  Profiler p("CachedFile::CachedFile[2]");
95 
96  SVDEBUG << "CachedFile::CachedFile: url is \""
97  << url.toString() << "\"" << endl;
98  checkFile();
99 }
100 
102 {
103 }
104 
105 bool
107 {
108  return m_ok;
109 }
110 
111 QString
113 {
114  return m_localFilename;
115 }
116 
117 void
119 {
121  // objects for same url used in more than one thread -- need to
122  // lock appropriately. also consider race condition between
123  // separate instances of the program!
124 
125  OriginLocalFilenameMap::const_iterator i = m_knownGoodCaches.find(m_origin);
126  if (i != m_knownGoodCaches.end()) {
127  m_ok = true;
128  m_localFilename = i->second;
129  return;
130  }
131 
133 
134  if (!QFileInfo(m_localFilename).exists()) {
135  SVDEBUG << "CachedFile::check: Local file does not exist, making a note that it hasn't been retrieved" << endl;
136  updateLastRetrieval(false); // empirically!
137  }
138 
139  QDateTime lastRetrieval = getLastRetrieval();
140 
141  if (lastRetrieval.isValid()) {
142  SVDEBUG << "CachedFile::check: Valid last retrieval at "
143  << lastRetrieval.toString() << endl;
144  // this will not be the case if the file is missing, after
145  // updateLastRetrieval(false) was called above
146  m_ok = true;
147  if (lastRetrieval.addDays(2) < QDateTime::currentDateTime()) {
148  SVDEBUG << "CachedFile::check: Out of date; trying to retrieve again" << endl;
149  // doesn't matter if retrieval fails -- we just don't
150  // update the last retrieval time
151 
153  // timestamp so as to ensure we aren't retrying the
154  // retrieval every single time if it isn't working
155 
156  if (retrieve()) {
157  SVDEBUG << "CachedFile::check: Retrieval succeeded" << endl;
158  updateLastRetrieval(true);
159  } else {
160  SVCERR << "CachedFile::check: Retrieval failed, will try again later (using existing file for now)" << endl;
161  }
162  }
163  } else {
164  SVDEBUG << "CachedFile::check: No valid last retrieval" << endl;
165  // there is no acceptable file
166  if (retrieve()) {
167  SVDEBUG << "CachedFile::check: Retrieval succeeded" << endl;
168  m_ok = true;
169  updateLastRetrieval(true);
170  } else {
171  SVCERR << "CachedFile::check: Retrieval failed, remaining in invalid state" << endl;
172  // again, we don't need to do anything here -- the last
173  // retrieval timestamp is already invalid
174  }
175  }
176 
177  if (m_ok) {
179  }
180 }
181 
182 bool
184 {
190 
192 
193  if (!fs.isOK() || !fs.isAvailable()) {
194  SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported unavailable or failure" << endl;
195  return false;
196  }
197 
198  fs.waitForData();
199 
200  if (!fs.isOK()) {
201  SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported failure during receive" << endl;
202  return false;
203  }
204 
205  QString tempName = fs.getLocalFilename();
206  QFile tempFile(tempName);
207  if (!tempFile.exists()) {
208  SVDEBUG << "CachedFile::retrieve: ERROR: FileSource reported success, but local temporary file \"" << tempName << "\" does not exist" << endl;
209  return false;
210  }
211 
212  QFile previous(m_localFilename);
213  if (previous.exists()) {
214  if (!previous.remove()) {
215  SVCERR << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename << "\"" << endl;
216  return false;
217  }
218  }
219 
223 
224  if (!tempFile.copy(m_localFilename)) {
225  SVCERR << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName << "\" to \"" << m_localFilename << "\"" << endl;
226  return false;
227  }
228 
229  SVDEBUG << "CachedFile::retrieve: Successfully copied newly retrieved file \"" << tempName << "\" to its home at \"" << m_localFilename << "\"" << endl;
230 
231  return true;
232 }
233 
234 QDateTime
236 {
237  QSettings settings;
238  settings.beginGroup("FileCache");
239 
240  QString key("last-retrieval-times");
241 
242  QMap<QString, QVariant> timeMap = settings.value(key).toMap();
243  QDateTime lastTime = timeMap[m_localFilename].toDateTime();
244 
245  settings.endGroup();
246  return lastTime;
247 }
248 
249 void
251 {
255 
256  QSettings settings;
257  settings.beginGroup("FileCache");
258 
259  QString key("last-retrieval-times");
260 
261  QMap<QString, QVariant> timeMap = settings.value(key).toMap();
262 
263  QDateTime dt;
264  if (successful) dt = QDateTime::currentDateTime();
265 
266  timeMap[m_localFilename] = dt;
267  settings.setValue(key, timeMap);
268 
269  settings.endGroup();
270 }
271 
272 
bool retrieve()
Definition: CachedFile.cpp:183
bool m_ok
Definition: CachedFile.h:47
CachedFile(QString fileOrUrl, ProgressReporter *reporter=0, QString preferredContentType="")
Definition: CachedFile.cpp:71
void checkFile()
Definition: CachedFile.cpp:118
QString m_localFilename
Definition: CachedFile.h:44
QString getLocalFilename() const
Return the name of the local file this FileSource refers to.
Definition: FileSource.cpp:622
static OriginLocalFilenameMap m_knownGoodCaches
Definition: CachedFile.h:59
QString m_preferredContentType
Definition: CachedFile.h:45
void waitForData()
Block on a sub-event-loop until the whole of the data has been retrieved (if it is remote)...
Definition: FileSource.cpp:570
QDateTime getLastRetrieval()
Definition: CachedFile.cpp:235
QString m_origin
Definition: CachedFile.h:43
QString getLocalFilename() const
Definition: CachedFile.cpp:112
std::map< QString, QString > OriginLocalFilenameMap
Definition: CachedFile.h:58
static TempDirectory * getInstance()
static QString getLocalFilenameFor(QUrl url)
Definition: CachedFile.cpp:39
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
bool isOK() const
Definition: CachedFile.cpp:106
static QString getCacheDirectory()
Definition: CachedFile.cpp:54
#define SVDEBUG
Definition: Debug.h:106
virtual ~CachedFile()
Definition: CachedFile.cpp:101
#define SVCERR
Definition: Debug.h:109
QString getContainingPath()
Return the path of the directory in which the temporary directory has been or will be created...
ProgressReporter * m_reporter
Definition: CachedFile.h:46
void updateLastRetrieval(bool successful)
Definition: CachedFile.cpp:250
Profile point instance class.
Definition: Profiler.h:93
bool isOK() const
Return true if the FileSource object is valid and neither error nor cancellation occurred while retri...
Definition: FileSource.cpp:586
bool isAvailable()
Return true if the file or remote URL exists.
Definition: FileSource.cpp:544