<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<chapter id="scheduled-jobs">
  <title>TorqueBox Scheduled Jobs</title>

  <section id="what-are-scheduled-jobs">
    <title>What Are Scheduled Jobs?</title>

    <para>Scheduled jobs are simply components that execute on a
    possibly-recurring schedule instead of in response to user interaction.
    Scheduled jobs fire asynchronously, outside of the normal web-browser
    thread-of-control. Scheduled jobs have full access to the entire Ruby
    environment. This allows them to interact with database models and other
    application functionality.</para>
  </section>

  <section id="ruby-job-classes">
    <title>Ruby Job Classes</title>

    <para>Each scheduled job maps to exactly one Ruby class. The path and
    filename should match the class name of the job contained in the
    file.</para>

    <informaltable>
      <tgroup cols="2">
        <thead>
          <row>
            <entry align="center">File name</entry>

            <entry align="center">Class name</entry>
          </row>
        </thead>

        <tbody>
          <row>
            <entry><filename>mail_notifier.rb</filename></entry>

            <entry><type>MailNotifier</type></entry>
          </row>

          <row>
            <entry><filename>mail/notifier.rb</filename></entry>

            <entry><type>Mail::Notifier</type></entry>
          </row>
        </tbody>
      </tgroup>
    </informaltable>

    <para><example>
        <title>Skeleton scheduled job class
        (<filename>mail/notifier.rb</filename>)</title>

        <para><programlisting>module Mail
  class Notifier

    # implementation goes here

  end
end</programlisting></para>
      </example>Each job class should implement a no-argument
    <methodname>run()</methodname> method to perform the work when
    fired.</para>

    <para><example>
        <title>Scheduled job implementation
        (<filename>mail/notifier.rb</filename>)</title>

        <para><programlisting>module Mail
  class Notifier

    # optional, only needed if you pass config options to the job
    def initialize(options = {})
      @options = options
    end

    def run()

      # perform work here

    end

  end
end</programlisting></para>
      </example></para>

    <para>From within the class's <methodname>run()</methodname> method, the
    full application environment is available.</para>
  </section>

  <section id="scheduling-jobs">
    <title>Scheduling Jobs</title>

    <para>The job schedule defines the time(s) that a job should execute. This
    may be defined to be single point in time, or more often, as recurring
    event. The job schedule is defined in your deployment descriptor.</para>

    <section id="jobs-format">
      <title>Configuration Format</title>

      <para>Within the internal <filename>torquebox.yml</filename>
      descriptor (or through an external
      <filename><replaceable>*</replaceable>-knob.yml</filename>
      descriptor), scheduled jobs are configured using the
      <parameter>jobs:</parameter> section, in which a block of
      information is provided for each job. The block starts with an
      arbitrary name for the job. Each block must also define the job
      class and the schedule specification. Optionally a
      <parameter>description</parameter> and a
      <parameter>config</parameter> may be provided. If you do provide
      a <parameter>config</parameter>, its value will be passed to the
      initialize method of the job class.</para>

      <para>If you are using the DSL (via <filename>torquebox.rb</filename>) in
      your internal descriptor, each job is defined using the <methodname>job</methodname>
      directive, with very similar options to the YAML syntax described above. 
      The DSL does not require a name for each job, unless you intend to share
      a job class across multiple jobs.</para>

      <para><example>
          <title>Example deployment descriptor</title>

          <para>Using the YAML syntax:<programlisting>application:
  ..
jobs:
  mail.notifier:
    job:         Mail::Notifier
    cron:        '0 */5 * * * ?'
    description: Deliver queued mail notifications
    config: 
      throttle: true</programlisting></para>

         <para>And via the DSL:<programlisting>TorqueBox.configure do
  ...
  job Mail::Notifier do
    name 'mail.notifier' # optional, unless the job class is used by multiple jobs
    cron '0 */5 * * * ?'
    description 'Deliver queued mail notifications' # optional
    config do
      throttle true
    end
  end
end</programlisting></para>
        </example></para>

      <para>The cron attribute should contain a typical crontab-like entry. It
      is composed of 7 fields (6 are required).</para>

      <para><informaltable>
          <tgroup cols="7">
            <thead>
              <row>
                <entry align="center">Seconds</entry>

                <entry align="center">Minutes</entry>

                <entry align="center">Hours</entry>

                <entry align="center">Day of Month</entry>

                <entry align="center">Month</entry>

                <entry align="center">Day of Week</entry>

                <entry align="center">Year</entry>
              </row>
            </thead>

            <tbody>
              <row>
                <entry>0-59</entry>

                <entry>0-59</entry>

                <entry>0-23</entry>

                <entry>1-31</entry>

                <entry>1-12 or JAN-DEC</entry>

                <entry>1-7 or SUN-SAT</entry>

                <entry>1970-2099 (optional)</entry>
              </row>
            </tbody>
          </tgroup>
        </informaltable></para>

      <para>For several fields, you may denote subdivision by using the
      forward-slash (<code>/</code>) character. To execute a job every 5
      minutes, <code>*/5</code> in the minutes field would specify this
      condition.</para>

      <para>Spans may be indicated using the dash (<code>-</code>) character.
      To execute a job Monday through Friday, <code>MON-FRI</code> should be
      used in the day-of-week field.</para>

      <para>Multiple values may be separated using the comma (<code>,</code>)
      character. The specification of <code>1,15</code> in the day-of-month
      field would result in the job firing on the 1st and 15th of each
      month.</para>

      <para>Either day-of-month or day-of-week must be specified using the
      <code>?</code> character, since specifying both is contradictory.</para>
    </section>
  </section>

  <section id="clustered-jobs">
    <title>Clustered Jobs</title>

    <section id="ha-singleton-jobs">
      <title>High Availability Singleton Jobs</title>

      <para>TorqueBox supports highly-available singleton jobs. By
      default, a job only runs on one node in the cluster and if that
      node goes down or the job fails to run to completion, it is
      automatically scheduled on a new node.</para>

      <para>To use high availability singleton jobs, you must
      start TorqueBox with a clustered configuration. For example:</para>

      <para><screen> <prompt>$</prompt> <command>$JBOSS_HOME/bin/standalone.sh --server-config=standalone-ha.xml</command></screen></para>

      <para>
        Alternatively, use the <command>torquebox</command> command:
        <screen> <prompt>$</prompt> <command>torquebox run --clustered</command></screen>
      </para>

      <para>HA jobs are configured using the <code>singleton</code>
      key in the job specification in your deployment descriptor. Its
      default value is <code>true</code> so you must manually
      configure it with a value of <code>false</code> for the job to
      run on every node in the cluster.</para>

      <para><example>
          <title>Example deployment descriptor</title>

          <para>Using the YAML syntax:<programlisting>application:
  ..
jobs:
  mail.notifier:
    job:         Mail::Notifier
    cron:        '0 */5 * * * ?'
    description: Deliver queued mail notifications
    singleton: true
    config: 
      throttle: true</programlisting></para>

         <para>And via the DSL:<programlisting>TorqueBox.configure do
  ...
  job Mail::Notifier do
    name 'mail.notifier' # optional, unless the job class is used by multiple jobs
    cron '0 */5 * * * ?'
    description 'Deliver queued mail notifications' # optional
    singleton true
    config do
      throttle true
    end
  end
end</programlisting>This is the same
        deployment descriptor from the example above.
        Including the <code>singleton</code> attribute with a value
        of <code>true</code> is redundant of course, since jobs will
        only run on a single node when clustered, by default.</para>
      </example></para>
    </section>

    <section id="jobs-running-on-every-node">
      <title>Jobs Running on Every Node</title>

      <para>To configure a job to run on every node in a cluster, set
      <code>singleton</code> to <code>false</code> in the deployment
      descriptor.</para>
    </section>

  </section>

  <section id="resouce-injection-with-jobs">
    <title>Resource Injection with Jobs</title>

    <para>If a job requires access to other resources, such as messaging
    topics and queues, or Java CDI components these should be injected using
    the resource injection facilities provided by TorqueBox (see <xref
    linkend="resource-injection" />).</para>

    <para>In order for resource injection to function with scheduled jobs,
    they must reside either at the root of your application directory (typical
    for simple Rack applications), or underneath an
    <filename>app/jobs/</filename> directory. If you place the job anywhere
    else, it may still function, but resources injection will not be
    available.</para>
  </section>
</chapter>
