All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.ext.shell.impl.ShellImpl Maven / Gradle / Ivy

There is a newer version: 5.0.0.CR1
Show newest version
/*
 * Copyright 2015 Red Hat, Inc.
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  and Apache License v2.0 which accompanies this distribution.
 *
 *  The Eclipse Public License is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  The Apache License v2.0 is available at
 *  http://www.opensource.org/licenses/apache2.0.php
 *
 *  You may elect to redistribute this code under either of these licenses.
 *
 *
 * Copyright (c) 2015 The original author or authors
 * ------------------------------------------------------
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 *
 *     The Eclipse Public License is available at
 *     http://www.eclipse.org/legal/epl-v10.html
 *
 *     The Apache License v2.0 is available at
 *     http://www.opensource.org/licenses/apache2.0.php
 *
 * You may elect to redistribute this code under either of these licenses.
 *
 */

package io.vertx.ext.shell.impl;

import io.termd.core.util.Helper;
import io.vertx.core.Promise;
import io.vertx.ext.shell.Shell;
import io.vertx.ext.shell.session.Session;
import io.vertx.ext.shell.session.impl.SessionImpl;
import io.vertx.ext.shell.system.*;
import io.vertx.ext.shell.system.Process;
import io.vertx.ext.shell.system.impl.InternalCommandManager;
import io.vertx.ext.shell.system.impl.JobControllerImpl;
import io.vertx.ext.shell.cli.CliToken;
import io.vertx.ext.shell.term.Term;

import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;

/**
 * The shell session as seen from the shell server perspective.
 *
 * @author Julien Viet
 */
public class ShellImpl implements Shell {

  final String id;
  final Promise closedPromise;
  private final InternalCommandManager commandManager;
  private final Session session = new SessionImpl();
  private final JobControllerImpl jobController;
  private final Term term;
  private String welcome;
  private Function promptFunc = s -> "% ";

  public ShellImpl(Term term, InternalCommandManager commandManager) {

    session.put("vert.x-command-manager", commandManager);

    this.id = UUID.randomUUID().toString();
    this.jobController = new JobControllerImpl();
    this.commandManager = commandManager;
    this.closedPromise = Promise.promise();
    this.term = term;

    if (term != null) {
      term.setSession(session);
      jobController.foregroundUpdatedHandler(job -> {
        if (job == null) {
          readline();
        }
      });
    }
  }

  public JobController jobController() {
    return jobController;
  }

  @Override
  public synchronized Job createJob(List args) {
    StringBuilder line = new StringBuilder();
    args.stream().map(CliToken::raw).forEach(line::append);
    Process process = commandManager.createProcess(args);
    return jobController.createJob(process, line.toString());
  }

  @Override
  public Job createJob(String line) {
    return createJob(CliToken.tokenize(line));
  }

  @Override
  public Session session() {
    return session;
  }

  public long lastAccessedTime() {
    return term.lastAccessedTime();
  }

  public void setWelcome(String welcome) {
    this.welcome = welcome;
  }

  @Override
  public void setPrompt(Function prompt) {
    this.promptFunc = prompt;
  }

  public ShellImpl init() {

    term.interruptHandler(key -> jobController().foregroundJob().interrupt());

    term.suspendHandler(key -> {
      term.echo(Helper.fromCodePoints(new int[]{key, '\n'}));
      Job job = jobController.foregroundJob();
      term.echo(statusLine(job, ExecStatus.STOPPED) + "\n");
      job.suspend();
      return true;
    });

    term.closeHandler(v ->
        jobController.close(ar ->
            closedPromise.complete()
        )
    );
    if (welcome != null && welcome.length() > 0) {
      term.write(welcome);
    }
    return this;
  }

  private String statusLine(Job job, ExecStatus status) {
    StringBuilder sb = new StringBuilder("[").append(job.id()).append("]");
    if (findJob() == job) {
      sb.append("+");
    }
    sb.append(" ").append(Character.toUpperCase(status.name().charAt(0))).append(job.status().name().substring(1).toLowerCase());
    sb.append(" ").append(job.line());
    return sb.toString();
  }

  private Job findJob() {
    Job foregroundJob = jobController.foregroundJob();
    return jobController().jobs().
      stream().
      filter(job -> job != foregroundJob).min(Comparator.comparingLong(Job::lastStopped)).orElse(null);
  }

  public void readline() {
    String prompt;
    try {
      prompt = promptFunc.apply(session);
    } catch (Exception e) {
      prompt = "% ";
    }
    term.readline(prompt, line -> {

      if (line == null) {
        // EOF
        term.close();
        return;
      }

      List tokens = CliToken.tokenize(line);

      if (tokens.stream().filter(CliToken::isText).count() == 0) {
        // For now do like this
        ShellImpl.this.readline();
        return;
      }

      Optional first = tokens.stream().filter(CliToken::isText).findFirst();
      if (first.isPresent()) {
        String name = first.get().value();
        switch (name) {
          case "exit":
          case "logout":
            term.close();
            return;
          case "jobs":
            jobController.jobs().forEach(job -> {
              String statusLine = statusLine(job, job.status()) + "\n";
              term.write(statusLine);
            });
            readline();
            return;
          case "fg": {
            Job job = findJob();
            if (job == null) {
              term.write("no such job\n");
              readline();
            } else {
              if (job.status() == ExecStatus.STOPPED) {
                job.resume(true);
              } else {
                job.toForeground();
              }
            }
            return;
          }
          case "bg": {
            Job job = findJob();
            if (job == null) {
              term.write("no such job\n");
              readline();
            } else {
              if (job.status() == ExecStatus.STOPPED) {
                job.resume(false);
                term.echo(statusLine(job, ExecStatus.RUNNING) + "\n");
                readline();
              } else {
                term.write("job " + job.id() + " already in background\n");
                readline();
              }
            }
            return;
          }
        }
      }

      Job job;
      try {
        job = createJob(tokens);
      } catch (Exception e) {
        term.echo(e.getMessage() + "\n");
        readline();
        return;
      }
      job.setTty(term);
      job.setSession(session);
      job.run();
    }, commandManager::complete);
  }

  public void close() {
    if (term != null) {
      term.close();
    } else {
      jobController.close(ar -> closedPromise.complete());
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy