Skip to Content
Technical Articles

How to Modify a Recurring Scheduled Instance of a Crystal Report

Warning

SAP does not support modifying a recurring instance.  The only officially supported way of modifying a recurring instance is to recreate it.

 

How it Works

There are certain properties that can be modified on a recurring instance regardless of what format it is scheduled for.  For example, the ownership, the start and end dates, and the number of retries can be changed without needing to worry about the type of object (It would even work on scheduled webi reports).

However, other properties can only be changed if the instance is scheduled to native crystal report format.  For example, the database logon credentials or any destination option such the Unmanaged disk path or SMTP destination email.  The reason these cannot be changed is because the SDK methods to do so are only available to the Crystal Report Plugin type.

To get around this, the sample tricks the Enterprise server into thinking that the instance is scheduled to native crystal report format by changing the SI_KIND property before modifying it, and changing it back afterwards.

Note: As mentioned above – this is not an officially supported workflow.

How to Use It

  1. Copy the code pasted at the bottom of this blog into a jsp page and copy that jsp page into the folder C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\AdminTools
  2. Copy the following jar files from the folder:
    C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\java\lib

    to the folder

    C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\AdminTools\WEB-INF\lib

    SL_plugins.jar
    cereports.jar
    CrystalReportsSDK.jar – This one isn’t needed to run the sample – but copying the other two seems to break query builder unless you include this one.

  3. Restart Tomcat (Whenever you add a file to the lib directory you must restart the app server. Adding or modifying a jsp file as per step 1 does not require a restart)
  4. Edit the code and change the values in the following sections:
    String reportQuery = "Select * FROM CI_INFOOBJECTS where (SI_INSTANCE=1 or SI_RECURRING = 1) and SI_SCHEDULEINFO.SI_PROGRESS = 1";​

    Change this query to return back the reports you want to modify.  I recommend testing the query in query builder (http://myServer:8080/AdminTools) first.

    On line 164 change the logfile path to the location you want the log file to be written to.

    FSout = new FileOutputStream("C:\\Program Files (x86)\\SAP BusinessObjects\\tomcat\\webapps\\AdminTools\\TestOutput.txt", true);​

    Because this is java, all slashes must be double slashes.

    // User Credentials
    String username = "Administrator";
    String password = "Password1";
    String cmsname  = "<myserver>";
    String authType = "secEnterprise";

    The logon credentials for the Enterprise server.  It is recommended to use the Administrator user for this.

  5. Uncomment (remove the // in front) the calls to the functions you want to use.
    // Update the database username and password.
    //changeReportDBLogons(tmpReport, "myUsername", "myPassword");
    			
    // Change how many retries are allowed
    //changeReportRetries(tmpReport, 5);
    			
    // Change the file IO username and password for unmanaged disk location
    //changeReportUnmanagedDiskLogonCredentials(tmpReport, infoStore, "myOSUsername", "myOSPassword");
    
    // Change the time the next instance will run at
    //changeScheduleStartTime(tmpReport, new java.util.Date("7/13/25 4:15:00 PM"));
    			
    // Change the end time of the scheduled instance (The last time it will be allowed to run - defaults to 10 years in the future)
    //changeScheduleEndTime(tmpReport, new java.util.Date("7/13/26 4:15:00 PM"));
    			
    // Change the from field for a schedule to SMTP instance
    // changeScheduleFromFieldForScheduleSMTP(tmpReport, infoStore, "default@myserver.com");​

    There are 6 functions in this sample.

    changeReportDBLogons – Change the report database username and password

    changeReportRetries – Change how many retries to do

    changeReportUnmanagedDiskLogonCredentials  – If the report is scheduled to unmanaged disk – then you can change the OS logon credentials used

    changeScheduleStartTime – Change the time the next instance will run

    changeScheduleEndTime – Change the end time of the instance.  That is – the last time it will be allowed to run. This defaults to 10 years in the future.

    changeScheduleFromFieldForScheduleSMTP – If the report is scheduled to SMTP – then change the FROM field for the SMTP portion.

  6. Open up a browser and go to http://myserver:8080/AdminTools/modifyrecurringinstance.jsp

 

The Code

<%@ page import = "com.crystaldecisions.sdk.exception.SDKException,
                   com.crystaldecisions.sdk.framework.*,
                   com.crystaldecisions.sdk.occa.infostore.*,
				   com.crystaldecisions.sdk.properties.*,
			       com.crystaldecisions.sdk.plugin.desktop.report.*,
				   com.crystaldecisions.sdk.plugin.desktop.common.*,
				   com.crystaldecisions.sdk.plugin.destination.diskunmanaged.*,
				   com.crystaldecisions.sdk.plugin.destination.smtp.*,
				   com.crystaldecisions.sdk.plugin.desktop.report.*,
				   java.io.*,
                   java.util.*"
				   
%><%

// ------------------------------
// How to use:
//
// Copy this jsp page into the folder C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\AdminTools
//
// Copy the following jar files from the folder: C:\Program Files (x86)\SAP BusinessObjects\SAP BusinessObjects Enterprise XI 4.0\java\lib 
// to the folder 
// C:\Program Files (x86)\SAP BusinessObjects\tomcat\webapps\AdminTools\WEB-INF\lib
//
// SL_plugins.jar
// cereports.jar
// CrystalReportsSDK.jar - This one isn't needed to run the sample - but copying the other two seems to break query builder unless you include this one.
//
//
// Modify the User Credentials Section to appropriate logon credentials for your system
// Modify the infostore query to return back the reports you want to modify.  I recommend testing the query in query builder first (http://myServer:8080/AdminTools)
// In the WriteToLog function - change the logging path to whatever desired path you want the log file to be written to.  The default is in the AdminTools folder referenced above
// Uncomment the functions for the properties you want to change.
// Run this by opening a new browser and going to http://myServer:8080/AdminTools/modifyrecurringinstance.jsp
// ------------------------------

// User Credentials
String username = "Administrator";
String password = "Password1";
String cmsname  = "localhost";
String authType = "secEnterprise";

// This is the query used to return back the report instances you want to modify.

// All pending instances  (Both recurring and non-recurring)
String reportQuery = "Select * FROM CI_INFOOBJECTS where (SI_INSTANCE=1 or SI_RECURRING = 1) and SI_SCHEDULEINFO.SI_PROGRESS = 1";

// All pending recurring instances.  Note that it does not have SI_KIND='CrystalReport' in it.  This is because if you schedule a crystal report to PDF then SI_KIND will be 'PDF'
//String reportQuery = "Select * FROM CI_INFOOBJECTS where SI_RECURRING =1 and SI_SCHEDULEINFO.SI_PROGRESS = 1";

// All pending recurring instances owned by administrator (ID 12)
//String reportQuery = "Select * FROM CI_INFOOBJECTS where SI_RECURRING =1 and SI_OWNERID=12 and SI_SCHEDULEINFO.SI_PROGRESS = 1";



IEnterpriseSession enterpriseSession  = null;
IInfoStore infoStore;
IInfoObjects boInfoObjects; 
  
enterpriseSession = CrystalEnterprise.getSessionMgr().logon(username, password, cmsname, authType);
infoStore = (IInfoStore)enterpriseSession.getService("", "InfoStore");

int max_id = 0;
int querysize = 0;
boolean isCrystalReport = false;
String curKind = "";
out.println("Starting </BR>");
writeToLog("Starting Sample");

for(;;) { 
	boInfoObjects = (IInfoObjects)infoStore.query(reportQuery + " AND SI_ID > " + max_id +  " ORDER BY SI_ID ASC");
	
	querysize = boInfoObjects.size();
	if( querysize == 0) 
		break;
		

	for(int i =0; i< querysize; i++) {
		IInfoObject boReport1 = (IInfoObject)boInfoObjects.get(i);
		
		isCrystalReport = true;
		out.println("Processing Report " + boReport1.getID() + " : " + boReport1.getTitle() + "</BR>");
		writeToLog("Starting to Process Report " + boReport1.getID() + " : " + boReport1.getTitle());
		// If the object is not of type crystal report (Scheduled to any format other than crystalreports) then we need to modify it.
		if (!boReport1.getKind().equals("CrystalReport")) {
			
			isCrystalReport = false;
			curKind = boReport1.getKind();
			
			// This line updates the properties
			//SI_KIND
			//SI_PROGID_MACHINE
			//SI_PROGID
			//SI_SPECIFIC_KIND
			//SI_SPECIFIC_PROGID

			writeToLog("Report " + boReport1.getID() + " : " + boReport1.getTitle() + " is of type " + curKind.toString() + " - casting to CrystalReport temporarily");
			boReport1.properties().setProperty("SI_KIND", "CrystalReport");
			infoStore.commit(boInfoObjects); 
				
			// At this point we need to requery for the same report - otherwise the rest of the code will not work.
			// This also involves resetting the for loop
			boInfoObjects = (IInfoObjects)infoStore.query(reportQuery + " AND SI_ID >= " + (boReport1.getID()) +  " ORDER BY SI_ID ASC");
			boReport1 = (IInfoObject)boInfoObjects.get(0);
			querysize = boInfoObjects.size();
			i = 0;
				
		}
			
		// Now cast it to a crystal report object
		IReport tmpReport = (IReport)boReport1;
		try {

			// Change the Report
			// Uncomment the functions for the properties you want to change for the report.
		
			// Update the database username and password.
			//changeReportDBLogons(tmpReport, "myUsername", "myPassword");
			
			// Change how many retries are allowed
			//changeReportRetries(tmpReport, 5);
			
			// Change the file IO username and password for unmanaged disk location
			//changeReportUnmanagedDiskLogonCredentials(tmpReport, infoStore, "myOSUsername", "myOSPassword");

			// Change the time the next instance will run at
			//changeScheduleStartTime(tmpReport, new java.util.Date("7/13/25 4:15:00 PM"));
			
			// Change the end time of the scheduled instance (The last time it will be allowed to run - defaults to 10 years in the future)
			//changeScheduleEndTime(tmpReport, new java.util.Date("7/13/26 4:15:00 PM"));
			
			// Change the from field for a schedule to SMTP instance
			// changeScheduleFromFieldForScheduleSMTP(tmpReport, infoStore, "default@myserver.com");
			
		} catch (Exception ex) {
			writeToLog("Error modifying report: " + ex.toString());
		} finally {
			//  If we changed the SI_KIND - make sure we change it back to the original type
			if (isCrystalReport == false) {
				writeToLog("Changing report " + boReport1.getID() + " : " + boReport1.getTitle() + " back to type " + curKind.toString());
				tmpReport.properties().setProperty("SI_KIND", curKind);
			}
		}
		
		writeToLog("Finished Processing Report " + boReport1.getID() + " : " + boReport1.getTitle());
		
		// Update the max id so the next infostore query doesn't include this report.
		max_id = boReport1.getID();  
	}
	// Save the changes
	infoStore.commit(boInfoObjects); 
}
writeToLog("Finished Sample </BR>");

out.println("Job Completed");
%>
<%!

// Helper Methods
public void writeToLog(String msg) {
	try {
		// Set up Logging File
		FileOutputStream FSout;
		PrintStream pStream; // declare a print stream object
		FSout = new FileOutputStream("C:\\Program Files (x86)\\SAP BusinessObjects\\tomcat\\webapps\\AdminTools\\TestOutput.txt", true);  // Append
		pStream = new PrintStream(FSout); 
		pStream.println(msg);
		pStream.close();
	} catch (IOException e) {
		//error writing to log
    }
}

	// This function sets all database logons to the same username and password.  If you want to customize it for each logon, you will need to modify this function
	public void changeReportDBLogons(IReport myReport, String dbUsername, String dbPassword) {
		try {
			writeToLog("Changing Database Logon for report ID " + myReport.getID() + " : " + myReport.getTitle());
			ISDKList dbLogons = myReport.getReportLogons();
			for (int k=0; k<dbLogons.size(); k++) {
				IReportLogon dbLogon = (IReportLogon)dbLogons.get(k);
				dbLogon.setUserName(dbUsername);
				dbLogon.setPassword(dbPassword);
			}
		} catch (Exception ex) {
			writeToLog("Error changing report database logons: " + ex.toString());
		}
	}
	
	public void changeReportRetries(IReport myReport, int numRetries) {
		try {
			writeToLog("Changing retries for report ID " + myReport.getID() + " : " + myReport.getTitle());
			myReport.getSchedulingInfo().setRetriesAllowed(numRetries);
		} catch (Exception ex) {
			writeToLog("Error changing report retries: " + ex.toString());
		}
	}
	
	// This function assumes that the report has only been scheduled to one destination - in this case unmanaged disk
	public void changeReportUnmanagedDiskLogonCredentials(IReport myReport, IInfoStore boInfoStore, String osUsername, String osPassword) {
		try {
			writeToLog("Changing Unmanaged Username and Password for report ID " + myReport.getID() + " : " + myReport.getTitle());
			ISchedulingInfo boSchedulingInfo = myReport.getSchedulingInfo();
			IDestinations boDestinations = boSchedulingInfo.getDestinations();
			IDestination boDestination = (IDestination)boDestinations.get(0);
			IDestinationPlugin destinationPlugin = (IDestinationPlugin)boInfoStore.query("SELECT TOP 1 * FROM CI_SYSTEMOBJECTS WHERE SI_NAME='CrystalEnterprise.DiskUnmanaged'").get(0);
			boDestination.copyToPlugin(destinationPlugin);
			IDiskUnmanagedOptions diskUnmanagedOptions = (IDiskUnmanagedOptions) destinationPlugin.getScheduleOptions();
			diskUnmanagedOptions.setUserName(osUsername);
			diskUnmanagedOptions.setPassword(osPassword);
			boDestination.setFromPlugin(destinationPlugin);
		} catch (Exception ex) {
			writeToLog("Error changing Unmanaged Disk Logon Credentials: " + ex.toString() );
		}
	}

	public void changeScheduleStartTime(IReport myReport, java.util.Date newDate) {
		try {
			writeToLog("Changing schedule Start Time " + myReport.getID() + " : " + myReport.getTitle());
			IProperties tmpProps = myReport.getSchedulingInfo().properties();
			tmpProps.setProperty("SI_STARTTIME", newDate);
		} catch (Exception ex) {
			writeToLog("Error changing schedule Start Time: " + ex.toString());
		}
	}

	public void changeScheduleEndTime(IReport myReport, java.util.Date newDate) {
		try {
			writeToLog("Changing schedule End Time " + myReport.getID() + " : " + myReport.getTitle());
			IProperties tmpProps = myReport.getSchedulingInfo().properties();
			tmpProps.setProperty("SI_ENDTIME", newDate);
		} catch (Exception ex) {
			writeToLog("Error changing schedule End Time: " + ex.toString());
		}
	}
	// This function assumes that the report has only been scheduled to one destination - in this case SMTP
	public void changeScheduleFromFieldForScheduleSMTP(IReport myReport, IInfoStore boInfoStore, String newFrom) {
		try {
			writeToLog("Changing SMTP Schedule From value for report ID " + myReport.getID() + " : " + myReport.getTitle());
			ISchedulingInfo boSchedulingInfo = myReport.getSchedulingInfo();
			IDestinations boDestinations = boSchedulingInfo.getDestinations();
			IDestination boDestination = (IDestination)boDestinations.get(0);
			IDestinationPlugin destinationPlugin = (IDestinationPlugin)boInfoStore.query("SELECT TOP 1 * FROM CI_SYSTEMOBJECTS WHERE SI_NAME='CrystalEnterprise.Smtp'").get(0);
			boDestination.copyToPlugin(destinationPlugin);
			ISMTPOptions smtpOptions = (ISMTPOptions) destinationPlugin.getScheduleOptions();
			smtpOptions.setSenderAddress(newFrom);
			boDestination.setFromPlugin(destinationPlugin);
		} catch (Exception ex) {
			writeToLog("Error changing SMTP Schedule From value: " + ex.toString() );
		}
	}
	
%>
Be the first to leave a comment
You must be Logged on to comment or reply to a post.