package ru.vyarus.dropwizard.guice.module.installer.internal; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.inject.Injector; import io.dropwizard.Application; import io.dropwizard.cli.Command; import io.dropwizard.cli.EnvironmentCommand; import io.dropwizard.setup.Bootstrap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ru.vyarus.dropwizard.guice.module.context.ConfigurationContext; import ru.vyarus.dropwizard.guice.module.context.stat.StatsTracker; import ru.vyarus.dropwizard.guice.module.installer.scanner.ClassVisitor; import ru.vyarus.dropwizard.guice.module.installer.scanner.ClasspathScanner; import ru.vyarus.dropwizard.guice.module.installer.util.FeatureUtils; import java.util.List; import static ru.vyarus.dropwizard.guice.module.context.stat.Stat.CommandTime; /** * Provides support for commands injection support and classpath scanning resolution of commands. * * @author Vyacheslav Rusakov * @since 01.09.2014 */ public final class CommandSupport { private static final Logger LOGGER = LoggerFactory.getLogger(CommandSupport.class); private CommandSupport() { } /** * Scans classpath to find commands and register them. * Commands are instantiated using default constructor, but {@link io.dropwizard.cli.EnvironmentCommand} * must have constructor with {@link io.dropwizard.Application} argument. * * @param bootstrap bootstrap object * @param scanner configured scanner instance * @param context configuration context */ public static void registerCommands(final Bootstrap bootstrap, final ClasspathScanner scanner, final ConfigurationContext context) { final Stopwatch timer = context.stat().timer(CommandTime); final CommandClassVisitor visitor = new CommandClassVisitor(bootstrap); scanner.scan(visitor); context.registerCommands(visitor.getCommands()); timer.stop(); } /** * Inject dependencies into all registered environment commands. (only field and setter injection could be used) * There is no need to process other commands, because only environment commands will run bundles and so will * start the injector. * * @param commands registered commands * @param injector guice injector object * @param tracker stats tracker */ public static void initCommands(final List<Command> commands, final Injector injector, final StatsTracker tracker) { final Stopwatch timer = tracker.timer(CommandTime); if (commands != null) { for (Command cmd : commands) { if (cmd instanceof EnvironmentCommand) { injector.injectMembers(cmd); } } } timer.stop(); } /** * Search catch all {@link Command} derived classes. * Instantiate command with default constructor and {@link io.dropwizard.cli.EnvironmentCommand} * using constructor with {@link io.dropwizard.Application} argument. */ private static class CommandClassVisitor implements ClassVisitor { private final Bootstrap bootstrap; private final List<Class<Command>> commands = Lists.newArrayList(); CommandClassVisitor(final Bootstrap bootstrap) { this.bootstrap = bootstrap; } @Override @SuppressWarnings("unchecked") public void visit(final Class<?> type) { if (FeatureUtils.is(type, Command.class)) { try { final Command cmd; if (EnvironmentCommand.class.isAssignableFrom(type)) { cmd = (Command) type.getConstructor(Application.class) .newInstance(bootstrap.getApplication()); } else { cmd = (Command) type.newInstance(); } commands.add((Class<Command>) type); bootstrap.addCommand(cmd); LOGGER.debug("Command registered: {}", type.getSimpleName()); } catch (Exception e) { throw new IllegalStateException("Failed to instantiate command: " + type.getSimpleName(), e); } } } /** * @return list of installed commands or empty list */ public List<Class<Command>> getCommands() { return commands; } } }