package net.i2p.router.web; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import net.i2p.data.DataHelper; import net.i2p.router.Job; import net.i2p.router.JobStats; import net.i2p.util.ObjectCounter; public class JobQueueHelper extends HelperBase { private static final int MAX_JOBS = 50; public String getJobQueueSummary() { try { if (_out != null) { renderStatusHTML(_out); return ""; } else { ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024); renderStatusHTML(new OutputStreamWriter(baos)); return new String(baos.toByteArray()); } } catch (IOException ioe) { ioe.printStackTrace(); return ""; } } /** * Moved from JobQueue * @since 0.8.9 */ private void renderStatusHTML(Writer out) throws IOException { List readyJobs = new ArrayList(8); List timedJobs = new ArrayList(128); List activeJobs = new ArrayList(8); List justFinishedJobs = new ArrayList(8); int numRunners = _context.jobQueue().getJobs(readyJobs, timedJobs, activeJobs, justFinishedJobs); StringBuilder buf = new StringBuilder(32*1024); buf.append("

").append(_("I2P Job Queue")).append("


") .append(_("Job runners")).append(": ").append(numRunners) .append("
\n"); long now = _context.clock().now(); buf.append("
").append(_("Active jobs")).append(": ").append(activeJobs.size()).append("
    \n"); for (int i = 0; i < activeJobs.size(); i++) { Job j = activeJobs.get(i); buf.append("
  1. (").append(_("started {0} ago", DataHelper.formatDuration2(now-j.getTiming().getStartAfter()))).append("): "); buf.append(j.toString()).append("
  2. \n"); } buf.append("
\n"); buf.append("
").append(_("Just finished jobs")).append(": ").append(justFinishedJobs.size()).append("
    \n"); for (int i = 0; i < justFinishedJobs.size(); i++) { Job j = justFinishedJobs.get(i); buf.append("
  1. (").append(_("finished {0} ago", DataHelper.formatDuration2(now-j.getTiming().getActualEnd()))).append("): "); buf.append(j.toString()).append("
  2. \n"); } buf.append("
\n"); buf.append("
").append(_("Ready/waiting jobs")).append(": ").append(readyJobs.size()).append("
    \n"); ObjectCounter counter = new ObjectCounter(); for (int i = 0; i < readyJobs.size(); i++) { Job j = readyJobs.get(i); counter.increment(j.getName()); if (i >= MAX_JOBS) continue; buf.append("
  1. [waiting "); buf.append(DataHelper.formatDuration2(now-j.getTiming().getStartAfter())); buf.append("]: "); buf.append(j.toString()).append("
  2. \n"); } buf.append("
\n"); getJobCounts(buf, counter); out.write(buf.toString()); buf.setLength(0); buf.append("
").append(_("Scheduled jobs")).append(": ").append(timedJobs.size()).append("
    \n"); long prev = Long.MIN_VALUE; counter.clear(); for (int i = 0; i < timedJobs.size(); i++) { Job j = timedJobs.get(i); counter.increment(j.getName()); if (i >= MAX_JOBS) continue; long time = j.getTiming().getStartAfter() - now; // translators: {0} is a job name, {1} is a time, e.g. 6 min buf.append("
  1. ").append(_("{0} will start in {1}", j.getName(), DataHelper.formatDuration2(time))); // debug, don't bother translating if (time < 0) buf.append(" DELAYED"); if (time < prev) buf.append(" ** OUT OF ORDER **"); prev = time; buf.append("
  2. \n"); } buf.append("
\n"); getJobCounts(buf, counter); out.write(buf.toString()); buf.setLength(0); buf.append("
").append(_("Total Job Statistics")).append("\n"); getJobStats(buf); out.write(buf.toString()); } /** @since 0.9.5 */ private void getJobCounts(StringBuilder buf, ObjectCounter counter) { List names = new ArrayList(counter.objects()); if (names.size() < 4) return; buf.append("\n" + "\n"); } buf.append("
").append(_("Job")).append("").append(_("Queued")).append(""); Collections.sort(names, new JobCountComparator(counter)); for (String name : names) { buf.append("
").append(name) .append("").append(counter.count(name)) .append("
\n"); } /** * Render the HTML for the job stats. * Moved from JobQueue * @since 0.8.9 */ private void getJobStats(StringBuilder buf) { buf.append("\n" + "" + "" + "\n"); long totRuns = 0; long totExecTime = 0; long avgExecTime = 0; long maxExecTime = -1; long minExecTime = -1; long totPendingTime = 0; long avgPendingTime = 0; long maxPendingTime = -1; long minPendingTime = -1; List tstats = new ArrayList(_context.jobQueue().getJobStats()); Collections.sort(tstats, new JobStatsComparator()); for (JobStats stats : tstats) { buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append("\n"); totRuns += stats.getRuns(); totExecTime += stats.getTotalTime(); if (stats.getMaxTime() > maxExecTime) maxExecTime = stats.getMaxTime(); if ( (minExecTime < 0) || (minExecTime > stats.getMinTime()) ) minExecTime = stats.getMinTime(); totPendingTime += stats.getTotalPendingTime(); if (stats.getMaxPendingTime() > maxPendingTime) maxPendingTime = stats.getMaxPendingTime(); if ( (minPendingTime < 0) || (minPendingTime > stats.getMinPendingTime()) ) minPendingTime = stats.getMinPendingTime(); } if (totRuns != 0) { if (totExecTime != 0) avgExecTime = totExecTime / totRuns; if (totPendingTime != 0) avgPendingTime = totPendingTime / totRuns; } buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append(""); buf.append("
").append(_("Job")).append("").append(_("Runs")).append("").append(_("Time")).append("").append(_("Avg")).append("") .append(_("Max")).append("").append(_("Min")).append("").append(_("Pending")).append("").append(_("Avg")).append("") .append(_("Max")).append("").append(_("Min")).append("
").append(stats.getName()).append("").append(stats.getRuns()).append("").append(DataHelper.formatDuration2(stats.getTotalTime())).append("").append(DataHelper.formatDuration2(stats.getAvgTime())).append("").append(DataHelper.formatDuration2(stats.getMaxTime())).append("").append(DataHelper.formatDuration2(stats.getMinTime())).append("").append(DataHelper.formatDuration2(stats.getTotalPendingTime())).append("").append(DataHelper.formatDuration2(stats.getAvgPendingTime())).append("").append(DataHelper.formatDuration2(stats.getMaxPendingTime())).append("").append(DataHelper.formatDuration2(stats.getMinPendingTime())).append("
").append(_("Summary")).append("").append(totRuns).append("").append(DataHelper.formatDuration2(totExecTime)).append("").append(DataHelper.formatDuration2(avgExecTime)).append("").append(DataHelper.formatDuration2(maxExecTime)).append("").append(DataHelper.formatDuration2(minExecTime)).append("").append(DataHelper.formatDuration2(totPendingTime)).append("").append(DataHelper.formatDuration2(avgPendingTime)).append("").append(DataHelper.formatDuration2(maxPendingTime)).append("").append(DataHelper.formatDuration2(minPendingTime)).append("
\n"); } /** @since 0.8.9 */ private static class JobStatsComparator implements Comparator { public int compare(JobStats l, JobStats r) { return l.getName().compareTo(r.getName()); } } /** @since 0.9.5 */ private static class JobCountComparator implements Comparator { private final ObjectCounter _counter; public JobCountComparator(ObjectCounter counter) { _counter = counter; } public int compare(String l, String r) { // reverse int lc = _counter.count(l); int rc = _counter.count(r); if (lc > rc) return -1; if (lc < rc) return 1; return l.compareTo(r); } } }