| 1 | |
|
| 2 | |
|
| 3 | |
package org.galagosearch.tupleflow.execution; |
| 4 | |
|
| 5 | |
import java.io.IOException; |
| 6 | |
import java.io.PrintWriter; |
| 7 | |
import java.util.Date; |
| 8 | |
import java.util.HashMap; |
| 9 | |
import java.util.Map; |
| 10 | |
import java.util.Map.Entry; |
| 11 | |
import java.util.TreeMap; |
| 12 | |
import javax.servlet.ServletException; |
| 13 | |
import javax.servlet.http.HttpServletRequest; |
| 14 | |
import javax.servlet.http.HttpServletResponse; |
| 15 | |
import org.galagosearch.tupleflow.execution.JobExecutor.JobExecutionStatus; |
| 16 | |
import org.mortbay.jetty.handler.AbstractHandler; |
| 17 | |
|
| 18 | |
|
| 19 | |
|
| 20 | |
|
| 21 | |
|
| 22 | |
|
| 23 | |
|
| 24 | |
public class MasterWebHandler extends AbstractHandler { |
| 25 | |
JobExecutionStatus status; |
| 26 | 0 | Map<CounterName, AggregateCounter> counters = new TreeMap<CounterName, AggregateCounter>(); |
| 27 | |
|
| 28 | |
|
| 29 | |
|
| 30 | |
|
| 31 | |
|
| 32 | 0 | long lastPageLoad = 0; |
| 33 | |
|
| 34 | 0 | public static class CounterName implements Comparable<CounterName> { |
| 35 | |
String counterName; |
| 36 | |
String stageName; |
| 37 | |
|
| 38 | 0 | public CounterName(String stageName, String counterName) { |
| 39 | 0 | this.stageName = stageName; |
| 40 | 0 | this.counterName = counterName; |
| 41 | 0 | } |
| 42 | |
|
| 43 | 0 | public String getCounterName() { return counterName; } |
| 44 | 0 | public String getStageName() { return stageName; } |
| 45 | |
public int compareTo(CounterName other) { |
| 46 | 0 | int result = stageName.compareTo(other.stageName); |
| 47 | 0 | if (result != 0) return result; |
| 48 | 0 | return counterName.compareTo(other.counterName); |
| 49 | |
} |
| 50 | |
} |
| 51 | |
|
| 52 | |
private void handleRefresh(HttpServletRequest request, PrintWriter writer) { |
| 53 | 0 | int refresh = 5; |
| 54 | 0 | if (request.getParameter("refresh") != null) { |
| 55 | |
try { |
| 56 | 0 | refresh = Integer.parseInt(request.getParameter("refresh")); |
| 57 | 0 | } catch (Exception e) { |
| 58 | |
|
| 59 | 0 | } |
| 60 | |
} |
| 61 | 0 | if (refresh > 0) { |
| 62 | 0 | writer.append(String.format("<meta http-equiv=\"refresh\" content=\"%d\" />", refresh)); |
| 63 | |
} |
| 64 | 0 | } |
| 65 | |
|
| 66 | |
|
| 67 | |
|
| 68 | |
|
| 69 | |
|
| 70 | 0 | class AggregateCounter { |
| 71 | |
|
| 72 | |
public synchronized long getValue() { |
| 73 | 0 | return total; |
| 74 | |
} |
| 75 | |
|
| 76 | |
|
| 77 | |
public synchronized void setValue(String instance, long value) { |
| 78 | 0 | long oldValue = 0; |
| 79 | 0 | if (instances.containsKey(instance)) |
| 80 | 0 | oldValue = instances.get(instance); |
| 81 | 0 | long delta = value - oldValue; |
| 82 | 0 | total += delta; |
| 83 | 0 | instances.put(instance, value); |
| 84 | 0 | } |
| 85 | |
|
| 86 | 0 | HashMap<String, Long> instances = new HashMap(); |
| 87 | 0 | long total = 0; |
| 88 | |
} |
| 89 | |
|
| 90 | 0 | public MasterWebHandler(JobExecutionStatus status) { |
| 91 | 0 | this.status = status; |
| 92 | 0 | } |
| 93 | |
|
| 94 | |
public synchronized void setLastPageLoad(long value) { |
| 95 | 0 | lastPageLoad = value; |
| 96 | 0 | } |
| 97 | |
|
| 98 | |
public synchronized long getLastPageLoad() { |
| 99 | 0 | return lastPageLoad; |
| 100 | |
} |
| 101 | |
|
| 102 | |
|
| 103 | |
|
| 104 | |
|
| 105 | |
|
| 106 | |
|
| 107 | |
|
| 108 | |
|
| 109 | |
|
| 110 | |
|
| 111 | |
|
| 112 | |
public void waitForFinalPage() { |
| 113 | 0 | synchronized(this) { |
| 114 | |
|
| 115 | |
|
| 116 | |
|
| 117 | 0 | long timeDelta = System.currentTimeMillis() - getLastPageLoad(); |
| 118 | 0 | if (timeDelta <= 15 * 1000) { |
| 119 | |
try { |
| 120 | 0 | this.wait(15 * 1000); |
| 121 | 0 | } catch (InterruptedException e) { |
| 122 | |
|
| 123 | 0 | } |
| 124 | |
} |
| 125 | 0 | } |
| 126 | 0 | } |
| 127 | |
|
| 128 | |
public synchronized void handleSetCounter( |
| 129 | |
HttpServletRequest request, |
| 130 | |
HttpServletResponse response) throws IOException { |
| 131 | |
try { |
| 132 | 0 | String instance = request.getParameter("instance"); |
| 133 | 0 | String name = request.getParameter("counterName"); |
| 134 | 0 | String stageName = request.getParameter("stageName"); |
| 135 | 0 | String stringValue = request.getParameter("value"); |
| 136 | 0 | Long longValue = new Long(stringValue); |
| 137 | |
|
| 138 | 0 | if (instance == null || stringValue == null || name == null || stageName == null) |
| 139 | 0 | return; |
| 140 | |
|
| 141 | 0 | CounterName fullName = new CounterName(stageName, name); |
| 142 | 0 | if (!counters.containsKey(fullName)) { |
| 143 | 0 | counters.put(fullName, new AggregateCounter()); |
| 144 | |
} |
| 145 | |
|
| 146 | 0 | counters.get(fullName).setValue(instance, longValue); |
| 147 | 0 | } catch(Exception e) { |
| 148 | 0 | response.sendError(response.SC_NOT_ACCEPTABLE); |
| 149 | 0 | } |
| 150 | |
|
| 151 | 0 | response.setStatus(response.SC_OK); |
| 152 | 0 | } |
| 153 | |
|
| 154 | |
private String getElapsed(Date start) { |
| 155 | 0 | long remainingMs = System.currentTimeMillis() - start.getTime(); |
| 156 | 0 | long hours = remainingMs / 3600000; |
| 157 | 0 | remainingMs = remainingMs % 3600000; |
| 158 | 0 | long minutes = remainingMs / 60000; |
| 159 | 0 | remainingMs = remainingMs % 60000; |
| 160 | 0 | long seconds = remainingMs / 1000; |
| 161 | |
|
| 162 | 0 | return String.format("%d:%02d:%02d", hours, minutes, seconds); |
| 163 | |
} |
| 164 | |
|
| 165 | |
public synchronized void handleStatus( |
| 166 | |
HttpServletRequest request, |
| 167 | |
HttpServletResponse response) throws IOException { |
| 168 | 0 | PrintWriter writer = response.getWriter(); |
| 169 | 0 | response.setContentType("text/html"); |
| 170 | |
|
| 171 | 0 | Map<String, StageExecutionStatus> stagesStatus = status.getStageStatus(); |
| 172 | 0 | boolean isComplete = status.isComplete(); |
| 173 | 0 | setLastPageLoad(System.currentTimeMillis()); |
| 174 | |
|
| 175 | 0 | writer.append("<html>"); |
| 176 | 0 | if (!isComplete) { |
| 177 | 0 | handleRefresh(request, writer); |
| 178 | |
} |
| 179 | 0 | writer.append("<head>\n"); |
| 180 | 0 | writer.append("<style type=\"text/css\">\n"); |
| 181 | |
|
| 182 | 0 | writer.append("table { border-collapse: collapse; }\n"); |
| 183 | 0 | writer.append("tr.blocked td { background: #BBB; }\n"); |
| 184 | 0 | writer.append("tr.running td { background: #8D8; }\n"); |
| 185 | 0 | writer.append("tr.complete td { background: #5A5; }\n"); |
| 186 | 0 | writer.append("td { padding: 5px; }\n"); |
| 187 | 0 | writer.append("td.right { text-align: right; }\n"); |
| 188 | 0 | writer.append("</style>"); |
| 189 | 0 | writer.append("</head>\n"); |
| 190 | 0 | writer.append("<body>\n"); |
| 191 | 0 | writer.append("<font size=\"-3\">Refresh: <a href=\"/?refresh=1\">1 second</a> " + |
| 192 | |
"<a href=\"/?refresh=5\">5 seconds</a> " + |
| 193 | |
"<a href=\"/?refresh=15\">15 seconds</a> " + |
| 194 | |
"<a href=\"/?refresh=60\">1 minute</a> " + |
| 195 | |
"<a href=\"/?refresh=-1\">never</a></font><br/>" ); |
| 196 | |
|
| 197 | |
|
| 198 | 0 | writer.append("<table>"); |
| 199 | 0 | writer.append(String.format("<tr><td>Start</td><td>%s</td></tr>\n", |
| 200 | |
status.getStartDate().toString())); |
| 201 | 0 | writer.append(String.format("<tr><td>Elapsed</td><td>%s</td></tr>\n", |
| 202 | |
getElapsed(status.getStartDate()))); |
| 203 | 0 | writer.append(String.format("<tr><td>Max memory</td><td>%dM</td></tr>\n", |
| 204 | |
status.getMaxMemory()/1048576)); |
| 205 | 0 | writer.append(String.format("<tr><td>Free memory</td><td>%dM</td></tr>\n", |
| 206 | |
status.getFreeMemory()/1048576)); |
| 207 | 0 | if (isComplete) { |
| 208 | 0 | writer.append("<tr><td><b>Indexing Complete</b></td><td></td></tr>"); |
| 209 | |
} |
| 210 | 0 | writer.append("</table>"); |
| 211 | |
|
| 212 | 0 | writer.append("<table><tr><td>\n"); |
| 213 | 0 | writer.append("<table>\n"); |
| 214 | 0 | writer.append(String.format("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n", |
| 215 | |
"Stage", "Blocked", "Queued", "Running", "Completed")); |
| 216 | 0 | for (Entry<String, StageExecutionStatus> entry : stagesStatus.entrySet()) { |
| 217 | 0 | StageExecutionStatus stageStatus = entry.getValue(); |
| 218 | |
|
| 219 | 0 | if (stageStatus.getBlockedInstances() > 0) { |
| 220 | 0 | writer.append("<tr class=\"blocked\">"); |
| 221 | 0 | } else if (stageStatus.getQueuedInstances() + stageStatus.getRunningInstances() > 0) { |
| 222 | 0 | writer.append("<tr class=\"running\">"); |
| 223 | |
} else { |
| 224 | 0 | writer.append("<tr class=\"complete\">"); |
| 225 | |
} |
| 226 | |
|
| 227 | 0 | writer.append("<td>" + entry.getKey() + "</td>"); |
| 228 | 0 | writer.append("<td class=\"right\">" + stageStatus.getBlockedInstances() + "</td>"); |
| 229 | 0 | writer.append("<td class=\"right\">" + stageStatus.getQueuedInstances() + "</td>"); |
| 230 | 0 | writer.append("<td class=\"right\">" + stageStatus.getRunningInstances() + "</td>"); |
| 231 | 0 | writer.append("<td class=\"right\">" + stageStatus.getCompletedInstances() + "</td>"); |
| 232 | 0 | writer.append("</tr>"); |
| 233 | 0 | } |
| 234 | 0 | writer.append("</table>"); |
| 235 | |
|
| 236 | |
|
| 237 | 0 | writer.append("</td><td>\n"); |
| 238 | 0 | writer.append("<table>"); |
| 239 | 0 | writer.append("<tr><th>Stage</th><th>Counter</th><th>Value</th></tr>"); |
| 240 | 0 | for (Entry<CounterName, AggregateCounter> entry : this.counters.entrySet()) { |
| 241 | 0 | if (entry.getValue().getValue() == 0) continue; |
| 242 | 0 | String stageName = entry.getKey().getStageName(); |
| 243 | 0 | StageExecutionStatus stageStatus = stagesStatus.get(stageName); |
| 244 | |
|
| 245 | 0 | if (stageStatus != null && |
| 246 | |
stageStatus.getRunningInstances() + stageStatus.getQueuedInstances() > 0) { |
| 247 | 0 | writer.append("<tr class=\"running\">"); |
| 248 | |
} else { |
| 249 | 0 | writer.append("<tr>"); |
| 250 | |
} |
| 251 | 0 | writer.append("<td>" + entry.getKey().getStageName() + "</td>"); |
| 252 | 0 | writer.append("<td>" + entry.getKey().getCounterName() + "</td>"); |
| 253 | 0 | writer.append("<td>" + entry.getValue().getValue() + "</td>"); |
| 254 | 0 | writer.append("</tr>"); |
| 255 | 0 | } |
| 256 | |
|
| 257 | 0 | writer.append("</table>"); |
| 258 | 0 | writer.append("</td></tr></table>\n"); |
| 259 | 0 | writer.append("</body>"); |
| 260 | 0 | writer.append("</html>"); |
| 261 | 0 | writer.close(); |
| 262 | |
|
| 263 | 0 | if (isComplete) { |
| 264 | 0 | setLastPageLoad(0); |
| 265 | 0 | notifyAll(); |
| 266 | |
} |
| 267 | 0 | } |
| 268 | |
|
| 269 | |
public synchronized void handle( |
| 270 | |
String target, |
| 271 | |
HttpServletRequest request, |
| 272 | |
HttpServletResponse response, |
| 273 | |
int dispatch) |
| 274 | |
throws IOException, ServletException { |
| 275 | 0 | if (request.getPathInfo().equals("/setcounter")) { |
| 276 | 0 | handleSetCounter(request, response); |
| 277 | |
} else { |
| 278 | 0 | handleStatus(request, response); |
| 279 | |
} |
| 280 | 0 | } |
| 281 | |
} |