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