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