Tuesday, April 12, 2016

X++ Create task to monitor batch running overnight and send email

Good morning! In real-life we have many batches which are scheduled to run overnight. This scenario will show how to create a task to check their status and send email to related supporters.

There are two sub tasks for this. They are:
1. Batch status checking
2. Sending email
3. Schedule task and configuration

Let's start with the class and other methods.

class batchMonitor extends RunBaseBatch
{
    boolean isCompleted;
    #define.filename(@'E:\aaa\batch.csv')


public container pack()
{
    return conNull();
}

public boolean unpack(container packedClass)
{
    return true;
}

public void run()
{
    this.genCSV();
    this.sendEmail();
}

} // end class


1. Batch status checking

This method will check batch status and write csv.

private void genCSV()
{
    // ------------- Declaration -------------
    BatchJob            batchJob;
    BatchJobHistory     batchJobHistory;
    BatchCaption        jobName;

    int                 i=0,
                        iMax=0;

    container           listOfJobName   = ['B001', 'B002'];

    str                 strCaption,
                        strRecurPeriod,
                        strBatchStatus;

    // Declaration: CSV file
    CommaTextIo         file;
    container           line,
                        lineHeader      = ['B001', 'B002'];
    #File
    ;


    // ------------- Process -------------
    isCompleted = true;

    // Check file availability
    file = new CommaTextIo(#filename, #io_write);
    if (!file || file.status() != IO_Status::Ok)
    {
       throw error("File cannot be opened.");
    }

    // Write header
    line = lineHeader;
    file.writeExp(line);

    // Write line
    iMax = conLen(listOfJobName);
    while (i < iMax)
    {
        i++;
        jobName = conPeek(listOfJobName,i);
        batchJob = null;
        batchJobHistory = null;

        select  *
        from    batchJob
        where   batchJob.Caption == jobName && batchJob.Status == BatchStatus::Waiting
            join        batchJobHistory
            order by    batchJobHistory.EndDateTime desc
            where       batchJobHistory.BatchJobId == batchJob.RecId;

        strCaption      = strFmt('"%1"',batchJob.Caption);
        strRecurPeriod  = batchJob.recurrenceText();
        strBatchStatus  = strFmt('%1',batchJobHistory.Status);

        if (strBatchStatus != 'Ended')
            isCompleted = false;

        // Write to file
        line = [strCaption,
                strRecurPeriod,
                strBatchStatus
               ];
        file.writeExp(line);

    }

    file.finalize();

}

2. Sending email

This method will send an email.

private void sendEmail(Args _args)
{
    str                                   sender    = 'test@test.com';  // mail id of the sender
    str                                   recipient = 'test@test.com';  // mulitple recipients can be specified
    str                                   cc        = 'test@test.com';  // mulitple cc id's can be specified
    str                                   subject   = 'Subject of the mail';
    str                                   body      = 'Content of the mail';

    List              toList;
    List              ccList;
    ListEnumerator    enumList;
    Set               permissionSet;
    System.Exception  exception;

    str                                     mailServer;
    int                                     mailServerPort;
    System.Net.Mail.SmtpClient              mailClient;
    System.Net.Mail.MailMessage             mailMessage;
    System.Net.Mail.MailAddress             mailFrom;
    System.Net.Mail.MailAddress             mailTo;
    System.Net.Mail.MailAddressCollection   mailToCollection;
    System.Net.Mail.MailAddressCollection   mailCCCollection;
    System.Net.Mail.AttachmentCollection    mailAttachementCollection;
    System.Net.Mail.Attachment              mailAttachment;
    ;
 
    //Prepare init value
    subject     = strFmt('Batch job status as of %1',today());
    body        = strFmt('Hi all\r\n\rPlease kindly see the batch job status as of %1 in the attached. The status is',today());
    if (isCompleted)
    {
        subject += ' - Completed';
        body += ' all completed.';
    }
    else
    {
        subject += ' - Failed';
        body += ' failed.';
    }
    body += '\r\n\rRegards\r\n\rMisterAAA';  

    try
    {
        toList = strSplit(recipient, ';');
        ccList = strSplit(cc, ';');

        permissionSet = new Set(Types::Class);
        permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
        permissionSet.add(new FileIOPermission(#filename, 'test1'));
        CodeAccessPermission::assertMultiple(permissionSet);

        mailServer      = SysEmaiLParameters::find(false).SMTPRelayServerName;
        mailServerPort  = SysEmaiLParameters::find(false).SMTPPortNumber;
        mailClient      = new System.Net.Mail.SmtpClient(mailServer, mailServerPort);

        enumList = toList.getEnumerator();
        enumList.moveNext();

        mailFrom        = new System.Net.Mail.MailAddress(sender);
        mailTo          = new System.Net.Mail.MailAddress(strLTrim(strRTrim(enumList.current())));
        mailMessage     = new System.Net.Mail.MailMessage(mailFrom, mailTo);

        mailToCollection = mailMessage.get_To();
        while (enumList.moveNext())
        {
            mailToCollection.Add(strLTrim(strRTrim(enumList.current())));
        }

        enumList            = ccList.getEnumerator();
        mailCCCollection    = mailMessage.get_CC();
        while (enumList.moveNext())
        {
            mailCCCollection.Add(strLTrim(strRTrim(enumList.current())));
        }

        mailMessage.set_Priority(System.Net.Mail.MailPriority::Normal);
        mailMessage.set_Subject(subject);
        mailMessage.set_Body(body);

        mailAttachementCollection   = mailMessage.get_Attachments();
        mailAttachment              = new System.Net.Mail.Attachment(#fileName);
        mailAttachementCollection.Add(mailAttachment);

        mailClient.Send(mailMessage);
        mailMessage.Dispose();

        CodeAccessPermission::revertAssert();

        info("Email sent successfully");
    }
    catch (Exception::CLRError)
    {
        exception = ClrInterop::getLastException();
        while (exception)
        {
            info(exception.get_Message());
            exception = exception.get_InnerException();
        }
        CodeAccessPermission::revertAssert();
    }

}

3. Schedule task and configuration

     3.1 Create a folder on AOS server  - This is important as Batch is being run on Server side so we need a folder to keep the attachment file. In this case, it's E:\aaa.
     3.2 Check Outgoing email parameter

     3.3 Check Batch server parameter

     3.4 Schedule the class by the following job

static void batchSchedule(Args _args)
{
    BatchHeader     batchHeader;
    BatchInfo       batchInfo;
    RunBaseBatch    rbbTask;
    str sParmCaption = "Batch Monitoring";
    ;

    rbbTask     = new batchMonitor();
    batchInfo   = rbbTask.batchInfo();
    batchInfo.parmCaption(sParmCaption);
    batchInfo.parmGroupId(""); // The "Empty batch group".
    batchHeader = BatchHeader ::construct();
    batchHeader.addTask(rbbTask);
    batchHeader.save();
    info(strFmt("'%1' batch has been scheduled.", sParmCaption));
}

Finally, our task is in the batch list which can manage through dynamics ax standard. Thanks for reading. Until next post!

References:
Walkthrough: Extending RunBaseBatch Class to Create and Run a Batch [AX 2012]
Sending emails with CC and attachment in AX 2012

No comments:

Post a Comment