1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "AppFileStorage.h"
23
24 #include <QApplication>
25 #include <QDir>
26
27 #include <U2Core/AppContext.h>
28 #include <U2Core/AppSettings.h>
29 #include <U2Core/U2OpStatusUtils.h>
30 #include <U2Core/U2SafePoints.h>
31 #include <U2Core/UserApplicationsSettings.h>
32
33 namespace U2 {
34
35 const QString StorageRoles::SORTED_BAM("SORTED_BAM");
36 const QString StorageRoles::IMPORTED_BAM("IMPORTED_BAM");
37 const QString StorageRoles::HASH("HASH");
38 const QString StorageRoles::SAM_TO_BAM("SAM_TO_BAM");
39 const QString StorageRoles::CUSTOM_FILE_TO_FILE("CUSTOM_FILE_TO_FILE");
40
41 static const QString DB_FILE_NAME("fileinfo.ugenedb");
42 static const QString WD_DIR_NAME("workflow_data");
43
44 namespace FileStorage {
45 /************************************************************************/
46 /* FileInfo */
47 /************************************************************************/
FileInfo(const QString & url,const QString & role,const QString & info)48 FileInfo::FileInfo(const QString &url, const QString &role, const QString &info)
49 : U2Triplet(url, role, info) {
50 }
51
FileInfo(const U2Triplet & triplet)52 FileInfo::FileInfo(const U2Triplet &triplet)
53 : U2Triplet(triplet) {
54 }
55
getFile() const56 QString FileInfo::getFile() const {
57 return this->getKey();
58 }
59
getInfo() const60 QString FileInfo::getInfo() const {
61 return this->getValue();
62 }
63
isFileToFileInfo() const64 bool FileInfo::isFileToFileInfo() const {
65 CHECK(StorageRoles::CUSTOM_FILE_TO_FILE != getRole(), true);
66 CHECK(StorageRoles::SORTED_BAM != getRole(), true);
67 CHECK(StorageRoles::SAM_TO_BAM != getRole(), true);
68 CHECK(StorageRoles::IMPORTED_BAM != getRole(), true);
69 return false;
70 }
71
72 /************************************************************************/
73 /* WorkflowProcess */
74 /************************************************************************/
WorkflowProcess(const QString & _id)75 WorkflowProcess::WorkflowProcess(const QString &_id)
76 : id(_id) {
77 }
78
~WorkflowProcess()79 WorkflowProcess::~WorkflowProcess() {
80 this->unuseFiles();
81 }
82
getId() const83 QString WorkflowProcess::getId() const {
84 return id;
85 }
86
getTempDirectory() const87 QString WorkflowProcess::getTempDirectory() const {
88 QDir dir(tempDirectory);
89 if (!dir.exists()) {
90 bool created = dir.mkpath(tempDirectory);
91 if (!created) {
92 coreLog.error(QString("Can not create a folder: %1").arg(tempDirectory));
93 }
94 }
95 return tempDirectory;
96 }
97
addFile(const QString & url)98 void WorkflowProcess::addFile(const QString &url) {
99 QFile *f = new QFile(url);
100 bool opened = f->open(QIODevice::ReadOnly);
101 if (!opened) {
102 delete f;
103 return;
104 }
105 usedFiles << f;
106 }
107
unuseFiles()108 void WorkflowProcess::unuseFiles() {
109 foreach (QFile *f, usedFiles) {
110 f->close();
111 delete f;
112 }
113 usedFiles.clear();
114 }
115
116 } // namespace FileStorage
117
118 /************************************************************************/
119 /* AppFileStorage */
120 /************************************************************************/
AppFileStorage()121 AppFileStorage::AppFileStorage()
122 : storage(nullptr) {
123 }
124
init(U2OpStatus & os)125 void AppFileStorage::init(U2OpStatus &os) {
126 UserAppsSettings *settings = AppContext::getAppSettings()->getUserAppsSettings();
127 CHECK_EXT(nullptr != settings, os.setError("NULL user application settings"), );
128
129 storageDir = settings->getFileStorageDir();
130
131 QDir dir(storageDir);
132 if (!dir.exists()) {
133 bool created = dir.mkpath(storageDir);
134 CHECK_EXT(created, os.setError(QString("Can not create a folder: %1").arg(storageDir)), );
135 }
136 QString storageUrl = storageDir + "/" + DB_FILE_NAME;
137
138 storage = new U2SQLiteTripleStore();
139 storage->init(storageUrl, os);
140 }
141
~AppFileStorage()142 AppFileStorage::~AppFileStorage() {
143 if (nullptr != storage) {
144 U2OpStatusImpl os;
145 storage->shutdown(os);
146 if (os.isCoR()) {
147 coreLog.error(os.getError());
148 }
149 delete storage;
150 }
151 }
152
getStorageDir() const153 QString AppFileStorage::getStorageDir() const {
154 return storageDir;
155 }
156
addFileInfo(const FileStorage::FileInfo & info,FileStorage::WorkflowProcess & process,U2OpStatus & os)157 void AppFileStorage::addFileInfo(const FileStorage::FileInfo &info, FileStorage::WorkflowProcess &process, U2OpStatus &os) {
158 storage->addValue(info, os);
159 CHECK_OP(os, );
160
161 if (info.isFileToFileInfo()) {
162 process.addFile(info.getInfo());
163 }
164 }
165
contains(const QString & url,const QString & role,U2OpStatus & os) const166 bool AppFileStorage::contains(const QString &url, const QString &role, U2OpStatus &os) const {
167 return storage->contains(url, role, os);
168 }
169
getFileInfo(const QString & url,const QString & role,FileStorage::WorkflowProcess & process,U2OpStatus & os) const170 QString AppFileStorage::getFileInfo(const QString &url, const QString &role, FileStorage::WorkflowProcess &process, U2OpStatus &os) const {
171 QString info = storage->getValue(url, role, os);
172 if (!info.isEmpty()) {
173 FileStorage::FileInfo i(url, role, info);
174 if (i.isFileToFileInfo()) {
175 process.addFile(info);
176 }
177 }
178 return info;
179 }
180
addFileOwner(const FileStorage::FileInfo & info,FileStorage::WorkflowProcess & process,U2OpStatus & os)181 void AppFileStorage::addFileOwner(const FileStorage::FileInfo &info, FileStorage::WorkflowProcess &process, U2OpStatus &os) {
182 bool exists = storage->contains(info, os);
183 CHECK_OP(os, );
184 if (exists) {
185 if (info.isFileToFileInfo()) {
186 process.addFile(info.getInfo());
187 }
188 } else {
189 os.setError("The file info is not exists. The owner is not added");
190 }
191 }
192
registerWorkflowProcess(FileStorage::WorkflowProcess & process,U2OpStatus & os)193 void AppFileStorage::registerWorkflowProcess(FileStorage::WorkflowProcess &process, U2OpStatus &os) {
194 QString wdDirPath = storageDir + "/" + WD_DIR_NAME + "/" + process.getId();
195 QDir wdDir(wdDirPath);
196 bool created = wdDir.mkpath(wdDirPath);
197 if (!created) {
198 os.setError(QString("Can not create a folder: %1").arg(wdDirPath));
199 return;
200 }
201
202 process.tempDirectory = wdDirPath;
203 }
204
removeFile(const QString & url)205 bool removeFile(const QString &url) {
206 if (!QFile::exists(url)) {
207 return true;
208 }
209 bool ok = QFile::remove(url);
210 if (!ok) {
211 coreLog.error(QString("Can not remove a file: %1").arg(url));
212 }
213 return ok;
214 }
215
removeDirIfEmpty(const QString & url)216 void removeDirIfEmpty(const QString &url) {
217 QDir dir(url);
218 if (dir.exists()) {
219 QStringList subFiles = dir.entryList();
220 subFiles.removeOne(".");
221 subFiles.removeOne("..");
222 if (0 == subFiles.size()) {
223 dir.rmdir(url);
224 }
225 }
226 }
227
unregisterWorkflowProcess(FileStorage::WorkflowProcess & process,U2OpStatus &)228 void AppFileStorage::unregisterWorkflowProcess(FileStorage::WorkflowProcess &process, U2OpStatus & /*os*/) {
229 process.unuseFiles();
230
231 removeDirIfEmpty(process.tempDirectory);
232 }
233
cleanup(U2OpStatus & os)234 void AppFileStorage::cleanup(U2OpStatus &os) {
235 QMutexLocker lock(&cleanupMutex);
236 // 1. Find data
237 QList<U2Triplet> data = storage->getTriplets(os);
238 CHECK_OP(os, );
239
240 QList<U2Triplet> newData;
241 QStringList unremovedFiles;
242 // 2. Remove triplets' files
243 foreach (const U2Triplet &triplet, data) {
244 FileStorage::FileInfo info(triplet);
245 if (info.isFileToFileInfo()) {
246 QString url = info.getValue();
247 bool inTheStorage = url.startsWith(storageDir);
248 bool removed = removeFile(url);
249 if (removed) {
250 removeFile(url + ".bai");
251 }
252 if (removed || !inTheStorage) {
253 U2OpStatus2Log logOs;
254 storage->removeValue(info, logOs);
255 } else {
256 unremovedFiles << url;
257 unremovedFiles << info.getKey(); // source url for hash
258 }
259 } else {
260 newData << triplet;
261 }
262 }
263
264 // 3. Remove triplets' data
265 foreach (const U2Triplet &t, newData) {
266 if (unremovedFiles.contains(t.getKey())) {
267 continue;
268 }
269 U2OpStatus2Log logOs;
270 storage->removeValue(t, logOs);
271 }
272
273 // 4. Remove empty directories
274 QDir stDir(storageDir + "/" + WD_DIR_NAME);
275 foreach (const QFileInfo &info, stDir.entryInfoList()) {
276 if (info.isDir()) {
277 QString name = info.fileName();
278 if ("." == name || ".." == name) {
279 continue;
280 }
281 removeDirIfEmpty(info.absoluteFilePath());
282 }
283 }
284 }
285
createDirectory() const286 QString AppFileStorage::createDirectory() const {
287 QDir storageRoot(storageDir + "/" + WD_DIR_NAME);
288 if (!storageRoot.exists()) {
289 bool created = storageRoot.mkpath(storageRoot.path());
290 SAFE_POINT(created, QString("Can not create a folder: %1").arg(storageRoot.path()), "");
291 }
292 qint64 time = QDateTime::currentDateTime().toSecsSinceEpoch() / 1000;
293 QString baseDirName = QByteArray::number(time);
294
295 int idx = 0;
296 QString result;
297 bool created = false;
298 do {
299 result = baseDirName + "_" + QByteArray::number(idx);
300 created = storageRoot.mkdir(result);
301 idx++;
302 } while (!created);
303
304 return storageRoot.path() + "/" + result;
305 }
306
307 } // namespace U2
308