From 4b9aa5480a51b6b5243250a1dbcd9918bd3377df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Ju=C3=A1rez?= Date: Sat, 1 Apr 2017 14:58:58 -0500 Subject: [PATCH] BAEL-431 A CLI with Spring Shell (#1562) * A Simple CLI with Spring Shell Code for article * Correct file name --- spring-all/pom.xml | 7 ++ .../main/java/org/baeldung/shell/Main.java | 10 +++ .../shell/simple/SimpleBannerProvider.java | 34 ++++++++ .../org/baeldung/shell/simple/SimpleCLI.java | 81 +++++++++++++++++ .../simple/SimpleHistoryFileNameProvider.java | 22 +++++ .../shell/simple/SimplePromptProvider.java | 21 +++++ .../shell/simple/SimpleURLConverter.java | 35 ++++++++ .../META-INF/spring/spring-shell-plugin.xml | 12 +++ .../shell/simple/SimpleCLIUnitTest.java | 86 +++++++++++++++++++ 9 files changed, 308 insertions(+) create mode 100644 spring-all/src/main/java/org/baeldung/shell/Main.java create mode 100644 spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java create mode 100644 spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java create mode 100644 spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java create mode 100644 spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java create mode 100644 spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java create mode 100644 spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml create mode 100644 spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java diff --git a/spring-all/pom.xml b/spring-all/pom.xml index f28fe1f10d..a3a51425e2 100644 --- a/spring-all/pom.xml +++ b/spring-all/pom.xml @@ -43,6 +43,12 @@ spring-retry ${springretry.version} + + org.springframework.shell + spring-shell + ${org.springframework.shell.version} + + @@ -280,6 +286,7 @@ 4.3.4.RELEASE 4.2.0.RELEASE 1.1.5.RELEASE + 1.2.0.RELEASE 5.2.5.Final diff --git a/spring-all/src/main/java/org/baeldung/shell/Main.java b/spring-all/src/main/java/org/baeldung/shell/Main.java new file mode 100644 index 0000000000..3d9f7a5860 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/Main.java @@ -0,0 +1,10 @@ +package org.baeldung.shell; + +import java.io.IOException; +import org.springframework.shell.Bootstrap; + +public class Main { + public static void main(String[] args) throws IOException { + Bootstrap.main(args); + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java new file mode 100644 index 0000000000..df7a48cd32 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleBannerProvider.java @@ -0,0 +1,34 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultBannerProvider; +import org.springframework.shell.support.util.OsUtils; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleBannerProvider extends DefaultBannerProvider { + + public String getBanner() { + StringBuffer buf = new StringBuffer(); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("* Baeldung Shell *").append(OsUtils.LINE_SEPARATOR); + buf.append("=======================================").append(OsUtils.LINE_SEPARATOR); + buf.append("Version:").append(this.getVersion()); + return buf.toString(); + } + + public String getVersion() { + return "1.0.1"; + } + + public String getWelcomeMessage() { + return "Welcome to Baeldung CLI"; + } + + @Override + public String getProviderName() { + return "Baeldung Banner"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java new file mode 100644 index 0000000000..0bbc62cf2c --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleCLI.java @@ -0,0 +1,81 @@ +package org.baeldung.shell.simple; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.URL; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandMarker; +import org.springframework.shell.core.annotation.CliAvailabilityIndicator; +import org.springframework.shell.core.annotation.CliCommand; +import org.springframework.shell.core.annotation.CliOption; +import org.springframework.stereotype.Component; + +@Component +public class SimpleCLI implements CommandMarker { + + private String getContentsOfUrlAsString(URL url) { + StringBuilder sb = new StringBuilder(); + try { + try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) { + String inputLine; + while ((inputLine = in.readLine()) != null) { + sb.append(inputLine); + } + } + } catch (IOException ex) { + sb.append("ERROR"); + } + return sb.toString(); + } + + @CliCommand( + value = {"web-get", "wg"}, + help = "Displays the contents of a URL." + ) + public String webGet( + @CliOption( + key = {"", "url"}, + help = "URL whose contents will be displayed." + ) URL url) { + return getContentsOfUrlAsString(url); + } + + @CliCommand( + value = {"web-save", "ws"}, + help = "Saves the contents of a URL.") + public String webSave( + @CliOption(key = {"", "url"}, help = "URL whose contents will be saved.") URL url, + @CliOption(key = {"out", "file"}, mandatory = true, help = "The name of the file.") String file) { + String contents = getContentsOfUrlAsString(url); + try (PrintWriter out = new PrintWriter(file)) { + out.write(contents); + } catch (FileNotFoundException ex) { + //Ignore + } + return "Done."; + } + + private boolean adminEnableExecuted = false; + + @CliAvailabilityIndicator(value = {"web-save"}) + public boolean isAdminEnabled() { + return adminEnableExecuted; + } + + @CliCommand(value = "admin-enable") + public String adminEnable() { + adminEnableExecuted = true; + return "Admin commands enabled."; + } + + @CliCommand(value = "admin-disable") + public String adminDisable() { + adminEnableExecuted = false; + return "Admin commands disabled."; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java new file mode 100644 index 0000000000..cef53adc69 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleHistoryFileNameProvider.java @@ -0,0 +1,22 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultHistoryFileNameProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimpleHistoryFileNameProvider extends DefaultHistoryFileNameProvider { + + @Override + public String getHistoryFileName() { + return "baeldung-shell.log"; + } + + @Override + public String getProviderName() { + return "Baeldung History"; + } + +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java new file mode 100644 index 0000000000..9a84954e05 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimplePromptProvider.java @@ -0,0 +1,21 @@ +package org.baeldung.shell.simple; + +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.shell.plugin.support.DefaultPromptProvider; +import org.springframework.stereotype.Component; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class SimplePromptProvider extends DefaultPromptProvider { + + @Override + public String getPrompt() { + return "baeldung-shell>"; + } + + @Override + public String getProviderName() { + return "Baeldung Prompt"; + } +} diff --git a/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java new file mode 100644 index 0000000000..66bab5c488 --- /dev/null +++ b/spring-all/src/main/java/org/baeldung/shell/simple/SimpleURLConverter.java @@ -0,0 +1,35 @@ +package org.baeldung.shell.simple; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; +import org.springframework.shell.core.Completion; +import org.springframework.shell.core.Converter; +import org.springframework.shell.core.MethodTarget; +import org.springframework.stereotype.Component; + +@Component +public class SimpleURLConverter implements Converter { + + @Override + public URL convertFromText(String value, Class requiredType, String optionContext) { + try { + return new URL(value); + } catch (MalformedURLException ex) { + //Ignore + } + return null; + } + + @Override + public boolean getAllPossibleValues(List completions, Class requiredType, + String existingData, String optionContext, MethodTarget target) { + return false; + } + + @Override + public boolean supports(Class requiredType, String optionContext) { + return URL.class.isAssignableFrom(requiredType); + } + +} diff --git a/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml new file mode 100644 index 0000000000..aea1a663c1 --- /dev/null +++ b/spring-all/src/main/resources/META-INF/spring/spring-shell-plugin.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java new file mode 100644 index 0000000000..0353083943 --- /dev/null +++ b/spring-all/src/test/java/org/baeldung/shell/simple/SimpleCLIUnitTest.java @@ -0,0 +1,86 @@ +package org.baeldung.shell.simple; + +import java.io.File; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.shell.Bootstrap; +import org.springframework.shell.core.CommandResult; +import org.springframework.shell.core.JLineShellComponent; + +public class SimpleCLIUnitTest { + + static JLineShellComponent shell; + + @BeforeClass + public static void startUp() throws InterruptedException { + Bootstrap bootstrap = new Bootstrap(); + shell = bootstrap.getJLineShellComponent(); + } + + @AfterClass + public static void shutdown() { + shell.stop(); + } + + public static JLineShellComponent getShell() { + return shell; + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommand_thenCorrectResult() { + + CommandResult resultWebSave = shell.executeCommand("web-get --url https://www.google.com"); + + Assert.assertTrue(resultWebSave.isSuccess()); + } + + @Test + public void givenCommandConfig_whenExecutingWebSaveCommand_thenCorrectResult() { + + shell.executeCommand("admin-enable"); + CommandResult result = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + result.isSuccess(), + new File("contents.txt").exists()}, + new boolean[]{true, true}); + } + + @Test + public void givenCommandConfig_whenAdminEnableCommandExecuted_thenCorrectAvailability() { + + CommandResult resultAdminDisable = shell.executeCommand("admin-disable"); + CommandResult resultWebSaveUnavailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + CommandResult resultAdminEnable = shell.executeCommand("admin-enable"); + CommandResult resultWebSaveAvailable = shell.executeCommand("web-save --url https://www.google.com --out contents.txt"); + + Assert.assertArrayEquals( + new boolean[]{ + resultAdminDisable.isSuccess(), + resultWebSaveUnavailable.isSuccess(), + resultAdminEnable.isSuccess(), + resultWebSaveAvailable.isSuccess()}, + new boolean[]{true, false, true, true}); + } + + @Test + public void givenCommandConfig_whenWebSaveCommandExecutedNoOutArgument_thenError() { + + shell.executeCommand("admin-enable"); + CommandResult resultWebSave = shell.executeCommand("web-save --url https://www.google.com"); + + Assert.assertEquals(resultWebSave.isSuccess(), false); + } + + @Test + public void givenCommandConfig_whenExecutingWebGetCommandWithDefaultArgument_thenCorrectResult() { + + CommandResult result = shell.executeCommand("web-get https://www.google.com"); + + Assert.assertEquals(result.isSuccess(), true); + } + +}