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