function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Timothy SmithTimothy Smith 

Argument must be an object that implements Database.Batchable

Hello all,

I am trying to write a batch class (MY FIRST EVER), to take in the AsyncApexJob object and create a csv file of the errors and send to an email address.  I'm sure that some of my syntax is not correct (I would appreciate any input), but I can't even execute to see if it works.  I am receiving the above message when I open a second anonymous window and enter:

AriaBatchErrors objClass=new AriaBatchErrors();
Database.executeBatch(objClass);


So that I can execute:

global class AriaBatchErrors implements Database.Batchable<Sobject>
{
    global string[] email=new String[] {'myEmail@address.com'};
    string[] Lines;
    //start method
    global Database.QueryLocator start(Database.BatchableContext BC)
    {
        lines = new String[0];
        return Database.getQueryLocator('SELECT Id, Status, NumberOfErrors, JobItemsProcessed,TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE NumberOfErrors > 0');

    }

    //execute
    global void execute(Database.BatchableContext BC, List<String> scope)
    {
        // process
           for(String record: scope) 
        {
        String line = '';
        // header row
        if ( lines.size() == 0 ) {
            line = 'ID, Status, Number of Errors, Job Items Processed, Total Job Items, Created By';
            lines.add(line);
        }
        else {
            // build csv lines here
            line +='"' + record.get('Id')  +'"'
            +',' + '"' + record.get('Status') + '"'
            +',' + '"' + record.get('NumberOfErrors') +'"'
            +',' + '"' + record.get('JobItemsProcessed') + '"'
            +',' + '"' + record.get('TotalJobItems') + '"' 
            +',' + '"' + record.get('CreatedBy.Email') + '"'   ;               
            lines.add(line);

            system.debug('Line No >>> ' + line); 
        }
         }  // end of for loop   
        
    }


    //finish
    global void finish (Database.BatchableContext BC)
    {
        Messaging.SingleEmailMessage mail=new Messaging.SingleEmailMessage();
        AsyncApexJob a =[select a.TotalJobItems,a.Status,a.NumberOfErrors,a.JobItemsProcessed,a.ExtendedStatus,a.createdById,a.completedDate from AsynchApexJob];
        System.debug('Job Id')+BC.getJobId());
        mail.ToAddreses(email);
        mail.setSenderDisplayName('Apex Batch Processing Errors');
        mail.setSubject('Batch Errors');
        mail.setPlainTextBody('This will be the body of the email' + a.TotalJobItems + 'batches with '+ a.NumberOfErrors);
    }
}

Please help, I am so so new. 

Timothy
Best Answer chosen by Timothy Smith
Glyn Anderson 3Glyn Anderson 3
Timothy,  Because the "scope" argument in the execute method is not "List<sObject>", technically, you haven't implement the Database.Batchable<sObject> interface.  Here a version with that bit corrected, and some other ideas:

<pre>
global class AriaBatchErrors implements Database.Batchable<sObject>
{
    global List<String> email = new List<String>{ 'myEmail@address.com' };

    private List<String> lines = new List<String>{ 'ID, Status, Number of Errors, Job Items Processed, Total Job Items, Created By' };

    private String quote( String text ) { return '"' + text + '"'; }

    //start method
    global Database.QueryLocator start( Database.BatchableContext BC )
    {
        return Database.getQueryLocator( 'SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE NumberOfErrors > 0' );
    }

    //execute
    global void execute( Database.BatchableContext BC, List<sObject> scope )
    {
        // process
        for ( AsyncApexJob record : (List<AsyncApexJob>) scope )
        {
            lines.add
            (   String.join
                (   new List<String>
                    {   quote( (String) record.get( 'Id' ) )
                    ,   quote( (String) record.get( 'Status' ) )
                    ,   quote( (String) record.get( 'NumberOfErrors' ) )
                    ,   quote( (String) record.get( 'JobItemsProcessed' ) )
                    ,   quote( (String) record.get( 'TotalJobItems' ) )
                    ,   quote( (String) record.get( 'CreatedBy.Email' ) )
                    }
                ,   ','
                )
            );
        }
    }

    //finish
    global void finish( Database.BatchableContext BC )
    {
        Integer totalJobItems, numberOfErrors;
        for ( AggregateResult result :
            [   SELECT  SUM(TotalJobItems) totalJobItems, SUM(NumberOfErrors) numberOfErrors
                FROM    AsynchApexJob
                WHERE   NumberOfErrors > 0
            ]
            )
        {
            totalJobItems = (Integer) result.get( 'totalJobItems' );
            numberOfErrors = (Integer) result.get( 'numberOfErrors' );
        }

        Messaging.SingleEmailMessage mail=new Messaging.SingleEmailMessage();
        mail.ToAddreses( email );
        mail.setSenderDisplayName( 'Apex Batch Processing Errors' );
        mail.setSubject( 'Batch Errors' );
        mail.setPlainTextBody( 'This will be the body of the email: ' + totalJobItems + ' batches with ' + numberOfErrors );
    }
}
</pre>

All Answers

Glyn Anderson 3Glyn Anderson 3
Timothy,  Because the "scope" argument in the execute method is not "List<sObject>", technically, you haven't implement the Database.Batchable<sObject> interface.  Here a version with that bit corrected, and some other ideas:

<pre>
global class AriaBatchErrors implements Database.Batchable<sObject>
{
    global List<String> email = new List<String>{ 'myEmail@address.com' };

    private List<String> lines = new List<String>{ 'ID, Status, Number of Errors, Job Items Processed, Total Job Items, Created By' };

    private String quote( String text ) { return '"' + text + '"'; }

    //start method
    global Database.QueryLocator start( Database.BatchableContext BC )
    {
        return Database.getQueryLocator( 'SELECT Id, Status, NumberOfErrors, JobItemsProcessed, TotalJobItems, CreatedBy.Email FROM AsyncApexJob WHERE NumberOfErrors > 0' );
    }

    //execute
    global void execute( Database.BatchableContext BC, List<sObject> scope )
    {
        // process
        for ( AsyncApexJob record : (List<AsyncApexJob>) scope )
        {
            lines.add
            (   String.join
                (   new List<String>
                    {   quote( (String) record.get( 'Id' ) )
                    ,   quote( (String) record.get( 'Status' ) )
                    ,   quote( (String) record.get( 'NumberOfErrors' ) )
                    ,   quote( (String) record.get( 'JobItemsProcessed' ) )
                    ,   quote( (String) record.get( 'TotalJobItems' ) )
                    ,   quote( (String) record.get( 'CreatedBy.Email' ) )
                    }
                ,   ','
                )
            );
        }
    }

    //finish
    global void finish( Database.BatchableContext BC )
    {
        Integer totalJobItems, numberOfErrors;
        for ( AggregateResult result :
            [   SELECT  SUM(TotalJobItems) totalJobItems, SUM(NumberOfErrors) numberOfErrors
                FROM    AsynchApexJob
                WHERE   NumberOfErrors > 0
            ]
            )
        {
            totalJobItems = (Integer) result.get( 'totalJobItems' );
            numberOfErrors = (Integer) result.get( 'numberOfErrors' );
        }

        Messaging.SingleEmailMessage mail=new Messaging.SingleEmailMessage();
        mail.ToAddreses( email );
        mail.setSenderDisplayName( 'Apex Batch Processing Errors' );
        mail.setSubject( 'Batch Errors' );
        mail.setPlainTextBody( 'This will be the body of the email: ' + totalJobItems + ' batches with ' + numberOfErrors );
    }
}
</pre>
This was selected as the best answer
Glyn Anderson 3Glyn Anderson 3
Instead of doing the aggregate query in the finish method, you could just accumulate those values in the execute method.  Then, they would already be calculated when finish is called.
Timothy SmithTimothy Smith
Thank you so much. This was a BIG help!!!