I just tested this, there is a caveat, but it does work.
First, some code:
global class LongRunningBatch implements Database.Stateful, Database.Batchable<Account>, Iterable<Account>, Iterator<Account> {
Integer subcounter = 0, counter = 0, max = 0;
global LongRunningBatch(Integer total) {
max = total;
}
global Iterable<Account> start(Database.BatchableContext bc) {
return this;
}
global Iterator<Account> iterator() {
return this;
}
global Boolean hasNext() {
return counter < max;
}
global Account next() {
return new Account(Name=String.valueOf(++counter));
}
global void execute(Database.BatchableContext bc, Account[] records) {
if(Settings__c.getInstance('batch')!=null && Settings__c.getInstance('batch').Value__c == 'STOP') {
// counter = max;
// Above didn't stop my batch. *sigh*
// But at least it returns early.
return;
}
SavePoint sp = Database.setSavePoint();
try {
insert records;
delete records;
} catch(exception e) {
}
Database.rollback(sp);
subcounter = counter;
}
global void finish(Database.BatchableContext bc) {
Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
message.setSubject('Finished processing');
message.setPlainTextBody('Finished processing with '+String.valueof(subcounter)+' records processed, '+string.valueof(counter)+' records queued.');
message.setTargetObjectId(UserInfo.getUserId());
Messaging.sendEmail(new Messaging.SingleEmailMessage[] { message });
}
}
Let me explain the original rationale:
I thought the system scheduler code did something like this:
SObject[] records = new SObject[0];
while(iterator.hasNext()) {
records.add(iterator.next());
if(records.size()==scope) {
execute(records);
records.clear();
}
}
Unfortunately, it does not. It does something more like this:
SObject[] allrecords = new SObject[0], thisBatch = new SObject[0];
while(iterator.hasNext()) {
allRecords.add(iterator.next());
}
while(!allRecords.isEmpty()) {
thisBatch.add(allrecords.remove(0));
if(thisbatch.size()==scope) {
execute(context, thisbatch);
thisbatch.clear();
}
}
This is obviously an over-simplification, but for the purposes of what's going on here, it defines the logic we're working with. The design means that there's a practical upper limit to how many items you can process with an Iterable class (I believe it caps out around 500,000 for simple iterators).
At any rate, given this in mind, I kicked off the code with the following statement:
LongRunningBatch b = new LongRunningBatch(250000);
Database.executeBatch(b);
Here's a profiling log without the setting in place:
08:57:24:000 CUMULATIVE_PROFILING External entry point: global void execute(Database.BatchableContext, LIST<Account>): executed 1 time in 1302 ms
Here's a profiling log with the setting in place:
08:57:26:000 CUMULATIVE_PROFILING External entry point: global void execute(Database.BatchableContext, LIST<Account>): executed 1 time in 14 ms
The logs also indicate that no DML actions occurred while the batch setting was configured. In my Developer Console window, you can see it take place immediately:
Batch Apex 06:57:24 ...
/setup/ui/editCustomSettingsData 06:57:25 ...
Batch Apex 06:56:26 ...
Those debug lines from above are the from the log files immediately before and after the change in custom settings. The effect was visible in a single batch.
That being said, the batch can't fully abort itself early, but in the event of a long running batch, it can reduce its execution time significantly. You should note that there appears to be a significant delay between batch executions (because it is a shared resource), so the batch may only finish marginally faster if it is processing a large number of small transactions, as opposed to large transactions (e.g. if your execution time is less than 10k lines of code and/or 2 seconds of execution, there's probably no reason to implement a kill-switch like this).
Best Answer
@Ashish: Alternately, you should be able to create a Visualforce page which will emit JSON data. Within page you can place all labels and merge fields to emit label values (in logged-in user's language). This can also avoid creating a different custom setting/ object to store values and fully leverage usage of Custom labels for translated values.
Something like:- VF Page:-
Accessing this web page via mobile app (as a REST API) should not be an issue.