Changeset 2643


Ignore:
Timestamp:
Sep 15, 2006, 4:08:12 PM (16 years ago)
Author:
Nicklas Nordborg
Message:

References #351: External job server usage

In-process job executor is now working. Work has started on the out-of-process job executor.
Added sendMessage and stackTrace property to job for improved error reporting.

Location:
trunk
Files:
1 added
25 edited

Legend:

Unmodified
Added
Removed
  • trunk/build.xml

    r2641 r2643  
    9090  <property name="doc.public" location="${doc}/api/public" />
    9191
    92   <uptodate
    93     property="nohibernate"
    94     srcfile="${core.src}/net/sf/basedb/core/data/UserData.java"
    95     targetfile="${core.build}/net/sf/basedb/core/data/UserData.hbm.xml"
    96     >
    97   </uptodate>
    98  
    9992  <path id="core.compile.classpath">
    10093    <fileset dir="${lib}/compile">
     
    173166
    174167  <target
     168    name="check.hibernate"
     169    >
     170    <uptodate
     171      property="nohibernate"
     172      srcfile="${core.src}/net/sf/basedb/core/data/UserData.java"
     173      targetfile="${core.build}/net/sf/basedb/core/data/UserData.hbm.xml"
     174      >
     175    </uptodate>
     176  </target>
     177 
     178  <target
    175179    name="core.hibernate"
    176     depends="core.compile"
     180    depends="core.compile,check.hibernate"
    177181    description="Generates Hibernate mappings for the core."
    178182    unless="nohibernate"
  • trunk/src/clients/jobagent/jobagent.properties.in

    r2641 r2643  
    6262# =======================
    6363
    64 ## The name of the executor class that is responsible for starting the job
     64# The name of the executor class that is responsible for starting the job
    6565## The default is ProcessJobExecutor which starts job in a separate process
    6666## The class must implement the net.sf.basedb.clients.JobExecutor interface
     67
     68## Executor that executes the jobs in a separate process (recommended)
    6769agent.executor.class=net.sf.basedb.clients.jobagent.executors.ProcessJobExecutor
     70
     71## Executor that executes jobs in the same process as the job agent (not recommended)
     72# agent.executor.class=net.sf.basedb.clients.jobagent.executors.ThreadJobExecutor
     73
     74## Executor useful for debugging purposes
     75# agent.executor.class=net.sf.basedb.clients.jobagent.executors.DummyJobExecutor
    6876
    6977## Initialisation parameters to the job exector. This string will be passed
  • trunk/src/clients/jobagent/jobagent.sh

    r2634 r2643  
    3030
    3131# Execute JobAgent
    32 java -server -Xmx40M -Xms40M -cp $CP net.sf.basedb.clients.jobagent.AgentController $* &
     32java -server -Xmx50M -Xms50M -cp $CP net.sf.basedb.clients.jobagent.AgentController $* &
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/Agent.java

    r2641 r2643  
    291291  public Agent(Properties properties)
    292292  {
     293    if (log.isDebugEnabled())
     294    {
     295      log.debug("Creating new agent");
     296      for (Map.Entry<Object, Object> entry : properties.entrySet())
     297      {
     298        Object value = entry.getValue();
     299        Object key = entry.getKey();
     300        if ("agent.password".equals(key))
     301        {
     302          value = "******************";
     303        }
     304        log.debug("Property [" + key + " = " + value + "]");
     305      }
     306    }
     307   
    293308    // BASE settings
    294309    this.login = properties.getProperty("agent.user");
     
    504519      jobExecutor = createJobExecutor();
    505520      jobQueueChecker = createJobQueueChecker();
     521     
     522      // Start the BASE application in a new thread so we can return quickly
     523      Thread t = new Thread(
     524        new Runnable()
     525        {
     526          public void run()
     527          {
     528            SessionControl sc = null;
     529            try
     530            {
     531              sc = getSessionControl();
     532            }
     533            catch (Throwable t)
     534            {}
     535            if (sc == null || !sc.isLoggedIn())
     536            {
     537              stop();
     538            }
     539          }
     540        }
     541      );
     542      t.start();
    506543    }
    507544  }
     
    633670  public SessionControl getSessionControl()
    634671  {
    635     if (sc == null)
    636     {
    637       Application.start(false);
    638       sc = Application.newSessionControl("net.sf.basedb.clients.jobagent",
    639         SocketUtil.getLocalHost().toString(), null);
    640     }
    641     if (!sc.isLoggedIn())
    642     {
    643       sc.login(login, password, null, false);
     672    try
     673    {
     674      if (sc == null)
     675      {
     676        log.info("Starting BASE application");
     677        Application.start(false);
     678        sc = Application.newSessionControl("net.sf.basedb.clients.jobagent",
     679          SocketUtil.getLocalHost().toString(), null);
     680      }
     681      if (!sc.isLoggedIn())
     682      {
     683        log.info("Logging in as user: " + login);
     684        sc.login(login, password, "Job agent running on host " + getServerName(), false);
     685      }
     686    }
     687    catch (Throwable t)
     688    {
     689      log.error(t.getMessage(), t);
    644690    }
    645691    return sc;
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/JobExecutor.java

    r2641 r2643  
    6262    to the job executor to decide how to handle this. It may either decide
    6363    to kill the job or let it continue as if nothing happened.
     64    <p>
     65    The job sent to this method has it's status set to {@link Job.Status#PREPARED}.
     66    The implementation of this method must change the status to either
     67    {@link Job.Status#DONE} or {@link Job.Status#ERROR}. If the status hasn't changed
     68    the job agent will set the status to signal an unknown error.
    6469   
    6570    @param sc A <code>SessionControl</code> where the owner of the job is
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/JobRunner.java

    r2641 r2643  
    2424package net.sf.basedb.clients.jobagent;
    2525
     26import java.util.Arrays;
     27
     28import net.sf.basedb.core.DbControl;
     29import net.sf.basedb.core.ItemModifiedException;
    2630import net.sf.basedb.core.Job;
     31import net.sf.basedb.core.SessionControl;
    2732
    2833/**
     
    4550  */
    4651  private static final org.apache.log4j.Logger log =
    47     org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.jobagent.Agent");
     52    org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.jobagent.JobRunner");
    4853
    4954  private final Job job;
     
    8085      return;
    8186    }
     87    DbControl dc = null;
     88    SessionControl sc = null;
    8289    try
    8390    {
    8491      log.info("Executing job " + job + " using executor " + jobExecutor);
    85       jobExecutor.executeJob(agent.getImpersonatedSessionControl(job), agent, job, slotToUse);
     92     
     93      // Log in as the owner of the job
     94      sc = agent.getImpersonatedSessionControl(job);
     95      dc = sc.newDbControl();
     96     
     97      // Load a fresh copy of the job
     98      Job j = Job.getById(dc, job.getId());
     99     
     100      // Status may have changed since the queue was checked
     101      if (j.getStatus() != Job.Status.WAITING) return;
     102     
     103      // Change the status to Job.Status.PREPARED
     104      j.setPrepared(agent.getServerName());
     105      try
     106      {
     107        dc.commit();
     108      }
     109      catch (ItemModifiedException ex)
     110      {
     111        // Another thread or agent has started the job already, return
     112        // without starting the job
     113        return;
     114      }
     115     
     116      try
     117      {
     118        jobExecutor.executeJob(sc, agent, j, slotToUse);
     119      }
     120      catch (Throwable t)
     121      {
     122        // Something went wrong, update status to ERROR
     123        dc = sc.newDbControl();
     124        j = Job.getById(dc, j.getId());
     125        j.doneError(t.getMessage(), Arrays.asList(t));
     126        dc.commit();
     127        throw t;
     128      }
     129     
     130      // Check that that the status has changed from prepared
     131      dc = sc.newDbControl();
     132      j = Job.getById(dc, j.getId());
     133      if (j.getStatus() == Job.Status.PREPARED)
     134      {
     135        j.doneError("Unknown error, job was never executed.");
     136        log.error("Unknown error: Job " + job + " was never executed by executor " + jobExecutor);
     137      }
     138      else
     139      {
     140        log.info("Done executing job: " + job + " with executor " + jobExecutor);
     141      }
     142      dc.commit();
    86143    }
    87144    catch (Throwable t)
     
    92149    {
    93150      agent.jobDone(job, slotToUse);
     151      if (dc != null) dc.close();
     152      if (sc != null && sc.isLoggedIn())
     153      {
     154        try
     155        {
     156          sc.logout();
     157        }
     158        catch (Throwable t)
     159        {
     160          log.error(t.getMessage(), t);
     161        }
     162      }
    94163    }
    95164  }
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/DummyJobExecutor.java

    r2641 r2643  
    3333  This is dummy job executor implementation which just sets the status
    3434  of each job to {@link net.sf.basedb.core.Job.Status#DONE} without actually
    35   executin the job. This class is usful for debuggin the job agent application.
     35  executing the job. This class is usful for debugging the job agent application.
    3636
    3737  @author nicklas
     
    7171      job.doneOk("Not really, but used for testing job agent");
    7272      dc.commit();
     73      log.info("Done executing: " + job);
    7374    }
    7475    finally
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/ProcessJobExecutor.java

    r2641 r2643  
    2424package net.sf.basedb.clients.jobagent.executors;
    2525
     26import java.io.BufferedReader;
     27import java.io.IOException;
     28import java.io.InputStream;
     29import java.io.InputStreamReader;
     30import java.io.OutputStream;
     31import java.io.PrintWriter;
     32import java.io.Reader;
     33import java.io.StringWriter;
     34import java.io.Writer;
     35import java.util.ArrayList;
     36import java.util.Arrays;
     37import java.util.List;
     38
    2639import net.sf.basedb.clients.jobagent.Agent;
    2740import net.sf.basedb.clients.jobagent.JobExecutor;
     41import net.sf.basedb.core.DbControl;
    2842import net.sf.basedb.core.Job;
    2943import net.sf.basedb.core.SessionControl;
     
    7084  {
    7185    log.info("Executing job: " + job);
    72     // TODO - start new process for executing the job
     86    String classPath = System.getProperty("java.class.path");
     87   
     88    List<String> cmd = new ArrayList<String>(10);
     89    cmd.add("java");
     90    cmd.add("-server");
     91    cmd.add("-cp");
     92    cmd.add(classPath);
     93    cmd.add("net.sf.basedb.clients.jobagent.executors.ThreadJobExecutor");
     94    cmd.add(Integer.toString(job.getId()));
     95
     96    log.debug("Using class path: " + classPath);
     97    ProcessBuilder builder = new ProcessBuilder(cmd);
     98    builder.redirectErrorStream(true);
     99   
     100    Process process = null;
     101    DbControl dc = null;
     102    try
     103    {
     104      try
     105      {
     106        process = builder.start();
     107      }
     108      catch (Throwable t)
     109      {
     110        log.error("Error executing job: " + job, t);
     111        dc = sc.newDbControl();
     112        job = Job.getById(dc, job.getId());
     113        job.doneError(t.getMessage(), Arrays.asList(t));
     114        dc.commit();
     115        return;
     116      }
     117     
     118      // Set up threads for reading output
     119      StringWriter result = new StringWriter();
     120      Thread t = new Thread(new StreamRedirector(
     121        new InputStreamReader(process.getInputStream()), result));
     122      t.start();
     123
     124      try
     125      {
     126        log.info("Waiting for process to end");
     127        int exitCode = process.waitFor();
     128        if (exitCode != 0)
     129        {
     130          log.error("Process ended with exit code: " + exitCode);
     131          log.error("Process output is: " + result.toString());
     132          dc = sc.newDbControl();
     133          job = Job.getById(dc, job.getId());
     134          job.doneError("Job ended with exit code: " + exitCode,
     135            Arrays.asList(new Exception(result.toString())));
     136          dc.commit();
     137        }
     138        else
     139        {
     140          log.info("Process ended");
     141        }
     142      }
     143      catch (InterruptedException ex)
     144      {
     145        log.info("Job was interrupted: " + job, ex);
     146        // Kill the process
     147        process.destroy();
     148        dc = sc.newDbControl();
     149        job = Job.getById(dc, job.getId());
     150        job.doneError(ex.getMessage(), Arrays.asList(ex));
     151        dc.commit();
     152      }
     153    }
     154    finally
     155    {
     156      if (dc != null) dc.close();
     157    }
     158   
    73159  }
    74160
     
    76162  {}
    77163  // -------------------------------------------
     164 
     165 
     166  public static class StreamRedirector
     167    implements Runnable
     168  {
     169 
     170    private final Reader in;
     171    private final Writer out;
     172    public StreamRedirector(Reader in, Writer out)
     173    {
     174      this.in = in;
     175      this.out = out;
     176    }
     177   
     178    public void run()
     179    {
     180      BufferedReader reader = new BufferedReader(in);
     181      PrintWriter writer = new PrintWriter(out);
     182      String line = null;
     183      try
     184      {
     185        while ((line = reader.readLine()) != null)
     186        {
     187          writer.println(line);
     188        }
     189      }
     190      catch (IOException ex)
     191      {}
     192    }
     193  }
     194 
    78195}
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/ThreadJobExecutor.java

    r2641 r2643  
    2424package net.sf.basedb.clients.jobagent.executors;
    2525
     26import java.util.Arrays;
     27import java.util.List;
     28
    2629import net.sf.basedb.clients.jobagent.Agent;
    2730import net.sf.basedb.clients.jobagent.JobExecutor;
     31import net.sf.basedb.core.DbControl;
    2832import net.sf.basedb.core.Job;
     33import net.sf.basedb.core.PluginExecutionRequest;
     34import net.sf.basedb.core.PluginResponse;
    2935import net.sf.basedb.core.SessionControl;
     36import net.sf.basedb.core.plugin.Response;
    3037
    3138/**
     
    5057  */
    5158  private static final org.apache.log4j.Logger log =
    52     org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.jobagent.executors.ProcessJobExecutor");
     59    org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.jobagent.executors.ThreadJobExecutor");
    5360 
    5461 
     
    6673  {
    6774    log.info("Executing job: " + job);
    68     // TODO - execute job
     75    DbControl dc = null;
     76    try
     77    {
     78      dc = sc.newDbControl();
     79     
     80      // Load a fresh copy of the job item
     81      job = Job.getById(dc, job.getId());
     82      PluginExecutionRequest exec = null;
     83     
     84      try
     85      {
     86        exec = job.execute(null, agent.getServerName());
     87      }
     88      catch (Throwable t)
     89      {
     90        //  The job could not be started, set error status and send message to owner
     91        job.doneError(t.getMessage(), Arrays.asList(t));
     92        log.error("Error executing job: " + job, t);
     93      }
     94      dc.commit();
     95
     96      PluginResponse response = exec.invoke();
     97      if (response.getStatus() == Response.Status.ERROR)
     98      {
     99        // There was an error, job status and message is already sent to the user
     100        log.error("Error executing job: " + job + "; " + response.getMessage());
     101        List<? extends Throwable> errors = response.getErrorList();
     102        if (errors != null)
     103        {
     104          for (Throwable t : errors)
     105          {
     106            log.error(t.getMessage(), t);
     107          }
     108        }
     109      }
     110      else
     111      {
     112        log.info("Done executing: " + job);
     113      }
     114    }
     115    finally
     116    {
     117      if (dc != null) dc.close();
     118    }
    69119  }
    70120
     
    72122  {}
    73123  // -------------------------------------------
     124 
     125  public static void main(String[] args)
     126  {
     127    System.out.println("Running job with ID: " + args[0]);
     128    System.exit(11);
     129  }
     130 
    74131}
  • trunk/src/core/common-queries.xml

    r2636 r2643  
    21422142  </query>
    21432143 
     2144  <query id="SET_SENDMESSAGE_FOR_JOBS" type="HQL">
     2145    <sql>
     2146      UPDATE JobData job
     2147      SET job.sendMessage = true
     2148      WHERE job.sendMessage IS NULL
     2149    </sql>
     2150    <description>
     2151      A Hibernate query that sets the send message flag for all jobs with a null value.
     2152    </description>
     2153  </query>
     2154 
    21442155</predefined-queries>
  • trunk/src/core/net/sf/basedb/core/Application.java

    r2634 r2643  
    240240      {
    241241        InetAddress host =  InetAddress.getLocalHost();
    242         hostName = host == null ? "unknow" : host.getHostName();
     242        hostName = host == null ? "unknown" : host.getCanonicalHostName();
    243243      }
    244244      catch (Throwable ex)
     
    425425    timer.cancel();
    426426    timer = null;
     427    scheduler = null;
    427428    cleanSessionControlCache(true);
    428429    sessionCache = null;
  • trunk/src/core/net/sf/basedb/core/Install.java

    r2629 r2643  
    9292    method.
    9393  */
    94   public static final int NEW_SCHEMA_VERSION = 13;
     94  public static final int NEW_SCHEMA_VERSION = 15;
    9595
    9696  public static synchronized void createTables(boolean update, final ProgressReporter progress)
     
    625625    finally
    626626    {
    627       HibernateUtil.close(session);
     627      if (session != null) HibernateUtil.close(session);
    628628      session = null;
    629629      Application.stop();
  • trunk/src/core/net/sf/basedb/core/InternalJobQueue.java

    r2304 r2643  
    3030import net.sf.basedb.core.data.ItemKeyData;
    3131import net.sf.basedb.core.data.ProjectKeyData;
    32 import net.sf.basedb.core.data.MessageData;
    33 
     32
     33import java.util.Arrays;
    3434import java.util.List;
    3535import java.util.Set;
     
    3939import java.util.Collections;
    4040import java.util.TimerTask;
    41 import java.util.Date;
    42 import java.io.PrintWriter;
    43 import java.io.StringWriter;
    4441
    4542/**
     
    319316  }
    320317 
    321   private void sendErrorMessage(int jobId, Throwable ex)
     318  private void sendErrorMessage(SessionControl impersonated, int jobId, Throwable ex)
    322319  {
    323320    DbControl dc = null;
    324321    try
    325322    {
    326       dc = sc.newDbControl();
     323      dc = impersonated.newDbControl();
    327324      org.hibernate.Session session = dc.getHibernateSession();
    328325     
    329       JobData job = HibernateUtil.loadData(session, JobData.class, jobId);
    330       UserData owner = job.getOwner();
    331 
    332       // Update job status
    333       job.setStatus(Job.Status.ERROR.getValue());
    334       job.setEnded(new Date());
    335       job.setPercentComplete(100);
    336 
    337       // Create a message to send to the user
    338       MessageData message = new MessageData();
    339       message.setName(job.getName()+" completed with an error");
    340      
    341       StringWriter sw = new StringWriter();
    342       PrintWriter pw = new PrintWriter(sw);
    343       pw.println(ex.getMessage());
    344       ex.printStackTrace(pw);
    345       String description = sw.toString().replaceAll("net.sf.basedb.core.", "");
    346       message.setDescription(description);
    347       job.setStatusMessage(description);
    348      
    349       message.setTo(owner);
    350       message.setFrom("SYSTEM");
    351       message.setTimeSent(new Date());
    352       message.setJob(job);
    353       HibernateUtil.saveData(session, message);
    354 
     326      Job job = Job.getById(dc, jobId);
     327      job.doneError(ex.getMessage(), Arrays.asList(ex));
    355328      dc.commit();
    356329    }
     
    436409          exec = job.execute(null, Application.getHostName());
    437410        }
    438         catch (Throwable ex)
     411        catch (Throwable t)
    439412        {
    440413          // The job could not be started, set error status and send message to owner
    441           sendErrorMessage(jobId, ex);
    442           throw ex;
     414          sendErrorMessage(impersonated, jobId, t);
     415          throw t;
    443416        }
    444417        dc.close();
  • trunk/src/core/net/sf/basedb/core/Job.java

    r2601 r2643  
    3535import net.sf.basedb.core.plugin.Response;
    3636
     37import java.util.Collection;
    3738import java.util.Date;
    3839import java.util.Set;
     
    128129    jobData.setStatus(Status.UNCONFIGURED.getValue());
    129130    jobData.setCreated(new Date());
     131    jobData.setSendMessage(true);
    130132    return j;
    131133  }
     
    325327 
    326328  /**
     329    If a message should be sent to the owner once the job has finished
     330    or not.
     331  */
     332  public boolean getSendMessage()
     333  {
     334    return getData().getSendMessage();
     335  }
     336
     337  /**
     338    Set if a message should be sent to owner when the job finishes.
     339    @throws PermissionDeniedException If the logged in user doesn't have
     340      write permission
     341  */
     342  public void setSendMessage(boolean sendMessage)
     343    throws PermissionDeniedException
     344  {
     345    checkPermission(Permission.WRITE);
     346    getData().setSendMessage(sendMessage);
     347  }
     348
     349  /**
    327350    Get the estimated execution time of the job. This value can be
    328351    used by job queue managers to decide which job that should be
     
    355378  }
    356379 
     380  /**
     381    The maximum allowed length of the stack trace message.
     382  */
     383  public static final int MAX_STACK_TRACE_LENGTH = JobData.MAX_STACK_TRACE_LENGTH;
     384  /**
     385    Get the stack trace in case the job ended with an error
     386  */
     387  public String getStackTrace()
     388  {
     389    return getData().getStackTrace();
     390  }
     391
    357392  /**
    358393    An estimate of the percentage of the work currently completed by
     
    445480  }
    446481
     482  /**
     483    Set the job's status to {@link Status#PREPARED}. This status is
     484    used to signal that a job is about to be executed, but the actual exection
     485    hasn't started yet. This status prevents two job agents from trying to
     486    start the same job twice.
     487  */
     488  public void setPrepared(String server)
     489    throws PermissionDeniedException
     490  {
     491    checkPermission(Permission.WRITE);
     492    if (getStatus() != Status.WAITING)
     493    {
     494      throw new PermissionDeniedException("Can't prepare a job with status '"+getStatus()+"': "+toString());
     495    }
     496    JobData data = getData();
     497    data.setStarted(new Date());
     498    data.setStatus(Status.PREPARED.getValue());
     499    data.setServer(StringUtil.setNullableString(server, "server", MAX_SERVER_LENGTH));
     500  }
     501 
    447502  /**
    448503    Register the job as started. Note! This method doesn't start the actual
     
    512567    data.setStatus(Status.DONE.getValue());
    513568    data.setEnded(new Date());
     569    data.setStackTrace(null);
     570    if (getSendMessage()) sendMessage();
    514571  }
    515572
     
    530587    data.setPercentComplete(100);
    531588    data.setStatus(Status.ERROR.getValue());
     589    data.setStackTrace(null);
    532590    data.setEnded(new Date());
     591    if (getSendMessage()) sendMessage();
     592  }
     593 
     594  /**
     595    Register the job as completed with an error.
     596   
     597    @param statusMessage A message
     598    @param t The exception that caused the job to fail
     599    @throws PermissionDeniedException If the logged in user doesn't have
     600      write permission
     601    @throws InvalidDataException If the status message is too long
     602  */
     603  public void doneError(String statusMessage, Collection<? extends Throwable> errors)
     604  {
     605    checkPermission(Permission.WRITE);
     606    JobData data = getData();
     607    data.setStatusMessage(StringUtil.setNullableString(statusMessage, "statusMessage", MAX_STATUS_MESSAGE_LENGTH));
     608    data.setPercentComplete(100);
     609    data.setStatus(Status.ERROR.getValue());
     610    if (errors != null)
     611    {
     612      StringWriter sw = new StringWriter();
     613      PrintWriter pw = new PrintWriter(sw);
     614      for (Throwable t : errors)
     615      {
     616        t.printStackTrace(pw);
     617      }
     618      pw.close();
     619      data.setStackTrace(sw.toString());
     620    }
     621    data.setEnded(new Date());
     622    if (getSendMessage()) sendMessage();
    533623  }
    534624 
     
    546636    data.setEnded(null);
    547637    data.setStatusMessage(null);
     638    data.setStackTrace(null);
    548639    data.setServer(null);
    549640  }
     
    615706      throw new PermissionDeniedException("Not allowed for a job of type '"+getJobType()+"': "+toString());
    616707    }
    617     if (getStatus() != Status.WAITING)
     708    if (!getStatus().isExecutable())
    618709    {
    619710      throw new PermissionDeniedException("Cannot execute a job with status '"+getStatus()+"': "+toString());
     
    829920  }
    830921
     922 
     923  /**
     924    Send a message to the owner of the job when the job has finished
     925  */
     926  private void sendMessage()
     927  {
     928    MessageData message = new MessageData();
     929    JobData data = getData();
     930    message.setTo(data.getOwner());
     931    message.setFrom("SYSTEM");
     932    message.setTimeSent(new Date());
     933    message.setJob(data);
     934    message.setDescription(data.getStatusMessage());
     935   
     936    if (getStatus() == Job.Status.ERROR)
     937    {
     938      message.setName(net.sf.basedb.util.Values.trim("Error: " + getName(),
     939        Message.MAX_NAME_LENGTH));
     940    }
     941    else
     942    {
     943      message.setName(net.sf.basedb.util.Values.trim("Done: " + getName(),
     944        Message.MAX_NAME_LENGTH));
     945    }
     946    HibernateUtil.saveData(getDbControl().getHibernateSession(), message);
     947  }
    831948 
    832949  /**
     
    9191036        DbControl dc = sc.newDbControl();
    9201037        Job job = Job.getById(dc, jobId);
     1038        job.start("Starting...", server);
    9211039        JobData data = job.getData();
    922         data.setStatus(Status.EXECUTING.getValue());
    923         data.setStarted(new Date());
    924         data.setPercentComplete(0);
    925         data.setServer(server);
    9261040        dc.commit();
    9271041      }
     
    9361050        DbControl dc = sc.newDbControl();
    9371051        Job job = Job.getById(dc, jobId);
    938         JobData data = job.getData();
    939        
    940         MessageData message = new MessageData();
     1052
    9411053        if (response.getStatus() == Response.Status.ERROR)
    9421054        {
    943           data.setStatus(Status.ERROR.getValue());
    944           message.setName(job.getName()+" completed with an error");
    945          
    946           StringWriter sw = new StringWriter();
    947           PrintWriter pw = new PrintWriter(sw);
    948           pw.println(response.getMessage());
    949           if (response.getErrorList() != null)
    950           {
    951             for (Throwable t : response.getErrorList())
    952             {
    953               t.printStackTrace(pw);
    954             }
    955           }
    956           String description = sw.toString().replaceAll("net.sf.basedb.core.", "");
    957           data.setStatusMessage(description);
    958           message.setDescription(description);
     1055          job.doneError(response.getMessage(), response.getErrorList());
    9591056        }
    9601057        else
    9611058        {
    962           data.setStatus(Status.DONE.getValue());
    963           data.setStatusMessage(response.getMessage());
    964           message.setName(job.getName()+" completed successfully");
    965           message.setDescription(response.getMessage());
     1059          job.doneOk(response.getMessage());
    9661060        }
    967         data.setEnded(new Date());
    968         data.setPercentComplete(100);
    969 
    970         message.setTo(data.getOwner());
    971         message.setFrom("SYSTEM");
    972         message.setTimeSent(new Date());
    973         message.setJob(data);
    974         HibernateUtil.saveData(dc.getHibernateSession(), message);
    9751061        dc.commit();
    9761062      }
     
    10571143      The job hasn't been configured yet.
    10581144    */
    1059     UNCONFIGURED(0, "Not configured"),
     1145    UNCONFIGURED(0, "Not configured", false),
    10601146 
    10611147    /**
    10621148      The job is waiting to be started.
    10631149    */
    1064     WAITING(1, "Waiting"),
     1150    WAITING(1, "Waiting", true),
    10651151
    10661152    /**
    10671153      The job is executing.
    10681154    */
    1069     EXECUTING(2, "Executing"),
     1155    EXECUTING(2, "Executing", false),
    10701156   
    10711157    /**
    10721158      The job has finished successfully.
    10731159    */
    1074     DONE(3, "Done"),
     1160    DONE(3, "Done", false),
    10751161
    10761162    /**
    10771163      The job has finished with an error.
    10781164    */
    1079     ERROR(4, "Error");
     1165    ERROR(4, "Error", false),
     1166   
     1167    /**
     1168      The job is beeing prepared for execution.
     1169    */
     1170    PREPARED(5, "Preparing", true);
    10801171
    10811172    /**
     
    11081199    private final int value;
    11091200    private final String displayValue;
    1110     private Status(int value, String displayValue)
     1201    private final boolean executable;
     1202    private Status(int value, String displayValue, boolean executable)
    11111203    {
    11121204      this.value = value;
    11131205      this.displayValue = displayValue;
     1206      this.executable = executable;
    11141207    }
    11151208   
     
    11271220    {
    11281221      return value;
     1222    }
     1223   
     1224    /**
     1225      If the {@link Job#execute(ProgressReporter, String)} method can be
     1226      called when the job is in this status or not.
     1227    */
     1228    public boolean isExecutable()
     1229    {
     1230      return executable;
    11291231    }
    11301232  }
  • trunk/src/core/net/sf/basedb/core/JobAgent.java

    r2641 r2643  
    236236    checkPermission(Permission.WRITE);
    237237    getData().setPort(port);
     238  }
     239 
     240  public JobAgentInfo getInfo()
     241  {
     242    return getAgentInfo(this);
    238243  }
    239244 
  • trunk/src/core/net/sf/basedb/core/Update.java

    r2636 r2643  
    187187        The update sets the {@link net.sf.basedb.core.data.PluginDefinitionData#usePermissions}
    188188        property to true for all existing plugins.
     189      </ul>
     190    </td>
     191  </tr>
     192 
     193  <tr>
     194    <td>15</td>
     195    <td>
     196      <ul>
     197      <li>Added {@link net.sf.basedb.core.data.JobData#getSendMessage()} and
     198        {@link net.sf.basedb.core.data.JobData#getStackTrace()} properties.
     199        The update sets the send message to true for all existing jobs.
    189200      </ul>
    190201    </td>
     
    299310
    300311     
    301       if (schemaVersion < 14)
    302       {
    303         if (progress != null) progress.display((int)(13*progress_factor), "--Updating to schema version 14...");
    304         schemaVersion = setSchemaVersionInTransaction(session, 14);       
     312      if (schemaVersion < 15)
     313      {
     314        if (progress != null) progress.display((int)(14*progress_factor), "--Updating to schema version 15...");
     315        schemaVersion = setSchemaVersionInTransaction(session, 15);       
    305316      }
    306317     
    307318      /*
    308       if (schemaVersion < 15)
    309       {
    310         if (progress != null) progress.display((int)(14*progress_factor), "--Updating to schema version 15...");
    311         schemaVersion = setSchemaVersionInTransaction(session, 15);
     319      if (schemaVersion < 16)
     320      {
     321        if (progress != null) progress.display((int)(15*progress_factor), "--Updating to schema version 15...");
     322        schemaVersion = setSchemaVersionInTransaction(session, 16);
    312323        - or -
    313         schemaVersion = updateToSchemaVersion15();
     324        schemaVersion = updateToSchemaVersion16();
    314325      }
    315326      ... etc...
     
    743754        }
    744755       
     756        if (schemaVersion < 15)
     757        {
     758          // Set the JobData.sendMessage property to TRUE for all jobs with a null value.
     759          org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session, "SET_SENDMESSAGE_FOR_JOBS");
     760          /*
     761              UPDATE JobData job
     762              SET job.sendMessage = true
     763              WHERE job.sendMessage IS NULL
     764          */
     765          HibernateUtil.executeUpdate(query);
     766        }
     767     
    745768        //  Commit the changes
    746769        HibernateUtil.commit(tx);
  • trunk/src/core/net/sf/basedb/core/data/JobData.java

    r2304 r2643  
    128128  }
    129129
     130  private boolean sendMessage;
     131  /**
     132    If the core should send a message to the owner when the job is finished
     133    or not.
     134    @hibernate.property column="`send_message`" type="boolean" not-null="true"
     135  */
     136  public boolean getSendMessage()
     137  {
     138    return sendMessage;
     139  }
     140  public void setSendMessage(boolean sendMessage)
     141  {
     142    this.sendMessage = sendMessage;
     143  }
     144 
    130145  private int status;
    131146  /**
     
    158173  {
    159174    this.statusMessage = statusMessage;
     175  }
     176 
     177  /**
     178    The maximum allowed length of the stack trace message.
     179  */
     180  public static final int MAX_STACK_TRACE_LENGTH = 65535;
     181  private String stackTrace;
     182  /**
     183    Get the stack trace in case of an error
     184    @hibernate.property column="`stack_trace`" type="text" not-null="false"
     185  */
     186  public String getStackTrace()
     187  {
     188    return stackTrace;
     189  }
     190  public void setStackTrace(String stackTrace)
     191  {
     192    this.stackTrace = stackTrace;
    160193  }
    161194 
  • trunk/src/core/net/sf/basedb/util/Values.java

    r2634 r2643  
    743743    return message;
    744744  }
     745 
     746  /**
     747    Trim a string to a maximum length. If the input string is longer than
     748    maxLength the string is cut of at the end and ... is added to it.
     749  */
     750  public static String trim(String s, int maxLength)
     751  {
     752    if (s != null && s.length() > maxLength)
     753    {
     754      s = s.substring(0, maxLength - 3) + "...";
     755    }
     756    return s;
     757  }
     758 
    745759}
    746760
  • trunk/www/admin/jobagents/index.jsp

    r2631 r2643  
    5454<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
    5555<%!
    56   private static final ItemContext defaultContext = Base.createDefaultContext("name", "name,externalId,server,port,description");
     56  private static final ItemContext defaultContext = Base.createDefaultContext("name", "name,externalId,server,port,status,info");
    5757  private static final Item itemType = Item.JOBAGENT;
    5858%>
  • trunk/www/admin/jobagents/list_agents.jsp

    r2629 r2643  
    222222      />
    223223      <tbl:columndef
     224        id="status"
     225        title="Status"
     226      />
     227      <tbl:columndef
    224228        id="info"
    225229        title="CPU/Memory"
     
    242246        filterable="true"
    243247        exportable="true"
     248      />
     249      <tbl:columndef
     250        id="jobs"
     251        title="Jobs"
     252      />
     253      <tbl:columndef
     254        id="plugins"
     255        title="Plugins"
    244256      />
    245257      <tbl:columndef
     
    403415                <tbl:cell column="server"><%=HTML.encodeTags(item.getServer())%></tbl:cell>
    404416                <tbl:cell column="port"><%=item.getPort() == null ? "" : item.getPort().toString()%></tbl:cell>
     417                <tbl:cell column="status">
     418                  <%
     419                  Boolean paused = item.getInfo().isPaused();
     420                  %>
     421                  <%=paused == null ? "<i>- unknown -</i>" : paused == true ? "Paused" : "Running"%>
     422                </tbl:cell>
    405423                <tbl:cell column="info">
    406424                  CPU usage: <%=item.getCpuUsage() == null ? "<i>- unknown -</i>" : item.getCpuUsage()+"%" %><br>
     
    408426                  Used memory: <%=item.getUsedMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(item.getUsedMemory()) %>
    409427                </tbl:cell>
     428                <tbl:cell column="jobs">TODO</tbl:cell>
     429                <tbl:cell column="plugins">TODO</tbl:cell>
    410430                <tbl:cell column="owner"
    411431                  ><base:propertyvalue
  • trunk/www/common/plugin/index.jsp

    r2512 r2643  
    204204    sc.setSessionSetting("MESSAGES", contextMessages);
    205205    sc.setSessionSetting("PLUGINS", plugins);
    206     if (totalPlugins == 1 && contextMessages.size() == 0)
     206    if (totalPlugins == 1 && contextMessages.size() == 0 && false)
    207207    {
    208208      PluginDefinition thePlugin = plugins.keySet().iterator().next();
     
    381381    if (status == Response.Status.DONE)
    382382    {
    383       dc = sc.newDbControl();
    384383      Job job = (Job)sc.getSessionSetting("plugin.configure.job");
    385384      if (job != null)
    386385      {
    387         String jobName = Values.getStringOrNull(request.getParameter("job_name"));
    388         if (jobName != null) job.setName(jobName);
    389         dc.saveItem(job);
    390         sc.setSessionSetting("plugin.configure.job", null);
     386        sc.setSessionSetting("plugin.configure.response", pluginResponse);
     387        forward = "finish_job.jsp";
    391388      }
    392389      else
     
    394391        message = "Plugin configured";
    395392      }
    396       pluginResponse.saveParameters(dc);
    397       dc.commit();
    398       if (job != null)
    399       {
    400         redirect = "../../views/jobs/index.jsp?ID="+ID+"&cmd=ViewItem&item_id="+job.getId();
    401       }
    402       sc.setSessionSetting("plugin.configure.request", null);
    403       sc.setSessionSetting("plugin.configure.plugin", null);
    404       sc.setSessionSetting("plugin.configure.config", null);
    405       sc.setSessionSetting("plugin.configure.errors.message", null);
    406       sc.setSessionSetting("plugin.configure.errors.list", null);
    407393    }
    408394    else if (status == Response.Status.CONTINUE)
     
    421407    }
    422408  }
     409  else if ("FinishNewJob".equals(cmd))
     410  {
     411    dc = sc.newDbControl();
     412    Job job = (Job)sc.getSessionSetting("plugin.configure.job");
     413    PluginResponse pluginResponse = (PluginResponse)sc.getSessionSetting("plugin.configure.response");
     414
     415    job.setName(Values.getStringOrNull(request.getParameter("name")));
     416    job.setDescription(Values.getStringOrNull(request.getParameter("description")));
     417    job.setSendMessage(Values.getBoolean(request.getParameter("send_message")));
     418    dc.saveItem(job);
     419    pluginResponse.saveParameters(dc);
     420    dc.commit();
     421    redirect = "../../views/jobs/index.jsp?ID="+ID+"&cmd=ViewItem&item_id="+job.getId();
     422    sc.setSessionSetting("plugin.configure.job", null);
     423    sc.setSessionSetting("plugin.configure.request", null);
     424    sc.setSessionSetting("plugin.configure.response", null);
     425    sc.setSessionSetting("plugin.configure.plugin", null);
     426    sc.setSessionSetting("plugin.configure.config", null);
     427    sc.setSessionSetting("plugin.configure.errors.message", null);
     428    sc.setSessionSetting("plugin.configure.errors.list", null);
     429  }
    423430  else
    424431  {
  • trunk/www/common/plugin/select_plugin.jsp

    r2512 r2643  
    201201      }
    202202      %>
     203      <!--
    203204      <tr valign="top">
    204205        <td class="prompt">Job name</td>
     
    216217        </td>
    217218      </tr>
     219      -->
    218220      </table>
    219221      <div align=right>&nbsp;<i><base:icon image="required.gif" /> = required information</i></div>
  • trunk/www/my_base/messages/view_message.jsp

    r2425 r2643  
    225225      </table>
    226226    </t:tab>
     227    <%
     228    if (job.getStackTrace() != null)
     229    {
     230      %>
     231      <t:tab id="stacktrace" title="Stack trace" helpid="message.view.jobstacktrace">
     232        <div style="font-family: monospace">
     233        <%=HTML.niceFormat(job.getStackTrace())%>
     234        </div>
     235      </t:tab>
     236      <%
     237    }
     238    %>
    227239   
    228240    <t:tab id="Parameters" title="Parameters" helpid="message.view.jobparameters">
  • trunk/www/views/jobs/list_jobs.jsp

    r2559 r2643  
    234234        datatype="string"
    235235        title="Status message"
     236        sortable="true"
     237        filterable="true"
     238        exportable="true"
     239      />
     240      <tbl:columndef
     241        id="stackTrace"
     242        property="stackTrace"
     243        datatype="string"
     244        title="Stack trace"
    236245        sortable="true"
    237246        filterable="true"
     
    486495                <tbl:cell column="status"><%=item.getStatus()%></tbl:cell>
    487496                <tbl:cell column="statusMessage"><%=HTML.encodeTags(item.getStatusMessage())%></tbl:cell>
     497                <tbl:cell column="stackTrace"><%=HTML.encodeTags(item.getStackTrace())%></tbl:cell>
    488498                <tbl:cell column="server"><%=HTML.encodeTags(item.getServer())%></tbl:cell>
    489499                <tbl:cell column="estimatedExecutionTime"><%=item.getEstimatedExecutionTime()%></tbl:cell>
  • trunk/www/views/jobs/view_job.jsp

    r2425 r2643  
    213213      </table>
    214214      </t:tab>
     215     
     216      <%
     217      if (job.getStackTrace() != null)
     218      {
     219        %>
     220        <t:tab id="stacktrace" title="Stack trace" helpid="job.view.stacktrace">
     221          <div style="font-family: monospace">
     222          <%=HTML.niceFormat(job.getStackTrace())%>
     223          </div>
     224        </t:tab>
     225        <%
     226      }
     227      %>
    215228 
    216229      <t:tab id="parameters" title="Parameters" helpid="job.view.parameters">
     
    299312      {
    300313        %>
    301         <base:button onclick="restartJob()" title="Restart job" />
     314        <base:button onclick="restartJob()" title="Restart job" image="refresh.gif" />
    302315        <%
    303316      }
Note: See TracChangeset for help on using the changeset viewer.