r4 - 08 May 2008 - 08:14:01 - RandyLetnessYou are here: OSAF >  Journal Web  >  ContributorNotes > RandyLetnessNotes > NotificationArchNotes > SchedulerPrototypeNotes

The scheduler branch contains a scheduler component (org.osaf.cosmo.scheduler.*) that allows code to be run one some interval on the server, without any user interaction. The main use case for this is to provide notification reports to users that contain upcoming information gathered from the user's collections.

SchedulerImpl

SchedulerImpl.jpg

The meat of the scheduler is in the SchedulerImpl class. The SchedulerImpl relies on 3 main components: a UserScheduleManager, a map of JobTypeScheduler implementations, and the Quartz scheduler engine. All components are configured in spring, making adding to or extending the framework easy.

Quartz Scheduler Engine

The quartz engine provides the basic scheduling building blocks for a scheduler. It allows a job (code) to be scheduled and executed at configurable time intervals, modeled by Trigger s, and manages a pool of threads that are responsible for executing jobs.

UserScheduleManager

The UserScheduleManager interface provides an api for managing user's schedules. A user scheduler is modeled by the Schedule class. A Schedule contains a name and set of properties. The properties for a schedule are key/value pairs and vary depending on the type of schedule. This allows for different types of schedules with different properties to be supported. The UserScheduleManager is responsible for managing Schedule objects including determining which users have schedules, what those schedules are, as well as providing an api for operations such as disabling a schedule. The prototype implementation for UserScheduleManager is UserPreferencesScheduleManager which assumes schedules are stored as user preferences. One of the advantages of using user preferences is that no new persistence work or protocol work has to be done, as its already supported for user preferences.

UserPreferencesScheduleManager

UserPreferencesScheduleManager is the default implementation of UserScheduleManager that uses user preferences to store schedules. It assumes the following:

  • user schedules are enabled if the preference cosmo.scheduler.enabled=true is present
  • a schedule is define by the set of properties that start with cosmo.scheduler.job.[jobName].
  • a schedule is enabled if the preference cosmo.scheduler.job.[jobName].enabled=true is present
  • a schedule must include a "type" property like cosmo.scheduler.job.[jobName].type=[typeKey] where [typeKey] maps to a JobTypeScheduler that is responsible for scheduling that job type (explained later)

What this means is that all schedules are stored as user preferences (key/value pairs associated to a user). For example the following set of user preferences defines a single schedule named "1" that is handled by the JobTypeScheduler mapped to the key "dummy" and contains extra properties "message" and "notifier.name" (explained later).

key value
cosmo.scheduler.enable true
cosmo.scheduler.job.1.enabled true
cosmo.scheduler.job.1.type dummy
cosmo.scheduler.job.1.cronexp 5 * * * * ?
cosmo.scheduler.job.1.message Hello World!
cosmo.schedluer.job.1.notifier.name log

This translates into the following Schedule object:

  • name = 1
  • properties = { cronexp=>"5 * * * * ?" , message=> "Hello World!", notifier.name=>"log" }

Multiple schedules can be defined for a user by using a different jobName. Schedules can be enabled or disabled individually by setting the cosmo.scheduler.[jobName].enabled property. All schedules can be disabled by setting the cosmo.scheduler.enabled to false.

The server already provides atom protocol support for updating user preferences, so schedules can be added/removed/updated using this protocol.

JobTypeScheduler

The next piece in the puzzle is the JobTypeScheduler which is responsible for translating a Schedule object into runnable code that runs at a specific interval. Basically, it translates a set of properties into a quartz job. For the example above, the JobTypeScheduler DummyJobTypeScheduler , is registered with the SchedulerImpl under the key "dummy". This schedule object represented by the above properties will be passed, along with the user and quartz engine to the DummyJobTypeScheduler . The DummyJobTypeScheduler implementation simply grabs the "cronexp" property and creates a quartz org.quartz.CronTrigger and schedules a job in the quartz engine that ends up logging the value of the "message" property on an interval defined by the "cronexp", which in the example above means every minute on the 5th second.

It is important to note that a JobTypeScheduler is responsible for interfacing with the quartz scheduler engine in order to schedule the job. This means it needs to create a org.quartz.JobDetail, which represents a quartz job, and schedule it using a org.quartz.Trigger . This requires knowledge of how quartz works, but provides the most flexibility.

Implementing new types of Schedules

So to recap, the scheduler component allows schedules associated to a user to be setup and run. These schedules are defined by a name and a set of key/value properties. An associated JobTypeScheduler is responsible for translating the properties into a job that runs in the quartz scheduler. So the steps in supporting a new type of schedule are:

  1. define properties for schedule type (reserved properties are enabled=? and type=?)
  2. implement JobTypeScheduler for the schedule type and register with SchedulerImpl
  3. have the client manage schedules using user preferences api's/protocols

ForwardLookingJobTypeScheduler

The initial schedule type that will be supported in cosmo is the forward-looking notification job. This is a job that runs on a daily or weekly interval and queries a set of collections and returns the results in the form of an email notification. The properties for this type of schedule are:

name value description
type forward constant, must be present
enabled true or false constant, must be present
reportType daily or weekly defines time period to query, either 1 day forward or 1 week forward
timezone America/Chicago user's timezone, used to determine when to send report and how to interpret floating events. Optional, if not present, the server timezone is assumed
cronexp 0 0 6 ? * SUN (weekly) or 0 0 6 ? * MON-FRI (daily) defines the interval (examples are shown for daily and weekly interval that occurs at 6am
collection.UID1 true include collection with uid UID1 in results
collection.UID2 true include collection with uid UID2 in results
notifier.name email send results to Notifier "email"
notifier.email.addresses user1@foo.org,user2@foo.org email addresses to send results to. Optional. If not present, the report will be sent to the user's email address

Assuming the use of the UserPreferencesScheduleManager this schedule would be represented as the following user preferences:

key value
cosmo.scheduler.enable true
cosmo.scheduler.job.1.enabled true
cosmo.scheduler.job.1.type forward
cosmo.scheduler.job.1.reportType weekly
cosmo.scheduler.job.1.timezone America/Chicago
cosmo.scheduler.job.1.cronexp 0 0 6 ? * SUN
cosmo.scheduler.job.1.collection.UID1 true
cosmo.scheduler.job.1.collection.UID2 true
cosmo.schedluer.job.1.notifier.name email
cosmo.scheduler.job.1.notifier.email.addresses user1@foo.org,user2@foo.org

So in order to add/remove/update the schedule for a user, the service and protocol apis for user preferences can be used as long as the preferences conform to the above format.

ForwardLookingNotificationJob

The ForwardLookingJobTypeScheduler schedules jobs of type ForwardLookingNotificationJob into the quartz engine. This job extends from a hierarchy starting with Job, which is an abstract spring QuartzJobBean. By extending QuartzJobBean, properties stored in the quartz JobDetail can be injected into the job bean automatically. For example, instead of having to do something like:

   String username = context.getJobDetail().getJobDataMap().getString("username");

The job class can define a username setter like:

   public class MyJob extends Job {
      private String username;

      public void setUsername(String username) {
          this.username = username;
      }
  }

and the spring QuartzJobBean will automatcally populate username from the jobDataMap and scheduler context map.

JobHierarchy.jpg

The base Job class can be configured to execute a set of Filter objects, which allow code to be executed before/after a job. Just like servlet filters, job filters are executed in a filter chain. Most user jobs on the server are going to want at least 2 filters to execute before each job. The first filter is the HibernateSessionFilter, which sets up and tears down a hibernate session, which mimics the OpenSessionInView pattern used in the rest of cosmo. This allows for things like lazy loading in hibernate to work. The second filer is the SecurityContextFilter, which initializes the security context with the owner of the job. This allows the job to be executed as the user and allows security checks to be performed on that user while the job is executed. This prevents a user job from accessing a items the user does not have access to while at the same time leveraging existing auth code.

ServiceJob injects a ContentService into the job, allowing that api to be used during job execution. The ContentService is injected by being added to the quartz schedule context map, which makes it available to each job.

NotificationJob generates a report, represented by the Report interface and sends the results to a Notifier. NotificationJob has a set of Notifier implementations indexed by name. In the above schedule properties, the property notifier.name=email tells the NotificationJob to send the results along with a set of "notifier" properties to the Notifier implementation mapped by the key "email". In this example the notifier properties is the map {email.addresses=> "user1@foo.org,user2@foo.org"}. The set of Notifiers is present in the quartz schedule context map, this it is injected to each NotificationJob.

MutlipleCollectionJob adds a list of collection uids representing the collections that the job is working on.

Then there is ForwardLookingNotificationJob, the only non-abstract job type listed here, which simply implements generateReport(), returning a ForwardLookingReport based on the schedule parameters, to be passed to the specified Notifier instance.

Cosmo will initially provide a notifier that sends email, but it would be easy to plugin additional notifiers that for example use XMPP or SMS in the future.

The flow for a ForwardLookingNotificationJob execution is:

  1. Job creates filter chain from list of Filters
  2. HibernateSessionFilter sets up hibernate session
  3. SecurityContextFilter sets up security context
  4. ForwardLookingNotificationJob.generateReport() generates ForwardLookingReport
  5. report is passed to notifier, user is notified of results (email)
  6. SecurityContextFilter tears down security Context
  7. HibernateSessionFilter tears down hibernate session

EmailNotifier

Cosmo provides a Notifier implementation which takes results from a ForwardLookingNotificationJob (in the form of a ForwardLookingReport ) and sends email to either the user or a set of email addresses defined in the notifier property email.addresses. The implementation uses a spring JavaMailSender and the Velocity template engine to generate and send emails.

SchedulerImpl Notes

Configuration

All configuration is done in applicationContext.xml

Startup

The scheduler is started when the spring context is loaded.

Changes in User Schedules

User schedules are refreshed on a configurable interval (default 1 hour). Part of initialization includes scheduling a job with the quartz engine with a simple trigger that calls refresh() each time its fired. Ideally the server provides a way for components to "listen" for certain events such as "user updated" in order to provide a more efficient means of handling schedule updates.

-- RandyLetness - 07 May 2008

toggleopenShow attachmentstogglecloseHide attachments
Topic attachments
I Attachment Action Size Date Who Comment
jpgjpg SchedulerImpl.jpg manage 21.1 K 07 May 2008 - 11:17 RandyLetness  
jpgjpg JobHierarchy.jpg manage 64.5 K 07 May 2008 - 11:17 RandyLetness  
Edit | WYSIWYG | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r4 < r3 < r2 < r1 | More topic actions
 
Open Source Applications Foundation
Except where otherwise noted, this site and its content are licensed by OSAF under an Creative Commons License, Attribution Only 3.0.
See list of page contributors for attributions.