Among the many things that sets DOTS apart from the Domino Agent Manager is the opportunity to develop tasklets that can react to console commands. There might be many reasons for wanting that: it could be useful to refresh the currently cached tasklet configuration or running a specific task extemporaneously and even enable debugging for that run and that run only.
Giving a tasklet such capabilities is actually quite simple:
Tasklet commands definition
Let’s use the enum
approach to store which commands we will make available for the tasklet.
public class WatchmailTask extends AbstractServerTask {
protected enum Command {
reload,
program {
@Override
public String toHelpString() {
return "program= [debug=]";
}
},
debug {
@Override
public Object fromString(String s) {
return Boolean.valueOf(s);
}
@Override
public String toHelpString() {
return null;
}
},
help;
public Object fromString(String s) {
return s;
}
public String toHelpString() {
return this.toString();
}
public static String printHelp() {
StringBuffer buffer = new StringBuffer();
buffer.append("---Domino OSGi Watchmail Tasklet Container Commands---\n");
for (Command c : values()) {
if (c.toHelpString() != null) {
buffer.append(c.toHelpString() + "\n");
}
}
return buffer.toString();
}
public static Map<Command, Object> fromArguments(String[] args) throws IllegalArgumentException {
Map<Command, Object> m = new EnumMap<Command, Object>(Command.class);
for (String arg : args) {
Matcher matcher = ARGUMENT_PATTERN.matcher(arg);
if (matcher.find()) {
Command c = valueOf(matcher.group(1));
m.put(c, matcher.groupCount() > 1 ? c.fromString(matcher.group(2)) : null);
}
}
return m;
}
private static final Pattern ARGUMENT_PATTERN = Pattern.compile("-{0,2}(\\w*)=?(.*)");
}
...
Granted, the above code might look like lots of code lines. Yes, but such structured enum
will make it for easy management later on.
To expound on that, we have 4 defined commands (or arguments): reload
, program
, debug
(actually this is treated as additional parameter and not as a directly invokable command) and help
.
Each enum comes with 2 ovverridable methods: fromString
and toHelpString
. fromString
can evaluate the passed argument value to a more specialized class than the default String
(eg. debug=true to Boolean
). toHelpString
works hand in hand with the static method Command.printHelp
in order to provide some insight regarding which commands are valid and what mandatory and optional additional parameters they take if any. At the end of the article I will show some console examples.
Command.fromArguments
is the static helper method that will process any given argument we will pass to the tasklet when invoking it from the console. Argument syntax is determined by Pattern ARGUMENT_PATTERN
. In can take command
or command=value
. Prefix dashes are contemplated (eg. -command
, --command
).
Tasklet command invocation method
Once the commands are defined we just have to provide a tasklet dedicated method that we will use to process the commands.
public class WatchmailTask extends AbstractServerTask {
...
@Run(id = "invoke")
public void invoke(String[] args, IProgressMonitor monitor) {
Map<Command, Object> commands = null;
try {
commands = Command.fromArguments(args);
} catch (IllegalArgumentException e) {
logException(e);
logMessage(Command.printHelp());
return;
}
if (commands.containsKey(Command.reload)) { // RELOAD
if (Preferences.INSTANCE.isRunning()) {
logMessage("The task won't run because an instance of it is already running.");
return;
}
runOnStart(monitor);
logMessage("Configuration reloaded");
} else if (commands.containsKey(Command.program)) { // PROGRAM
if (Preferences.INSTANCE.isRunning()) {
logMessage("The task won't run because an instance of it is already running.");
return;
}
String programId = (String) commands.get(Command.program);
if (programId == null) {
logMessage(Command.printHelp());
return;
}
boolean debug = false;
if (commands.containsKey(Command.debug)) {
debug = (Boolean) commands.get(Command.debug);
}
if (debug) {
logMessage("[DEBUG] program ID " + programId);
}
// Your logic here
logMessage("Tasklet execution completed.");
} else { // HELP OR UNRECOGNIZED COMMAND
logMessage(Command.printHelp());
}
}
...
By means of the annotation @Run(id = "invoke")
we make the invoke
method identifiable and invokable by means of the word invoke.
So, what does the invocation syntax look like now?
Assuming our plugin task id is named WatchmailTask
(see plugin.xml) we can type the following commands and expect the following outcomes:
tell dots run WatchmailTask.invoke help
[0CC8:0018-08F0] 06/27/2016 11:04:23 AM [DOTS] (WatchmailTask.invoke) ---Domino OSGi Watchmail Tasklet Container Commands---
[0CC8:0018-08F0] 06/27/2016 11:04:23 AM [DOTS] (WatchmailTask.invoke) reload
[0CC8:0018-08F0] 06/27/2016 11:04:23 AM [DOTS] (WatchmailTask.invoke) program= [debug=]
[0CC8:0018-08F0] 06/27/2016 11:04:23 AM [DOTS] (WatchmailTask.invoke) help
tell dots run WatchmailTask.invoke reload
[0CC8:0019-0EBC] 06/27/2016 11:40:42 AM [DOTS] (WatchmailTask.invoke) Configuration reloaded
tell dots run WatchmailTask.invoke program=dummy
[0CC8:001A-08F0] 06/27/2016 11:41:21 AM [DOTS] (WatchmailTask.invoke) Tasklet execution completed.
tell dots run WatchmailTask.invoke program=dummy debug=true
[0CC8:001B-08F0] 06/27/2016 11:41:53 AM [DOTS] (WatchmailTask.invoke) Debugging for program ID dummy
[0CC8:001B-08F0] 06/27/2016 11:41:53 AM [DOTS] (WatchmailTask.invoke) Tasklet execution completed.