Using Scheduled Apex jobs to retrieve stock quotes.

Spring '10 saw the general availability of one of my favorite new features of the platform: the Apex Scheduler. 

With the Apex Scheduler, developers can now schedule cron style jobs to run any Apex classes which implement the Schedulable interface.  The Schedule Apex page, available from Setup | Develop | Apex Classes, lets you define the how often your particular job will run.

Apex-schedule
 One of things which you may notice is that you are can only schedule jobs to run up to once a day using the Schedule Apex page. Never fear, however, for with a little bit of code you can schedule your jobs to run every hour.  Let's look at a real world example:

Suppose that every hour I wanted to retrieve the current stock quote for an account using the very handy, but often overlooked Yahoo stock API. 

global class getStockPrice implements Schedulable
{
global void execute(SchedulableContext ctx)
{
//call a future method so we can do callouts within a schedule    CallYahooQuotes.getQuotes();
}
}

You will notice that I have implemented the Schedulable interface and overriden the execute method in my Apex class. That is all I need to do to take advantage of Apex Scheduling. Pretty cool, huh. In this example I also used the @future annotation to allow me to do a callout from within a Scheduled Job. Here is the code which does the actual retrieval of stock price information from Yahoo's service:

global class CallYahooQuotes
{
    @future(callout=true)
    public static void getQuotes()
    {   //for our simple example we are going to just update the quotes on one Account
       Account acct = [select id, TickerSymbol, stock_price__c from account where name = 'Salesforce.com'];
   
       //where f stands for the fields to be displayed (in this case: s - symbol; l1 - last price; c1 - change; d1 = last date)
       // String url = 'http://quote.yahoo.com/d/quotes.csv?s='+acct.TickerSymbol+'&f=sl1c1d1&e=.csv';
     
     String url = 'http://download.finance.yahoo.com/d/quotes.csv?s='+acct.TickerSymbol+'&f=l1&e=.csv';
    
    Http h = new Http();
    
    HttpRequest req = new HttpRequest();
    req.setEndpoint(url);
    req.setMethod('GET');
    
    HttpResponse res = h.send(req);
    
    //omitting error handling for example
    acct.stock_price__c = decimal.valueOf(res.getBody().trim());  
    update acct;  
   }

}

 Next, I want to schedule my job to run every hour.

A best practice I like to follow is to create a wrapper Schedule class with a static start method. This way I can either call my class from, say a custom Visualforce page after a user clicks a button, or via  the execute function in the System Log.

global class StockPriceJobScheduler
{
global void StockPriceJobScheduler() {}
public static void start()
{
//Seconds Minutes Hours Day_of_month Month Day_of_week optional_year
System.schedule('Stock Price Update', '0 0 1-23 * * ?', new getStockPrice());
}
}

The System.schedule method takes three parameters: a job name, a cron expression, and the name of the schedulable class. The only tricky parameter is the cron expression. Take some time to read the docs to fully understand the syntax of cron expressions as they are a handy tool to have in your developer toolbox.

One thing that you will discover as you try out scheduling is that, although it is possible to schedule a job to run more frequently than every hour, the platform will assign a minimum frequency of 1 hour. 

Let's go ahead and use the System Log (click on the System Log link at the top of your screen) to start our new Scheduled Job:

System Log
Finally, navigate to Setup |   Monitoring | Scheduled Jobs and you should see our new job with the name we gave it during the scheduling process.

Scheduledjob
That's it. With less than 30 lines of code (a some of those are comments!) we just created a scheduled job to receive stocks quotes from the web and updated our Account. 

Spring is certainly here!

tagged , , , , , , , , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • thecrmninja

    Great example, thank you for sharing.
    Are there any gotcha’s to take into account when building testing classes around the various components you’ve introduced?

  • http://twitter.com/cloudcoder Quinton Wall

    Glad it helps. The docs I referenced in the post give a great example of writing test cases to cover Scheduled Apex Job. I would certainly start there. But re testing for the Yahoo stock quote, the first thing you will need to do it make the code provided more robust re error handling. I omitted all of that for brevity.

  • Rajesh

    Great example. Thanks for explaining in detail. Apex Scheduling can then also be used for latest currency rates. Might be pretty useful for an org having multiple currency enabled.

  • http://www.embracingthecloud.com/ Mike Leach

    Is the service guaranteed to run in perpetuity after calling start()?
    Do custom services need to be restarted after upgrades?

  • Hannes

    Dear Quinton,
    I just learned that Callouts are not allowed from Scheduled Apex. Is that because I am using POST as http method and GETs are allowed?
    Please see this forum discussion too: http://community.salesforce.com/sforce/board/message?board.id=general_development&thread.id=39811
    Cheers,
    Hannes

  • http://www.stmaartendeepseafishing.com Dinesh M.

    Thanks for explaining in detail. Apex Scheduling,, Is there an easier way for parameter in the cron expression.

  • http://south-african-jobs.co.za/ Sajobs

    This has really been helpful for finally getting over that once a day hurdle – thank you for sharing. Very Cool!!

  • yangkeesik

    Thank you for sharing a great example..

  • http://www.pajamajobs.com Telecommuting Job Queen

    Are Callouts allowed from Scheduled Apex?

  • Vikram Nanduri

    Thats a great example, thanks.
    But when I run the same example and wrote Test class, the code coverage is 0% , can you please explain me in brief how to write a test class for such a WebService Callout which includes a future call please.

  • http://quintonwall.com Quinton Wall
  • http://twitter.com/GazizTazhenov Gaziz Tazhenov

    Nice example! Thanks!

  • http://profile.yahoo.com/ONOM7VGC74NJQW6DWYBKLI6LIU tan

    The code given has bugs in it. I slap the code and it gives me error. What is the use if I have to debug the code?