Ignore:
Timestamp:
Sep 19, 2006, 1:30:33 PM (16 years ago)
Author:
Nicklas Nordborg
Message:

References #351: External job server usage

Most functionality is now in place. Only needs a bit more testing.

Location:
trunk/src/clients/jobagent
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/clients/jobagent/jobagent.properties.in

    r2643 r2648  
    6666## The class must implement the net.sf.basedb.clients.JobExecutor interface
    6767
    68 ## Executor that executes the jobs in a separate process (recommended)
     68# -------------------------------------------------------------------
     69# Executor that executes the jobs in a separate process (recommended)
     70# Options:
     71#  java = Path to the java executable
     72#  options = Extra command line options to pass to the java executable
     73# -------------------------------------------------------------------
    6974agent.executor.class=net.sf.basedb.clients.jobagent.executors.ProcessJobExecutor
     75agent.executor.process.java=java
     76agent.executor.process.options=-server
    7077
    71 ## Executor that executes jobs in the same process as the job agent (not recommended)
     78# -------------------------------------------------------------------
     79# Executor that executes jobs in the same process as the job agent (not recommended)
     80# -------------------------------------------------------------------
    7281# agent.executor.class=net.sf.basedb.clients.jobagent.executors.ThreadJobExecutor
    7382
    74 ## Executor useful for debugging purposes
     83# -------------------------------------------------------------------
     84# Executor useful for debugging purposes. It just marks the job as
     85# executed without really executing it.
     86# Options:
     87#  wait = Number of seconds it takes to execute a job
     88# -------------------------------------------------------------------
    7589# agent.executor.class=net.sf.basedb.clients.jobagent.executors.DummyJobExecutor
     90# agent.executor.dummy.wait=120
    7691
    77 ## Initialisation parameters to the job exector. This string will be passed
    78 ## unmodified to the JobExecutor.init() method
    79 agent.executor.init=
    8092
    8193## Number of seconds between checks to the job queue
  • trunk/src/clients/jobagent/jobagent.sh

    r2643 r2648  
    3030
    3131# Execute JobAgent
    32 java -server -Xmx50M -Xms50M -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

    r2643 r2648  
    4343import net.sf.basedb.core.Job;
    4444import net.sf.basedb.core.JobAgent;
     45import net.sf.basedb.core.JobAgentSettings;
    4546import net.sf.basedb.core.Project;
    4647import net.sf.basedb.core.SessionControl;
     
    230231    org.apache.log4j.LogManager.getLogger("net.sf.basedb.clients.jobagent.JobAgentServerConnection");
    231232 
     233  private final Properties properties;
     234 
    232235  // Base settings
    233236  private final String login;
     
    247250  private final long checkInterval;
    248251  private final Class<? extends JobExecutor> executorClass;
    249   private final String executorInitParameters;
    250252 
    251253  private InetAddress serverAddress;
     
    291293  public Agent(Properties properties)
    292294  {
     295    this.properties = properties;
     296   
    293297    if (log.isDebugEnabled())
    294298    {
     
    323327    this.checkInterval = Values.getInt(properties.getProperty("agent.checkinterval"), DEFAULT_CHECK_INTERVAL);
    324328    this.executorClass = getJobExecutorClass(properties.getProperty("agent.executor.class"));
    325     this.executorInitParameters = properties.getProperty("agent.executor.init");
    326329   
    327330    // Slots and priorities
     
    350353 
    351354  /**
     355    Get a configuration property.
     356    @param key The key
     357    @return The configured value or null if not found
     358  */
     359  public String getProperty(String key)
     360  {
     361    return properties.getProperty(key);
     362  }
     363 
     364  /**
    352365    Get the <code>agent.id</code> configuration value.
    353366    @return The ID
     
    359372 
    360373  /**
     374    Get the <code>agent.user</code> configuration value.
     375    @return The login
     376  */
     377  public String getLogin()
     378  {
     379    return login;
     380  }
     381 
     382  /**
     383    Get the <code>agent.password</code> configuration value.
     384    @return The password
     385  */
     386  public String getPassword()
     387  {
     388    return password;
     389  }
     390 
     391  /**
    361392    Get the <code>agent.name</code> configuration value.
    362393    @return The name or the ID if the name is null
     
    383414  {
    384415    return port;
     416  }
     417 
     418  /**
     419    Get the thread priority to use for the specified execution time slot.
     420    @param slot
     421    @return
     422  */
     423  public int getThreadPriority(Job.ExecutionTime slot)
     424  {
     425    return priorities.get(slot);
    385426  }
    386427 
     
    448489      }
    449490    }
    450     catch (ClassNotFoundException ex)
     491    catch (Throwable t)
    451492    {
    452493      log.warn("Class " + className + " not found, using " + DEFAULT_JOB_EXECUTOR.getName() +
    453         " instead", ex);
     494        " instead", t);
    454495    }
    455496    return executor;
     
    618659  public Set<Integer> getRunningJobs()
    619660  {
     661    if (log.isDebugEnabled()) log.debug("Active jobs: " + activeJobs);
    620662    return Collections.unmodifiableSet(activeJobs);
    621663  }
     
    764806    @return The assigned slot or null if no slot is available
    765807  */
    766   synchronized Job.ExecutionTime getSlot(Job.ExecutionTime requested)
    767   {
    768     log.debug("Requesting slot for job: " + requested);
     808  synchronized Job.ExecutionTime getSlot(Job job)
     809  {
     810    log.debug("Requesting slot for job: " + job);
     811    Job.ExecutionTime requested = job.getEstimatedExecutionTime();
    769812    Job.ExecutionTime slotToUse = null;
    770813    Job.ExecutionTime[] slots =  Job.ExecutionTime.values();
     
    778821        slotToUse = slots[i];
    779822        usedSlots.put(slotToUse, usedSlots.get(slotToUse) + 1);
     823        activeJobs.add(job.getId());
    780824        log.debug("Slot: " + slotToUse + "; used: " + usedSlots.get(slotToUse) + "; max: " + maxSlots.get(slotToUse));
    781825        break;
     
    795839    @param job The job to start
    796840  */
    797   public void startJob(Job job)
    798   {
    799     JobRunner runner = new JobRunner(this, job, jobExecutor);
     841  public void startJob(Job job, JobAgentSettings settings)
     842  {
     843    JobRunner runner = new JobRunner(this, job, settings, jobExecutor);
    800844    Thread t = new Thread(runnersGroup, runner);
    801845    t.setDaemon(false);
     
    868912    {
    869913      executor = executorClass.newInstance();
    870       executor.init(executorInitParameters);
     914      executor.init(this);
    871915    }
    872916    catch (Throwable t)
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/CmdLine.java

    r2641 r2648  
    6363    {
    6464      this.cmd = args[lastIndex];
    65       lastIndex--;
    6665    }
    6766    else
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/JobExecutor.java

    r2643 r2648  
    2525
    2626import net.sf.basedb.core.Job;
     27import net.sf.basedb.core.JobAgentSettings;
    2728import net.sf.basedb.core.SessionControl;
    2829
     
    5051  /**
    5152    Initialize the job executor. This method is called once immediately
    52     after the object has been created.
    53     @param parameters The parameters as specified by the
    54       <code>agent.executor.init</code> configuration property
     53    after the object has been created.
     54    @param agent The agent
    5555  */
    56   public void init(String parameters);
     56  public void init(Agent agent);
    5757 
    5858  /**
     
    7272    @param agent The agent that sent the request to execute the job
    7373    @param job The job to execute
     74    @param settings The specific settings used for the plugin on this job agent
     75      or null if no settings has been specified
    7476    @param usedSlot The slot that was used to execut the job
    7577  */
    76   public void executeJob(SessionControl sc, Agent agent, Job job, Job.ExecutionTime usedSlot);
     78  public void executeJob(SessionControl sc, Agent agent, Job job, JobAgentSettings settings,
     79    Job.ExecutionTime usedSlot);
    7780 
    7881  /**
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/JobQueueChecker.java

    r2641 r2648  
    3131import net.sf.basedb.core.Job;
    3232import net.sf.basedb.core.JobAgent;
     33import net.sf.basedb.core.JobAgentSettings;
    3334import net.sf.basedb.core.SessionControl;
    3435
     
    5758  private final Agent agent;
    5859 
     60  /**
     61    Cached query for waiting jobs. Reloaded only when the
     62    job agent's version has changed. We cache the query since loading
     63    all users that are allowed to use the job agent is an expensive operation.
     64  */
     65  private ItemQuery<Job> jobQuery = null;
     66  /**
     67    The job agent's version when the query was cached.
     68  */
     69  private int agentVersionForJobQuery = -1;
     70 
    5971  public JobQueueChecker(Agent agent)
    6072  {
     
    6981  {
    7082    log.info("Checking for jobs to execute");
    71     Job job = findJob();
    72     if (job != null)
    73     {
    74       agent.startJob(job);
    75     }
     83    checkForJobs();
    7684  }
    7785  public boolean cancel()
     
    8290  // -------------------------------------------
    8391 
    84   private Job findJob()
     92  private void checkForJobs()
    8593  {
    8694    DbControl dc = null;
    87     Job job = null;
    8895    try
    8996    {
     
    9299      JobAgent jobAgent = agent.getJobAgent(dc);
    93100     
    94       ItemQuery<Job> jobQuery = jobAgent.getWaitingJobs();
    95       jobQuery.setMaxResults(1);
     101      // Do we need to create a new query for jobs or not?
     102      // We create a new query only if none exists or if the cached
     103      // version is different from the current version since that may
     104      // be because permissions, etc have changed.
     105      if (jobQuery == null || jobAgent.getVersion() != agentVersionForJobQuery)
     106      {
     107        jobQuery = jobAgent.getWaitingJobs();
     108        jobQuery.setMaxResults(1);
     109        agentVersionForJobQuery = jobAgent.getVersion();
     110      }
    96111     
    97112      List<Job> jobs = jobQuery.list(dc);
     
    102117      else
    103118      {
    104         job = jobs.get(0);
     119        Job job = jobs.get(0);
     120        JobAgentSettings settings = jobAgent.getSettings(job);
     121        agent.startJob(job, settings);
    105122      }
    106123    }
     
    113130      if (dc != null) dc.close();
    114131    }
    115     return job;
    116132  }
    117133 
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/JobRunner.java

    r2643 r2648  
    2929import net.sf.basedb.core.ItemModifiedException;
    3030import net.sf.basedb.core.Job;
     31import net.sf.basedb.core.JobAgentSettings;
    3132import net.sf.basedb.core.SessionControl;
    3233
     
    5354
    5455  private final Job job;
     56  private final JobAgentSettings settings;
    5557  private final JobExecutor jobExecutor;
    5658  private final Agent agent;
     
    6062    @param agent The agent that created this object
    6163    @param job The job to be executed
     64    @param settings The settigns for the plugin that is executing the job
    6265    @param jobExecutor The job executor that should execute the job
    6366  */
    64   JobRunner(Agent agent, Job job, JobExecutor jobExecutor)
     67  JobRunner(Agent agent, Job job, JobAgentSettings settings, JobExecutor jobExecutor)
    6568  {
    6669    this.agent = agent;
    6770    this.job = job;
     71    this.settings = settings;
    6872    this.jobExecutor = jobExecutor;
    6973  }
     
    7983  public void run()
    8084  {
    81     Job.ExecutionTime slotToUse = agent.getSlot(job.getEstimatedExecutionTime());
     85    Job.ExecutionTime slotToUse = agent.getSlot(job);
    8286    if (slotToUse == null)
    8387    {
     
    116120      try
    117121      {
    118         jobExecutor.executeJob(sc, agent, j, slotToUse);
     122        jobExecutor.executeJob(sc, agent, j, settings, slotToUse);
    119123      }
    120124      catch (Throwable t)
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/DummyJobExecutor.java

    r2643 r2648  
    2828import net.sf.basedb.core.DbControl;
    2929import net.sf.basedb.core.Job;
     30import net.sf.basedb.core.JobAgentSettings;
    3031import net.sf.basedb.core.SessionControl;
     32import net.sf.basedb.util.Values;
    3133
    3234/**
     
    3436  of each job to {@link net.sf.basedb.core.Job.Status#DONE} without actually
    3537  executing the job. This class is usful for debugging the job agent application.
     38  <p>
     39  <table>
     40  <tr>
     41    <th>Configuration parameter</th>
     42    <th>Default value</th>
     43    <th>Description</th>
     44  </tr>
     45 
     46  <tr>
     47    <td>agent.executor.dummy.wait</td>
     48    <td></td>
     49    <td>
     50      Number of seconds it takes to execute a job. The implementation simly
     51      waits the specified number of seconds. If not specified the execution will
     52      return immediately.
     53    </td>
     54  </tr>
     55  </table>
    3656
    3757  @author nicklas
     
    5070 
    5171 
     72  private int wait;
     73 
    5274  public DummyJobExecutor()
    5375  {}
     
    5779    -------------------------------------------
    5880  */
    59   public void init(String parameters)
    60   {}
     81  public void init(Agent agent)
     82  {
     83    wait = Values.getInt(agent.getProperty("agent.executor.wait"), -1);
     84  }
    6185 
    62   public void executeJob(SessionControl sc, Agent agent, Job job, Job.ExecutionTime usedSlot)
     86  public void executeJob(SessionControl sc, Agent agent, Job job, JobAgentSettings settings,
     87    Job.ExecutionTime usedSlot)
    6388  {
    6489    log.info("Executing job: " + job);
     
    6994      job = Job.getById(dc, job.getId());
    7095      job.start("Not really, but used for testing the job agent", agent.getServerName());
     96      if (wait > 0)
     97      {
     98        job.setProgress(50, "Halfway; waiting " + wait + " seconds");
     99        dc.commit();
     100        try
     101        {
     102          synchronized (Thread.currentThread())
     103          {
     104            Thread.currentThread().wait(wait * 1000);
     105          }
     106        }
     107        catch (Throwable t)
     108        {
     109          log.error(t.getMessage(), t);
     110        }
     111        dc  = sc.newDbControl();
     112        dc.reattachItem(job);
     113      }
    71114      job.doneOk("Not really, but used for testing job agent");
    72115      dc.commit();
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/ProcessJobExecutor.java

    r2643 r2648  
    2626import java.io.BufferedReader;
    2727import java.io.IOException;
    28 import java.io.InputStream;
    2928import java.io.InputStreamReader;
    30 import java.io.OutputStream;
    3129import java.io.PrintWriter;
    3230import java.io.Reader;
     
    4139import net.sf.basedb.core.DbControl;
    4240import net.sf.basedb.core.Job;
     41import net.sf.basedb.core.JobAgentSettings;
     42import net.sf.basedb.core.PluginDefinition;
    4343import net.sf.basedb.core.SessionControl;
     44import net.sf.basedb.util.Values;
    4445
    4546/**
     
    5556  The disadvantage is the overhead in time and memory of starting a new
    5657  process.
    57 
     58  <p>
     59  <table>
     60  <tr>
     61    <th>Configuration parameter</th>
     62    <th>Default value</th>
     63    <th>Description</th>
     64  </tr>
     65 
     66  <tr>
     67    <td>agent.executor.process.java</td>
     68    <td></td>
     69    <td>
     70      Path to the java executable to use. not specified the envoronment variable
     71      <code>JAVA_HOME</code> is used if it exists. Otherwise we let the operating
     72      system find the <code>java</code> command.
     73    </td>
     74  </tr>
     75 
     76  <tr>
     77    <td>agent.executor.process.options</td>
     78    <td>-server</td>
     79    <td>
     80      Extra command line options to pass to the java executable:
     81      <code>java &lt;options&gt; -cp ... className ...</code>.
     82    </td>
     83  </tr>
     84  </table>
     85 
    5886  @author nicklas
    5987  @version 2.0
     
    7199 
    72100 
     101  private String javaBin;
     102  private String options;
     103 
    73104  public ProcessJobExecutor()
    74105  {}
     
    78109    -------------------------------------------
    79110  */
    80   public void init(String parameters)
    81   {}
    82  
    83   public void executeJob(SessionControl sc, Agent agent, Job job, Job.ExecutionTime usedSlot)
     111  public void init(Agent agent)
     112  {
     113    javaBin = Values.getStringOrNull(agent.getProperty("agent.executor.process.java"));
     114    if (javaBin == null)
     115    {
     116      javaBin = System.getenv("JAVA_HOME");
     117      char sep = java.io.File.separatorChar;
     118      if (javaBin != null) javaBin += sep + "jre" + sep + "bin" + sep + "java";
     119    }
     120    if (javaBin == null) javaBin = "java";
     121    options = Values.getString(agent.getProperty("agent.executor.process.options"), "-server");
     122  }
     123 
     124  public void executeJob(SessionControl sc, Agent agent, Job job, JobAgentSettings settings,
     125    Job.ExecutionTime usedSlot)
    84126  {
    85127    log.info("Executing job: " + job);
     128   
     129    // Generate command for new process
     130    List<String> cmd = new ArrayList<String>(10);
     131   
     132    // Java executable
     133    cmd.add(javaBin);
     134   
     135    // Other java options
     136    cmd.add(options);
     137
     138    // Max allowed memory
     139    Long maxMemory = settings == null ? null : settings.getEffectiveMaxMemory();
     140    if (maxMemory != null)
     141    {
     142      maxMemory = maxMemory / 1024 / 1024;
     143      if (maxMemory >= 1) cmd.add("-Xmx" + maxMemory + "M");
     144    }
     145   
     146    // Class path
    86147    String classPath = System.getProperty("java.class.path");
    87    
    88     List<String> cmd = new ArrayList<String>(10);
    89     cmd.add("java");
    90     cmd.add("-server");
    91148    cmd.add("-cp");
    92149    cmd.add(classPath);
     150   
     151    // ThreadJobExecutor will execute the job
    93152    cmd.add("net.sf.basedb.clients.jobagent.executors.ThreadJobExecutor");
     153    cmd.add("-j"); // Job id
    94154    cmd.add(Integer.toString(job.getId()));
    95 
    96     log.debug("Using class path: " + classPath);
    97     ProcessBuilder builder = new ProcessBuilder(cmd);
    98     builder.redirectErrorStream(true);
     155    cmd.add("-s"); // JobAgentSettings id
     156    cmd.add(Integer.toString(settings.getId()));
     157    cmd.add("-u"); // User login
     158    cmd.add(agent.getLogin());
     159    cmd.add("-p"); // Password
     160    cmd.add(agent.getPassword());
     161    cmd.add("-t"); // Thread priority
     162    cmd.add(Integer.toString(agent.getThreadPriority(job.getEstimatedExecutionTime())));
    99163   
    100164    Process process = null;
     
    102166    try
    103167    {
     168      // Update progress on job
     169      dc = sc.newDbControl();
     170      job = Job.getById(dc, job.getId());
     171      job.setProgress(0, "Starting new process");
     172      dc.commit();
     173     
     174      if (log.isDebugEnabled())
     175      {
     176        log.debug("Executing cmd: " + Values.getString(cmd, " ", true));
     177      }
     178      ProcessBuilder builder = new ProcessBuilder(cmd);
     179      builder.redirectErrorStream(true);
     180     
    104181      try
    105182      {
     
    163240  // -------------------------------------------
    164241 
    165  
     242  /**
     243    Used for redirecting standard output to a string.
     244  */
    166245  public static class StreamRedirector
    167246    implements Runnable
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/executors/ThreadJobExecutor.java

    r2643 r2648  
    2828
    2929import net.sf.basedb.clients.jobagent.Agent;
     30import net.sf.basedb.clients.jobagent.CmdLine;
    3031import net.sf.basedb.clients.jobagent.JobExecutor;
     32import net.sf.basedb.core.Application;
    3133import net.sf.basedb.core.DbControl;
    3234import net.sf.basedb.core.Job;
     35import net.sf.basedb.core.JobAgentSettings;
    3336import net.sf.basedb.core.PluginExecutionRequest;
    3437import net.sf.basedb.core.PluginResponse;
     38import net.sf.basedb.core.Project;
    3539import net.sf.basedb.core.SessionControl;
    3640import net.sf.basedb.core.plugin.Response;
     41import net.sf.basedb.util.SocketUtil;
     42import net.sf.basedb.util.Values;
    3743
    3844/**
     
    6773    -------------------------------------------
    6874  */
    69   public void init(String parameters)
     75  public void init(Agent agent)
    7076  {}
    7177 
    72   public void executeJob(SessionControl sc, Agent agent, Job job, Job.ExecutionTime usedSlot)
     78  public void executeJob(SessionControl sc, Agent agent, Job job, JobAgentSettings settings,
     79    Job.ExecutionTime usedSlot)
    7380  {
    7481    log.info("Executing job: " + job);
     
    8491      try
    8592      {
    86         exec = job.execute(null, agent.getServerName());
     93        exec = job.execute(null, Application.getHostName(), settings);
    8794      }
    8895      catch (Throwable t)
     
    125132  public static void main(String[] args)
    126133  {
    127     System.out.println("Running job with ID: " + args[0]);
    128     System.exit(11);
     134    int exitCode = 0;
     135    CmdLine cmdLine = new CmdLine(args);
     136    int jobId = Values.getInt(cmdLine.getOption("-j"));
     137    int settingsId = Values.getInt(cmdLine.getOption("-s"));
     138    String login = cmdLine.getOption("-u");
     139    String password = cmdLine.getOption("-p");
     140    int threadPriority = Values.getInt(cmdLine.getOption("-t"), Thread.NORM_PRIORITY);
     141
     142    String loginComment = "Job agent running on host " +
     143      Application.getHostName() + " executing job with ID = " + jobId;
     144   
     145    log.info("Running job with ID: " + jobId);
     146    DbControl dc = null;
     147    SessionControl sc = null;
     148    SessionControl impersonated = null;
     149    try
     150    {
     151      ThreadJobExecutor executor = new ThreadJobExecutor();
     152     
     153      sc = Application.newSessionControl("net.sf.basedb.clients.jobagent",
     154        SocketUtil.getLocalHost().toString(), null);
     155      sc.login(login, password, loginComment, false);
     156      dc = sc.newDbControl();
     157      Job job = Job.getById(dc, jobId);
     158      JobAgentSettings settings = JobAgentSettings.getById(dc, settingsId);
     159      dc.close();
     160     
     161      impersonated = sc.impersonateLogin(job, "Running job: " + job.getName());
     162      if (job.getActiveProjectId() != 0)
     163      {
     164        dc = impersonated.newDbControl();
     165        Project activeProject = Project.getById(dc, job.getActiveProjectId());
     166        impersonated.setActiveProject(activeProject);
     167        dc.close();
     168      }
     169     
     170      Thread.currentThread().setPriority(threadPriority);
     171      executor.executeJob(impersonated, null, job, settings, null);
     172    }
     173    catch (Throwable t)
     174    {
     175      log.error("Error executing job with ID: " + jobId);
     176      exitCode = 1;
     177    }
     178    finally
     179    {
     180      if (dc != null) dc.close();
     181      if (impersonated != null && impersonated.isLoggedIn()) impersonated.logout();
     182      if (sc != null && sc.isLoggedIn()) sc.logout();
     183    }
     184    System.exit(exitCode);
    129185  }
    130186 
  • trunk/src/clients/jobagent/net/sf/basedb/clients/jobagent/handlers/InfoRequestHandler.java

    r2641 r2648  
    5959    {
    6060      Runtime runtime = Runtime.getRuntime();
    61       int cpu = (int)(Math.random()*100);
    62       long totalMemory = runtime.maxMemory();
    63       long usedMemory = runtime.totalMemory() - runtime.freeMemory();
    64       JobAgentInfo info = new JobAgentInfo(!agent.isRunning(), cpu, totalMemory, usedMemory, agent.getRunningJobs());
     61      // TODO - Get this info when it can be done by the JVM or use external calls
     62      // Useful link: http://builder.com.com/5100-6370_14-6067049.html
     63      Integer cpu = null; // (int)(Math.random()*100);
     64      Long totalMemory = null; // runtime.maxMemory();
     65      Long usedMemory = null; //runtime.totalMemory() - runtime.freeMemory();
     66      JobAgentInfo info = new JobAgentInfo(!agent.isRunning(), cpu, usedMemory, totalMemory, agent.getRunningJobs());
    6567      answer = "OK\n"+info.toString();
    6668    }
Note: See TracChangeset for help on using the changeset viewer.