/*
* Strongback
* Copyright 2015, Strongback and individual contributors by the @authors tag.
* See the COPYRIGHT.txt in the distribution for a full listing of individual
* contributors.
*
* Licensed under the MIT License; you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.strongback.command;
import org.strongback.Executable;
import org.strongback.Logger;
import org.strongback.Strongback;
/**
* The scheduler used to execute {@link Command}s.
*
* @see Strongback#submit(Command)
*/
public class Scheduler implements Executable {
private static CommandListener NO_OP = (command, state) -> {
};
public static interface CommandListener {
public void record(Command command, CommandState state);
public static CommandListener noOp() {
return NO_OP;
}
}
private final Commands commands = new Commands();
private final CommandRunner.Context context;
public Scheduler(Logger logger) {
this(logger, null);
}
public Scheduler(Logger logger, CommandListener listener) {
Logger log = logger != null ? logger : Logger.noOp();
CommandListener commandListener = listener != null ? listener : CommandListener.noOp();
this.context = CommandRunner.Context.with(commandListener, log);
}
/**
* Kill all commands.
*/
public void killAll() {
commands.killAll();
}
/**
* Schedule a {@link Command} to be added to the {@link Scheduler}.
*
* @param command the {@link Command} to be added
*/
public void submit(Command command) {
if (command != null) {
if (command instanceof CommandGroup) {
command = ((CommandGroup) command).getRoot();
}
CommandRunner runner = buildRunner(command, null);
commands.add(runner);
}
}
private CommandRunner buildRunner(Command command, CommandRunner last) {
if (command instanceof CommandGroup) {
CommandGroup cg = (CommandGroup) command;
Command[] commands = cg.getCommands();
switch (cg.getType()) {
case SEQUENTIAL:
for (int i = commands.length - 1; i >= 0; i--) {
last = buildRunner(commands[i], last);
}
return last;
case PARRALLEL:
CommandRunner[] crs = new CommandRunner[commands.length];
for (int i = 0; i < crs.length; i++) {
crs[i] = buildRunner(commands[i], null);
}
return new CommandRunner(context, last, crs);
case FORK:
assert commands.length == 1;
return new CommandRunner(context, last, new CommandRunner(context, buildRunner(commands[0], null)));
}
// This line should never happen, the switch will throw an exception first
throw new IllegalStateException("Unexpected command type: " + cg.getType());
}
return new CommandRunner(context, last, command);
}
/**
* Steps once though all of the {@link Command}s in the {@link Scheduler}.
*
* @param timeInMillis the current system time in milliseconds
*/
@Override
public void execute(long timeInMillis) {
commands.step(timeInMillis);
}
/**
* Tests if there are any {@link Command}s currently executing or pending execution.
*
* @return {@code true} if there are no {@link Command}s executing or pending; {@code false} otherwise
*/
public boolean isEmpty() {
return commands.isEmpty();
}
}