ctkBasicLocation.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkBasicLocation_p.h"
  16. #include "ctkPluginFrameworkProperties_p.h"
  17. #include <ctkException.h>
  18. #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0))
  19. #define HAVE_QT_QLOCKFILE
  20. #include <QLockFile>
  21. #endif
  22. #include <QFileInfo>
  23. #include <QUrl>
  24. #include <QDir>
  25. #include <QDebug>
  26. #include <QReadLocker>
  27. #include <QWriteLocker>
  28. static const QString PROP_OSGI_LOCKING = "blueberry.locking";
  29. static const QString DEFAULT_LOCK_FILENAME = ".metadata/.lock";
  30. //----------------------------------------------------------------------------
  31. struct ctkBasicLocation::Locker
  32. {
  33. virtual ~Locker() {}
  34. virtual bool lock() = 0;
  35. virtual bool isLocked() const = 0;
  36. virtual void release() = 0;
  37. };
  38. //----------------------------------------------------------------------------
  39. struct ctkBasicLocation::MockLocker : public ctkBasicLocation::Locker
  40. {
  41. bool lock() { return true; }
  42. bool isLocked() const { return false; }
  43. void release() {}
  44. };
  45. #ifdef HAVE_QT_QLOCKFILE
  46. //----------------------------------------------------------------------------
  47. class ctkBasicLocation::FileLocker : public ctkBasicLocation::Locker
  48. {
  49. QLockFile m_locker;
  50. QString m_lockFile;
  51. public:
  52. FileLocker(const QString& lockFile)
  53. : m_locker(lockFile)
  54. , m_lockFile(lockFile)
  55. {
  56. m_locker.setStaleLockTime(0);
  57. }
  58. bool lock()
  59. {
  60. bool result = m_locker.tryLock(100);
  61. if(!result)
  62. {
  63. switch (m_locker.error())
  64. {
  65. case QLockFile::PermissionError:
  66. {
  67. qDebug() << "PermissionError: Could not create lock file" << m_lockFile;
  68. break;
  69. }
  70. case QLockFile::LockFailedError:
  71. {
  72. qint64 pid = 0;
  73. QString hostname;
  74. QString appname;
  75. if (m_locker.getLockInfo(&pid, &hostname, &appname))
  76. {
  77. qDebug() << "The lock file" << m_lockFile << "on host" << hostname
  78. << "is already locked by process" << pid;
  79. }
  80. else
  81. {
  82. // lock file was probably just deleted, try again
  83. result = m_locker.tryLock(10);
  84. }
  85. break;
  86. }
  87. case QLockFile::UnknownError:
  88. {
  89. qDebug() << "Could not create or lock file" << m_lockFile << "(unknown error, disk full?)";
  90. break;
  91. }
  92. case QLockFile::NoError: break;
  93. }
  94. }
  95. return result;
  96. }
  97. bool isLocked() const
  98. {
  99. return m_locker.isLocked();
  100. }
  101. void release()
  102. {
  103. m_locker.unlock();
  104. }
  105. };
  106. #else
  107. //----------------------------------------------------------------------------
  108. class ctkBasicLocation::FileLocker : public ctkBasicLocation::MockLocker
  109. {
  110. public:
  111. FileLocker(const QString&) {}
  112. };
  113. #endif
  114. //----------------------------------------------------------------------------
  115. ctkBasicLocation::ctkBasicLocation(const QString& property, const QUrl& defaultValue, bool isReadOnly, const QString& dataAreaPrefix)
  116. : m_isReadOnly(isReadOnly)
  117. , m_parent(NULL)
  118. , m_defaultValue(defaultValue)
  119. , m_property(property)
  120. , m_dataAreaPrefix(dataAreaPrefix)
  121. , m_locker(NULL)
  122. , m_sync()
  123. {
  124. }
  125. //----------------------------------------------------------------------------
  126. ctkBasicLocation::~ctkBasicLocation()
  127. {
  128. QWriteLocker l(&this->m_sync);
  129. delete this->m_locker;
  130. }
  131. //----------------------------------------------------------------------------
  132. bool ctkBasicLocation::allowsDefault() const
  133. {
  134. QReadLocker l(&this->m_sync);
  135. return !this->m_defaultValue.isEmpty();
  136. }
  137. //----------------------------------------------------------------------------
  138. QUrl ctkBasicLocation::getDefault() const
  139. {
  140. QReadLocker l(&this->m_sync);
  141. return this->m_defaultValue;
  142. }
  143. //----------------------------------------------------------------------------
  144. ctkLocation* ctkBasicLocation::getParentLocation() const
  145. {
  146. QReadLocker l(&this->m_sync);
  147. return this->m_parent.data();
  148. }
  149. //----------------------------------------------------------------------------
  150. QUrl ctkBasicLocation::getUrl() const
  151. {
  152. QWriteLocker l(&this->m_sync);
  153. if (this->m_location.isEmpty() && !this->m_defaultValue.isEmpty())
  154. {
  155. const_cast<ctkBasicLocation*>(this)->set_unlocked(this->m_defaultValue, false);
  156. }
  157. return this->m_location;
  158. }
  159. //----------------------------------------------------------------------------
  160. bool ctkBasicLocation::isSet_unlocked() const
  161. {
  162. return !this->m_location.isEmpty();
  163. }
  164. //----------------------------------------------------------------------------
  165. bool ctkBasicLocation::isSet() const
  166. {
  167. QReadLocker l(&this->m_sync);
  168. return this->isSet_unlocked();
  169. }
  170. //----------------------------------------------------------------------------
  171. bool ctkBasicLocation::isReadOnly() const
  172. {
  173. QReadLocker l(&this->m_sync);
  174. return this->m_isReadOnly;
  175. }
  176. //----------------------------------------------------------------------------
  177. bool ctkBasicLocation::set_unlocked(const QUrl& value, bool lock)
  178. {
  179. return this->set_unlocked(value, lock, QString::null);
  180. }
  181. //----------------------------------------------------------------------------
  182. bool ctkBasicLocation::set(const QUrl& value, bool lock)
  183. {
  184. QWriteLocker l(&this->m_sync);
  185. return this->set_unlocked(value, lock, QString::null);
  186. }
  187. //----------------------------------------------------------------------------
  188. bool ctkBasicLocation::set_unlocked(const QUrl& value_, bool lock, const QString& lockFilePath)
  189. {
  190. if (!this->m_location.isEmpty())
  191. {
  192. throw ctkIllegalStateException("Cannot change the location once it is set.");
  193. }
  194. QUrl value = value_;
  195. QFileInfo file;
  196. if (value.scheme().compare("file", Qt::CaseInsensitive) == 0)
  197. {
  198. QString basePath = QFileInfo(value.toLocalFile()).absolutePath();
  199. if (!basePath.isEmpty())
  200. {
  201. value = QUrl::fromLocalFile(basePath);
  202. }
  203. if (!lockFilePath.isEmpty())
  204. {
  205. QFileInfo givenLockFile(lockFilePath);
  206. if (givenLockFile.isAbsolute())
  207. {
  208. file = givenLockFile;
  209. }
  210. else
  211. {
  212. file = QFileInfo(QDir(value.toLocalFile()), lockFilePath);
  213. }
  214. }
  215. else
  216. {
  217. file = QFileInfo(QDir(value.toLocalFile()), DEFAULT_LOCK_FILENAME);
  218. }
  219. }
  220. lock = lock && !this->m_isReadOnly;
  221. if (lock)
  222. {
  223. if (!this->lock_unlocked(file, value))
  224. return false;
  225. }
  226. this->m_lockFile = file;
  227. this->m_location = value;
  228. if (!this->m_property.isEmpty())
  229. {
  230. ctkPluginFrameworkProperties::setProperty(this->m_property, this->m_location.toString());
  231. }
  232. return lock;
  233. }
  234. //----------------------------------------------------------------------------
  235. bool ctkBasicLocation::set(const QUrl& value, bool lock, const QString& lockFilePath)
  236. {
  237. QWriteLocker l(&this->m_sync);
  238. return this->set_unlocked(value, lock, lockFilePath);
  239. }
  240. //----------------------------------------------------------------------------
  241. bool ctkBasicLocation::lock()
  242. {
  243. QWriteLocker l(&this->m_sync);
  244. if (!isSet_unlocked())
  245. {
  246. throw ctkRuntimeException("The location has not been set.");
  247. }
  248. return this->lock_unlocked(this->m_lockFile, this->m_location);
  249. }
  250. //----------------------------------------------------------------------------
  251. void ctkBasicLocation::release()
  252. {
  253. QWriteLocker l(&this->m_sync);
  254. if (this->m_locker)
  255. {
  256. this->m_locker->release();
  257. }
  258. }
  259. //----------------------------------------------------------------------------
  260. bool ctkBasicLocation::isLocked() const
  261. {
  262. QReadLocker l(&this->m_sync);
  263. if (!isSet_unlocked()) return false;
  264. return this->isLocked_unlocked(this->m_lockFile);
  265. }
  266. //----------------------------------------------------------------------------
  267. ctkLocation* ctkBasicLocation::createLocation(ctkLocation* parent, const QUrl& defaultValue, bool readOnly)
  268. {
  269. QWriteLocker l(&this->m_sync);
  270. ctkBasicLocation* result = new ctkBasicLocation(QString::null, defaultValue, readOnly, this->m_dataAreaPrefix);
  271. result->m_parent.reset(parent);
  272. return result;
  273. }
  274. //----------------------------------------------------------------------------
  275. QUrl ctkBasicLocation::getDataArea(const QString& filename_) const
  276. {
  277. QUrl base = getUrl();
  278. if (base.isEmpty())
  279. {
  280. throw ctkRuntimeException("The location has not been set.");
  281. }
  282. QString prefix = base.toLocalFile();
  283. if (!prefix.isEmpty() && prefix.at(prefix.size() - 1) != '/')
  284. {
  285. prefix += '/';
  286. }
  287. QString filename = filename_;
  288. filename.replace('\\', '/');
  289. if (!filename.isEmpty() && filename.at(0) == '/')
  290. {
  291. filename = filename.mid(1);
  292. }
  293. QReadLocker l(&this->m_sync);
  294. return QUrl(prefix + this->m_dataAreaPrefix + filename);
  295. }
  296. //----------------------------------------------------------------------------
  297. void ctkBasicLocation::setParent(ctkLocation* parent)
  298. {
  299. QWriteLocker l(&this->m_sync);
  300. this->m_parent.reset(parent);
  301. }
  302. //----------------------------------------------------------------------------
  303. bool ctkBasicLocation::lock_unlocked(const QFileInfo& lock, const QUrl& locationValue)
  304. {
  305. if (this->m_isReadOnly)
  306. {
  307. throw ctkRuntimeException(QString("The folder \"%1\" is read-only.").arg(lock.absoluteFilePath()));
  308. }
  309. if (lock.fileName().isEmpty())
  310. {
  311. if (locationValue.scheme().compare("file", Qt::CaseInsensitive) != 0)
  312. {
  313. throw ctkRuntimeException(QString("Unable to lock the location. The set location is not a file URL: ") + locationValue.toString());
  314. }
  315. throw ctkIllegalStateException("The lock file has not been set"); // this is really unexpected
  316. }
  317. if (this->isLocked())
  318. {
  319. return false;
  320. }
  321. QDir parentDir = lock.dir();
  322. if (!parentDir.exists())
  323. {
  324. if (!parentDir.mkpath(lock.canonicalPath()))
  325. {
  326. throw ctkRuntimeException(QString("The folder \"%1\" is read-only.").arg(parentDir.absolutePath()));
  327. }
  328. }
  329. setLocker_unlocked(lock);
  330. if (this->m_locker == NULL)
  331. {
  332. return true;
  333. }
  334. bool locked = this->m_locker->lock();
  335. if (!locked) delete this->m_locker;
  336. return locked;
  337. }
  338. //----------------------------------------------------------------------------
  339. void ctkBasicLocation::setLocker_unlocked(const QFileInfo& lock)
  340. {
  341. if (this->m_locker != NULL) return;
  342. QString lockMode = ctkPluginFrameworkProperties::getProperty(PROP_OSGI_LOCKING).toString();
  343. this->m_locker = this->createLocker_unlocked(lock, lockMode);
  344. }
  345. //----------------------------------------------------------------------------
  346. ctkBasicLocation::Locker* ctkBasicLocation::createLocker_unlocked(const QFileInfo& lock, const QString& lockMode_)
  347. {
  348. QString lockMode = lockMode_;
  349. if (lockMode.isEmpty())
  350. {
  351. lockMode = ctkPluginFrameworkProperties::getProperty(PROP_OSGI_LOCKING).toString();
  352. }
  353. if (lockMode == "none")
  354. {
  355. return new MockLocker();
  356. }
  357. if (lockMode == "qt.lockfile")
  358. {
  359. return new FileLocker(lock.canonicalFilePath());
  360. }
  361. // Backup case if an invalid value has been specified
  362. return new FileLocker(lock.canonicalFilePath());
  363. }
  364. //----------------------------------------------------------------------------
  365. bool ctkBasicLocation::isLocked_unlocked(const QFileInfo& lock) const
  366. {
  367. if (lock.fileName().isEmpty() || this->m_isReadOnly)
  368. {
  369. return true;
  370. }
  371. if (!lock.exists())
  372. {
  373. return false;
  374. }
  375. const_cast<ctkBasicLocation*>(this)->setLocker_unlocked(lock);
  376. return this->m_locker->isLocked();
  377. }