source: trunk/www/views/jobs/view_job.jsp @ 5073

Last change on this file since 5073 was 5073, checked in by Nicklas Nordborg, 14 years ago

References #108: Logging the change history of an item

Added "Changed items" tab to the job popup that makes it possible to show all changes made by that job.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Id
File size: 18.6 KB
Line 
1<%-- $Id: view_job.jsp 5073 2009-08-21 10:15:11Z nicklas $
2  ------------------------------------------------------------------
3  Copyright (C) 2005 Nicklas Nordborg
4  Copyright (C) 2006 Jari Häkkinen, Nicklas Nordborg, Gregory Vincic
5  Copyright (C) 2007 Nicklas Nordborg
6
7  This file is part of BASE - BioArray Software Environment.
8  Available at http://base.thep.lu.se/
9
10  BASE is free software; you can redistribute it and/or
11  modify it under the terms of the GNU General Public License
12  as published by the Free Software Foundation; either version 3
13  of the License, or (at your option) any later version.
14
15  BASE is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License
21  along with BASE. If not, see <http://www.gnu.org/licenses/>.
22  ------------------------------------------------------------------
23
24  @author Nicklas
25  @version 2.0
26--%>
27<%@ page session="false"
28  import="net.sf.basedb.core.SessionControl"
29  import="net.sf.basedb.core.DbControl"
30  import="net.sf.basedb.core.SystemItems"
31  import="net.sf.basedb.core.Item"
32  import="net.sf.basedb.core.ItemContext"
33  import="net.sf.basedb.core.Permission"
34  import="net.sf.basedb.core.Job"
35  import="net.sf.basedb.core.JobAgent"
36  import="net.sf.basedb.core.BasicItem"
37  import="net.sf.basedb.core.Nameable"
38  import="net.sf.basedb.core.File"
39  import="net.sf.basedb.core.Path"
40  import="net.sf.basedb.core.User"
41  import="net.sf.basedb.core.ChangeHistory"
42  import="net.sf.basedb.core.ItemResultIterator"
43  import="net.sf.basedb.core.ItemQuery"
44  import="net.sf.basedb.core.ItemResultList"
45  import="net.sf.basedb.core.PermissionDeniedException"
46  import="net.sf.basedb.core.PluginDefinition"
47  import="net.sf.basedb.core.PluginConfiguration"
48  import="net.sf.basedb.core.ParameterInfo"
49  import="net.sf.basedb.core.query.Orders"
50  import="net.sf.basedb.core.query.Hql"
51  import="net.sf.basedb.core.plugin.GuiContext"
52  import="net.sf.basedb.core.plugin.Plugin"
53  import="net.sf.basedb.core.signal.SignalTransporter"
54  import="net.sf.basedb.core.signal.Signal"
55  import="net.sf.basedb.plugins.util.Parameters"
56  import="net.sf.basedb.clients.web.Base"
57  import="net.sf.basedb.clients.web.ChangeHistoryUtil"
58  import="net.sf.basedb.clients.web.util.HTML"
59  import="net.sf.basedb.util.Values"
60  import="net.sf.basedb.util.formatter.Formatter"
61  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
62  import="java.util.Date"
63  import="java.util.Map"
64  import="java.util.Set"
65  import="java.util.List"
66  import="java.util.Collections"
67  import="java.util.Collection"
68%>
69<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
70<%@ taglib prefix="t" uri="/WEB-INF/tab.tld" %>
71<%@ taglib prefix="tbl" uri="/WEB-INF/table.tld" %>
72<%!
73  private static final Item itemType = Item.JOB;
74  private static final GuiContext guiContext = new GuiContext(itemType, GuiContext.Type.ITEM);
75
76  private String getItemName(BasicItem item)
77  {
78    String itemName = "";
79    if (item instanceof File)
80    {
81      itemName = ((File)item).getPath().toString();
82    }
83    else if (item instanceof Nameable)
84    {
85      itemName = ((Nameable)item).getName();
86    }
87    else
88    {
89      itemName = item.toString();
90    }
91    return itemName;
92  }
93%>
94<%
95final SessionControl sc = Base.getExistingSessionControl(pageContext, true);
96final String ID = sc.getId();
97final ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, null);
98final int itemId = cc.getId();
99final String tab = Values.getString(request.getParameter("tab"), "properties");
100final float scale = Base.getScale(sc);
101final DbControl dc = sc.newDbControl();
102try
103{
104  Job job = Job.getById(dc, itemId);
105  String title = "View job -- " + HTML.encodeTags(job.getName());
106 
107  final boolean writePermission = job.hasPermission(Permission.WRITE);
108  final boolean deletePermission = job.hasPermission(Permission.DELETE);
109  final boolean isRemoved = job.isRemoved();
110  final boolean isUsed = isRemoved && job.isUsed();
111  final boolean deletePermanentlyPermission = deletePermission && !isUsed;
112 
113  boolean readCurrentConfig = true;
114  PluginConfiguration currentConfig = null;
115  try
116  {
117    currentConfig = job.getPluginConfiguration();
118  }
119  catch (PermissionDeniedException ex)
120  {
121    readCurrentConfig = false;
122  }
123  boolean readAgent = true;
124  JobAgent agent = null;
125  try
126  {
127    agent = job.getJobAgent();
128  }
129  catch (PermissionDeniedException ex)
130  {
131    readAgent = false;
132  }
133
134  int parameterVersion = job.getParameterVersion();
135  int latestParameterVersion = currentConfig == null ? parameterVersion : currentConfig.getParameterVersion();
136
137  final Job.Status status = job.getStatus();
138  final boolean autoUpdate = status == Job.Status.WAITING || 
139    status == Job.Status.PREPARED || status == Job.Status.EXECUTING || status == Job.Status.ABORTING;
140  Formatter<Date> dateFormatter = FormatterFactory.getDateFormatter(sc);
141  Formatter<Date> dateTimeFormatter = FormatterFactory.getDateTimeFormatter(sc);
142  Set<String> jobParameters = job.getParameterNames();
143  File logFile = null;
144  if (!autoUpdate && jobParameters != null && jobParameters.contains(Parameters.LOGFILE_PARAMETER))
145  {
146    try
147    {
148      Object temp = job.getParameterValue(Parameters.LOGFILE_PARAMETER);
149      if (temp instanceof File)
150      {
151        logFile = (File)temp;
152      }
153      else if (temp instanceof String)
154      {
155        logFile = File.getByPath(dc, new Path((String)temp, Path.Type.FILE), false);
156      }
157    }
158    catch (Throwable t)
159    {}
160  }
161 
162  // Check if the plug-in supports the "Abort" signal
163  boolean supportsAbort = status == Job.Status.WAITING && writePermission;
164  if (status == Job.Status.EXECUTING && writePermission)
165  {
166    try
167    {
168      SignalTransporter signalTransporter = job.getSignalTransporter();
169      Collection<Signal> supportedSignals = signalTransporter != null ? 
170        signalTransporter.getSupportedSignals() : null;
171      supportsAbort = signalTransporter != null && 
172        (supportedSignals == null || supportedSignals.contains(Signal.ABORT));
173    }
174    catch (Exception ex)
175    {}
176  }
177  %>
178  <base:page type="popup" title="<%=title%>"> 
179  <base:head scripts="tabcontrol.js,table.js" styles="tabcontrol.css,progressbar.css,table.css">
180  <script language="JavaScript">
181  function autoUpdate()
182  {
183    var autoUpdate = <%=autoUpdate ? "true" : "false"%>;
184    if (autoUpdate)
185    {
186      setTimeout('location.reload()', 10000);
187    }
188  }
189  function abortJob()
190  {
191    if (confirm('Are you sure? This action may not be undone'))
192    {
193      location.href = 'index.jsp?ID=<%=ID%>&cmd=AbortJob&item_id=<%=itemId%>';
194    }
195  }
196  function restartJob(clearDryRun)
197  {
198    var parameterVersion = <%=parameterVersion%>;
199    var latestVersion = <%=latestParameterVersion%>;
200    var useLatestConfiguration = 0;
201    if (parameterVersion != latestVersion)
202    {
203      var msg = 'The configuration parameters for the plugin has changed since\n';
204      msg += 'the job was added to the job queue. Do you want to use the new\n';
205      msg += 'configuration parameters?\n\n';
206      msg += 'Ok / Yes = Use the new parameters (version = '+latestVersion + ')\n';
207      msg += 'Cancel / No = Use the current parameters (version = ' + parameterVersion + ')';
208      if (confirm(msg)) useLatestConfiguration = 1;
209    }
210    var url = 'index.jsp?ID=<%=ID%>&cmd=RestartJob&item_id=<%=itemId%>';
211    url += '&useLatestConfiguration='+useLatestConfiguration;
212    if (clearDryRun) url += '&clearDryRun=' + clearDryRun;
213    location.href = url;
214  }
215  function reconfigureJob()
216  {
217    location.href = '../../common/plugin/index.jsp?ID=<%=ID%>&cmd=ConfigureJob&job_id=<%=itemId%>';
218  }
219 
220    function deleteItemPermanently()
221    {
222      Main.deleteItemPermanently('<%=ID%>', true, '<%=itemType.name()%>', <%=itemId%>, '&callback=itemDeleted');
223    }
224    function itemDeleted()
225    {
226      location.href = getRoot() + 'common/close_popup.jsp?ID=<%=ID%>&refresh_opener=1&wait=0';
227    }
228    function showUsingItems()
229    {
230      Main.showUsingItems('<%=ID%>', '<%=itemType.name()%>', <%=itemId%>);
231    }
232  </script>
233  </base:head>
234  <base:body onload="autoUpdate()">
235    <h3 class="docked"><%=title%> <base:help tabcontrol="main" /></h3>
236    <t:tabcontrol id="main" active="<%=tab%>" position="bottom" contentstyle="<%="height: "+(int)(scale*350)+"px;"%>">
237    <t:tab id="properties" title="Properties" helpid="job.view.properties">
238
239      <%
240      if (job.isRemoved())
241      {
242        %>
243        <div class="itemstatus">
244          <base:icon 
245            image="<%=deletePermanentlyPermission ? "deleted.gif" : "deleted_disabled.gif"%>"
246            onclick="<%=deletePermanentlyPermission ? "deleteItemPermanently()" : null%>"
247            tooltip="<%=deletePermanentlyPermission ? "Permanently delete this item" : null%>"
248            visible="<%=isRemoved%>"> This item has been flagged for deletion<br></base:icon>
249          <base:icon image="used.gif" 
250            onclick="showUsingItems()"
251            tooltip="Show the items that are using this one"
252            visible="<%=isUsed%>"> This item is used by other items and can't be permanently deleted<br></base:icon>
253        </div>
254        <%
255      }
256      %>
257      <table class="form" cellspacing=0>
258      <tr>
259        <td class="prompt">Name</td>
260        <td><%=HTML.encodeTags(job.getName())%></td>
261      </tr>
262      <tr>
263        <td class="prompt">Description</td>
264        <td><%=HTML.niceFormat(job.getDescription())%></td>
265      </tr>
266      <tr valign="top">
267        <td class="prompt">Priority</td>
268        <td>
269          <%=job.getPriority()%> (1 = highest, 10 = lowest)
270        </td>
271      </tr>
272      <tr valign="top">
273        <td class="prompt">Status</td>
274        <td <%=job.getStatus() == Job.Status.ERROR ? "class=\"error\" style=\"text-align: left;\"" : "" %>>
275          <%=job.getStatus()%><%=job.isDryRun() ? " (dry-run)" : "" %>: <%=HTML.niceFormat(job.getStatusMessage())%>
276        </td>
277      </tr>
278      <tr valign="middle">
279        <td class="prompt">Percent complete</td>
280        <td>
281          <table border=0 cellspacing=0 cellpadding=0>
282          <tr>
283          <td width="100" valign="middle">
284            <table width="100" class="progressbar" border=0 cellspacing=0 cellpadding=0>
285            <tr>
286              <%
287              int percent = job.getPercentComplete();
288              if (percent > 0) 
289              {
290                %>
291                <td width="<%=percent%>%" class="done">&nbsp;</td>
292                <% 
293              }
294              if (percent < 100) 
295              { 
296                %>
297                <td width="<%=100-percent%>%" class="remain">&nbsp;</td>
298                <%
299              }
300              %>
301            </tr>
302            </table>
303          </td>
304          <td valign="middle">&nbsp;<%=percent%>%</td>
305          <%
306          if (logFile != null)
307          {
308            %>
309            <td valign="middle" style="padding-left: 10px;"><base:button image="view.gif" 
310                title="View log&hellip;"
311                onclick="<%="Main.viewFile('" + ID + "', " + logFile.getId() + ")"%>"
312                tooltip="View the log file with detailed information about this job"
313                /></td>
314              <%
315            }
316            %>
317          </tr>
318          </table>
319        </td>
320      </tr>
321      <tr valign="top">
322        <td class="prompt">Created</td>
323        <td>
324          <%=dateTimeFormatter.format(job.getCreated())%>
325        </td>
326      </tr>
327      <tr valign="top">
328        <td class="prompt">Started</td>
329        <td>
330          <%=dateTimeFormatter.format(job.getStarted())%>
331        </td>
332      </tr>
333      <tr valign="top">
334        <td class="prompt">Ended</td>
335        <td>
336          <%=dateTimeFormatter.format(job.getEnded())%>
337        </td>
338      </tr>
339      <tr valign="top">
340        <td class="prompt">Running time</td>
341        <td>
342          <%
343          Date started = job.getStarted();
344          if (started != null)
345          {
346            Date ended = job.getEnded();
347            if (ended == null) ended = new Date();
348            long runningTime = ended.getTime() - started.getTime();
349            %>
350            <%=Values.formatTime(runningTime / 1000)%>
351            <%
352          }
353          %>
354        </td>
355      </tr>
356      <tr valign="top">
357        <td class="prompt">Server</td>
358        <td>
359          <%=HTML.encodeTags(job.getServer())%>
360        </td>
361      </tr>
362      <tr valign="top">
363        <td class="prompt">Job agent</td>
364        <td><%=Base.getEncodedName(agent, !readAgent)%></td>
365      </tr>
366      <tr valign="top">
367        <td class="prompt">User</td>
368        <td>
369          <base:propertyvalue item="<%=job%>" property="owner.name" />
370        </td>
371      </tr>
372      <tr valign="top">
373        <td class="prompt">Experiment</td>
374        <td>
375          <base:propertyvalue item="<%=job%>" property="experiment.name" />
376        </td>
377      </tr>
378      <tr valign="top">
379        <td class="prompt">Plugin</td>
380        <td>
381          <base:propertyvalue item="<%=job%>" property="pluginDefinition.name" /> 
382          <%=job.getPluginVersion() == null ?
383            "" : "(version " + HTML.encodeTags(job.getPluginVersion()) + ")"%>
384        </td>
385      </tr>
386      <tr valign="top">
387        <td class="prompt">Configuration</td>
388        <td>
389          <base:propertyvalue item="<%=job%>" property="pluginConfiguration.name" />
390        </td>
391      </tr>
392      </table>
393      </t:tab>
394      <%
395      if (job.getStackTrace() != null)
396      {
397        %>
398        <t:tab id="stacktrace" title="Stack trace" helpid="job.view.stacktrace">
399          <div style="font-family: monospace">
400          <%=HTML.niceFormat(job.getStackTrace())%>
401          </div>
402        </t:tab>
403        <%
404      }
405      %>
406 
407      <t:tab id="parameters" title="Parameters" helpid="job.view.parameters">
408        <h4>Job parameters</h4>
409        <table class="form" cellspacing=0>
410        <%
411        List<String> names = new java.util.ArrayList<String>(jobParameters);
412        Collections.sort(names);
413        for (String name : names)
414        {
415          StringBuilder sb = new StringBuilder();
416          String displayValue = "";
417          String description = "";
418          try
419          {
420            ParameterInfo pi = job.getParameterInfo(name);
421            if (pi.getLabel() != null) name = HTML.encodeTags(pi.getLabel());
422            description = HTML.encodeTags(pi.getDescription());
423            List<?> values = pi.getValues();
424            int i = 0;
425            for (Object value : values)
426            {
427              if (value != null)
428              {
429                if (i > 0) sb.append(", ");
430                i++;
431                if (value instanceof BasicItem)
432                {
433                  sb.append(HTML.encodeTags(getItemName((BasicItem)value)));
434                }
435                else if (value instanceof Date)
436                {
437                  sb.append(dateFormatter.format((Date)value));
438                }
439                else
440                {
441                  sb.append(HTML.encodeTags(value.toString()));
442                }
443              }
444            }
445            displayValue = sb.toString();
446          }
447          catch (Throwable ex)
448          {
449            displayValue = "<i>ERROR: "+ex.getMessage()+"</i>";
450          }
451          %>
452          <tr>
453            <td class="prompt"><span title="<%=description%>"><%=name%></span></td>
454            <td>
455              <%=displayValue%>
456            </td>
457          </tr>
458          <%
459        }
460        %>
461        </table>
462
463        <%
464        if (!readCurrentConfig)
465        {
466          %>
467          <h4>Plugin configuration parameters</h4>
468          <i>- denied -</i>
469          <%
470        }
471        else if (currentConfig != null)
472        {
473          %>
474          <h4>Plugin configuration parameters</h4>
475          <table class="form" cellspacing=0>
476          <tr>
477            <td class="prompt"><span 
478              title="The version of the parmeters used for this job, current version in paranthesis">Parameter version</span></td>
479            <td><%=parameterVersion%> (<%=latestParameterVersion %>)</td>
480          </tr>
481          <%
482          names = new java.util.ArrayList<String>(currentConfig.getParameterNames(parameterVersion));
483          Collections.sort(names);
484          for (String name : names)
485          {
486            StringBuilder sb = new StringBuilder();
487            String displayValue = "";
488            String description = "";
489            try
490            {
491              ParameterInfo pi = currentConfig.getParameterInfo(name, parameterVersion);
492              if (pi.getLabel() != null) name = HTML.encodeTags(pi.getLabel());
493              description = HTML.encodeTags(pi.getDescription());
494              List<?> values = pi.getValues();
495              int i = 0;
496              for (Object value : values)
497              {
498                if (value != null)
499                {
500                  if (i > 0) sb.append(", ");
501                  i++;
502                  if (value instanceof BasicItem)
503                  {
504                    sb.append(HTML.encodeTags(getItemName((BasicItem)value)));
505                  }
506                  else if (value instanceof Date)
507                  {
508                    sb.append(dateFormatter.format((Date)value));
509                  }
510                  else
511                  {
512                    sb.append(HTML.encodeTags(value.toString()));
513                  }
514                }
515              }
516              displayValue = sb.toString();
517            }
518            catch (Throwable ex)
519            {
520              displayValue = "<i>ERROR: "+ex.getMessage()+"</i>";
521            }
522            %>
523            <tr>
524              <td class="prompt"><span title="<%=description%>"><%=name%></span></td>
525              <td>
526                <%=displayValue%>
527              </td>
528            </tr>
529            <%
530          }
531          %>
532          </table>
533          <%
534        }
535        %>
536       
537      </t:tab>
538     
539      <t:tab id="changes" title="Changed items"
540        tooltip="Display a log of all modifications made by this job"
541        visible="<%=!autoUpdate && ChangeHistoryUtil.showChangeHistoryTab(sc) %>">
542        <%
543        ItemQuery<ChangeHistory> query = ChangeHistory.getChangesBy(job);
544        query.order(Orders.asc(Hql.property("id")));
545        ItemResultIterator<ChangeHistory> changes = query.iterate(dc);
546        int numChanges = 0;
547        %>
548        <tbl:table id="history" clazz="itemlist" columns="all">
549          <tbl:columndef id="changeType" title="Change type" />
550          <tbl:columndef id="item" title="Item" />
551          <tbl:columndef id="info" title="Info" />
552         
553          <tbl:data>
554            <tbl:columns>
555            </tbl:columns>
556            <tbl:rows>
557            <%
558            while (changes.hasNext())
559            {
560              numChanges++;
561              ChangeHistory change = changes.next();
562              %>
563              <tbl:row>
564                <tbl:cell column="changeType"><%=change.getChangeType()%> <%=change.getItemType()%></tbl:cell>
565                <tbl:cell column="item"><%=ChangeHistoryUtil.getItem(dc, change, false, false)%></tbl:cell>
566                <tbl:cell column="info"><%=HTML.encodeTags(change.getChangeInfo())%></tbl:cell>
567              </tbl:row>
568              <%
569            }
570            %>
571            </tbl:rows>
572          </tbl:data>
573          <%
574          if (numChanges == 0)
575          {
576            %>
577            <tbl:panel>
578            No log entries found for this job. NOTE! This job may have made
579            other changes that are not recorded by the logging mechanism.
580            </tbl:panel>
581            <%
582          }
583          %>
584        </tbl:table>
585       
586      </t:tab>
587     
588      </t:tabcontrol>
589 
590    <base:buttongroup align="center">
591      <%
592      if (autoUpdate)
593      {
594        %>
595        <base:button onclick="location.reload()" title="Refresh" image="refresh.gif" />
596        <%
597      }
598      %>
599      <%
600      if (supportsAbort)
601      {
602        %>
603        <base:button onclick="abortJob()" title="Abort&hellip;" image="abort.png" />
604        <%
605      }
606      %>
607      <%
608      if (job.getStatus() == Job.Status.ERROR && job.getJobType() == Job.Type.RUN_PLUGIN)
609      {
610        %>
611        <base:button onclick="restartJob(0)" title="Restart job" 
612          image="refresh.gif" 
613          tooltip="Try to run this job again with the same parameters"
614        />
615        <base:button onclick="reconfigureJob()" title="Re-configure job"
616          image="runplugin.gif"
617          tooltip="Change the parameters for this job and try again"
618          visible="<%=job.hasContext()%>"
619        />
620        <%
621      }
622      if (job.getStatus() == Job.Status.DONE && job.isDryRun())
623      {
624        %>
625        <base:button onclick="restartJob(1)" title="Really run" 
626          image="refresh.gif" 
627          tooltip="Run this dry-run job for real"
628        />
629        <%
630      }
631      %>
632      <base:button onclick="window.close()" title="Close" />
633     
634    </base:buttongroup>
635    <center>
636    <%
637    if (autoUpdate)
638    {
639      %>
640      <i>(This page will automatically refresh itself every 10 seconds)</i>
641      <%
642    }
643    %></center>
644  </base:body>
645  </base:page>
646  <%
647}
648finally
649{
650  if (dc != null) dc.close();
651}
652
653%>
Note: See TracBrowser for help on using the repository browser.