Code Monkey home page Code Monkey logo

spring-shell's Introduction

Spring Shell Revved up by Develocity

Spring Shell helps you to create Spring-powered, production-grade applications targeting CLI space. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need.

You can use Spring Shell to create stand-alone Java applications that can be started using java -jar or more sophisticated GraalVM native ways to create platform dependant apps.

Our primary goals are:

  • Provide a radically faster and widely accessible getting started experience for shell development.

  • Be opinionated, but get out of the way quickly as requirements start to diverge from the defaults.

Installation and Getting Started

Here is a quick teaser of a complete Spring Shell application in Java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;

@SpringBootApplication
@ShellComponent
public class DemoApplication {

	@ShellMethod
	public String hi() {
		return "hi";
	}

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

Running it as jar interactive:

$ java -jar demo.jar

shell:>help
AVAILABLE COMMANDS

Built-In Commands
       help: Display help about available commands
       stacktrace: Display the full stacktrace of the last error.
       clear: Clear the shell screen.
       quit, exit: Exit the shell.
       history: Display or save the history of previously run commands
       version: Show version info
       script: Read and execute commands from a file.

Demo Application
       hi:

shell:>hi
hi

Running it as jar non-interactive:

$ java -jar demo.jar hi

hi

Getting Help

Are you having trouble with Spring Shell? We want to help!

  • Join our GitHub Discussion section and post your question there.

Reporting Issues

Spring Shell uses GitHub’s integrated issue tracking system to record bugs and feature requests. If you want to raise an issue, please follow the recommendations below:

  • Before you log a bug, please search the {github}/issues[issue tracker] to see if someone has already reported the problem.

  • If the issue doesn’t already exist, {github}/issues/new[create a new issue].

  • Please provide as much information as possible with the issue report. We like to know the Spring Boot and Shell version, operating system, and JVM version you’re using.

  • If you need to paste code or include a stack trace, use Markdown. ``` escapes before and after your text.

  • If possible, try to create a test case or project that replicates the problem and attach it to the issue.

Building from Source

Active development branch is main targeting work for 3.2.x. 3.1.x, 3.0.x and 2.1.x are for maintaining current active releases.

Building and running tests:

./gradlew build

Publishing to local maven cache:

./gradlew publishToMavenLocal

Example

Samples contains various examples how to use spring-shell.

./gradlew :spring-shell-samples:spring-shell-sample-catalog:build -x test
./gradlew :spring-shell-samples:spring-shell-sample-commands:build -x test
./gradlew :spring-shell-samples:spring-shell-sample-e2e:build -x test

Native build can be enabled using property -PspringShellSampleNative=true. On linux a musl can be activated with -PspringShellSampleMusl=true. Note that musl needs to be properly configured in the environment.

License

Spring Shell is Open Source software released under the Apache 2.0 license.

spring-shell's People

Contributors

adityakishore avatar altus34 avatar baybatu avatar cachescrubber avatar camilojc avatar danielsoro avatar ericbottard avatar eubnara avatar github-actions[bot] avatar guang384 avatar houseofdouglas avatar jvalkeal avatar klr8 avatar leejianwei avatar markpollack avatar odrotbohm avatar onobc avatar pidster avatar robertbachmann avatar rweisleder avatar rwinch avatar selimhorri avatar simonverhoeven avatar splawrence avatar spring-builds avatar spring-operator avatar sualeh avatar terminalsin avatar tim-patterson avatar wilkinsona avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-shell's Issues

Quick Fix - Use dedicated Logger Name for Exception Logging

Right now we use the common logger of the AbstractShell to log errors to the shell. This is a problem if you like to minimize console output, as changing log-levels will eliminate all shell output.

https://github.com/spring-projects/spring-shell/blob/master/src/main/java/org/springframework/shell/core/AbstractShell.java#L147

As a quick fix until we tackle #99 we should use a dedicated Logger for Exception Logging. So it can be easily switched off via the common logger configuration facilities.

This is required by spring-cloud/spring-cloud-dataflow#696

Feature Request: Tab completion callback mechanism for arguments

When filling in command line arguments, it would be useful to have a callback mechanism that Spring Shell uses to activate custom handlers for completion of argument values.

Example: export data --file <TAB>

The <TAB> in this case could call a file match handler that then prints a list of potential file matches to the screen, or even returns a single match. Use of a Collection as the return from the handler might work well here, where if the Collection is only 1 element large, Spring Shell uses that element and moves to the next argument.

Build with Gradle 1.12 failes

D:\Code\Java\spring-shell>gradle build

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'spring-shell'.
> Could not resolve all dependencies for configuration ':classpath'.
   > Could not find org.springframework.build.gradle:docbook-reference-plugin:0.1.5.
     Required by:
         :spring-shell:1.1.0.BUILD-SNAPSHOT
   > Could not find org.springframework.build.gradle:spring-io-plugin:0.0.3.RELEASE.
     Required by:
         :spring-shell:1.1.0.BUILD-SNAPSHOT

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 2.402 secs

D:\Code\Java\spring-shell>gradle -v

------------------------------------------------------------
Gradle 1.12
------------------------------------------------------------

Build time:   2014-04-29 09:24:31 UTC
Build number: none
Revision:     a831fa866d46cbee94e61a09af15f9dd95987421

Groovy:       1.8.6
Ant:          Apache Ant(TM) version 1.9.3 compiled on December 23 2013
Ivy:          2.2.0
JVM:          1.8.0_05 (Oracle Corporation 25.5-b02)
OS:           Windows 7 6.1 amd64

SimpleVerticalAligner doesn't work with empty content

Having something like using SimpleVerticalAligner.middle:

builder.on(column(0)).addAligner(middle)

throws below error if cell content is empty.

java.lang.ArrayIndexOutOfBoundsException
    at java.lang.System.arraycopy(Native Method)
    at org.springframework.shell.table.SimpleVerticalAligner.align(SimpleVerticalAligner.java:81)
    at org.springframework.shell.table.Table.render(Table.java:136)
    at org.springframework.shell.table.Table.render(Table.java:51)
    at org.springframework.shell.core.AbstractShell.handleExecutionResult(AbstractShell.java:309)
    at org.springframework.shell.core.AbstractShell.executeCommand(AbstractShell.java:142)
    at org.springframework.shell.core.JLineShell.promptLoop(JLineShell.java:533)
    at org.springframework.shell.core.JLineShell.run(JLineShell.java:179)

It works i.e. with SimpleHorizontalAligner.center.

builder.on(column(0)).addAligner(center)

Converter must allow conversion based on generic parameter types

org.springframework.shell.core.Converter should support conversion using the generic type of method parameters not just their basic raw type.
I have lots of methods that take generic arguments and I have absolutely no clue how to work around this deficiency. Rewriting SimpleParser from scratch is an option but overkill.

Option Validation

It would be very helpful when spring-shell support Validation like JSR-311 for options.

E.g. an String option should have minimum length of 3 (search key word)
public void search(@SiZe(min = 3, message = "Search text must have minimum length of 3.")
@CliOption key = "text"
searchText)

simultaneous command execution on different instances on Bootstrap.getJLineShellComponent

In a JVM say I create two instances aka instance1 and instance2 using new Bootstrap(). I expected that if a user send's multiple commands to instance1 then the commands must execute sequentially. However if a user sends commands to instance1 and then immediately to instance2, then both the commands should be able to run in parallel. Unfortunately this doesn't happen, i.e. the commands on instance1 and instance2 are executed sequentially.

This behaviour is observed cause in SimpleExecutionStrategy.execute method synchronization is on SimpleExecutionStrategy.class. This should instead be synchronized on instance of SimpleExecutionStrategy.

I'll be glad to provide a patch if the guru's agree with me, just send me an email.

Regards!

By default spring shell supports keyword args. Provide support for list+keyword args

@CliCommand(value = "do something", help = "do something")
  public String doSomething(
    @CliOption(key = {"option1"}, mandatory = true, help = "option1") String option1,
    @CliOption(key = {"option2"}, mandatory = true, help = "option2") String option2)

Allows me to run do something --option1 value1 --option2 value2 on the cli. This is taking arguments as keyword arguments.

Now I want to be able to run do something value1 value2 and have the same effect as the previous command. For this I have to do this:

@CliCommand(value = "do something", help = "do something")
  public String doSomething(
    @CliOption(key = {""}, mandatory = true, help = "pair") String pair) {
    split = pair.split("\s+");
    if(pair.length != 2) {
        throw Exception
    }
    option1 = pair[0];
    option2 = pair[1];
   ...
   ...
}

This was taking arguments as list arguments.

I want both, i.e. do something --option1 value1 --option2 value2 and do something value1 value2 both should work and have the same effect.

My current approach was to do this:

public abstract class BaseCommand implements ExecutionProcessor {
  @Override
  public ParseResult beforeInvocation(ParseResult parseResult) {
    Object[] args = parseResult.getArguments();
    if (args != null && Sets.newHashSet(args).size() == 1) {
      if (args[0] instanceof String) {
        String[] split = ((String) args[0]).split("\\s+");
        if (split.length == args.length) {
          parseResult = new ParseResult(parseResult.getMethod(), parseResult.getInstance(), split);
        }
      }
    }
    return parseResult;
  }
}

And have all my Component subclasses extend BaseCommand.

All I'm saying is, this should be supported out of the box, without having to hack around.

As an extended functionality, I'd like to suggest a mixing of list and keyword args. I like python's way of doing this, which enforces that all list arguments have to be provided before all keyword arguments.

History file remains open if promptLoop terminates without executing exit command

spring-shell version: 1.2.0-RELEASE

In JLineShell class, promptLoop method blocks on invocation of reader.readLine().
If readLine() returns null, the loop will be terminated and it forgets to close the fileLog.
This happens quite easily for example by pressing CTRL+D on a ssh terminal.
fileLog is currently only closed after calling logCommandToOutput method, which was a very optimistic decision in the first place.

Add hooks for custom shutdown logic

I'd like to hook into Spring Shell's shutdown procedure, calling some custom handlers to clean up the system before exiting fully. Registering with the JVM's shutdown notifier results in some race conditions where things that I may want to use might be half-shutdown at that time. Utilizing Spring Shell's exit/Ctrl-C mechanisms would be more ideal.

Add Error Handling Infrastructure

The truth is, shell command can fail. E.g. underlying REST services may return either expected or unexpected errors. Currently, the output of errors cannot be customized in a decent manner.

This issue is possibly wrapper issue and may spawn sub-tickets for the following use-cases:

Allow for the customization of errors using a central hook

This could be a common dedicated method that implements current behavior but that can be overridden by developers to fit their needs.

Expected versus unexpected errors

In many scenarios, Spring Shell commands may trigger exceptions that are expected (Such as a 404 returned by an underlying REST endpoint) or unexpected (Such as a 500 returned by an underlying REST endpoint).

For instance, an unexpected exception may trigger more console output (e.g. stack-traces) than expected exceptions.

It would be nice if there was a facility to specify Exception types that represent expected exceptions.

Remember Errors

Users should also be able to introspect current and past errors. E.g:

  1. Do not display a stacktrace when one occurs, but remember it
  2. Add a show last error shell command, or something similar, to display the trace when one wants it

Current situation

Currently there are 2 places exceptions are printed to the console:

org.springframework.shell.core.SimpleExecutionStrategy

private Object invoke(ParseResult parseResult) {
    try {
        Method method = parseResult.getMethod();
        ReflectionUtils.makeAccessible(method);
        return ReflectionUtils.invokeMethod(method, parseResult.getInstance(), parseResult.getArguments());
    } catch (Throwable th) {
        logger.severe("Command failed " + th);
        return handleThrowable(th);
    }
}

Produces:

Command failed org.springframework.cloud.dataflow.rest.client.DataFlowClientException: Could not find stream definition named ticktock

And then in:

org.springframework.shell.core.AbstractShell
public CommandResult executeCommand(String line)

} catch (RuntimeException e) {
            setShellStatus(Status.EXECUTION_FAILED, line, parseResult);
            logger.log(Level.WARNING, e.getMessage(), e);
            // We rely on execution strategy to log it
            try {
                logCommandIfRequired(line, false);
            } catch (Exception ignored) {}
            return new CommandResult(false, null, e);
        }

Which produces:

org.springframework.cloud.dataflow.rest.client.DataFlowClientException: Could not find stream definition named ticktock

    at org.springframework.cloud.dataflow.rest.client.VndErrorResponseErrorHandler.handleError(VndErrorResponseErrorHandler.java:52)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:641)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:597)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.delete(RestTemplate.java:429)
    at org.springframework.cloud.dataflow.rest.client.StreamTemplate.destroy(StreamTemplate.java:107)
    at org.springframework.cloud.dataflow.shell.command.StreamCommands.destroyStream(StreamCommands.java:159)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216)
    at org.springframework.shell.core.SimpleExecutionStrategy.invoke(SimpleExecutionStrategy.java:68)
    at org.springframework.shell.core.SimpleExecutionStrategy.execute(SimpleExecutionStrategy.java:59)
    at org.springframework.shell.core.AbstractShell.executeCommand(AbstractShell.java:130)
    at org.springframework.shell.core.JLineShell.promptLoop(JLineShell.java:533)
    at org.springframework.shell.core.JLineShell.run(JLineShell.java:179)
    at java.lang.Thread.run(Thread.java:745)

See also: spring-cloud/spring-cloud-dataflow#696

Command help is confusing for new users

When running "help ", users always seem to be confused, particularly by the "Default if specified/unspecified" lines. Here is an example of a "search" command:

Keyword:                   search
Description:               Perform a search
 Keyword:                  ** default **
   Help:                   This is the text for the default option
   Mandatory:              true
   Default if specified:   '__NULL__'
   Default if unspecified: '__NULL__'

 Keyword:                  format
   Help:                   This is the help text for the format option.
   Mandatory:              true
   Default if specified:   'xml'
   Default if unspecified: '__NULL__'

 Keyword:                  limit
   Help:                   The maximum number of results to return.
   Mandatory:              false
   Default if specified:   '__NULL__'
   Default if unspecified: '__NULL__'

* search - Perform a search
  • search - Perform a search

It would be great if we could provide a plugin to customize this, but that could be tricky since we would need access to the annotation information. At minimum, I would recommend improving the default help with the following changes:

  1. Rename first "keyword" to "command" since they are already called commands elsewhere.
  2. Fewer labels (i.e. help and description)
  3. Remove default and/or mandatory info since we can include that in our help text
  4. Add a usage example

Combined, changes 1 to 3 might look like this:

Command:     search
Description: Perform a search. I can specify that a default operand is needed in the help text.

Required:
      format -- This is the help text for the format option. Maybe I'll specify the default if I want to.
Optional:
      limit -- The maximum number of results to return. Maybe I'll specify the default.

Another example with usage info (we could grab the field names or field types using reflection):

Command:     search
Description: Perform a search
Usage:

      search --format <text> [--limit <int>] <This is the text for the default option>

Options:
      format -- This is the help text for the format option. Maybe I'll specify the default if I want to.
      limit -- (Optional) The maximum number of results to return. Maybe I'll specify the default.

NPE thrown in Table.calculateColumnWidths() line 85 when only a header is defined

The following causes an NPE to be thrown:

Table table = new Table();

table.addHeader(0, new TableHeader("A"));
table.addHeader(1, new TableHeader("B"));

table.newRow().addValue(0, "C");

Expected result:

A  B
C

Actual result:

Null pointer exception thrown on line 85/86 of Table.calculateColumnWidths() due to use of tableRow.getValue(headerEntryKey).length() without checking whether tableRow.getValue(headerEntryKey) is valid first.

Make Result of last command available to all commands

Taking inspiration from python shell, where last command's output is stored in a special vairalbe _ and can be used.

I can see some use cases for such a functionality in sprint shell too.

If there's not enough bandwidth, I can pick this up too, I'd need some pointers regarding where the changes are supposed to go.

How can i block shell thread until my own thread pool executed

@Component
public class LoaderCommand implements CommandMarker {

    @CliCommand(value = "load file", help = "load local file data to target database")
    public String load(){
          // some code 
         Executors.newFixedThreadPool(1).execute(()->{
                      //do some thing
         });
    }
}

ExecutionProcessor is not called, documentation might be to short

Implementing the following I can see the interceptor is not called. The bean will be created, there is no scanning issue. I would expect the interceptor will be called before every command. A debug session at SimpleExecutionStrategy line 44 showed that there is no ExecutionProcessor in the ParseResult. Following the interceptor couldn't be called this way ?!

Example code:

@Component
public class CommandInterceptor implements ExecutionProcessor {

    /**
     * @see org.springframework.shell.core.ExecutionProcessor#beforeInvocation(org.springframework.shell.event.ParseResult)
     */
    @Override
    public ParseResult beforeInvocation(ParseResult invocationContext) {
        for (Object argument : invocationContext.getArguments()) {
            System.out.println(argument);
        }
        return invocationContext;
    }
    …
}

Allow more than one @CliAvailabilityIndicator

Hi guys,

something is strange here. I have two classes that implement a CommandMarker (to group them by what they do).

Both get a object autowired that implements the state of the shell (like connected, disconnected and executes external commands).

Now here is the code in each of them:

    private static final String CONNECT = "cluster connect";

    private static final String DISCONNECT = "cluster disconnect";

    @CliAvailabilityIndicator({DISCONNECT})
    public boolean isConnected() {
        return shell.isConnected();
    }

    @CliAvailabilityIndicator({CONNECT})
    public boolean isDisconnected() {
        return !shell.isConnected();
    }
    private static final String GET = "bucket get";
    private static final String SET = "bucket set";
    private static final String ADD = "bucket add";
    private static final String REPLACE = "bucket replace";
    private static final String DELETE = "bucket delete";
    private static final String COUNT_DOCS = "bucket count-docs";

    @CliAvailabilityIndicator({GET, SET, ADD, REPLACE, DELETE, COUNT_DOCS})
    public boolean isConnected() {
        return shell.isConnected();
    }

The thing that both have in common is this:

    @Autowired
    private CouchbaseShell shell;

which is just a

@Component
public class CouchbaseShell {
....
}

Now when I run it I get this exception

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shell': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Cannot specify an availability indicator for 'cluster disconnect' more than once
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1455)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:609)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:469)
    at org.springframework.shell.Bootstrap.<init>(Bootstrap.java:93)
    at org.springframework.shell.Bootstrap.<init>(Bootstrap.java:74)
    at org.springframework.shell.Bootstrap.main(Bootstrap.java:58)
Caused by: java.lang.IllegalArgumentException: Cannot specify an availability indicator for 'cluster disconnect' more than once
    at org.springframework.util.Assert.isTrue(Assert.java:65)
    at org.springframework.shell.core.SimpleParser.add(SimpleParser.java:1037)
    at org.springframework.shell.core.JLineShellComponent.afterPropertiesSet(JLineShellComponent.java:102)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
    ... 12 more

When I comment out the annotation with the method in one of the classes it works, but I'm not sure how they relate.. can someone clarify?

Have Shell Play Nice With Spring Boot

I've tried combining Spring Boot and Spring Shell because I liked how Spring Boot can create a nice self-contained executable JAR. What I found was that both projects create competing Spring contexts. I've tried a couple different hacks with less than satisfactory results. It would be nice if Spring Shell and Spring Boot were aware of each other so that we could write nice shell-based tools and get all the Spring Boot goodness as well.

Improve error message for failed commands

As a user, I'm trying to use the http post command and upon failures, I'm seeing very high-level error messages without any "caused by" or other details.

example:

dataflow:>http post --file /home/gpadmin/labs/scdf-intro/dataFileEnriched.json
Command failed java.lang.reflect.UndeclaredThrowableException

The actual cause of the failure is that the file does not exist.

Bootstrap starts StopWatch instance in main(), but stops in run()

If a user runs Bootstrap by invoking its main method, then everything works as expected. But if I user creates an instance and manually invokes the run() method in their application, when run() finishes, it tries to stop the StopWatch instance, which throws an exception because it has never been started. The StopWatch instance needs to be started at the beginning of the the run() method, so that stopping the stopwatch works as expected. Pull request incoming for this issue.

Does spring-shell support unicode?

For example I want to create a command to import a file with japanese file name , something like

import  --f "some japanese fiile name"

currently seems it does not work.
My code is like

@CliCommand(value = "import", help = "Import file")
  public String importFile(@CliOption(key = {"", "f", "file"}, mandatory = true, help = "file name") String fileName)
{
}

Empty List value causes ArrayIndexOutOfBoundsException

When adding a Table value that consists of an empty List<String>(), I am encountering an ArrayIndexOutOfBoundsException when rendering the Table.

modelBuilder.addRow().addValue("Roles").addValue(new ArrayList<String>());
...
builder.build().render(600);
java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at org.springframework.shell.table.SimpleVerticalAligner.align(SimpleVerticalAligner.java:81)
	at org.springframework.shell.table.Table.render(Table.java:136)
	at org.springframework.cloud.dataflow.shell.command.ConfigCommands.info(ConfigCommands.java:318)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216)
	at org.springframework.shell.core.SimpleExecutionStrategy.invoke(SimpleExecutionStrategy.java:68)
	at org.springframework.shell.core.SimpleExecutionStrategy.execute(SimpleExecutionStrategy.java:59)
	at org.springframework.shell.core.AbstractShell.executeCommand(AbstractShell.java:134)
	at org.springframework.shell.core.JLineShell.promptLoop(JLineShell.java:533)
	at org.springframework.shell.core.JLineShell.run(JLineShell.java:179)
	at java.lang.Thread.run(Thread.java:745)

Remove guava dependency

  1. Guava is used only in one class:
    org.springframework.shell.support.table.TableRenderer
  2. Only in 2 places in that class use guava:
Iterable<String> chunks = Splitter.fixedLength(
header.getWidth()).split(header.getName());

(both use same Splitter.fixedLength) and later it is used in foreach loop.
3) Guava is 2.3 mb large

  1. I believe it can be replaced with something dumb as this:
    https://gist.github.com/tttomat19/05bb30756252fb1fac07247c68caac00

sample app not working anymore

The helloworld example isn't working for me with a fresh build of revision 82adda6, neither is my own.

It starts and simply shows an unconfigured shell, i.e. no Banner, no Prompt, no commands.

Tweak the shell itself

I'd like to make some modifications to the actual Shell implementation, but I can't really find a good way. What I did was creating a class, extending JLineShellComponent and I did my tweaks there. I annotated it with @Component("shell"), but it seems like Bootstrap adds its own instance, overriding mine, even if one already exists in the container.

Any ideas of what I can do?

Is Spring Shell dead?

Hell there!

Soo we are goingto start a new project and i wonder if spring shell is dead, since spring is promoting similar functionality along with spring boot.

Remote shell on github seems not to have a heart beat anymore.

Thank you for this project, i am looking forward to your answer.

Cheers

Allow cancel of currently input line using CTRL-C

At the moment, pressing CTRL-C (which is a typing reflex used by many) simply triggers the shutdown of the shell.
It would be nice if one could simply cancel an already typed line, as many *nix shells do.
The proposed behavior is:

prompt>foo bar[CTRL-C]      // simply ignore what has been typed
prompt>[CTRL-C]             // if nothing has been typed, exit

See #108

Note that this is independent of killing a long running command execution, which may block the main prompt loop (which may or may not be addressed in the same PR for this)

Plain Help Command should not show unavailable Commands

CommandMarker methods that are unavailable (@CliAvailabilityIndicator returns false), should not by default listed using the plain help command. Instead, unavailable commands might be listed when help is supplied with an additional (optional) parameter e.g.:

help --all
help --all stream

Right now there is no visual indication, whether a command is available or not.

Run a script ignoring errors

I want a way to run a script file, ignoring errors i.e. if one command causes an error, then it shouldn't stop the execution, rather go on to execute the next line, and so on. Running a script can be done in two ways and neither provides this functionality.

One way it to provide a script via --cmdfile, but that doesn't work because of the following code in Bootstrap.java:

  public ExitShellRequest run() {
    String[] commandsToExecuteAndThenQuit = commandLine.getShellCommandsToExecute();
    JLineShellComponent shell = (JLineShellComponent)this.ctx.getBean("shell", JLineShellComponent.class);
    ExitShellRequest exitShellRequest;
    if(null != commandsToExecuteAndThenQuit) {
      boolean successful = false;
      exitShellRequest = ExitShellRequest.FATAL_EXIT;
      String[] arr$ = commandsToExecuteAndThenQuit;
      int len$ = commandsToExecuteAndThenQuit.length;

      for(int i$ = 0; i$ < len$; ++i$) {
        String cmd = arr$[i$];
        successful = shell.executeCommand(cmd).isSuccess();
        if(!successful) {
          break;
        }
      }

      if(successful) {
        exitShellRequest = ExitShellRequest.NORMAL_EXIT;
      }
    } else {

The break statement ensures that shell quits when one command fails.

The other way is to use script command in shell, and even that fails because of the following code in ScriptCommands.java:

      while(true) {
        String e;
        if((e = in.readLine()) == null) {
          var15 = false;
          break;
        }

        ++i;
        if(lineNumbers) {
          this.logger.fine("Line " + i + ": " + e);
        } else {
          this.logger.fine(e);
        }

        if(!"".equals(e.trim())) {
          boolean success = this.shell.executeScriptLine(e);
          if(success) {
            if(e.trim().startsWith("q")) {
              var15 = false;
              break;
            }

            if(e.trim().startsWith("ex")) {
              var15 = false;
              break;
            }
          }

          if(!success) {
            throw new IllegalStateException("Script execution aborted");
          }
        }

when support input with tab?

such as this command import --T file --name "product" --table "product" --d \t

When I enter the \t look into the tab key ,i have an error "You should specify a default option d"

Incredibly bad startup performance caused by loading shell-history file with long lines.

The code of JLineShell.filterLogEntry()
is performing incredibly bad if the history file spring-shell.log contains very long lines.

To give you an example:
We have a history file containing 87 lines and a size of about 10MB, i.e. some of the contained lines are really long.
filterLogEntry() takes 6.5 minutes to read that file during the start of the application.

The current code looks like this:

private String[] filterLogEntry() {
    ArrayList<String> entries = new ArrayList<String>();
    ReversedLinesFileReader reversedReader = null;
    try {
        reversedReader = new ReversedLinesFileReader(new File(getHistoryFileName()), 4096, Charset.forName("UTF-8"));
        int size = 0;
        String line = null;
        while ((line = reversedReader.readLine()) != null) {
            if (!line.startsWith("//")) {
                size++;
                if (size > historySize) {
                    break;
                }
                else {
                    entries.add(line);
                }
            }
        }
    }
    catch (IOException e) {
        logger.warning("read history file failed. Reason:" + e.getMessage());
    }
    finally {
        closeReversedReader(reversedReader);
    }
    Collections.reverse(entries);
    return entries.toArray(new String[0]);
}

ReversedLinesFileReader is the culprit here since it performs very very badly if the length of a line is larger than the buffer size.

Straightforward replacement suggestion:

private String[] filterLogEntryNew() {
    try {
        List<String> lines = IOUtils.readLines(new BufferedInputStream(new FileInputStream(historyFileName)), Charset.forName("UTF-8"));
        Iterator<String> iter = lines.iterator();
        while (iter.hasNext()) {
            String line = iter.next();
            if (line.startsWith("//")) {
                iter.remove();
            }
        }

        int totalSize = lines.size();
        int size = Math.min(totalSize, historySize);

        String[] result = new String[size];
        int startIndex = totalSize - size;
        for(int i=startIndex ; i<totalSize ; i++) {
            result[i-startIndex] = lines.get(i);
        }

        return result;
    } catch (IOException e) {
        logger.warning("read history file failed. Reason:" + e.getMessage());

        return new String[0];
    }
}

This code loads the 10MB spring-shell.log mentioned above in 150ms.
Feel free to use it if you like.

It's not possible to just replace the above method in an extending class because it's a private method, unfortunately.

Converse with user mode

Most of the options in Spring Shell are around single command execution. If we could have a converse mode, that will be a good addition (my thoughts).

I have made some changes on my local copy and it seems to work well with existing commands.

Let me know, if this is already present and I have missed it or if anyone is interested in this feature.

Eclipse console: ANSI output problem

Hi guys,

I'm running my spring shell in eclipse's console window, but unfortunately it doesn't work correctly.
I get strange characters printed along my messages.

It looks like this:
bildschirmfoto 2016-10-18 um 19 40 59

I debugged the problem and it seems that UnixTerminal is detecting that the Eclipse console is supporting ANSI colors, which is obviously not true.
So the colors are not interpreted and directly printed on screen

I hope you can fix this bug soon.

Support for Contextual Commands

If you have 100 commands defined, they can be easily grouped by first word of the command. Next level grouping are also possible. I'll explain the idea by an example. Apache Lens has a shell based on spring shell. There are a bunch of commands that start with the word fact

* fact add partitions - add multiple partition to fact <fact_name>'s storage <storage_name>, reading partition list spec from <partition-list-spec-path>
* fact add single-partition - add single partition to fact <fact_name>'s storage <storage_name>, reading spec from <partition-spec-path>
* fact add storage - adds a new storage to fact <fact_name>, taking storage spec from <path-to-storage-spec>
* fact drop all storages - drop all storages associated to fact <fact_name>
* fact drop partitions - drop all partitions associated with fact <fact_name>, storage <storage_name> filtered by <partition-filter>
* fact drop storage - drop storage <storage_name> from fact <fact_name>
* fact get storage - describe storage <storage_name> of fact <fact_name>
* fact list partitions - get all partitions associated with fact <fact_name>, storage <storage_name> filtered by <partition-filter>
* fact list storage - display list of storages associated to fact <fact_name>
* fact timelines - get timelines for fact. Can optionally specify storage, update period and time dimension to filter by. Instead of time dimension, partition column can be directly passed as <time_dimension>
* fact update partitions - update multiple partition of fact <fact_name>'s storage <storage_name>, reading partition list spec from <partition-list-spec-path> The partitions have to exist to be eligible for updation.
* fact update single-partition - update single partition to fact <fact_name>'s storage <storage_name>, reading spec from <partition-spec-path> The partition has to exist to be eligible for updation.

I should be able to define fact as a temporary global context and have all the following commands parsed with fact prepended to them. Basically allowing me to give commands like list storages and have it interpreted as fact list storages.

Unable to post process non-interactive output with bash commands

I have a need to be able to process the output of a custom CLI (written with spring-shell) with standard bash commands like 'cut', 'sort', 'uniq', etc. when executing commands in non-interactive mode.

Currently there are two issues:

  1. every command executed in non-interactive mode prints a line that looks something like:
    Dec 03, 2014 5:25:39 PM org.springframework.shell.core.AbstractShell handleExecutionResult
  2. it appears that the log formatter is stripping tab characters from the output even though that's what i am returning from the various commands.

I was able to work around both of these issues by applying the following changes:

diff --git a/src/main/java/org/springframework/shell/Bootstrap.java b/src/main/java/org/springframework/shell/Bootstrap.java
index 35d397e..0af509c 100644
--- a/src/main/java/org/springframework/shell/Bootstrap.java
+++ b/src/main/java/org/springframework/shell/Bootstrap.java
@@ -16,7 +16,11 @@
 package org.springframework.shell;

 import java.io.IOException;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
 import java.util.logging.Level;
+import java.util.logging.LogRecord;
 import java.util.logging.Logger;

 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -153,7 +157,22 @@ public class Bootstrap {
        if (null != commandsToExecuteAndThenQuit) {
            boolean successful = false;
            exitShellRequest = ExitShellRequest.FATAL_EXIT;
-
+
+           for(Handler h : Logger.getLogger("").getHandlers()){
+               Logger.getLogger("").removeHandler(h);
+           }
+
+           Handler consoleLogHandler = new ConsoleHandler();
+           consoleLogHandler.setFormatter(new Formatter() {
+
+               @Override
+               public String format(LogRecord record) {
+                   // if record.getMessage() is returned the TSV format gets trashed
+                   System.out.println(record.getMessage());
+                   return "";
+               }
+           });
+           Logger.getLogger("").addHandler(consoleLogHandler);
+
            for (String cmd : commandsToExecuteAndThenQuit) {
                successful = shell.executeCommand(cmd).isSuccess();
                if (!successful)

However this seems very hacky... Is there currently anyway to inject a custom formatter from my application? Is there a better solution for avoiding corrupting the TSV output?

Thanks!

Is is possible to override the default Date converter?

Hello, everyone,

I'm working on a CLI and I'd like to have a Date parameter that accepts values in yyyy-MM-dd HH:mm:ss format.

I've implemented a Converter<Date> class and marked it as a @Component but it never gets invoked.

Is it possible to override default converters? Or maybe I'm doing something wrong?

My command looks like this:

@Component
public class MyCommands implements CommandMarker {

    @CliCommand(value = "addEntity", help = "Command to add entities to the DB")
    public String addEntity(
        @CliOption(key = "date", mandatory = true, help = "Set the date (format: yyyy-MM-dd HH:mm:ss)") final Date date
    ) throws ParseException
    {
        (...)
    }
}

Thanks!

Command failed java.lang.reflect.UndeclaredThrowableException

SimpleExecutionStrategy throwing UndeclaredThrowableException at ReflectionUtils.invokeMethod.

How do we register (if that is the correct word) our application exceptions that might occur within a command ? I need to capture thrown exceptions correctly.

For example this command makes a remote connection to a service and might fail for a number of reasons.

 @CliCommand(value = "ife scep ca", help = "SCEP get CA certificate")
    public final String ca() throws ClientException, IOException {
        this.cacert = auto.getCaCertificate();
        return "Got CA certificate CN " + this.cacert.getSubjectDN().getName();
    }

I have tried implements ExecutionProcessor also no luck as of yet (exception is null).

Paul

Add feature to allow read lines

When trying to use System.console().readLine characters typed are not visible.
Add some mechanism so that it can be visible

Spring Shell 1.0.0.RELEASE

Ctrl+left, Ctrl+right not bound to backwards-word, forward-word

ctrl-left yields: 1;5D
ctrl-right yeilds: 1;5C

These are bound in /etc/inputrc which works in all other readline-based applications.

# mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving
"\e[1;5C": forward-word
"\e[1;5D": backward-word
"\e[5C": forward-word
"\e[5D": backward-word
"\e\e[C": forward-word
"\e\e[D": backward-word

Is spring-shell using JLine? JLine supports inputrc.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.