2.4. Scheduler

2.4.1. Introduction

EspressReport has a Scheduler interface through which reports can be exported at a specific time or specific intervals. This allows EspressReport to handle mundane tasks such as exporting reports than having a user log in at a specific time and explicitly perform the operation. For more information on how to use Scheduler, please see Section 1.13 - Scheduling.

When writing Scheduler API code, two main classes are used; ScheduleObject, which is used to give details on the schedule job, and ScheduleModifier, which is used to alter Scheduler.

Both ReportAPIWithChart.jar and Scheduler.jar (in the EspressReport/lib directory) need to be added to the CLASSPATH for any code using the Scheduler.

All examples and code given in the manual assume that EspressManager is running locally (i.e., on your local machine) and default port number (22071). You can change this by going to the source code (you can download the source code by clicking on the Full Source Code link in the corresponding chapter), editing the code to enter the new connection information and recompiling the code.

2.4.2. Connecting to EspressManager

You cannot run Scheduler (or any Scheduler code) without connecting to EspressManager first. Depending on whether EspressManager is running as an application or a servlet, you can use API methods to specify connection information.

2.4.2.1. EspressManager Running as Application

If EspressManager is running as an application, you can use API methods to specify the IP address/machine name where EspressManager is located and the port number EspressManager is listening on.

You use the following two API methods to set the connection information:

static void setServerAddress(java.lang.String address);
static void setServerPortNumber(int port);

For example, the following lines of code:

QbScheduler.setServerAddress("someMachine");
QbScheduler.setServerPortNumber(somePortNumber);

will connect to EspressManager running on someMachine and listening on somePortNumber.

Please note that if the EspressManager connection information is not specified, the code will attempt to connect to EspressManager on the local machine and listening to the default port number (22071).

Please note that these methods exist in the QbScheduler and ScheduleModifier classes.

2.4.2.2. EspressManager running as servlet

If EspressManager is running as a servlet, you can use the following methods:

public static void useServlet(boolean b);
public static void setServletRunner(String comm_URL);
public static void setServletContext(String context);

For example, the following lines of code:

QbScheduler.useServlet(true);
QbScheduler.setServletRunner("http://someMachine:somePortNumber");
QbScheduler.setServletContext("EspressReport/servlet");

will connect to EspressManager running at http://someMachine:somePortNumber/EspressReport/servlet.

Please note that these methods exist in the QbScheduler and ScheduleModifier classes.

2.4.3. Invoking Scheduler Graphical Interface

Scheduler can be called from Report API in either an application or an applet. The following example, which can run as an applet or application, invokes Scheduler by constructing a QbScheduler Object:

QbScheduler qbScheduler = new QbScheduler((Applet)null, frame);
((JPanel)frame.getContentPane()).add("Center", qbScheduler.getScheduler());
frame.pack();
frame.setVisible(true);

A few set methods are also available if the programmer wishes to configure the Scheduler Interface to open with specific features turned on or off. Here are two methods that can be used to set the root directory for browsing and to set the insert command feature on of off:

qbScheduler.setRootDirectoryForBrowse(String root);
qbScheduler.setInsertCommandEnabled(boolean state);

2.4.4. Scheduling an Export

You can schedule a report to be exported using Scheduler. The following code, given below, shows how to do this:

    ScheduleObject sObj = new ScheduleObject("Report_OneTime", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("help/manual/code/templates/Account.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	String exportLocation = sObj.pickDefaultExportLocation();
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 1);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

Note that any references to a data source or to a report file using the relative URL reference (e.g. help/manual/code/templates/Account.rpt) are relative to the directory from where EspressManager is running in.

The above code schedules a report (Account.rpt) to be exported to DHTML, in the default export directory and sets the export to take place once and in one minute.

The examples below show how to set up a periodic schedule:

    sObj.setFileLocation("Account.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	String exportLoc = sObj.pickDefaultExportLocation();

	sObj.setTaskOption(ScheduleObject.TIME_INTERVAL);
	/*** every 5 mins ****/
	sObj.setIntervalType(ScheduleObject.TIME);
	sObj.setTimeInterval(5); // export every 5 mins

	/*** every day ****/
	//		 sObj.setIntervalType(ScheduleObject.DAYS);
	//		 sObj.setDayInterval(1); // export everyday

	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 5);
	sObj.setStartDate(calendar.getTimeInMillis());
	Calendar calendar2 = Calendar.getInstance();
	calendar2.add(Calendar.MINUTE, 26);
	sObj.setEndDate(calendar2.getTimeInMillis());
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

The above code snippet shows how to schedule a job to run every five minutes (or every day, depending on which section of the code you comment).

The following code snippet shows to schedule a job to run at certain time periods:

    ScheduleObject sObj = new ScheduleObject("Report_Time", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("Account.rpt");
	String exportLoc = sObj.pickDefaultExportLocation();
	sObj.setReportType(IExportConstants.PDF);
	sObj.setTaskOption(ScheduleObject.FIXED_DAYS);
	/*** export every 2 hrs. monday through friday ****/
	sObj.setSpecifyDays(new int[] { 1, 2, 3, 4, 5 });
	sObj.setStartTime(9 * 60); // 9:00AM in minutes
	sObj.setEndTime(22 * 60); // 10:00PM in minutes
	sObj.setTimeInterval(2 * 60); // export every 2 hours
	/*** export at specific times on specific dates ***/
	// sObj.setSpecifyDates(new int[]{26,27,28,29}); // export on specific dates
	// sObj.setSpecifyTime(new int[]{16 * 60, 17 * 60, 18 * 60}); // 4PM, 5PM and 6PM in minute
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 5);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setEndDate(-1); // run indefinitely
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

You can also specify the parameters when scheduling a parameterized chart or report to be exported. The following code shows how:

   ScheduleObject sObj = new ScheduleObject("ParamReport_OneTime", ScheduleObject.REPORTOBJ);
	//		 Single Param Report
	sObj.setFileLocation("EmployeeDetails.pak");
	String exportLoc = sObj.pickDefaultExportLocation();
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	//		 1st param set {"Denise Carron"}
	Object tmp1[] = { "Denise Carron" };
	//		 2st param set {"Frank Carnody"}
	Object tmp2[] = { "Frank Carnody" };
	Vector paramList = new Vector();
	paramList.addElement(tmp1);
	paramList.addElement(tmp2);
	sObj.setParamList(paramList);
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 5);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

A security level for the report can also be specified. The following code shows how to specify a security level (for report cells and/or query parameters):

   ScheduleObject sObj = new ScheduleObject("Report_Security", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("OrderFormByRegion.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	sObj.setSecurityLevel("ParamSet");
	String exportLocation = sObj.pickDefaultExportLocation();
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 10);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

You can also specify the schedules to be sent to one or more email addresses, either as a link or an attachment. The following code shows how to send a scheduled job which contains a non-parameterized report to multiple recipients:

    ScheduleObject sObj = new ScheduleObject("Report_Email", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("Account.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	String exportLocation = sObj.pickDefaultExportLocation();
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 10);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(true);
	sObj.setFromAddress("user1@quadbase.com");
	sObj.setSubject("Report Email Account Report");
	sObj.setBodyText("Scheduled Export of Account from user1");
	sObj.setEmailType(ScheduleObject.ASATTACHMENT);
	sObj.setToAddresses(new String[] { "user2@quadbase.com", "user3@quadbase.com" });

	ScheduleModifier.addScheduleTask(sObj);

Similarly, emails can be sent for each parameter set chosen for a parameterized report or chart when scheduling an export. Note that at least one email address must be specified for each parameter set and the same recipient can be used for multiple parameter sets.

   ScheduleObject sObj = new ScheduleObject("ParamReport_Email", ScheduleObject.REPORTOBJ);
	//		Single Param Report
	sObj.setFileLocation("EmployeeDetails.pak");
	String exportLoc = sObj.pickDefaultExportLocation();
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	//		1st param set {"Denise Carron"}
	Object tmp1[] = { "Denise Carron" };
	//		2nd param set {"Frank Carnody"}
	Object tmp2[] = { "Frank Carnody" };
	Vector paramList = new Vector();
	paramList.addElement(tmp1);
	paramList.addElement(tmp2);
	sObj.setParamList(paramList);
	Vector paramNameList = new Vector();
	paramNameList.addElement("Denise");
	paramNameList.addElement("Frank");
	sObj.setParamList(paramNameList, paramList);
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 5);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(true);
	sObj.setFromAddress("user1@quadbase.com");
	sObj.setSubject("Param Report Email Account Report");
	sObj.setBodyText("Scheduled Export of Employee Details from user1");
	sObj.setEmailType(ScheduleObject.ASATTACHMENT);
	Hashtable toAddr = new Hashtable();
	toAddr.put("Denise", new String[] { "user2@quadbase.com" });
	toAddr.put("Frank", new String[] { "user3@quadbase.com" });
	sObj.setParamAddresses(toAddr);

	ScheduleModifier.addScheduleTask(sObj);

The scheduled reports can also be stored in a web-accessible location and the URL sent via email to various recipients. The following code shows how:

    ScheduleObject sObj = new ScheduleObject("Report_Email", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("Account.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	String exportLocation = sObj.pickDefaultExportLocation();
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 10);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(true);
	sObj.setFromAddress("user1@quadbase.com");
	sObj.setSubject("Report Email Account Report");
	sObj.setBodyText("Scheduled Export of Account from user1");
	sObj.setEmailType(ScheduleObject.ASLINK);
	sObj.setURLMapping("http://someMachine:somePort/ScheduledReports/");
	sObj.setToAddresses(new String[] { "user2@quadbase.com", "user3@quadbase.com" });

	ScheduleModifier.addScheduleTask(sObj);

2.4.5. Scheduling a Command

In addition to scheduling exports, you can also schedule commands to be run at a specific time (or time interval). The code remains the same as the one for scheduling exports except the command to be executed is specified (rather than the report location, export format and export location).

ScheduleObject sObj = new ScheduleObject("Command_OneTime", ScheduleObject.COMMANDOBJ);
	// Note that someClass does not exist
	sObj.setCommand("java someClass");
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 5);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(false);

	ScheduleModifier.addScheduleTask(sObj);

2.4.6. Removing a Schedule

In addition to adding schedules, you can also use the API to remove any schedules. This is done by getting a list of all the jobs scheduled and then delete a particular job.

The following code shows how to delete a particular job:

    Vector schedList = ScheduleModifier.getScheduler().getScheduleList();

    ScheduleObject obj = (ScheduleObject)schedList.elementAt(0);
    ScheduleModifier.removeScheduleTask(obj);

Note that the above code removes the first job in the schedule list. The job may not be visible if it has been already marked for deletion. The recommended approach is to go through the vector and search each ScheduleObject by the name and then deleting the correct one.

2.4.7. Getting Details of a Failed Schedule

Sometimes, a scheduled job may fail for some reason or another. EspressManager keeps a track of all the failed jobs so that its details can be obtained later. The following code shows how to obtain the details:

    Vector vec = ScheduleLog.getFailedScheduledJob();
    for (int i = 0; i < vec.size(); i++) {

        FailedScheduledJob obj = (FailedScheduledJob) vec.elementAt(i);
        System.out.println("FAIL # " + i + ": Name = " + obj.getScheduledJobName());
        System.out.println("File Location = " + obj.getFileLocation());
        System.out.println("Time = " + obj.getExportTime());
        System.out.println("StackTrace = " + obj.getStackTrace());

    } 

You can also set up an email to be sent in case the schedule fails. The following code illustrates how:

	ScheduleObject sObj = new ScheduleObject("Report_FailedEmail", ScheduleObject.REPORTOBJ);
	sObj.setFileLocation("Account.rpt");
	sObj.setReportType(IExportConstants.DHTML);
	sObj.setTaskOption(ScheduleObject.ONE_TIME);
	String exportLocation = sObj.pickDefaultExportLocation();
	Calendar calendar = Calendar.getInstance();
	calendar.add(Calendar.MINUTE, 10);
	sObj.setStartDate(calendar.getTimeInMillis());
	sObj.setSendEmail(true);
	sObj.setFromAddress("user1@quadbase.com");
	sObj.setSubject("Report Email Account Report");
	sObj.setBodyText("Scheduled Export of Account from user1");
	sObj.setEmailType(ScheduleObject.ASATTACHMENT);
	sObj.setToAddresses(new String[] { "user2@quadbase.com", "user3@quadbase.com" });
	sObj.setFailSubject("Report_FailedEmail schedule failed");
	sObj.setFailBodyText("Cannot send Account report from Report_FailedEmail");
	sObj.setFailToAddress("user1@quadbase.com");

	ScheduleModifier.addScheduleTask(sObj);

2.4.8. ICallBackScheduler Interface

You can also create additional code that performs certain actions based on the Scheduler performing a job (whether successful or not). This code implements the ICallBackScheduler interface and specifies what to do when a job has been successfully completed or not.

Please note that after creating the code you need to change EspressManager.bat/.sh (in the <EspressReport-installation-directory> directory) and add the following:

-SchedulerCallBackClass:<name of class file implementing ICallBackScheduler>

The following code shows how to use the ICallBackScheduler interface:

    import quadbase.scheduler.ICallBackScheduler;
    import quadbase.scheduler.ScheduleObject;

    public class CallBackScheduler implements ICallBackScheduler {

        public CallBackScheduler() {};

        public void exportSucceeded(ScheduleObject obj, String path) {

            System.out.println(obj.getName() + "- EXPORT SUCCEEDED");
            System.out.println("PATH = " + path);

        }

        public void exportFailed(ScheduleObject obj, String path, Throwable e) {

            System.out.println(obj.getName() + "- EXPORT FAILED");
            System.out.println("PATH = " + path);
            System.out.println("ERROR = " + e.toString());

        }

    }

To use the above code, add the following to EspressManager.bat/.sh:

-SchedulerCallbackClass:CallBackScheduler

Make sure that CallBackScheduler is in the CLASSPATH. The code will then print messages at the end of each job saying whether the job was run successfully or not.

2.4.9. Scheduler Listener

EspressReport allows scheduled reports to be modified via custom code using server extensions. These custom classes will intercept reports before they are exported and allow users to implement additional business logic to the scheduling process.

To use the Scheduler Listener, you will have to write your own custom class that implements SchedulerListener. Given below is an example:

    import quadbase.reportdesigner.ReportAPI.QbReport;
    import quadbase.scheduler.ScheduleObject;
    import quadbase.ext.SchedulerListener;

    public class MyEspressReportSchedulerListener implements SchedulerListener {

        public QbReport modifyBeforeExport(QbReport report, ScheduleObject so, String exportPath) {

            System.out.println("modifyBeforeExport(" + report + "," + so + "," + exportPath + ")");
            return report;

        }

    }

The above example prints a simple System.out.println statement before exporting either the report.

To use any custom class implementing SchedulerListener, you will have to implement another custom class implementing DefaultListenerManager. For example:

    import quadbase.ext.DefaultListenerManager;
    import quadbase.ext.SchedulerListener;

    public class MyEspressReportListenerManager extends DefaultListenerManager {

        public MyEspressReportListenerManager() {}

        public EspressReportSchedulerListener getSchedulerListener() {

            return new MyEspressReportSchedulerListener();

        }

    }

To use the above code, add the following to EspressManager.bat/.sh:

-ListenerManagerClass:MyEspressReportListenerManager

Make sure that both MyEspressReportListenerManager and MyEspressReportSchedulerListener are in the CLASSPATH.

2.4.10. Report Bursting

You can also use the Report Bursting feature in the Scheduler by using the API. Note that the report must use a database as the datasource and must be grouped in order for the report to be bursted.

Given below is an example on how to use report bursting:

ScheduleObject sObj = new ScheduleObject("ReportBursting_All", ScheduleObject.REPORTOBJ);
sObj.setFileLocation("InventoryInformation.rpt");
String exportLoc = sObj.pickDefaultExportLocation();
sObj.setReportType(IExportConstants.DHTML);
sObj.setTaskOption(ScheduleObject.ONE_TIME);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 5);
sObj.setStartDate(calendar.getTimeInMillis());
sObj.setSendEmail(true);
sObj.setFromAddress("user1@quadbase.com");
sObj.setSubject("Test report bursting");
sObj.setBodyText("You should find a report of one group");
sObj.setEmailType(ScheduleObject.ASATTACHMENT);
// export every group as a single report and email using report column values
sObj.setBurstReport(ScheduleObject.ALLBURSTING);
// get email address from 16th column in template
sObj.setEmailColumnIndex(16);

ScheduleModifier.addScheduleTask(sObj);

The above code bursts all groups and sends out an email using a report column as the source for the addresses.

Note that in the above code, column 16 does not exist in the template. The method is provided in the example to show how to pass in the email column for bursting.

2.4.11. Javadoc

Javadoc for the entire API is provided along with EspressReport. It is located at Quadbase website.

2.4.12. Summary

EspressReport API provides an easy-to-use and powerful API to query the Scheduler interface, as well as to add and remove schedules. You can also write code to perform your own action when a job has been completed successfully (or not) in Scheduler.

Please note that the API requires a JDK 1.8 or above. The EspressReport API has been tested on Windows, Solaris, Linux, AIX, and HP platforms.