Changeset 2648


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
Files:
9 added
1 deleted
37 edited

Legend:

Unmodified
Added
Removed
  • trunk/build.xml

    r2643 r2648  
    174174      >
    175175    </uptodate>
     176    <condition property="hibernate.message"
     177      value="up to date; skipping"
     178      >
     179      <istrue value="${nohibernate}" />
     180    </condition>
     181    <condition property="hibernate.message"
     182      value="not up to date; generating new"
     183      >
     184      <isfalse value="${nohibernate}" />
     185    </condition>
     186    <echo>Hibernate mapping files are ${hibernate.message}</echo>
    176187  </target>
    177188 
     
    695706    description="Copy required documentation"
    696707    >
     708    <copy todir="${dist}/doc/admin">
     709      <fileset dir="${doc}/admin" />
     710    </copy>
    697711    <copy todir="${dist}" file="${doc}/installation.html" />
    698712    <copy todir="${dist}" file="base2.license.txt" />
  • trunk/src/base.config.in

    r2306 r2648  
    9999#jobqueue.internal.medium.threadpriority = 3
    100100
     101# ==========================
     102# External job agent section
     103# ==========================
     104
     105# Number of seconds to cache information from a job agent before it
     106# is reloaded. The default value is 60 seconds.
     107agent.maxage=60
     108
     109# The timeout in milliseconds to use when connecting to job agents to
     110# get information. The default value is 1000 milliseconds.
     111agent.connection.timeout=1000
     112
     113
    101114# =========================
    102115# Secondary storage section
  • 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    }
  • trunk/src/core/common-queries.xml

    r2646 r2648  
    135135    </description>
    136136  </query>
     137 
     138  <query id="GET_CHILDGROUPS_IDS_FOR_GROUPS" type="HQL">
     139    <sql>
     140      SELECT DISTINCT gg.childId
     141      FROM GroupGroups gg
     142      WHERE gg.parentId IN (:groups)
     143      AND gg.childId NOT IN (:groups)
     144    </sql>
     145    <description>
     146      Load the ID of all groups that are children to at least one of
     147      the given groups, excluding the given groups.
     148    </description>
     149  </query>
    137150
    138151  <query id="GET_PROJECT_IDS_FOR_OWNER" type="HQL">
  • trunk/src/core/net/sf/basedb/core/Group.java

    r2572 r2648  
    2929import net.sf.basedb.core.query.Hql;
    3030
     31import java.util.HashSet;
    3132import java.util.Set;
    3233import java.util.Collections;
     
    113114  }
    114115
     116  /**
     117    Load all groups withing groups.
     118    @param dc The DbControl to use for database access
     119    @param groups The groups to start with
     120    @return A set containing the starting groups, the groups that are members of the
     121      start groups and the groups that are members following subgroups down as
     122      far as possible
     123  */
     124  public static Set<Integer> getGroupsRecursive(DbControl dc, Set<Integer> groups)
     125  {
     126    Set<Integer> allGroups = new HashSet<Integer>(groups);
     127    if (allGroups.size() > 0)
     128    {
     129     
     130      org.hibernate.Query query = HibernateUtil.getPredefinedQuery(dc.getHibernateSession(),
     131        "GET_CHILDGROUPS_IDS_FOR_GROUPS");
     132      /*
     133        SELECT DISTINCT gg.childId
     134        FROM GroupGroups gg
     135        WHERE gg.parentId IN (:groups)
     136        AND gg.childId NOT IN (:groups)
     137      */
     138      do
     139      {
     140        query.setParameterList("groups", allGroups, org.hibernate.Hibernate.INTEGER);
     141      } while (allGroups.addAll(HibernateUtil.loadList(Integer.class, query)));
     142    }
     143    return allGroups;
     144  }
     145 
    115146  /**
    116147    Get a query configured to retrieve groups.
  • trunk/src/core/net/sf/basedb/core/Job.java

    r2643 r2648  
    525525 
    526526  /**
    527     Set the progress of the job. The job must be in the {@link Status#EXECUTING} status.
     527    Set the progress of the job. The job must be in the {@link Status#EXECUTING} or
     528    {@link Status#PREPARED} status.
    528529    @param percentComplete The number of percent completed
    529530    @param statusMessage A message
     
    537538  {
    538539    checkPermission(Permission.WRITE);
    539     if (getStatus() != Status.EXECUTING)
     540    if (getStatus() != Status.EXECUTING && getStatus() != Status.PREPARED)
    540541    {
    541542      throw new PermissionDeniedException("Cannot set progress for a job with status '"+getStatus()+"': "+toString());
     
    688689    before calling {@link net.sf.basedb.core.PluginRequest#invoke()}.
    689690    See {@link net.sf.basedb.core.PluginRequest} for more  information.
    690 
     691 
    691692    @param progress The {@link ProgressReporter} where the plugin can report its progress. If
    692693      <code>null</code> {@link net.sf.basedb.core.Job.ProgressReporterImpl} is used
     
    701702    throws PermissionDeniedException, InvalidDataException, BaseException
    702703  {
     704    return execute(progress, server, null);
     705  }
     706 
     707 
     708  /**
     709    Start the execution sequence for a job. Call {@link DbControl#commit()}
     710    before calling {@link net.sf.basedb.core.PluginRequest#invoke()}.
     711    See {@link net.sf.basedb.core.PluginRequest} for more  information.
     712
     713    @param progress The {@link ProgressReporter} where the plugin can report its progress. If
     714      <code>null</code> {@link net.sf.basedb.core.Job.ProgressReporterImpl} is used
     715    @param server The name of the server executing the plugin
     716    @param settings The job agent settings to use or null to use default settings
     717    @return A <code>Plugin.Request</code> object
     718    @throws PermissionDeniedException If the logged in user doesn't have
     719      write permission or the job isn't in the {@link Status#WAITING} status
     720    @throws InvalidDataException If the server name is too long
     721    @throws BaseException If there is another error
     722  */
     723  public PluginExecutionRequest execute(ProgressReporter progress, String server, JobAgentSettings settings)
     724    throws PermissionDeniedException, InvalidDataException, BaseException
     725  {
    703726    checkPermission(Permission.WRITE);
    704727    if (getJobType() != Type.RUN_PLUGIN)
     
    717740    PluginDefinition pd = getPluginDefinition();
    718741    PluginConfiguration config = getPluginConfiguration();
    719     Plugin plugin = pd.newInstance();
     742    Plugin plugin = pd.newInstance(settings == null ? null : settings.getJarPath());
    720743    SessionControl sc = getSessionControl();
    721744    ParameterValuesImpl parameters = new ParameterValuesImpl(this, true);
  • trunk/src/core/net/sf/basedb/core/JobAgent.java

    r2643 r2648  
    5959  extends CommonItem<JobAgentData>
    6060{
     61 
     62  /**
     63    Log events.
     64  */
     65  private static final org.apache.log4j.Logger log =
     66    org.apache.log4j.LogManager.getLogger("net.sf.basedb.core.JobAgent");
     67 
    6168  /**
    6269    The type of item represented by this class.
     
    238245  }
    239246 
     247  /**
     248    Get info about the running job agent. For performance reasons the
     249    information is cached and only updated if older than
     250    specified by the <code>agent.maxage</code> configuration setting.
     251    @return A <code>JobAgentInfo</code> object
     252  */
    240253  public JobAgentInfo getInfo()
    241254  {
    242255    return getAgentInfo(this);
     256  }
     257 
     258  /**
     259    Get a connection object for connecting to the job agent. It is only
     260    possible to connect to job agents which has specified a server and port.
     261    @param timeout The timeout for the connection, null will use the default
     262      timeout as configued by the <code>agent.connection.timeout</code> setting
     263      in the <code>base.config</code> file
     264    @return The connection object or null if none could be created
     265    @throws PermissionDeniedException If the logged in user doesn't have
     266      WRITE permission on the job agent
     267  */
     268  public JobAgentConnection getConnection(Integer timeout)
     269    throws PermissionDeniedException
     270  {
     271    checkPermission(Permission.WRITE);
     272    String server = getServer();
     273    Integer port = getPort();
     274    if (timeout == null)
     275    {
     276      timeout = Config.getInt("agent.connection.timeout", 1000);
     277    }
     278    JobAgentConnection conn = null;
     279    if (port != null && server != null)
     280    {
     281      conn = new JobAgentConnection(server, port, timeout);
     282      agentInfo.remove(getId());
     283    }
     284    return conn;
    243285  }
    244286 
     
    246288    Get the current CPU usage in percent of the server where the job agent is running.
    247289    The CPU usage is not available unless a server name/IP and port has been specified.
    248     For performance reasons the CPU usage is cached and only updated if older than one minute.
    249     (TODO - could be in base.config)
     290    For performance reasons the CPU usage is cached and only updated if older than
     291    specified by the <code>agent.maxage</code> configuration setting.
    250292    @return The CPU usage of the job agent's server in percent, or null if not known
    251293  */
     
    258300    is running. The memory usage is not available unless a server name/IP and port
    259301    has been specified. For performance reasons the memory usage is cached and only
    260     updated if older than one minute. (TODO - could be in base.config)
     302    updated if older than specified by the <code>agent.maxage</code> configuration setting.
    261303    @return The memory usage on the job agent's server, or null if not known
    262304  */
     
    270312    is running. The memory is not available unless a server name/IP and port
    271313    has been specified. For performance reasons the memory is cached and only
    272     updated if older than one minute. (TODO - could be in base.config)
     314    updated if older than specified by the <code>agent.maxage</code> configuration
     315    setting.
    273316    @return The memory on the job agent's server, or null if not known
    274317  */
     
    299342      settings = JobAgentSettings.getNew(dc, this, plugin);
    300343    }
     344    return settings;
     345  }
     346 
     347  /**
     348    Get the settings for the plugin that is used for the specified job.
     349    @param job The job to get the settings for
     350    @return A <code>JobAgentSettings</code> object or null if not
     351      settings exist for the job
     352  */
     353  public JobAgentSettings getSettings(Job job)
     354  {
     355    DbControl dc = getDbControl();
     356    JobAgentSettings settings = dc.getItem(JobAgentSettings.class,
     357      getData().getPlugins().get(job.getData().getPluginDefinition()));
    301358    return settings;
    302359  }
     
    346403    );
    347404   
    348     // Create restrictions for owner of the job
     405    // Create restriction for the owner of the job
    349406    query.include(Include.MINE, Include.OTHERS);
    350407    Restriction userRestriction = Restrictions.in(
     
    352409      Expressions.parameter("users")
    353410    );
     411   
     412    // Load all users that this job agent is shared to
    354413    Set<Integer> userIds = new HashSet<Integer>();
    355414    userIds.add(getData().getOwner().getId());
     
    366425      {
    367426        userIds.addAll(itemKey.getUserIds(Permission.USE));
    368         // TODO - load groups within groups and then all members of those groups
    369       }
    370     }
    371    
     427        groupIds = Group.getGroupsRecursive(getDbControl(), groupIds);
     428        userIds.addAll(User.getAllMembers(getDbControl(), groupIds));
     429      }
     430    }
     431   
     432    // Load all projects this job agent is shared to
    372433    Restriction projectRestriction = null;
    373434    Set<Integer> projectIds = new HashSet<Integer>();
     
    440501  private static final Map<Integer, JobAgentInfo> agentInfo = new HashMap<Integer, JobAgentInfo>();
    441502 
     503  /**
     504    Get a <code>JobAgentInfo</code> for a job agent. If the job agent hasn't specified
     505    a server and port or if there is an error while connecting to the job agent
     506    an empty info object is returned.
     507   
     508    @param agent The job agent to get the info for
     509    @return A <code>JobAgentInfo</code> object
     510  */
    442511  private static JobAgentInfo getAgentInfo(JobAgent agent)
    443512  {
    444513    JobAgentInfo info = agentInfo.get(agent.getId());
    445     if (info == null || info.getAge() > 60000)
     514    if (info == null || info.getAge() > Config.getInt("agent.maxage", 60) * 1000)
    446515    {
    447516      String server = agent.getServer();
     
    451520        try
    452521        {
    453           JobAgentConnection conn = new JobAgentConnection(server, port, 1000);
     522          JobAgentConnection conn = new JobAgentConnection(server, port,
     523            Config.getInt("agent.connection.timeout", 1000));
    454524          info = conn.getInfo(true);
    455525        }
    456526        catch (Throwable t)
    457527        {
    458           // TODO
    459           t.printStackTrace();
     528          log.warn("Could not get info for job agent on server " + server + ":" + port, t);
     529          info = new JobAgentInfo();
    460530        }
    461531      }
    462       if (info == null)
     532      else
    463533      {
    464534        info = new JobAgentInfo();
  • trunk/src/core/net/sf/basedb/core/JobAgentSettings.java

    r2627 r2648  
    3131/**
    3232  This class contains a job agent's settings for a specific plugin.
    33   The settings may override the default settings for TODO
     33  The settings may override the default settings for the JAR path
     34  where to plugin is located, maximum memory to use, and if the plugin
     35  is trusted or not.
    3436
    3537  @author nicklas
     
    6365    settings.setPluginDefinition(plugin);
    6466    return settings;
     67  }
     68 
     69  /**
     70    Get a <code>JobAgentSettings</code> item when you know the ID.
     71 
     72    @param dc The <code>DbControl</code> which will be used for
     73      permission checking and database access
     74    @param id The ID of the item to load
     75    @return The <code>JobAgentSettings</code> item
     76    @throws ItemNotFoundException If an item with the specified ID is not found
     77    @throws PermissionDeniedException If the logged in user doesn't have
     78      read permission for the item
     79    @throws BaseException If there is another error
     80  */
     81  public static JobAgentSettings getById(DbControl dc, int id)
     82    throws ItemNotFoundException, PermissionDeniedException, BaseException
     83  {
     84    JobAgentSettings jas = dc.loadItem(JobAgentSettings.class, id);
     85    if (jas == null) throw new ItemNotFoundException("JobAgentSettings[id="+id+"]");
     86    return jas;
    6587  }
    6688 
  • trunk/src/core/net/sf/basedb/core/Message.java

    r2474 r2648  
    9999    @param user The user to count the unread messages for, or null to
    100100      count the messages for the logged in user
     101    @param cacheResult If the result should be cached or not
    101102    @return The number of unread messages
    102103    @throws BaseException If there is an error
    103104  */
    104   public static long countUnreadMessages(DbControl dc, User user)
     105  public static long countUnreadMessages(DbControl dc, User user, boolean cacheResult)
    105106    throws BaseException
    106107  {
     
    113114    */
    114115    query.setInteger("user", user == null ? dc.getSessionControl().getLoggedInUserId() : user.getId());
    115     query.setCacheable(true);
     116    query.setCacheable(cacheResult);
    116117    return HibernateUtil.loadData(Long.class, query);
    117118  }
  • trunk/src/core/net/sf/basedb/core/PluginDefinition.java

    r2646 r2648  
    5656public class PluginDefinition
    5757  extends SharedItem<PluginDefinitionData>
    58   implements Removable
     58  implements Nameable, Removable
    5959{
    6060  /**
     
    376376  }
    377377  /**
     378    The name cannot be changed.
     379    @throws PermissionDeniedException Always
     380  */
     381  public void setName(String name)
     382    throws PermissionDeniedException
     383  {
     384    throw new PermissionDeniedException("Not allowed to set the name of a plugin");
     385  }
     386
     387  /**
    378388    The name cannot be changed. Used internally only.
    379389
     
    383393    @throws InvalidDataException If the name is null or too long
    384394  */
    385   private void setName(String name)
     395  private void setNameInternal(String name)
    386396    throws InvalidDataException
    387397  {
     
    397407    return getData().getDescription();
    398408  }
     409  /**
     410    The description cannot be changed.
     411    @throws PermissionDeniedException Always
     412  */
     413  public void setDescription(String description)
     414    throws PermissionDeniedException
     415  {
     416    throw new PermissionDeniedException("Not allowed to set the description of a plugin");
     417  }
     418 
    399419  /**
    400420    The description cannot be changed. Used internally only.
     
    405425    @throws InvalidDataException If the description is too long
    406426  */
    407   protected void setDescription(String description)
     427  protected void setDescriptionInternal(String description)
    408428    throws InvalidDataException
    409429  {
     
    801821  }
    802822
    803   public <P extends Plugin> P newInstance(Class<P> clazz, SessionControl sc, PluginConfiguration configuration, Job job)
     823  Plugin newInstance(String jarPath)
    804824    throws PermissionDeniedException, BaseException
    805825  {
    806     Plugin p = newInstance();
     826    checkPermission(Permission.USE);
     827    return newInstance(jarPath == null ? getData().getJarPath() : jarPath, getData().getClassName(), false);
     828  }
     829 
     830  /**
     831    Get an instance of the plugin implementation.
     832    @param clazz The class of the plugin which must be a subclass of <code>Plugin</code>
     833    @param jarPath Overrides the default JAR path for a plugin, null to use the default
     834      JAR path
     835    @param sc The session control to initialise the plugin with
     836    @param configuration The configuration parameters to initialise the plugin with,
     837      or null if no configuration exists
     838    @param job The job parameters to intialise the plugin with, or null if no
     839      job exists
     840    @return An initialised plugin instance
     841    @throws PermissionDeniedException
     842    @throws BaseException
     843   */
     844  public <P extends Plugin> P newInstance(Class<P> clazz, String jarPath, SessionControl sc, PluginConfiguration configuration, Job job)
     845    throws PermissionDeniedException, BaseException
     846  {
     847    Plugin p = newInstance(jarPath);
    807848    if (!clazz.isInstance(p))
    808849    {
     
    921962    // Everything returned by the getAbout() method...
    922963    About about = plugin.getAbout();
    923     setName(about.getName());
    924     setDescription(about.getDescription());
     964    setNameInternal(about.getName());
     965    setDescriptionInternal(about.getDescription());
    925966    setVersionString(about.getVersion());
    926967    setCopyright(about.getCopyright());
  • trunk/src/core/net/sf/basedb/core/User.java

    r2382 r2648  
    3434
    3535import java.util.Date;
     36import java.util.HashSet;
    3637import java.util.Set;
    3738import java.util.Collections;
     
    110111  }
    111112
     113  /**
     114    Load the ID:s of all users that are direct members of
     115    the specified groups.
     116    @param groupIds The ID:s of the groups
     117    @return The ID:s of all members
     118    @see Group#getGroupsRecursive(DbControl, Set)
     119  */
     120  public static Set<Integer> getAllMembers(DbControl dc, Set<Integer> groupIds)
     121  {
     122    Set<Integer> userIds = new HashSet<Integer>();
     123    if (groupIds != null && groupIds.size() > 0)
     124    {
     125      org.hibernate.Session session = dc.getHibernateSession();
     126      // Get the users which are members of the same groups
     127      org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session, "GET_USER_IDS_FOR_GROUPS");
     128      /*
     129        SELECT ug.userId
     130        FROM UserGroups ug
     131        WHERE ug.groupId IN (:groups)
     132      */
     133      query.setParameterList("groups", groupIds, org.hibernate.Hibernate.INTEGER);
     134      userIds.addAll(HibernateUtil.loadList(Integer.class, query));
     135     
     136      // Get the users that have a quota group among the same groups
     137      query = HibernateUtil.getPredefinedQuery(session, "GET_USER_IDS_FOR_QUOTAGROUPS");
     138      /*
     139        SELECT usr.id
     140        FROM UserData usr
     141        WHERE usr.quotaGroup.id IN (:groups)
     142      */
     143      query.setParameterList("groups", groupIds, org.hibernate.Hibernate.INTEGER);
     144      userIds.addAll(HibernateUtil.loadList(Integer.class, query));
     145    }
     146    return userIds;
     147   
     148  }
     149 
    112150  /**
    113151    Get a query configured to retrieve users. If the logged in user
  • trunk/src/core/net/sf/basedb/util/AutoDetectFileFormat.java

    r2304 r2648  
    116116      {
    117117        AutoDetectingImporter importer = plugin.newInstance(AutoDetectingImporter.class,
    118           sc, config, null);
     118          null, sc, config, null);
    119119        if (currentItem == null || ((InteractivePlugin)importer).isInContext(context, currentItem) == null)
    120120        {
     
    139139      {
    140140        AutoDetectingImporter importer = plugin.newInstance(AutoDetectingImporter.class,
    141           sc, null, null);
     141          null, sc, null, null);
    142142        if (currentItem == null || ((InteractivePlugin)importer).isInContext(context, currentItem) == null)
    143143        {
  • trunk/src/core/net/sf/basedb/util/SocketUtil.java

    r2641 r2648  
    3737import java.nio.channels.ServerSocketChannel;
    3838
     39
    3940/**
    4041  This class contains some useful methods when working with sockets. Mainly
     
    5859    catch (Throwable t)
    5960    {
    60       // TODO - log maybe?
    6161      t.printStackTrace();
    6262    }
     
    231231    catch (UnknownHostException ex)
    232232    {
    233       // TODO - how to handle?
    234233      ex.printStackTrace();
    235234    }
  • trunk/src/log4j.properties.in

    r2634 r2648  
    5050# Job agent loggers
    5151# -----------------
    52 log4j.logger.net.sf.basedb.clients.jobagent=debug
     52log4j.logger.net.sf.basedb.clients.jobagent=warn
    5353
    5454# -----------------
  • trunk/src/test/TestAll.java

    r2576 r2648  
    127127    results.put("TestPluginConfiguration", TestPluginConfiguration.test_all());
    128128    results.put("TestJob", TestJob.test_all());
     129    results.put("TestJobAgent", TestJobAgent.test_all());
    129130    results.put("TestReporterFlatFileImporter", TestReporterFlatFileImporter.test_all());
    130131    results.put("TestReporterMapFlatFileImporter", TestReporterMapFlatFileImporter.test_all());
  • trunk/www/admin/jobagents/edit_agent.jsp

    r2631 r2648  
    7272    title = "Create job agent";
    7373    cc.removeObject("item");
    74     port = Values.getInteger(cc.getPropertyValue("port"), null);
     74    port = Values.getInteger(cc.getPropertyValue("port"), JobAgent.DEFAULT_PORT);
    7575  }
    7676  else
     
    434434        <td class="prompt">Port</td>
    435435        <td><input <%=clazz%> type="text" name="port"
    436           value="<%=port == null ?  JobAgent.DEFAULT_PORT : port %>"
     436          value="<%=port == null ?  "" : port.toString() %>"
    437437          size="12" maxlength="10"
    438           onkeypress="return Numbers.integerOnly(event)"></td>
     438          onkeypress="return Numbers.integerOnly(event)">
     439          <span class="link" onclick="document.forms['agent'].port.value = '<%=JobAgent.DEFAULT_PORT%>'"
     440            >Use default (<%=JobAgent.DEFAULT_PORT%>)</span>
     441          </td>
    439442      </tr>
    440443      <tr valign=top>
  • trunk/www/admin/jobagents/index.jsp

    r2643 r2648  
    4141  import="net.sf.basedb.util.RemovableUtil"
    4242  import="net.sf.basedb.util.ShareableUtil"
     43  import="net.sf.basedb.util.jobagent.JobAgentConnection"
    4344  import="net.sf.basedb.clients.web.Base"
    4445  import="net.sf.basedb.clients.web.WebException"
     
    293294    redirect = "../../common/plugin/index.jsp?ID="+ID+"&cmd=SelectPlugin&item_type="+itemType.name()+"&context_type=ITEM&main_type=OTHER&title=Run+plugin";
    294295  }
     296  else if ("Pause".equals(cmd) || "Start".equals(cmd))
     297  {
     298    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
     299    dc = sc.newDbControl();
     300    JobAgent agent = JobAgent.getById(dc, cc.getId());
     301    dc.close();
     302    JobAgentConnection conn = agent.getConnection(null);
     303    String answer = null;
     304    if (conn != null)
     305    {
     306      try
     307      {
     308        if ("Start".equals(cmd))
     309        {
     310          answer = conn.sendStart();
     311        }
     312        else if ("Pause".equals(cmd))
     313        {
     314          answer = conn.sendPause();
     315        }
     316      }
     317      catch (Throwable t)
     318      {
     319        answer = t.getMessage();
     320      }
     321      if (!"OK".equals(answer))
     322      {
     323        cc.setMessage("Could not " + cmd.toLowerCase() + " the job agent. The answer is:  " + answer);
     324      }
     325    }
     326    else
     327    {
     328      cc.setMessage("Could not create connection to job agent, probably because no " +
     329        "server and/or port has been specified.");
     330    }
     331    redirect = viewPage;
     332  }
     333  else if ("PauseSelected".equals(cmd) || "StartSelected".equals(cmd))
     334  {
     335    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, pageContext, defaultContext);
     336    dc = sc.newDbControl();
     337    boolean start = "StartSelected".equals(cmd);
     338    int total = cc.getSelected().size();
     339    int started = 0;
     340    int paused = 0;
     341   
     342    for (int agentId : cc.getSelected())
     343    {
     344      JobAgent agent = JobAgent.getById(dc, agentId);
     345      if (agent.hasPermission(Permission.WRITE))
     346      {
     347        try
     348        {
     349          JobAgentConnection conn = agent.getConnection(null);
     350          if (conn != null)
     351          {
     352            if (start)
     353            {
     354              String answer = conn.sendStart();
     355              started += "OK".equals(answer) ? 1 : 0;
     356            }
     357            else
     358            {
     359              String answer = conn.sendPause();
     360              paused += "OK".equals(answer) ? 1 : 0;
     361            }
     362          }
     363        }
     364        catch (Throwable t)
     365        {}
     366      }
     367    }
     368    if (total != started + paused)
     369    {
     370      message = "Only " + (start ? started : paused) + " job agents of " + total +
     371        " could be " + (start ? "started" : "paused");
     372    }
     373    redirect = listPage+(message != null ? "&popmessage="+HTML.urlEncode(message) : "");
     374  }
    295375  else
    296376  {
  • trunk/www/admin/jobagents/list_agents.jsp

    r2643 r2648  
    3737  import="net.sf.basedb.core.Permission"
    3838  import="net.sf.basedb.core.PluginDefinition"
     39  import="net.sf.basedb.core.Job"
     40  import="net.sf.basedb.core.Type"
     41  import="net.sf.basedb.core.query.Expressions"
     42  import="net.sf.basedb.core.query.Restrictions"
    3943  import="net.sf.basedb.core.query.Orders"
    4044  import="net.sf.basedb.core.query.Hql"
     
    4953  import="java.util.List"
    5054  import="java.util.Map"
     55  import="java.util.Set"
    5156%>
    5257<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
     
    6974try
    7075{
     76  final ItemQuery<PluginDefinition> pluginQuery = PluginDefinition.getQuery();
     77  pluginQuery.include(cc.getInclude());
     78  pluginQuery.join(Hql.innerJoin("jobAgentSettings", Item.JOBAGENTSETTINGS.getAlias()));
     79  pluginQuery.restrict(Restrictions.eq(Hql.property(Item.JOBAGENTSETTINGS.getAlias(), "jobAgent"), Expressions.parameter("agent")));
     80  pluginQuery.order(Orders.asc(Hql.property("name")));
     81 
    7182  Map<Plugin.MainType, Integer> pluginCount = PluginDefinition.countPlugins(dc, guiContext);
    7283  try
     
    151162      Table.presetOnChange('<%=ID%>', formId, '<%=itemType.name()%>', '<%=(String)cc.getObject("defaultColumns")%>');
    152163    }
     164    function controlJobAgents(cmd)
     165    {
     166      var frm = document.forms[formId];
     167      if (Forms.numChecked(frm) == 0)
     168      {
     169        alert('Please select at least one item in the list');
     170        return;
     171      }
     172      frm.action = submitPage;
     173      frm.cmd.value = cmd;
     174      frm.submit();
     175    }
    153176    </script>
    154177  </base:head>
     
    313336          tooltip="Run a plugin"
    314337          visible="<%=pluginCount.containsKey(Plugin.MainType.OTHER)%>"
     338        />
     339        <tbl:button
     340          image="pause.png"
     341          onclick="controlJobAgents('PauseSelected')"
     342          title="Pause"
     343          tooltip="Pause the selected job agents"
     344        />
     345        <tbl:button
     346          image="start.png"
     347          onclick="controlJobAgents('StartSelected')"
     348          title="Start"
     349          tooltip="Start the selected job agents"
    315350        />
    316351      </tbl:toolbar>
     
    426461                  Used memory: <%=item.getUsedMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(item.getUsedMemory()) %>
    427462                </tbl:cell>
    428                 <tbl:cell column="jobs">TODO</tbl:cell>
    429                 <tbl:cell column="plugins">TODO</tbl:cell>
     463                <tbl:cell column="jobs">
     464                  <%
     465                  Set<Integer> jobs = item.getInfo().getJobs();
     466                  if (jobs == null || jobs.size() == 0)
     467                  {
     468                    %>
     469                    <i>- none -</i>
     470                    <%
     471                  }
     472                  else
     473                  {
     474                    String separator = "";
     475                    for (int jobId : jobs)
     476                    {
     477                      try
     478                      {
     479                        Job job = Job.getById(dc, jobId);
     480                        out.write(separator);
     481                        if (mode.hasPropertyLink())
     482                        {
     483                          out.write(Base.getLinkedName(ID, job, false, mode.hasEditLink()) +
     484                            " (" + job.getPercentComplete() + "%)");
     485                        }
     486                        else
     487                        {
     488                          out.write(HTML.encodeTags(job.getName()) +
     489                            " (" + job.getPercentComplete() + "%)");
     490                        }
     491                        separator = ", ";
     492                      }
     493                      catch (Throwable t)
     494                      {
     495                        %>
     496                        <div class="error"><%=t.getMessage()%></div>
     497                        <%
     498                      }
     499                    }
     500                  }
     501                  %>
     502                </tbl:cell>
     503                <tbl:cell column="plugins">
     504                  <%
     505                  pluginQuery.setParameter("agent", itemId, Type.INT);
     506                  try
     507                  {
     508                    String separator = "";
     509                    for (PluginDefinition p : pluginQuery.list(dc))
     510                    {
     511                      out.write(separator);
     512                      if (mode.hasPropertyLink())
     513                      {
     514                        out.write(Base.getLink(ID, p.getName(), Item.PLUGINDEFINITION, p.getId(), mode.hasEditLink()));
     515                      }
     516                      else
     517                      {
     518                        out.write(HTML.encodeTags(p.getName()));
     519                      }
     520                      separator = ", ";
     521                    }
     522                  }
     523                  catch (Throwable t)
     524                  {
     525                    %>
     526                    <div class="error"><%=t.getMessage()%></div>
     527                    <%
     528                  }
     529                  %>
     530                </tbl:cell>
    430531                <tbl:cell column="owner"
    431532                  ><base:propertyvalue
  • trunk/www/admin/jobagents/view_agent.jsp

    r2631 r2648  
    3232  import="net.sf.basedb.core.ItemContext"
    3333  import="net.sf.basedb.core.Permission"
     34  import="net.sf.basedb.core.Job"
    3435  import="net.sf.basedb.core.JobAgent"
    3536  import="net.sf.basedb.core.JobAgentSettings"
     37  import="net.sf.basedb.util.jobagent.JobAgentInfo"
    3638  import="net.sf.basedb.core.User"
    3739  import="net.sf.basedb.core.PermissionDeniedException"
     
    4951  import="net.sf.basedb.clients.web.util.NameablePluginAdaptor"
    5052  import="java.util.Map"
     53  import="java.util.Set"
    5154%>
    5255<%@ taglib prefix="base" uri="/WEB-INF/base.tld" %>
     
    7275  JobAgent agent = JobAgent.getById(dc, itemId);
    7376 
     77  JobAgentInfo info = agent.getInfo();
     78  Boolean paused = info.isPaused();
     79 
    7480  final boolean writePermission = agent.hasPermission(Permission.WRITE);
    7581  final boolean deletePermission = agent.hasPermission(Permission.DELETE);
     
    7884
    7985  <base:page title="<%=title%>">
    80   <base:head scripts="tabcontrol.js,table.js" styles="toolbar.css,headertabcontrol.css,path.css,table.css">
     86  <base:head scripts="tabcontrol.js,table.js" styles="toolbar.css,headertabcontrol.css,path.css,table.css,progressbar.css">
    8187    <script language="JavaScript">
    8288    function editItem()
     
    99105    {
    100106      Main.viewOrEditItem('<%=ID%>', '<%=itemType.name()%>', <%=itemId%>, true, '&plugin_id='+pluginId);
     107    }
     108    function controlJobAgent(cmd)
     109    {
     110      location.href = 'index.jsp?ID=<%=ID%>&cmd='+cmd+'&item_id=<%=itemId%>';
    101111    }
    102112    </script>
     
    166176      />
    167177      <tbl:button
     178        image="start.png"
     179        onclick="controlJobAgent('Start')"
     180        title="Start"
     181        tooltip="Start the job agent"
     182        visible="<%=writePermission && paused != null && paused == true%>"
     183      />
     184      <tbl:button
     185        image="pause.png"
     186        onclick="controlJobAgent('Pause')"
     187        title="Pause"
     188        tooltip="Pause the job agent"
     189        visible="<%=writePermission && paused != null && paused == false%>"
     190      />
     191      <tbl:button
    168192        image="help.gif"
    169193        onclick="<%="Main.openHelp('" + ID +"', 'jobagent.view.properties')"%>"
     
    173197      </tbl:toolbar>
    174198    <div class="boxedbottom">
     199      <%
     200      if (cc.getMessage() != null)
     201      {
     202        %>
     203        <div class="error"><%=cc.getMessage()%></div>
     204        <%
     205        cc.setMessage(null);
     206      }
     207      %>
     208   
    175209      <div class="itemstatus">Permissions on this item: <i><%=PermissionUtil.getFullPermissionNames(agent)%></i></div>
    176210      <div class="itemstatus">
     
    201235      </tr>
    202236      <tr>
     237        <td class="prompt">Status</td>
     238        <td>
     239        <%=paused == null ? "<i>- unknown -</i>" : paused == true ? "Paused" : "Running"%>
     240        </td>
     241      </tr>
     242      <tr>
    203243        <td class="prompt">CPU usage</td>
    204         <td><%=agent.getCpuUsage() == null ? "<i>- unknown -</i>" : agent.getCpuUsage() + "%"%></td>
     244        <td><%=info.getCpuUsage() == null ? "<i>- unknown -</i>" : info.getCpuUsage() + "%"%></td>
    205245      </tr>
    206246      <tr>
    207247        <td class="prompt">Memory</td>
    208248        <td>
    209           Total: <%=agent.getTotalMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(agent.getTotalMemory())%>,
    210           Used: <%=agent.getUsedMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(agent.getUsedMemory())%>
     249          Total: <%=info.getTotalMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(info.getTotalMemory())%>,
     250          Used: <%=info.getUsedMemory() == null ? "<i>- unknown -</i>" : Values.formatBytes(info.getUsedMemory())%>
    211251        </td>
    212252      </tr>
     
    315355      }
    316356      %>
     357
     358      <%
     359      Set<Integer> jobs = agent.getInfo().getJobs();
     360      if (jobs == null || jobs.size() == 0)
     361      {
     362        %>
     363        <h4>Executing jobs</h4>
     364        <%
     365        if (jobs == null)
     366        {
     367          %>
     368          Unknown
     369          <%
     370        }
     371        else
     372        {
     373          %>
     374          No jobs are currently executing on this job agent
     375          <%
     376        }
     377      }
     378      else
     379      {
     380        %>
     381        <h4 class="docked">Executing jobs</h4>
     382        <tbl:table
     383          id="jobs"
     384          clazz="itemlist"
     385          columns="all"
     386          >
     387          <tbl:columndef
     388            id="job"
     389            title="Job"
     390          />
     391          <tbl:columndef
     392            id="plugin"
     393            title="Plugin"
     394          />
     395          <tbl:columndef
     396            id="started"
     397            title="Started"
     398          />
     399          <tbl:columndef
     400            id="percentComplete"
     401            title="Percent complete"
     402          />
     403          <tbl:columndef
     404            id="owner"
     405            title="Owner"
     406          />
     407          <tbl:data>
     408            <tbl:columns>
     409            </tbl:columns>
     410            <tbl:rows>
     411            <%
     412            for (int jobId : jobs)
     413            {
     414              Job job = null;
     415              boolean readJob = true;
     416              try
     417              {
     418                job = Job.getById(dc, jobId);
     419              }
     420              catch (Throwable t)
     421              {
     422                readJob = false;
     423              }
     424              %>
     425              <tbl:row>
     426                <tbl:cell column="job"><%=Base.getLinkedName(ID, job, !readJob, true) %></tbl:cell>
     427                <tbl:cell column="plugin"><base:propertyvalue item="<%=job%>" property="pluginDefinition"/></tbl:cell>
     428                <tbl:cell column="started"><%=job == null ? "" : Values.formatDateTime(job.getStarted())%></tbl:cell>
     429                <tbl:cell column="percentComplete">
     430                  <%
     431                  if (job != null)
     432                  {
     433                    %>
     434                    <table border=0 cellspacing=0 cellpadding=0>
     435                    <tr>
     436                    <td width="100">
     437                      <table width="100" class="progressbar" border=0 cellspacing=0 cellpadding=0>
     438                      <tr>
     439                        <%
     440                        int percent = job.getPercentComplete();
     441                        if (percent > 0)
     442                        {
     443                          %>
     444                          <td width="<%=percent%>%" class="done">&nbsp;</td>
     445                          <%
     446                        }
     447                        if (percent < 100)
     448                        {
     449                          %>
     450                          <td width="<%=100-percent%>%" class="remain">&nbsp;</td>
     451                          <%
     452                        }
     453                        %>
     454                      </tr>
     455                      </table>               
     456                    </td>
     457                    <td>&nbsp;<%=percent%>%</td>
     458                    </tr>
     459                    </table>
     460                    <%
     461                  }
     462                  %>
     463                </tbl:cell>
     464                <tbl:cell column="owner"><base:propertyvalue item="<%=job%>" property="owner" /></tbl:cell>
     465              </tbl:row>
     466              <%
     467            }
     468            %>
     469            </tbl:rows>
     470          </tbl:data>
     471        </tbl:table>
     472        <%
     473      }
     474      %>     
     475     
    317476    </div>
    318477    </t:tab>
  • trunk/www/common/import/index.jsp

    r2512 r2648  
    107107      for (PluginConfiguration pluginConfig : configQuery.list(dc))
    108108      {
    109         InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, sc, pluginConfig, null);
     109        InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, null, sc, pluginConfig, null);
    110110        String contextMessage = null;
    111111        try
     
    134134      else if (!plugin.requiresConfiguration())
    135135      {
    136         InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, sc, null, null);
     136        InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, null, sc, null, null);
    137137        String contextMessage = null;
    138138        try
  • trunk/www/common/import/select_plugin.jsp

    r2512 r2648  
    213213      }
    214214      %>
    215       <tr valign="top">
    216         <td class="prompt">Job name</td>
    217         <td><input type="text" class="text"
    218           name="job_name" value="<%=HTML.encodeTags(jobName)%>" size="40" maxlength="<%=Job.MAX_NAME_LENGTH%>">
    219         </td>
    220       </tr>
    221       <tr valign=top>
    222         <td class="prompt">Job description</td>
    223         <td nowrap>
    224           <textarea class="text" rows="4" cols="40" name="job_description" wrap="virtual"
    225             ></textarea>
    226           <a href="javascript:Main.zoom('Job description', 'plugin', 'job_description')"
    227             title="Edit in larger window"><base:icon image="zoom.gif" /></a>
    228         </td>
    229       </tr>
    230215      </table>
    231216      <div align=right>&nbsp;<i><base:icon image="required.gif" /> = required information</i></div>
  • trunk/www/common/plugin/finish_job.jsp

    r2643 r2648  
    6363  PluginDefinition plugin = (PluginDefinition)sc.getSessionSetting("plugin.configure.plugin");
    6464  PluginConfiguration pluginConfig = (PluginConfiguration)sc.getSessionSetting("plugin.configure.config");
     65  boolean sendMessage = Values.getBoolean(sc.getUserClientSetting("plugins.sendmessage"), true);
    6566  %>
    6667  <base:page type="popup" title="Set job name and options">
     
    118119        <td class="prompt">Send message</td>
    119120        <td>
    120           <input type="checkbox" name="send_message" value="1" checked>
     121          <input type="checkbox" name="send_message" value="1" <%=sendMessage ? "checked" : "" %>>
    121122          <a href="javascript:document.forms['plugin'].send_message.click();">Send a message when the job is completed</a>
    122123        </td>
  • trunk/www/common/plugin/index.jsp

    r2643 r2648  
    141141      for (PluginConfiguration pluginConfig : configQuery.list(dc))
    142142      {
    143         InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, sc, pluginConfig, null);
     143        InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, null, sc, pluginConfig, null);
    144144        String contextMessage = null;
    145145        try
     
    167167      else if (!plugin.requiresConfiguration())
    168168      {
    169         InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, sc, null, null);
     169        InteractivePlugin ip = plugin.newInstance(InteractivePlugin.class, null, sc, null, null);
    170170        String contextMessage = null;
    171171        try
  • trunk/www/footnote.jsp

    r2474 r2648  
    5959try
    6060{
    61   if (sc != null && sc.isLoggedIn()) numNewMessages = Message.countUnreadMessages(dc, null);
     61  if (sc != null && sc.isLoggedIn()) numNewMessages = Message.countUnreadMessages(dc, null, false);
    6262  %>
    6363  <base:page type="popup" title="">
  • trunk/www/my_base/user/preferences.jsp

    r2596 r2648  
    8484      return true;
    8585    }
     86    function validatePlugins()
     87    {
     88      return true;
     89    }
    8690    // Changes the scale value
    8791    function setScale(newScale)
     
    110114  <t:tabcontrol active="<%=activePage%>" id="settings" contentstyle="<%="height: "+(int)(scale*160)+"px;"%>"
    111115    position="bottom">
    112   <t:tab id="appearance" title="Appearance" validate="validateAppearance()" helpid="userpreferences.appearance">
     116  <t:tab
     117    id="appearance"
     118    title="Appearance"
     119    validate="validateAppearance()"
     120    helpid="userpreferences.appearance"
     121    >
    113122    <table class="form" cellspacing=0>
    114123    <tr>
     
    118127        String fontsize = Values.getString(sc.getUserClientSetting("appearance.fontsize"), "size_m.css");
    119128        %>
    120         <input type="radio" name="fontsize" value="size_xs.css" <%="size_xs.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(80)">XS
    121         <input type="radio" name="fontsize" value="size_s.css" <%="size_s.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(90)">S
    122         <input type="radio" name="fontsize" value="size_m.css" <%="size_m.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(100)">M
    123         <input type="radio" name="fontsize" value="size_l.css" <%="size_l.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(120)">L
    124         <input type="radio" name="fontsize" value="size_xl.css" <%="size_xl.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(140)">XL
     129        <input type="radio" name="fontsize" value="size_xs.css"
     130          <%="size_xs.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(80)"
     131          ><a href="javascript:document.forms['preferences'].fontsize[0].click()">XS</a>
     132        <input type="radio" name="fontsize" value="size_s.css"
     133          <%="size_s.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(90)"
     134          ><a href="javascript:document.forms['preferences'].fontsize[1].click()">S</a>
     135        <input type="radio" name="fontsize" value="size_m.css"
     136          <%="size_m.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(100)"
     137          ><a href="javascript:document.forms['preferences'].fontsize[2].click()">M</a>
     138        <input type="radio" name="fontsize" value="size_l.css"
     139          <%="size_l.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(120)"
     140          ><a href="javascript:document.forms['preferences'].fontsize[3].click()">L</a>
     141        <input type="radio" name="fontsize" value="size_xl.css"
     142          <%="size_xl.css".equals(fontsize) ? "checked" : ""%> onclick="setScale(140)"
     143          ><a href="javascript:document.forms['preferences'].fontsize[4].click()">XL</a>
    125144      </td>
    126145    </tr>
     
    137156      <td class="prompt">Toolbar</td>
    138157      <td>
    139         <input type="radio" name="toolbar" value="both" <%=hasImages && hasText ? "checked" : "" %>>Images and text
    140         <input type="radio" name="toolbar" value="images" <%=hasImages && !hasText ? "checked" : "" %>>Images only
    141         <input type="radio" name="toolbar" value="text" <%=!hasImages && hasText ? "checked" : "" %>>Text only
     158        <input type="radio" name="toolbar" value="both"
     159          <%=hasImages && hasText ? "checked" : "" %>
     160          ><a href="javascript:document.forms['preferences'].toolbar[0].click()">Images and text</a>
     161        <input type="radio" name="toolbar" value="images"
     162          <%=hasImages && !hasText ? "checked" : "" %>
     163          ><a href="javascript:document.forms['preferences'].toolbar[1].click()">Images only</a>
     164        <input type="radio" name="toolbar" value="text"
     165          <%=!hasImages && hasText ? "checked" : "" %>
     166          ><a href="javascript:document.forms['preferences'].toolbar[2].click()">Text only</a>
    142167      </td>
    143168    </tr>
     
    154179    <div align="right">&nbsp;<i><base:icon image="required.gif" /> = required information</i></div>
    155180  </t:tab>
     181 
     182  <t:tab
     183    id="plugins"
     184    title="Plugins"
     185    validate="validatePlugins()"
     186    helpid="userpreferences.plugins"
     187    >
     188    <table class="form" cellspacing=0>
     189    <tr>
     190      <td class="prompt">Messages</td>
     191      <td>
     192        <%
     193        boolean sendMessage = Values.getBoolean(sc.getUserClientSetting("plugins.sendmessage"), true);
     194        %>
     195        <input type=checkbox name="sendmessage" value="1" <%=sendMessage ? "checked" : ""%>
     196          ><a href="javascript:document.forms['preferences'].sendmessage.click()">Send message when plugin completes</a>
     197      </td>
     198    </tr>
     199    </table>
     200  </t:tab>
     201 
    156202  </t:tabcontrol>
    157203
  • trunk/www/my_base/user/submit_user.jsp

    r2304 r2648  
    104104    sc.setUserClientSetting("toolbar.text", hasText ? "1" : "0");
    105105   
     106    sc.setUserClientSetting("plugins.sendmessage", Values.getString(request.getParameter("sendmessage"), "0"));
     107   
    106108    message = "Preferences saved";
    107109  }
  • trunk/www/views/jobs/view_job.jsp

    r2643 r2648  
    8686    readCurrentOwner = false;
    8787  }
    88   final boolean autoUpdate = job.getStatus() == Job.Status.WAITING || job.getStatus() == Job.Status.EXECUTING;
     88  final Job.Status status = job.getStatus();
     89  final boolean autoUpdate = status == Job.Status.WAITING ||
     90    status == Job.Status.PREPARED || status == Job.Status.EXECUTING;
    8991  %>
    9092
     
    296298        </table>
    297299      </t:tab>
    298      
    299300      </t:tabcontrol>
    300      
     301 
    301302    <base:buttongroup align="center">
    302303      <%
    303       if (job.getStatus() == Job.Status.WAITING || job.getStatus() == Job.Status.EXECUTING)
     304      if (autoUpdate)
    304305      {
    305306        %>
     
    309310      %>
    310311      <%
    311       if (job.getStatus() == Job.Status.ERROR && sc.hasPermission(Permission.CREATE, Item.PLUGINDEFINITION))
     312      if (job.getStatus() == Job.Status.ERROR)
    312313      {
    313314        %>
     
    321322    <center>
    322323    <%
    323     if (job.getStatus() == Job.Status.WAITING || job.getStatus() == Job.Status.EXECUTING)
     324    if (autoUpdate)
    324325    {
    325326      %>
Note: See TracChangeset for help on using the changeset viewer.