New code update
[jabaws.git] / webservices / compbio / stat / collector / ExecutionStatUpdater.java
1 /* Copyright (c) 2013 Alexander Sherstnev\r
2  * Copyright (c) 2011 Peter Troshin\r
3  *  \r
4  *  JAva Bioinformatics Analysis Web Services (JABAWS) @version: 2.0     \r
5  * \r
6  *  This library is free software; you can redistribute it and/or modify it under the terms of the\r
7  *  Apache License version 2 as published by the Apache Software Foundation\r
8  * \r
9  *  This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without\r
10  *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Apache \r
11  *  License for more details.\r
12  * \r
13  *  A copy of the license is in apache_license.txt. It is also available here:\r
14  * @see: http://www.apache.org/licenses/LICENSE-2.0.txt\r
15  * \r
16  * Any republication or derived work distributed in source code form\r
17  * must include this copyright and license notice.\r
18  */\r
19 package compbio.stat.collector;\r
20 \r
21 import java.io.File;\r
22 import java.io.FileFilter;\r
23 import java.io.IOException;\r
24 import java.sql.SQLException;\r
25 import java.text.ParseException;\r
26 import java.text.SimpleDateFormat;\r
27 import java.util.ArrayList;\r
28 import java.util.Date;\r
29 import java.util.HashSet;\r
30 import java.util.List;\r
31 import java.util.Set;\r
32 \r
33 import com.beust.jcommander.JCommander;\r
34 import com.beust.jcommander.Parameter;\r
35 \r
36 import org.apache.log4j.Logger;\r
37 \r
38 import compbio.engine.client.PathValidator;\r
39 import compbio.engine.client.SkeletalExecutable;\r
40 \r
41 /**\r
42  * Class assumptions: 1. Number of runs of each WS = number of folders with name\r
43  * 2. Number of successful runs = all runs with no result file 3. Per period of\r
44  * time = limit per file creating time 4. Runtime (avg/max) = finish time -\r
45  * start time 5. Task & result size = result.size\r
46  * \r
47  * Abandoned runs - not collected runs\r
48  * \r
49  * Cancelled runs - cancelled\r
50  * \r
51  * Cluster vs local runs\r
52  * \r
53  * Reasons for failure = look in the err out?\r
54  * \r
55  * \r
56  * Metadata required:\r
57  * \r
58  * work directory for local and cluster tasks = from Helper or cmd parameter. WS\r
59  * names - enumeration. Status file names and content.\r
60  * \r
61  * @author Peter Troshin\r
62  * @author Alexander Sherstnev\r
63  * \r
64  */\r
65 \r
66 class mainJCommander {\r
67         @Parameter\r
68         private List<String> parameters = new ArrayList<String>();\r
69 \r
70         @Parameter(names = { "-log", "-verbose" }, description = "Level of verbosity")\r
71         public Integer verbose = 1;\r
72 \r
73         @Parameter(names = "-start", description = "Start time")\r
74         public String starttime;\r
75 \r
76         @Parameter(names = "-end", description = "Start time")\r
77         public String endtime;\r
78 \r
79         @Parameter(names = "-db", description = "Path to database")\r
80         public String dbname;\r
81 \r
82         @Parameter(names = "-dir", description = "Path to job directory")\r
83         public String workingdir;\r
84 }\r
85 \r
86 public class ExecutionStatUpdater {\r
87         static SimpleDateFormat DF = new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");\r
88         static SimpleDateFormat shortDF = new SimpleDateFormat("dd/MM/yyyy");\r
89         private static final Logger log = Logger.getLogger(ExecutionStatUpdater.class);\r
90 \r
91         final private File workingDirectory;\r
92         final private List<JobStat> stats;\r
93         /**\r
94          * Consider the job that has been working for longer than timeOutInHours\r
95          * completed, whatever the outcome\r
96          */\r
97         final private int timeOutInHours;\r
98 \r
99         /**\r
100          * List subdirectories in the job directory\r
101          * \r
102          * @param workingDirectory\r
103          * @param timeOutInHours\r
104          */\r
105         public ExecutionStatUpdater(String workingDirectory, int timeOutInHours) {\r
106                 log.info("Starting stat collector for directory: " + workingDirectory);\r
107                 log.info("Maximum allowed runtime(h): " + timeOutInHours);\r
108                 if (!PathValidator.isValidDirectory(workingDirectory)) {\r
109                         throw new IllegalArgumentException("workingDirectory '" + workingDirectory + "' does not exist!");\r
110                 }\r
111                 this.workingDirectory = new File(workingDirectory);\r
112                 stats = new ArrayList<JobStat>();\r
113                 if (timeOutInHours <= 0) {\r
114                         throw new IllegalArgumentException("Timeout value must be greater than 0! Given value: " + timeOutInHours);\r
115                 }\r
116                 this.timeOutInHours = timeOutInHours;\r
117         }\r
118 \r
119         boolean hasCompleted(JobDirectory jd) {\r
120                 JobStat jstat = jd.getJobStat();\r
121                 if (jstat.hasResult() || jstat.getIsCancelled() || jstat.getIsFinished() || hasTimedOut(jd)) {\r
122                         return true;\r
123                 }\r
124                 return false;\r
125         }\r
126 \r
127         boolean hasTimedOut(JobDirectory jd) {\r
128                 return ((System.currentTimeMillis() - jd.jobdir.lastModified()) / (1000 * 60 * 60)) > timeOutInHours;\r
129         }\r
130 \r
131         /*\r
132          * Make sure that collectStatistics methods was called prior to calling\r
133          * this! TODO consider running collectStatistics from here on the first call\r
134          */\r
135         StatProcessor getStats() {\r
136                 if (stats.isEmpty()) {\r
137                         log.info("Please make sure collectStatistics method was called prior to calling getStats()!");\r
138                 }\r
139                 return new StatProcessor(stats);\r
140         }\r
141 \r
142         void writeStatToDB(String dbname) throws SQLException {\r
143                 Set<JobStat> rjobs = new HashSet<JobStat>(stats);\r
144                 StatDB statdb = new StatDB(dbname);\r
145                 log.debug("Removing records that has already been recorded");\r
146                 statdb.removeRecordedJobs(rjobs);\r
147                 log.debug("New records left: " + rjobs.size());\r
148                 statdb.insertData(rjobs);\r
149         }\r
150 \r
151         /**\r
152          * main function\r
153          * @throws ParseException \r
154          */\r
155         public static void main(String[] args) throws IOException, SQLException, ParseException {\r
156                 mainJCommander jct = new mainJCommander();\r
157                 new JCommander(jct, args);\r
158                 String WorkingDir = "jobsout";\r
159                 String DBname = "ExecutionStatistic";\r
160 \r
161                 long StartTime = 0L;\r
162                 Date currDate = new Date();\r
163                 long EndTime = currDate.getTime();\r
164                 if (null != jct.starttime) {\r
165                         Date ST = shortDF.parse(jct.starttime);\r
166                         if (null != ST) {\r
167                                 StartTime = ST.getTime();\r
168                         }\r
169                 }\r
170                 if (null != jct.endtime) {\r
171                         Date ET = shortDF.parse(jct.endtime);\r
172                         if (null != ET) {\r
173                                 EndTime = ET.getTime();\r
174                         }\r
175                 }\r
176                 if (null != jct.dbname) {\r
177                         DBname = jct.dbname;\r
178                 }\r
179                 if (null != jct.workingdir) {\r
180                         WorkingDir = jct.workingdir;\r
181                 }\r
182 \r
183                 System.out.println("Start time: " + jct.starttime + " = " + StartTime);\r
184                 System.out.println("End time: " + jct.endtime + " = " + EndTime);\r
185 \r
186                 ExecutionStatUpdater esu = new ExecutionStatUpdater(WorkingDir, 1);\r
187                 esu.collectStatistics(StartTime, EndTime);\r
188                 esu.writeStatToDB(DBname);\r
189         }\r
190 \r
191         static FileFilter directories = new FileFilter() {\r
192                 @Override\r
193                 public boolean accept(File pathname) {\r
194                         return pathname.isDirectory() && !pathname.getName().startsWith(".");\r
195                 }\r
196         };\r
197 \r
198         // TODO test!\r
199         private void collectStatistics(long StartTime, long EndTime) {\r
200                 // clear stats array;\r
201                 stats.clear();\r
202                 File[] dirs = workingDirectory.listFiles(directories);\r
203                 log.debug("Start time = " + StartTime + ", end time = " + EndTime);\r
204                 for (File dir : dirs) {\r
205                         // skip work directory with test inputs and out of ordered time\r
206                         // range\r
207                         log.debug("check directory: " + dir.getName() + "...");\r
208                         if (InputFilter.accept(new File(dir.getPath() + File.separator + SkeletalExecutable.INPUT)) && StartTime < dir.lastModified()\r
209                                         && dir.lastModified() < EndTime) {\r
210                                 log.debug(", ACCEPTED");\r
211                                 JobDirectory jd = new JobDirectory(dir);\r
212                                 JobStat jstat = jd.getJobStat();\r
213                                 // Do not record stats on the job that has not completed yet\r
214                                 if (hasCompleted(jd)) {\r
215                                         stats.add(jstat);\r
216                                 } else {\r
217                                         log.debug("Skipping the job: " + jstat + " as it has not completed yet");\r
218                                 }\r
219                         } else {\r
220                                 log.debug(", REJECTED");\r
221                                 log.trace("training input: " + dir.getName() + File.separator + SkeletalExecutable.INPUT);\r
222                         }\r
223                 }\r
224                 log.debug("Statistics collected!");\r
225         }\r
226 \r
227 }\r