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