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 "WorkflowContext.h"
23 
24 #include <QApplication>
25 #include <QDir>
26 #include <QFileInfo>
27 #include <QMutexLocker>
28 #include <QTextStream>
29 
30 #include <U2Core/AppContext.h>
31 #include <U2Core/AppFileStorage.h>
32 #include <U2Core/CMDLineRegistry.h>
33 #include <U2Core/FileAndDirectoryUtils.h>
34 #include <U2Core/U2OpStatusUtils.h>
35 #include <U2Core/U2SafePoints.h>
36 
37 #include <U2Lang/ActorModel.h>
38 #include <U2Lang/BaseTypes.h>
39 #include <U2Lang/Datatype.h>
40 #include <U2Lang/GrouperOutSlot.h>
41 #include <U2Lang/IntegralBus.h>
42 #include <U2Lang/WorkflowMonitor.h>
43 #include <U2Lang/WorkflowSettings.h>
44 
45 namespace U2 {
46 namespace Workflow {
47 
getWorkflowId(WorkflowContext * ctx)48 static QString getWorkflowId(WorkflowContext *ctx) {
49     qint64 pid = QApplication::applicationPid();
50     QString wId = QByteArray::number(pid) + "_" + QByteArray::number(qint64(ctx));
51 
52     return wId;
53 }
54 
WorkflowContext(const QList<Actor * > & procs,WorkflowMonitor * _monitor)55 WorkflowContext::WorkflowContext(const QList<Actor *> &procs, WorkflowMonitor *_monitor)
56     : monitor(_monitor), storage(nullptr), process("") {
57     foreach (Actor *p, procs) {
58         procMap.insert(p->getId(), p);
59     }
60 
61     {  // register WD process
62         AppFileStorage *fileStorage = AppContext::getAppFileStorage();
63         CHECK(nullptr != fileStorage, );
64 
65         U2OpStatusImpl os;
66         process = WorkflowProcess(getWorkflowId(this));
67         fileStorage->registerWorkflowProcess(process, os);
68         CHECK_OP(os, );
69     }
70 }
71 
~WorkflowContext()72 WorkflowContext::~WorkflowContext() {
73     foreach (const QString &url, externalProcessFiles) {
74         QFile::remove(url);
75     }
76     delete storage;
77 
78     // unregister WD process
79     if (!process.getId().isEmpty()) {
80         AppFileStorage *fileStorage = AppContext::getAppFileStorage();
81         CHECK(nullptr != fileStorage, );
82 
83         U2OpStatusImpl os;
84         fileStorage->unregisterWorkflowProcess(process, os);
85     }
86 }
87 
init()88 bool WorkflowContext::init() {
89     storage = new DbiDataStorage();
90     CHECK(initWorkingDir(), false);
91     return storage->init();
92 }
93 
getDataStorage()94 DbiDataStorage *WorkflowContext::getDataStorage() {
95     return storage;
96 }
97 
getMonitor()98 WorkflowMonitor *WorkflowContext::getMonitor() {
99     return monitor;
100 }
101 
addExternalProcessFile(const QString & url)102 void WorkflowContext::addExternalProcessFile(const QString &url) {
103     QMutexLocker locker(&addFileMutex);
104     externalProcessFiles << url;
105 }
106 
getOutSlotType(const QString & slotStr)107 DataTypePtr WorkflowContext::getOutSlotType(const QString &slotStr) {
108     QStringList tokens = slotStr.split(">");
109     assert(tokens.size() > 0);
110     tokens = tokens[0].split(".");
111     assert(2 == tokens.size());
112 
113     Actor *proc = procMap.value(tokens[0], nullptr);
114     if (nullptr == proc) {
115         return DataTypePtr();
116     }
117 
118     QString slotId = tokens[1];
119     foreach (Port *port, proc->getOutputPorts()) {
120         assert(port->getOutputType()->isMap());
121         QMap<Descriptor, DataTypePtr> typeMap = port->getOutputType()->getDatatypesMap();
122 
123         if (typeMap.keys().contains(slotId)) {
124             DataTypePtr type = typeMap.value(slotId);
125             assert(DataType::Single == type->kind());
126             return type;
127         }
128     }
129 
130     return DataTypePtr();
131 }
132 
getWorkflowProcess() const133 const WorkflowProcess &WorkflowContext::getWorkflowProcess() const {
134     return process;
135 }
136 
getWorkflowProcess()137 WorkflowProcess &WorkflowContext::getWorkflowProcess() {
138     return process;
139 }
140 
workingDir() const141 QString WorkflowContext::workingDir() const {
142     return _workingDir;
143 }
144 
absolutePath(const QString & relative) const145 QString WorkflowContext::absolutePath(const QString &relative) const {
146     CHECK(!relative.isEmpty(), "");
147     QFileInfo info(relative);
148     if (info.isAbsolute()) {
149         return info.absoluteFilePath();
150     }
151 
152     if (relative.startsWith(".") || relative.startsWith("..")) {
153         return info.absoluteFilePath();
154     }
155 
156     return workingDir() + relative;
157 }
158 
getMetadataStorage()159 MessageMetadataStorage &WorkflowContext::getMetadataStorage() {
160     return metadataStorage;
161 }
162 
initWorkingDir()163 bool WorkflowContext::initWorkingDir() {
164     U2OpStatus2Log os;
165 
166     QString root = WorkflowContextCMDLine::getOutputDirectory(os);
167     CHECK_OP(os, false);
168 
169     if (!root.endsWith("/")) {
170         root += "/";
171     }
172 
173     if (WorkflowContextCMDLine::useSubDirs()) {
174         QString dirName = WorkflowContextCMDLine::createSubDirectoryForRun(root, os);
175         CHECK_OP(os, false);
176         _workingDir = root + dirName + "/";
177     } else {
178         _workingDir = root;
179     }
180     if (!AppContext::isGUIMode()) {
181         WorkflowContextCMDLine::saveRunInfo(workingDir());
182     }
183     monitor->setOutputDir(workingDir());
184     coreLog.details("Workflow output folder is: " + workingDir());
185     return true;
186 }
187 
188 /************************************************************************/
189 /* WorkflowContextCMDLine */
190 /************************************************************************/
191 
192 const QString WorkflowContextCMDLine::WORKING_DIR = "working-dir";
193 
getOutputDirectory(U2OpStatus & os)194 QString WorkflowContextCMDLine::getOutputDirectory(U2OpStatus &os) {
195     // 1. Detect folder
196     QString root;
197 
198     CMDLineRegistry *cmdlineReg = AppContext::getCMDLineRegistry();
199     assert(cmdlineReg != nullptr);
200 
201     if (useOutputDir()) {
202         root = WorkflowSettings::getWorkflowOutputDirectory();
203     } else if (cmdlineReg != nullptr && cmdlineReg->hasParameter(WORKING_DIR)) {
204         root = FileAndDirectoryUtils::getAbsolutePath(cmdlineReg->getParameterValue(WORKING_DIR));
205     } else {
206         root = QProcess().workingDirectory();
207     }
208 
209     // 2. Create folder if it does not exist
210     QDir rootDir(root);
211     if (!rootDir.exists()) {
212         bool created = rootDir.mkpath(rootDir.absolutePath());
213         if (!created) {
214             os.setError(QObject::tr("Can not create folder: ") + root);
215             return "";
216         }
217     }
218     return rootDir.absolutePath();
219 }
220 
createSubDirectoryForRun(const QString & root,U2OpStatus & os)221 QString WorkflowContextCMDLine::createSubDirectoryForRun(const QString &root, U2OpStatus &os) {
222     QDir rootDir(root);
223     // 1. Find free sub-folder name
224     QString baseDirName = QDateTime::currentDateTime().toString("yyyy.MM.dd_hh-mm");
225     QString dirName = baseDirName;
226     {
227         int counter = 1;
228         while (rootDir.exists(dirName)) {
229             dirName = QString("%1_%2").arg(baseDirName).arg(QString::number(counter));
230             counter++;
231         }
232     }
233 
234     // 2. Try to create the sub-folder
235     bool created = rootDir.mkdir(dirName);
236     if (!created) {
237         os.setError(QObject::tr("Can not create folder %1 in the folder %2")
238                         .arg(dirName)
239                         .arg(rootDir.absolutePath()));
240         return "";
241     }
242     return dirName;
243 }
244 
useOutputDir()245 bool WorkflowContextCMDLine::useOutputDir() {
246     return AppContext::isGUIMode();
247 }
248 
useSubDirs()249 bool WorkflowContextCMDLine::useSubDirs() {
250     return useOutputDir();
251 }
252 
saveRunInfo(const QString & dir)253 void WorkflowContextCMDLine::saveRunInfo(const QString &dir) {
254     QFile runInfo(dir + "run.info");
255     bool opened = runInfo.open(QIODevice::WriteOnly);
256     CHECK(opened, );
257 
258     QTextStream stream(&runInfo);
259     stream.setCodec("UTF-8");
260     stream << QCoreApplication::arguments().join(" ") + "\n";
261     stream.flush();
262 
263     runInfo.close();
264 }
265 
266 }  // namespace Workflow
267 }  // namespace U2
268