/** * */ package vnet.sms.common.shell.springshellsshd.internal; import static org.apache.commons.lang.Validate.notNull; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.concurrent.atomic.AtomicReference; import org.apache.sshd.server.Command; import org.apache.sshd.server.Environment; import org.apache.sshd.server.ExitCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import vnet.sms.common.shell.springshell.JLineShellComponent; import vnet.sms.common.shell.springshell.JLineShellComponentFactory; import vnet.sms.common.shell.springshell.event.ShellStatus; import vnet.sms.common.shell.springshell.event.ShellStatusListener; /** * @author obergner * */ public class JLineShellLauncherCommand implements Command { static final class Factory implements org.apache.sshd.common.Factory<Command> { private final JLineShellComponentFactory jlineShellFactory; Factory(final JLineShellComponentFactory jlineShellFactory) { this.jlineShellFactory = jlineShellFactory; } @Override public Command create() { return new JLineShellLauncherCommand(this.jlineShellFactory); } } private final Logger log = LoggerFactory .getLogger(getClass()); private final JLineShellComponentFactory jlineShellFactory; private final AtomicReference<JLineShellComponent> launchedShell = new AtomicReference<JLineShellComponent>(); private InputStream input; private OutputStream output; private ExitCallback exitCallback; /** * @param jlineShellFactory */ JLineShellLauncherCommand(final JLineShellComponentFactory jlineShellFactory) { notNull(jlineShellFactory, "Argument 'jlineShellFactory' must not be null"); this.jlineShellFactory = jlineShellFactory; } /** * @see org.apache.sshd.server.Command#setInputStream(java.io.InputStream) */ @Override public void setInputStream(final InputStream in) { this.input = in; } /** * @see org.apache.sshd.server.Command#setOutputStream(java.io.OutputStream) */ @Override public void setOutputStream(final OutputStream out) { this.output = new LfToCrLfFilterOutputStream(out); } private final class LfToCrLfFilterOutputStream extends FilterOutputStream { private boolean lastWasCr; public LfToCrLfFilterOutputStream(final OutputStream out) { super(out); } @Override public void write(final int b) throws IOException { if (!this.lastWasCr && (b == '\n')) { this.out.write('\r'); this.out.write('\n'); } else { this.out.write(b); } this.lastWasCr = b == '\r'; } } /** * @see org.apache.sshd.server.Command#setErrorStream(java.io.OutputStream) */ @Override public void setErrorStream(final OutputStream err) { // Ignored for now } /** * @see org.apache.sshd.server.Command#setExitCallback(org.apache.sshd.server.ExitCallback) */ @Override public void setExitCallback(final ExitCallback callback) { this.exitCallback = callback; } /** * @see org.apache.sshd.server.Command#start(org.apache.sshd.server.Environment) */ @Override public void start(final Environment env) throws IOException { this.log.info("Launching new Spring Shell ..."); if (this.launchedShell.compareAndSet(null, this.jlineShellFactory.newShell(this.input, this.output))) { this.launchedShell.get().addShellStatusListener( this.new ShellCloseListener()); this.launchedShell.get().start(); this.log.info("New Spring Shell {} launched", this.launchedShell.get()); } else { this.log.warn( "Spring Shell {} has already been launched - ignoring attempt to launch it again", this.launchedShell.get()); } } private final class ShellCloseListener implements ShellStatusListener { @Override public void onShellStatusChange(final ShellStatus oldStatus, final ShellStatus newStatus) { if ((oldStatus.getStatus() != ShellStatus.Status.SHUTTING_DOWN) && (newStatus.getStatus() == ShellStatus.Status.SHUTTING_DOWN)) { JLineShellLauncherCommand.this.exitCallback.onExit(0); JLineShellLauncherCommand.this.log .info("Spring Shell has been terminated"); } } } /** * @see org.apache.sshd.server.Command#destroy() */ @Override public void destroy() { if (this.launchedShell.get().getShellStatus().getStatus() != ShellStatus.Status.SHUTTING_DOWN) { this.launchedShell.get().stop(); } this.exitCallback.onExit(0); this.log.info("Spring Shell has been terminated"); } }