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