369f8e71adb0eb58155d028151c4352bcad94593
[jabaws.git] / engine / compbio / engine / client / SkeletalExecutable.java
1 /* Copyright (c) 2009 Peter Troshin\r
2  *  \r
3  *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 1.0     \r
4  * \r
5  *  This library is free software; you can redistribute it and/or modify it under the terms of the\r
6  *  Apache License version 2 as published by the Apache Software Foundation\r
7  * \r
8  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\r
9  *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache \r
10  *  License for more details.\r
11  * \r
12  *  A copy of the license is in apache_license.txt. It is also available here:\r
13  * @see: http://www.apache.org/licenses/LICENSE-2.0.txt\r
14  * \r
15  * Any republication or derived work distributed in source code form\r
16  * must include this copyright and license notice.\r
17  */\r
18 \r
19 package compbio.engine.client;\r
20 \r
21 import java.io.File;\r
22 import java.security.InvalidParameterException;\r
23 import java.util.Arrays;\r
24 import java.util.List;\r
25 \r
26 import org.apache.log4j.Logger;\r
27 \r
28 import compbio.engine.client.CommandBuilder.Parameter;\r
29 import compbio.engine.conf.PropertyHelperManager;\r
30 import compbio.metadata.Limit;\r
31 import compbio.metadata.LimitsManager;\r
32 import compbio.util.PropertyHelper;\r
33 import compbio.util.Util;\r
34 \r
35 public abstract class SkeletalExecutable<T> implements Executable<T> {\r
36 \r
37         protected static final PropertyHelper ph = PropertyHelperManager\r
38                         .getPropertyHelper();\r
39 \r
40         private static Logger log = Logger.getLogger(SkeletalExecutable.class);\r
41 \r
42         // Cache for Limits information\r
43         private LimitsManager<T> limits;\r
44 \r
45         public static final String INPUT = "input.txt";\r
46         public static final String OUTPUT = "result.txt";\r
47         public static final String ERROR = "error.txt";\r
48 \r
49         protected String inputFile = INPUT;\r
50         protected String outputFile = OUTPUT;\r
51         protected String errorFile = ERROR;\r
52 \r
53         private boolean isInputSet = false;\r
54         private boolean isOutputSet = false;\r
55         private boolean isErrorSet = false;\r
56 \r
57         /**\r
58          * This has to allow duplicate parameters as different options may have the\r
59          * same value e.g. Muscle -weight1 clustalw -weight2 clustalw\r
60          */\r
61         protected CommandBuilder<T> cbuilder;\r
62 \r
63         public SkeletalExecutable() {\r
64                 cbuilder = new CommandBuilder<T>(" ");\r
65         }\r
66 \r
67         public SkeletalExecutable(String parameterKeyValueDelimiter) {\r
68                 assert parameterKeyValueDelimiter != null;\r
69                 cbuilder = new CommandBuilder<T>(parameterKeyValueDelimiter);\r
70         }\r
71 \r
72         public SkeletalExecutable<T> setInput(String inFile) {\r
73                 if (compbio.util.Util.isEmpty(inFile)) {\r
74                         throw new IllegalArgumentException("Input file must not be NULL");\r
75                 }\r
76                 this.inputFile = inFile;\r
77                 this.isInputSet = true;\r
78                 return this;\r
79         }\r
80 \r
81         public SkeletalExecutable<T> setOutput(String outFile) {\r
82                 if (compbio.util.Util.isEmpty(outFile)\r
83                                 || PathValidator.isAbsolutePath(outFile)) {\r
84                         throw new IllegalArgumentException(\r
85                                         "Output file must not be NULL and Absolute path could not be used! Please provide the filename only. Value provided: "\r
86                                                         + outFile);\r
87                 }\r
88                 this.outputFile = outFile;\r
89                 this.isOutputSet = true;\r
90                 return this;\r
91         }\r
92 \r
93         public SkeletalExecutable<T> setError(String errFile) {\r
94                 if (compbio.util.Util.isEmpty(errFile)\r
95                                 || PathValidator.isAbsolutePath(errFile)) {\r
96                         throw new IllegalArgumentException(\r
97                                         "Error file must not be NULL and Absolute path could not be used! Please provide the filename only. Value provided: "\r
98                                                         + errFile);\r
99                 }\r
100                 this.errorFile = errFile;\r
101                 this.isErrorSet = true;\r
102                 return this;\r
103         }\r
104 \r
105         @Override\r
106         public CommandBuilder<T> getParameters(ExecProvider provider) {\r
107                 /*\r
108                  * Prevent modification of the parameters unintentionally. This is\r
109                  * important to preserve executable parameters intact as engine could\r
110                  * add things into the array as it see fit. For instance\r
111                  * ExecutableWrapper (part of local engines) add command line as the\r
112                  * first element of an array.\r
113                  */\r
114                 paramValueUpdater();\r
115                 return cbuilder;\r
116         }\r
117 \r
118         @Override\r
119         public Executable<T> addParameters(List<String> parameters) {\r
120                 cbuilder.addParams(parameters);\r
121                 return this;\r
122         }\r
123 \r
124         public Executable<T> setParameter(String parameter) {\r
125                 cbuilder.setParam(parameter);\r
126                 return this;\r
127         }\r
128 \r
129         /**\r
130          * This is a generic method of changing values of the parameters with\r
131          * properties\r
132          * \r
133          * This method iterates via commands for an executable finding matches from\r
134          * the Executable.properties file and replacing values in CommandBuilder\r
135          * with a combination of value from CommandBuilder to merge path from\r
136          * properties\r
137          */\r
138         void paramValueUpdater() {\r
139                 for (Parameter command : cbuilder.getCommandList()) {\r
140                         if (command.value == null) {\r
141                                 continue;\r
142                         }\r
143                         String propertyPath = compbio.engine.client.Util.getExecProperty(\r
144                                         command.name + ".path", getType());\r
145                         if (Util.isEmpty(propertyPath)) {\r
146                                 continue;\r
147                         }\r
148                         if (new File(command.value).isAbsolute()) {\r
149                                 // Matrix can be found so no actions necessary\r
150                                 // This method has been called already and the matrix name\r
151                                 // is modified to contain full path // no further actions is\r
152                                 // necessary\r
153                                 continue;\r
154                         }\r
155                         String absMatrixPath = compbio.engine.client.Util\r
156                                         .convertToAbsolute(propertyPath);\r
157                         command.value = absMatrixPath + File.separator + command.value;\r
158                         cbuilder.setParam(command);\r
159                 }\r
160         }\r
161 \r
162         /**\r
163          * This method cannot really tell whether the files has actually been\r
164          * created or not. It must be overridden as required.\r
165          * \r
166          * @see compbio.engine.client.Executable#getCreatedFiles()\r
167          */\r
168         @Override\r
169         public List<String> getCreatedFiles() {\r
170                 return Arrays.asList(getOutput(), getError());\r
171         }\r
172 \r
173         @Override\r
174         public String getInput() {\r
175                 return inputFile;\r
176         }\r
177 \r
178         protected boolean isInputSet() {\r
179                 return isInputSet;\r
180         }\r
181 \r
182         protected boolean isOutputSet() {\r
183                 return isOutputSet;\r
184         }\r
185 \r
186         protected boolean isErrorSet() {\r
187                 return isErrorSet;\r
188         }\r
189 \r
190         @Override\r
191         public String getOutput() {\r
192                 return outputFile;\r
193         }\r
194 \r
195         @Override\r
196         public String getError() {\r
197                 return errorFile;\r
198         }\r
199 \r
200         @Override\r
201         public String toString() {\r
202                 String value = "Input: " + this.getInput() + "\n";\r
203                 value += "Output: " + this.getOutput() + "\n";\r
204                 value += "Error: " + this.getError() + "\n";\r
205                 value += "Class: " + this.getClass() + "\n";\r
206                 value += "Params: " + cbuilder + "\n";\r
207                 return value;\r
208         }\r
209 \r
210         @Override\r
211         public Executable<?> loadRunConfiguration(RunConfiguration rconfig) {\r
212                 if (!compbio.util.Util.isEmpty(rconfig.getOutput())) {\r
213                         setOutput(rconfig.getOutput());\r
214                 }\r
215                 if (!compbio.util.Util.isEmpty(rconfig.getError())) {\r
216                         setError(rconfig.getError());\r
217                 }\r
218                 if (!compbio.util.Util.isEmpty(rconfig.getInput())) {\r
219                         setInput(rconfig.getInput());\r
220                 }\r
221                 this.cbuilder = (CommandBuilder<T>) rconfig.getParameters();\r
222                 return this;\r
223         }\r
224 \r
225         @Override\r
226         public boolean equals(Object obj) {\r
227                 if (obj == null) {\r
228                         return false;\r
229                 }\r
230                 if (!(obj instanceof SkeletalExecutable<?>)) {\r
231                         return false;\r
232                 }\r
233                 SkeletalExecutable<?> exec = (SkeletalExecutable<?>) obj;\r
234                 if (!Util.isEmpty(this.inputFile) && !Util.isEmpty(exec.inputFile)) {\r
235                         if (!this.inputFile.equals(exec.inputFile)) {\r
236                                 return false;\r
237                         }\r
238                 }\r
239                 if (!Util.isEmpty(this.outputFile) && !Util.isEmpty(exec.outputFile)) {\r
240                         if (!this.outputFile.equals(exec.outputFile)) {\r
241                                 return false;\r
242                         }\r
243                 }\r
244                 if (!Util.isEmpty(this.errorFile) && !Util.isEmpty(exec.errorFile)) {\r
245                         if (!this.errorFile.equals(exec.errorFile)) {\r
246                                 return false;\r
247                         }\r
248                 }\r
249                 if (!this.cbuilder.equals(exec.cbuilder)) {\r
250                         return false;\r
251                 }\r
252                 return true;\r
253         }\r
254 \r
255         @Override\r
256         public int hashCode() {\r
257                 int code = inputFile.hashCode();\r
258                 code += outputFile.hashCode();\r
259                 code += errorFile.hashCode();\r
260                 code *= this.cbuilder.hashCode();\r
261                 return code;\r
262         }\r
263 \r
264         @Override\r
265         public String getClusterJobSettings() {\r
266                 String settings = ph.getProperty(getType().getSimpleName()\r
267                                 .toLowerCase() + ".cluster.settings");\r
268                 return settings == null ? "" : settings;\r
269         }\r
270 \r
271         /**\r
272          * \r
273          * @return number of cpus to use on the cluster or 0 if the value is\r
274          *         undefined\r
275          */\r
276         public static int getClusterCpuNum(Class<? extends Executable<?>> type) {\r
277                 int cpus = 0;\r
278                 String cpuNum = ph.getProperty(type.getSimpleName().toLowerCase()\r
279                                 + ".cluster.cpunum");\r
280                 if (compbio.util.Util.isEmpty(cpuNum)) {\r
281                         return 0;\r
282                 }\r
283                 try {\r
284                         cpus = Integer.parseInt(cpuNum);\r
285                 } catch (NumberFormatException e) {\r
286                         // safe to ignore\r
287                         log.debug("Number of cpus to use for cluster execution is defined but could not be parsed as integer! Given value is: "\r
288                                         + cpuNum);\r
289                         return 0;\r
290                 }\r
291                 if (cpus < 1 || cpus > 100) {\r
292                         throw new InvalidParameterException(\r
293                                         "Number of cpu for cluster execution must be within 1 and 100! "\r
294                                                         + "Look at the value of 'tcoffee.cluster.cpunum' property. Given value is "\r
295                                                         + cpus);\r
296                 }\r
297                 return cpus;\r
298         }\r
299 \r
300         @Override\r
301         public synchronized Limit<T> getLimit(String presetName) {\r
302                 // Assume this function is called for the first time and thus need\r
303                 // initialization\r
304                 if (limits == null) {\r
305                         limits = getLimits();\r
306                 }\r
307                 // Either the initialization failed or limits were not configured.\r
308                 if (limits == null) {\r
309                         return null;\r
310                 }\r
311 \r
312                 Limit<T> limit = null;\r
313                 if (limits != null) {\r
314                         // this returns default limit if preset is undefined!\r
315                         limit = limits.getLimitByName(presetName);\r
316                 }\r
317                 // If limit is not defined for a particular preset, then return default\r
318                 // limit\r
319                 if (limit == null) {\r
320                         log.debug("Limit for the preset " + presetName\r
321                                         + " is not found. Using default");\r
322                         limit = limits.getDefaultLimit();\r
323                 }\r
324                 return limit;\r
325         }\r
326 \r
327         @Override\r
328         public LimitsManager<T> getLimits() {\r
329                 synchronized (SkeletalExecutable.class) {\r
330                         if (limits == null) {\r
331                                 limits = compbio.engine.client.Util.getLimits(this.getType());\r
332                         }\r
333                 }\r
334                 return limits;\r
335         }\r
336 \r
337         /**\r
338          * \r
339          * @return subclasses must return their type\r
340          */\r
341         public abstract Class<T> getType();\r
342 \r
343 }\r