Batch Apex Status Bar
Batch Apex is here and it is cool. In some of the demos and webinars salesforce.com has put together you may have seen a status bar that represents how far along a batch job has proceeded. This is useful as you only need but a quick glance to see how your batch jobs are doing. Below is an image of what this would look like.
All of this magic status bar stuff is pretty simple. You have two divs, an inner and an outer that construct the status bar, the inner being the percent complete. All of the info to construct this status bar is located in a sObject called AsyncApexJob. This stores information about your batch jobs and other asynchronous operations.
The two important fields in this object are JobItemsProcessed and TotalJobItems. When a batch job is executing it may process millions of records but it will break these up into batches of 200, or less if defined……hence the name Batch Apex. So if I have 5,000 records to be processed there would be 25 batches of 200. In the AsyncApexJob object these 25 batches would be the TotalJobItems. As the job runs the number JobItemsProcessed will increase as the batches are completed.
Now that we have these two number calculating the percent complete is an utterly simple division equation. JobItemsProcessed divided by TotalJobItems times 100 gives us the percent complete. We then pass this value to the width of the inner div and our status bar is complete! To give the status bar the appearance of being animated we add an actionPoller to the page and rerender the table of batch jobs every five seconds which will intern update that status bar with the new values.
As always there is a fun little demo to play with and you can access that here: Click here for nifty cool demo.
I’ve also packaged up this component so you can install it directly to your org. You can download it here.
Here is all the code and markup. First the page that will contain the batchJobs component.
<apex:page controller="batchStatus"> <apex:form> <apex:commandButton value="Start Batch Job" action="{!startBatch}" reRender="jobs,error"/> <apex:outputText id="error" value="{!error}" style="font-weight: bold; color: red"/> <br/><br/> <c:batchJobs id="jobs" numberOfJobs="20"/> </apex:form> </apex:page>
Next we have the Visualforce component:
<apex:component controller="batchJobs" selfClosing="true"> <apex:attribute name="numberOfJobs" type="Integer" assignTo="{!numberOfJobs}" description="The number of batch jobs to display in the table."/> <!-- Here is the css styles that will be used for the progress bars --> <style> .progressBar{ background-color: #f8f8f8; border:1px solid #DDDDDD; height: 19px; width: 300px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } .progress{ background-color: #F7B64B; border:1px solid #E78F08; height: 100%; margin: -1px; text-align: center; -moz-border-radius: 5px; -webkit-border-radius: 5px; line-height: 18px; } </style> <!-- This action poller will check the status of the batch jobs every 5 seconds --> <apex:actionPoller rerender="jobs" interval="5"/> <apex:pageBlock title="Batch Apex Jobs"> <apex:pageBlockTable value="{!batchJobs}" var="b" id="jobs"> <apex:column headerValue="Apex Class" value="{!b.job.ApexClass.Name}"/> <apex:column value="{!b.job.CreatedDate}"/> <apex:column value="{!b.job.CreatedById}"/> <apex:column value="{!b.job.Status}"/> <apex:column width="320px" > <!-- Here with have two divs that construct our progresses bar. An outter which is the entire bar, and and inner that represents the percent complete. We simply pass the percentComplete value to the inner div width and this will show how far along the job is. Brilliant! --> <div class="progressBar"> <div class="progress" style="width: {!b.percentComplete}%;"> {!b.percentComplete}% </div> </div> </apex:column> <apex:column value="{!b.job.CompletedDate}"/> </apex:pageBlockTable> </apex:pageBlock> </apex:component>
And finally the custom controller for the component….. where all the magic happens:
public class batchJobs{ public List<BatchJob> batchJobs; public Integer numberOfJobs {get; set;} public List<BatchJob> getBatchJobs(){ //Create new list of BatchJobs, a wrapper class that includes the job and percent complete. batchJobs = new List(); //If number of jobs was not defined, default to 20 if(numberOfJobs== null || numberofJobs <= 0){ numberofJobs = 20; } //Query the Batch apex jobs for(AsyncApexJob a : [select TotalJobItems, Status, NumberOfErrors, MethodName, JobType, JobItemsProcessed, Id, CreatedDate, CreatedById, CompletedDate, ApexClassId, ApexClass.Name From AsyncApexJob order by CreatedDate desc limit :numberOfJobs]){ Double itemsProcessed = a.JobItemsProcessed; Double totalItems = a.TotalJobItems; BatchJob j = new BatchJob(); j.job = a; //Determine the pecent complete based on the number of batches complete if(totalItems == 0){ //A little check here as we don't want to divide by 0. j.percentComplete = 0; }else{ j.percentComplete = ((itemsProcessed / totalItems) * 100.0).intValue(); } batchJobs.add(j); } return batchJobs; } //This is the wrapper class the includes the job itself and a value for the percent complete public Class BatchJob{ public AsyncApexJob job {get; set;} public Integer percentComplete {get; set;} } /*--------------------TEST METHOD------------------------*/ static testMethod void batchStatusBarTest(){ batchJobs controller = new batchJobs(); controller.getBatchJobs(); } }
I initially planned on adding all sorts of cool features to this component such as color coding the status bar based on the status of the job and estimated time to completion. These were axed in 1.0 as it was taking too long and there our some other cool posts I have lined up that I want to get working on. Maybe in the future I’ll post an updated version of this component with all the flair it deserves.
UPDATE:
I’ve been asked a couple times what exactly happens when a user clicks the <apex:commandButton value=”Start Batch Job” action=”{!startBatch}” reRender=”jobs,error”/> button. This calls a very simple method in the controller that starts a batch job.
public void startBatch(){ error = ''; batchJob job = new batchJob(); List<AsyncApexJob> openJobs = [select Id from AsyncApexJob where Status = 'Processing' OR Status = 'Queued']; if(openJobs.size() < 5){ ID batchprocessid = Database.executeBatch(job); }else{ error = 'WHOA BUDDY! Only five batch jobs at a time.'; } }
And here is the batch apex job.
global class batchJob implements Database.Batchable<SObject>{ global database.querylocator start(Database.BatchableContext bc){ return Database.getQueryLocator('select Id, Name from Account where Type = \'Batch Test\''); } global void execute(Database.BatchableContext bc, sObject[] objects){ List<Account> accns = new List<Account>(); for(sObject s : objects){ Account a = (Account)s; a.Description = 'batch testing. blah blah blah. ' + system.now(); accns.add(a); } update accns; } global void finish(Database.BatchableContext bc){ system.debug('all done'); } }

Nice piece of code
Nice work! One question, I’m unable to find the “batchStatus” Apex code. Do you think you could explain about that class?
This is awesome, thank you so much for sharing. One comment, though. The code you have posted does not work, it is not the same as what you have in your package for installation. If you look at line 3 of your controller, you will see the first invalid code:
public List batchJobs;
This should be
public List batchJobs;
Other than that, it is great, and I appreciate you publishing it, and for all of your great support on the community forums.
Thanks for pointing that out Jim. Sometimes the blog editor messes up the code with the <> characters. Same thing happen in your comment but I think I know what you were trying to say and the code has been updated.
I think you forgot to post the “batchStatus” Apex code.
what will the command button action=”{!startBatch}” will do??
eagerly waiting for your reply.
Additional code as been added.
Nice piece of code. Very useful.
This is a solid piece of code. I modified slightly to accept a parameter for the job number instead of the number of jobs to display. I wanted to be able to render a progress bar on the page for a specific job instead of all jobs in queue. Thanx for doing all the heavy lifting!
Thanks, this code was a huge help for me. I modified the progress bar calculation slightly because some of my batches process 0 records, which was leaving the bar at 0% even when the job is complete.
//Determine the pecent complete based on the number of batches complete
if(totalItems == 0){
//There were no items to process, so percentage is 0% until status is complete, then becomes 100%.
j.percentComplete = a.Status != ‘Completed’ ? 0 : 100;
}else{
j.percentComplete = ((itemsProcessed / totalItems) * 100.0).intValue();
}
I love this!