I actually just tried this myself a few days ago, because we had a similar issue-- we wanted to be able to run batches to do things like mass updating records using particular logic that we could define from execute anonymous calls. As it went, we also ran into Internal Server Errors trying to run the code. The problem stems from the fact that classes defined in an execute anonymous call aren't persisted to the database-- their metadata literally disappears before the batch class has a chance to run. In my case, I came up with a limited workaround to perform the two most usual actions: touch a group of records to allow triggers/workflows to run, and delete a set of records that match certain criteria.
Ultimately, that led me to writing the following class:
public class Batch implements Database.Batchable<Object> {
public interface Generator {
Object iterable(Database.BatchableContext context);
}
public interface Action {
void execute(Database.BatchableContext context, Object[] scope);
}
public interface Finish {
void finish(Database.BatchableContext context);
}
Generator gen;
Action act;
Finish fin;
public Batch(Generator gen, Action act, Finish fin, Integer batchSize) {
this.gen = gen;
this.act = act;
this.fin = fin;
Database.executeBatch(this, batchSize);
}
public Batch(Generator gen, Action act, Finish fin) {
this.gen = gen;
this.act = act;
this.fin = fin;
Database.executeBatch(this);
}
public Iterable<Object> start(Database.BatchableContext context) {
return (Iterable<Object>)gen.iterable(context);
}
public void execute(Database.BatchableContext context, Object[] scope) {
act.execute(context, scope);
}
public void finish(Database.BatchableContext context) {
fin.finish(context);
}
}
Then, I had separate classes for the various things I wanted to do. Here's my Actions class:
public class Actions {
public class UpdateRecords implements Batch.Action {
public void execute(Database.BatchableContext context, Object[] scope) {
Database.update((SObject[])scope, false);
}
}
public class DeleteRecords implements Batch.Action {
public void execute(Database.BatchableContext context, Object[] scope) {
Database.delete((SObject[])scope, false);
}
}
}
Similarly, I wrote a default Generator:
public class Generators {
public class FromQuery implements Database.Generator {
String query;
public FromQuery(String query) {
this.query = query;
}
Object iterable(Database.BatchableContext context) {
return Database.getQueryLocator(query);
}
}
}
The Finish methods are uninteresting, but were primarily a concept for sending an email to an administrator, doing other cleanup activity, chaining to another batch, and so on.
There's still some cleanup going on in these classes as they evolve, but I thought I'd share some stuff I've done so that it might help you out.
So, a typical use might look like this:
new Batch(new Generator.FromQuery('SELECT Id FROM Opportunity'), new Action.UpdateRecords(), new Finish.DoNothing());
A single statement to execute, and exceptionally customizable. I plan on placing this into a managed package to share, but do feel free to borrow the design for your own internal use.