1 /******************************************************************************
2
3 This source file is part of the MoleQueue project.
4
5 Copyright 2011-2012 Kitware, Inc.
6
7 This source code is released under the New BSD License, (the "License").
8
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 ******************************************************************************/
16
17 #include "program.h"
18
19 #include "logger.h"
20 #include "queue.h"
21 #include "queues/remote.h"
22 #include "queuemanager.h"
23 #include "server.h"
24
25 #include <qjsondocument.h>
26
27 #include <QtCore/QDebug>
28 #include <QtCore/QFileInfo>
29
30 namespace MoleQueue {
31
Program(Queue * parentQueue)32 Program::Program(Queue *parentQueue) :
33 QObject(parentQueue),
34 m_queue(parentQueue),
35 m_queueManager((m_queue) ? m_queue->queueManager() : NULL),
36 m_server((m_queueManager) ? m_queueManager->server() : NULL),
37 m_name("Program"),
38 m_executable("program"),
39 m_arguments(),
40 m_outputFilename("$$inputFileBaseName$$.out"),
41 m_launchSyntax(REDIRECT),
42 m_customLaunchTemplate("")
43 {
44 }
45
Program(const Program & other)46 Program::Program(const Program &other)
47 : QObject(other.parent()),
48 m_queue(other.m_queue),
49 m_name(other.m_name),
50 m_executable(other.m_executable),
51 m_arguments(other.m_arguments),
52 m_outputFilename(other.m_outputFilename),
53 m_launchSyntax(other.m_launchSyntax),
54 m_customLaunchTemplate(other.m_customLaunchTemplate)
55 {
56 }
57
~Program()58 Program::~Program()
59 {
60 }
61
operator =(const Program & other)62 Program &Program::operator=(const Program &other)
63 {
64 m_queue = other.m_queue;
65 m_name = other.m_name;
66 m_executable = other.m_executable;
67 m_arguments = other.m_arguments;
68 m_outputFilename = other.m_outputFilename;
69 m_launchSyntax = other.m_launchSyntax;
70 m_customLaunchTemplate = other.m_customLaunchTemplate;
71 return *this;
72 }
73
queueName() const74 QString Program::queueName() const
75 {
76 if (m_queue)
77 return m_queue->name();
78 else
79 return "None";
80 }
81
importSettings(const QString & fileName)82 bool Program::importSettings(const QString &fileName)
83 {
84 if (!QFile::exists(fileName))
85 return false;
86
87 QFile stateFile(fileName);
88 if (!stateFile.open(QFile::ReadOnly | QFile::Text)) {
89 Logger::logError(tr("Cannot read program information from %1.")
90 .arg(fileName));
91 return false;
92 }
93 QByteArray inputText = stateFile.readAll();
94 stateFile.close();
95
96 // Try to read existing data in
97 QJsonParseError error;
98 QJsonDocument doc = QJsonDocument::fromJson(inputText, &error);
99 if (error.error != QJsonParseError::NoError) {
100 Logger::logError(tr("Error parsing program state from %1: %2\n%3")
101 .arg(fileName)
102 .arg(tr("%1 (at offset %2)")
103 .arg(error.errorString())
104 .arg(error.offset))
105 .arg(inputText.data()));
106 return false;
107 }
108
109 if (!doc.isObject()) {
110 Logger::logError(tr("Error reading program state from %1: "
111 "root is not an object!\n%2")
112 .arg(fileName)
113 .arg(inputText.data()));
114 return false;
115 }
116
117 return readJsonSettings(doc.object(), true);
118 }
119
exportSettings(const QString & fileName) const120 bool Program::exportSettings(const QString &fileName) const
121 {
122 QFile stateFile(fileName);
123 if (!stateFile.open(QFile::ReadWrite | QFile::Text | QFile::Truncate)) {
124 Logger::logError(tr("Cannot save program information for %1 in queue %2: "
125 "Cannot open file %3.").arg(name())
126 .arg(m_queue->name()).arg(fileName));
127 return false;
128 }
129
130 QJsonObject root;
131 if (!this->writeJsonSettings(root, true)) {
132 stateFile.close();
133 return false;
134 }
135
136 // Write the data back out:
137 stateFile.write(QJsonDocument(root).toJson());
138 stateFile.close();
139
140 return true;
141 }
142
writeJsonSettings(QJsonObject & json,bool exportOnly) const143 bool Program::writeJsonSettings(QJsonObject &json, bool exportOnly) const
144 {
145 // No export sensitive data.
146 Q_UNUSED(exportOnly)
147
148 json.insert("executable", m_executable);
149 json.insert("arguments", m_arguments);
150 json.insert("outputFilename", m_outputFilename);
151 json.insert("customLaunchTemplate", m_customLaunchTemplate);
152 json.insert("launchSyntax", static_cast<double>(m_launchSyntax));
153
154 return true;
155 }
156
readJsonSettings(const QJsonObject & json,bool importOnly)157 bool Program::readJsonSettings(const QJsonObject &json, bool importOnly)
158 {
159 // No import sensitive data.
160 Q_UNUSED(importOnly)
161
162 // Validate JSON
163 if (!json.value("executable").isString() ||
164 !json.value("arguments").isString() ||
165 !json.value("outputFilename").isString() ||
166 !json.value("customLaunchTemplate").isString() ||
167 !json.value("launchSyntax").isDouble()) {
168 Logger::logError(tr("Error reading program config: Invalid format:\n%1")
169 .arg(QString(QJsonDocument(json).toJson())));
170 return false;
171 }
172
173 m_executable = json.value("executable").toString();
174 m_arguments = json.value("arguments").toString();
175 m_outputFilename = json.value("outputFilename").toString();
176 m_customLaunchTemplate = json.value("customLaunchTemplate").toString();
177 m_launchSyntax =
178 static_cast<LaunchSyntax>(
179 static_cast<int>(json.value("launchSyntax").toDouble() + 0.5));
180
181 return true;
182 }
183
launchTemplate() const184 QString Program::launchTemplate() const
185 {
186 if (m_launchSyntax == CUSTOM)
187 return m_customLaunchTemplate;
188
189 QString result = m_queue ? m_queue->launchTemplate()
190 : QString("$$programExecution$$");
191 if (result.contains("$$programExecution$$")) {
192 const QString progExec = Program::generateFormattedExecutionString(
193 m_executable, m_arguments, m_outputFilename, m_launchSyntax);
194 result.replace("$$programExecution$$", progExec);
195 }
196 if (QueueRemote *remoteQueue = qobject_cast<QueueRemote*>(m_queue)) {
197 if (result.contains("$$remoteWorkingDir$$")) {
198 const QString remoteWorkingDir = QString("%1/%2/")
199 .arg(remoteQueue->workingDirectoryBase())
200 .arg("$$moleQueueId$$");
201 result.replace("$$remoteWorkingDir$$", remoteWorkingDir);
202 }
203 }
204
205 return result;
206 }
207
generateFormattedExecutionString(const QString & executable_,const QString & arguments_,const QString & outputFilename_,Program::LaunchSyntax syntax_)208 QString Program::generateFormattedExecutionString(
209 const QString &executable_, const QString &arguments_,
210 const QString &outputFilename_, Program::LaunchSyntax syntax_)
211 {
212 if (syntax_ == Program::CUSTOM) {
213 return "";
214 }
215
216 QString execStr;
217 QString command = executable_ + (arguments_.isEmpty() ? QString()
218 : " " + arguments_);
219
220 switch (syntax_) {
221 case Program::PLAIN:
222 execStr += command;
223 break;
224 case Program::INPUT_ARG:
225 execStr += QString("%1 $$inputFileName$$\n").arg(command);
226 break;
227 case Program::INPUT_ARG_NO_EXT:
228 {
229 execStr += QString("%1 $$inputFileBaseName$$\n").arg(command);
230 }
231 break;
232 case Program::REDIRECT:
233 execStr += QString("%1 < $$inputFileName$$ > %3\n")
234 .arg(command).arg(outputFilename_);
235 break;
236 case Program::INPUT_ARG_OUTPUT_REDIRECT:
237 execStr += QString("%1 $$inputFileName$$ > %3\n")
238 .arg(command).arg(outputFilename_);
239 break;
240 case Program::CUSTOM:
241 // Should be handled as a special case earlier.
242 execStr = tr("Internal MoleQueue error: Custom syntax type not handled.\n");
243 break;
244 default:
245 execStr = tr("Internal MoleQueue error: Unrecognized syntax type.\n");
246 break;
247 }
248
249 return execStr;
250 }
251
252 } // End namespace
253