Showing posts with label spring shell. Show all posts
Showing posts with label spring shell. Show all posts

Wednesday, 17 February 2021

Spring Shell: Customize output colors

Step 1: Define an enum to represent colors.

 

ShellPromptColor.java

package com.sample.app.enums;

public enum ShellPromptColor {
	BLACK(0), RED(1), GREEN(2), YELLOW(3), BLUE(4), MAGENTA(5), CYAN(6), WHITE(7), BRIGHT(8);

	private final int value;

	ShellPromptColor(int value) {
		this.value = value;
	}

	public int toJlineAttributedStyle() {
		return this.value;
	}
}

Step 2: Add following properties in ‘application.properties’ file.

shell.out.info=CYAN
shell.out.success=GREEN
shell.out.warning=YELLOW
shell.out.error=RED


Step 3: Define ShellOutputHelper class.

 

ShellOutputHelper.java

package com.sample.app.customizations;

import org.jline.terminal.Terminal;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.springframework.beans.factory.annotation.Value;

import com.sample.app.enums.ShellPromptColor;

public class ShellOutputHelper {

	@Value("${shell.out.info}")
	public String infoColor;

	@Value("${shell.out.success}")
	public String successColor;

	@Value("${shell.out.warning}")
	public String warningColor;

	@Value("${shell.out.error}")
	public String errorColor;

	private Terminal terminal;

	public ShellOutputHelper(Terminal terminal) {
		this.terminal = terminal;
	}

	public String getColored(String message, ShellPromptColor color) {
		return (new AttributedStringBuilder())
				.append(message, AttributedStyle.DEFAULT.foreground(color.toJlineAttributedStyle())).toAnsi();
	}

	public String getInfoMessage(String message) {
		return getColored(message, ShellPromptColor.valueOf(infoColor));
	}

	public String getSuccessMessage(String message) {
		return getColored(message, ShellPromptColor.valueOf(successColor));
	}

	public String getWarningMessage(String message) {
		return getColored(message, ShellPromptColor.valueOf(warningColor));
	}

	public String getErrorMessage(String message) {
		return getColored(message, ShellPromptColor.valueOf(errorColor));
	}
}


Step 4: Define a bean of ShellHelper class.

 

SpringShellConfig.java

package com.sample.app.config;

import org.jline.terminal.Terminal;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import com.sample.app.customizations.ShellOutputHelper;

@Configuration
public class SpringShellConfig {

	@Bean
	public ShellOutputHelper shellHelper(@Lazy Terminal terminal) {
		return new ShellOutputHelper(terminal);
	}

}


Step 5: Define ‘CustomizeColorCommand’ like below.

 

CustomizeColorCommand.java

package com.sample.app.commmands;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;

import com.sample.app.customizations.ShellOutputHelper;

@ShellComponent
public class CustomizeColorCommand {
	@Autowired
	ShellOutputHelper shellHelper;

	@ShellMethod("Displays greeting message to the user whose name is supplied")
	public String beautifyMe(@ShellOption({ "-N", "--name" }) String name) {
		String output = shellHelper.getSuccessMessage(String.format("Hello %s", name));
		return output.concat(", How are you?");
	}
}


Run the application and execute the command ‘beautify-me Krishna’, you will see that the message ‘Hello Krishna’ is beautified in green color.





 

Previous                                                    Next                                                    Home

Spring Shell: Customizing prompt name

By default, spring shell application gives the prompt name as ‘shell:>’. We can customize this name by implementing PromptProvider interface.

import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStyle;
import org.springframework.shell.jline.PromptProvider;
import org.springframework.stereotype.Component;

@Component
public class MyPromptProvider implements PromptProvider {

	@Override
	public AttributedString getPrompt() {
		return new AttributedString("CLI-DEMO-PROMPT:>", AttributedStyle.DEFAULT.foreground(AttributedStyle.BLUE));
	}

}

 

Once you ran above application, you will see the prompt name as ‘CLI-DEMO-PROMPT:>’.

 

You can download complete working application from this link.

https://github.com/harikrishna553/springboot/tree/master/shell/hello-world

 

 

Previous                                                    Next                                                    Home

Sunday, 14 February 2021

Spring Shell: Override built-in commands

By implementing <Command>.Command interface, we can override the behavior of built-in command.

 

For example, below snippet overrides the built-in ‘exit’ command behavior.

import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.commands.Quit;

@ShellComponent
public class CustomExitCommand implements Quit.Command {

	@ShellMethod(value = "Exit the shell.", key = {"quit", "exit", "terminate"})
	public void quit() {
		System.out.println("Exiting the Application");
		System.out.println("Good Bye!!!!!!!!");
		System.exit(0);
		
	}
}

 When you execute the command ‘exit’, system will print the messages and exit from the application.

shell:>exit
Exiting the Application
Good Bye!!!!!!!!

 

             

Previous                                                    Next                                                    Home

Spring Shell: Disable specific commands

By setting the property ‘spring.shell.command.<command>.enabled’ to false in the app Environment we can disable specific commands.

 

For example, below snippet disable the command clear and add.

@SpringBootApplication
public class App {

	public static void main(String args[]) {
		String[] disabledCommands = {"--spring.shell.command.clear.enabled=false", "--spring.shell.command.add.enabled=false"};
		args = StringUtils.concatenateStringArrays(args, disabledCommands);
		
		SpringApplication.run(App.class, args);
	}

}

 

 

Previous                                                    Next                                                    Home

Spring Shell: How to disable built-in commands

Just exclude 'spring-shell-standard-commands' like below.

<dependency>
    <groupId>org.springframework.shell</groupId>
    <artifactId>spring-shell-starter</artifactId>
    <version>2.0.1.BUILD-SNAPSHOT</version>

    <exclusions>
        <exclusion>
            <groupId>org.springframework.shell</groupId>
            <artifactId>spring-shell-standard-commands</artifactId>
        </exclusion>
    </exclusions>
</dependency>

 

 

Previous                                                    Next                                                    Home

Spring Shell: Run a batch of commands

Using ‘script’ command, we can provide collection of commands in a file and execute them one by one.

 

batch.shell

add 10 20
sub 10 20
mul 10 20
div 10 20

greet-user Krishna

 

Execute batch.shell file using script command.

shell:>script /Users/krishna/examples/batch.shell
30
-10
200
0
Hello Krishna!!!!

 

  

Previous                                                    Next                                                    Home

Spring Shell: Get help about a command

Using below syntax, you can get help about a command.

 

Syntax

help {command_name}

 

Example 1

shell:>help add


NAME
    add - Add two integers together.

SYNOPSYS
    add [--a] int  [--b] int  

OPTIONS
    --a  int
        
        [Mandatory]

    --b  int
        
        [Mandatory]

 

Example 2:

shell:>help greet-user


NAME
    greet-user - Commands to greet user

SYNOPSYS
    greet-user [[-name] string]  

OPTIONS
    -name  string
        
        [Optional, default = User]

 

You can download complete application from this link.

https://github.com/harikrishna553/springboot/tree/master/shell/hello-world

 

 

 

Previous                                                    Next                                                    Home

Spring Shell: Organizing commands

When your shell application has lots of commands, we should organize them properly, so users can consume them easily.

 

What is the default behavior of help command?

By default ‘help’ command would see a daunting list of commands, organized by alphabetical order and commands group together according to the class they are implemented in, turning the camel case class name into separate words. For example, for the class name 'ArithmeticCommandCustomizeCommandName' group name will be 'Arithmetic Command Customize Command Name').

shell:>help
AVAILABLE COMMANDS

Arithmetic Command Customize Command Name
        sum: Add two integers together.

Arithmetic Commands
        add: Add two integers together.
        div: Devide two integers together.
        mul: Multiply two integers together.
        sub: Subtract two integers together.

Boolean Type Demo
        shutdown: Shutdown the system

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

Customize Multiple Named Keys For Single Parameter
        print-person: Commands to print Person Details

Customize Named Param Keys
        print-employee: Commands to print Employee Details

Customize Named Param Keys Prefix
        print-student: Commands to print Student Details

Default Values
        greet-user: Commands to greet user

Dynamic Command Validation Demo1
        connect: Connect to Database
      * print-table-records: Print all records of table

Dynamic Command Validation Demo2
        connect-to-oracle: Connect to Oracle Database
      * print-table-records-from-oracle: Print all records of table from Oracle DB

Dynamic Command Validation Demo3
        connect-to-db2: Connect to Db2 Database
      * download-table-records-from-db2: Download all records of table from DB2
      * print-table-records-from-db2: Print all records of table from DB2

Multi Valued Parameters Demo
        square: Commands to Calculate Square of given numbers

Multiple Aliases
        say-hello, welcome, wish: Commands to welcome user

Named Params
        get-user: Commands to print User Details

Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.

 

How can we customize this grouping behaviour?

‘Spring Shell’ provide following ways to group the related commands together.

 

a.   specifying a group() in the @ShellMethod annotation

 

b.   placing a @ShellCommandGroup on the class the command is defined in. This will apply the group for all commands defined in that class (unless overridden as above)

 

c.    placing a @ShellCommandGroup on the package (via package-info.java) the command is defined in. This will apply to all commands defined in the package (unless overridden at the method or class level as explained above)

 

Example 1:

 

public class ArithmeticCommands {
    @ShellMethod(value = "This command ends up in the 'Arithmetic Commands' group")
    public void foo() {}

    @ShellMethod(value = "This command ends up in the 'Other Commands' group",
            group = "Other Commands")
    public void bar() {}
}


Example 2:

@ShellCommandGroup("Other Commands")
public class SomeCommands {
        @ShellMethod(value = "This one is in 'Other Commands'")
        public void wizz() {}

        @ShellMethod(value = "And this one is 'Yet Another Group'",
                group = "Yet Another Group")
        public void last() {}
}


After I group the commands according to their behaviour, help command output looks like below.

shell:>help
AVAILABLE COMMANDS

Arithmetic Commands
        add: Add two integers together.
        div: Devide two integers together.
        mul: Multiply two integers together.
        sub: Subtract two integers together.
        sum: Add two integers together.

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

Dynamic Command Validation
        connect: Connect to Database
        connect-to-db2: Connect to Db2 Database
        connect-to-oracle: Connect to Oracle Database
      * download-table-records-from-db2: Download all records of table from DB2
      * print-table-records: Print all records of table
      * print-table-records-from-db2: Print all records of table from DB2
      * print-table-records-from-oracle: Print all records of table from Oracle DB

Miscellaneous
        greet-user: Commands to greet user
        say-hello, welcome, wish: Commands to welcome user
        shutdown: Shutdown the system
        square: Commands to Calculate Square of given numbers

Named Parameters
        get-user: Commands to print User Details
        print-employee: Commands to print Employee Details
        print-person: Commands to print Person Details
        print-student: Commands to print Student Details

Commands marked with (*) are currently unavailable.
Type `help <command>` to learn more.


You can download complete application from this link.

https://github.com/harikrishna553/springboot/tree/master/shell/hello-world






Previous                                                    Next                                                    Home

Spring Shell: Dynamic Command Availability

Spring shell provides a way to tell the availability of the command. For example, some commands may need user credentials to work with. In that case, user must execute ‘login’ command before proceeding to execute this command.

 

How can we achieve this dynamic command availability?

There are 3 possible ways to achieve dynamic command availability.


By creating '{actualMethodName}Availability' method.

@ShellComponent(value = "Connect to Database")
public class DynamicCommandValidationDemo1 {

	private boolean connected;

	@ShellMethod(value = "Connect to Database", key = "connect", prefix = "-")
	public void connectToDb(String userName, String password) {
		connected = true;
	}

	@ShellMethod(value = "Print all records of table", key = "print-table-records", prefix = "-")
	public void printAllRecordsOfTable(String tableName) {
		System.out.println("Printing Data.......");
	}

	public Availability printAllRecordsOfTableAvailability() {
		return connected ? Availability.available() : Availability.unavailable("you are not connected");
	}
}

 

As you see above example, it has two commands.

a.   connect: To connect to the database and set the flag connected to true.

b.   print-table-records: Take an employee table name as input and print employee table content.

But to execute the command ‘print-table-records’, user first connect to the database. This check is possible, because I defined another method named ‘printAllRecordsOfTableAvailability’ (actual method name followed by Availability suffix. printAllRecordsOfTable + Availability ). printAllRecordsOfTableAvailability method returns an instance of Availability, constructed with one of the two factory methods.  

Whenever the user tries to invoke the command while not being connected, here is what happens:

shell:>print-table-records employees
[31mCommand 'print-table-records' exists but is not currently available because you are not connected[0m
[31mDetails of the error have been omitted. You can use the [1mstacktrace[22m command to print the full stacktrace.[0m

 

Connect to the database using some credentials.

shell:>connect krishna password123
shell:>

 

Now execute the command print-table-records.

shell:>print-table-records employees
Printing Data.......

 

Using @ShellMethodAvailability annotation

@ShellComponent(value = "Connect to Oracle Database")
public class DynamicCommandValidationDemo2 {

	private boolean connected;

	@ShellMethod(value = "Connect to Oracle Database", key = "connect-to-oracle", prefix = "-")
	public void connectToDb(String userName, String password) {
		connected = true;
	}

	@ShellMethodAvailability("availabilityCheck")
	@ShellMethod(value = "Print all records of table from Oracle DB", key = "print-table-records-from-oracle", prefix = "-")
	public void printAllRecordsOfTable(String tableName) {
		System.out.println("Printing Data.......");
	}

	public Availability availabilityCheck() {
		return connected ? Availability.available() : Availability.unavailable("you are not connected");
	}
}

 

@ShellMethodAvailability("availabilityCheck")

This annotation calls the method ‘availabilityCheck’ to check the availability of a command.

 

You will get an error while trying to execute the command ‘print-table-records-from-oracle’ before connecting to it.

shell:>connect-to-oracle
[31mParameter '-user-name string' should be specified[0m
[31mDetails of the error have been omitted. You can use the [1mstacktrace[22m command to print the full stacktrace.[0m

shell:>connect-to-oracle krishna password123
shell:>
shell:>print-table-records-from-oracle employee
Printing Data.......
shell:>

 

Using @ShellMethodAvailability

This method is useful, when more than one command depends on the same internal state.

@ShellComponent(value = "Connect to DB2 Database")
public class DynamicCommandValidationDemo3 {

      private boolean connected;

      @ShellMethod(value = "Connect to Db2 Database", key = "connect-to-db2", prefix = "-")
      public void connectToDb(String userName, String password) {
            connected = true;
      }

      @ShellMethod(value = "Print all records of table from DB2", key = "print-table-records-from-db2", prefix = "-")
      public void printAllRecordsOfTable(String tableName) {
            System.out.println("Printing Data.......");
      }
      
      @ShellMethod(value = "Download all records of table from DB2", key = "download-table-records-from-db2", prefix = "-")
      public void download(String tableName) {
            System.out.println("Downloading Data.......");
      }

      @ShellMethodAvailability({"download-table-records-from-db2", "print-table-records-from-db2"})
      public Availability availabilityCheck() {
            return connected ? Availability.available() : Availability.unavailable("you are not connected");
      }
}

 

You can download complete application from this link.

https://github.com/harikrishna553/springboot/tree/master/shell/hello-world

 


Previous                                                    Next                                                    Home