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