├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── _config.yml ├── annotations ├── build.gradle └── src │ ├── main │ └── java │ │ └── net │ │ └── je2sh │ │ └── annotations │ │ └── CommandProcessor.java │ └── test │ └── java │ └── net │ └── je2sh │ └── annotations │ └── CommandProcessorTests.java ├── base-plugins ├── build.gradle └── src │ └── main │ └── java │ └── net │ └── je2sh │ └── base │ └── Help.java ├── build.gradle ├── core ├── build.gradle └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── je2sh │ │ │ └── core │ │ │ ├── AbstractCommand.java │ │ │ ├── CommandProvider.java │ │ │ └── plugins │ │ │ └── PluginContext.java │ └── kotlin │ │ └── net │ │ └── je2sh │ │ └── core │ │ ├── Bootstrap.kt │ │ ├── Command.kt │ │ ├── CommandContext.kt │ │ ├── CommandManager.kt │ │ ├── JeeShell.kt │ │ ├── annotations │ │ └── CommandProcessor.kt │ │ ├── errors │ │ └── DuplicateCommandException.kt │ │ ├── impl │ │ ├── DefaultCommandManager.kt │ │ └── EmptyCommandManager.kt │ │ └── plugins │ │ ├── Plugin.kt │ │ ├── PluginLifeCycle.kt │ │ └── RunnablePlugin.kt │ └── test │ └── groovy │ └── net │ └── je2sh │ └── core │ └── CommandTests.groovy ├── examples ├── build.gradle └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── je2sh │ │ │ └── examples │ │ │ ├── DemoApplication.java │ │ │ ├── DemoController.java │ │ │ ├── commands │ │ │ ├── Echo.java │ │ │ └── Hello.java │ │ │ └── plugin │ │ │ └── SpringPlugin.java │ └── resources │ │ ├── application.properties │ │ ├── static │ │ └── js │ │ │ ├── home.js │ │ │ └── unix_formatting.js │ │ └── templates │ │ └── home.html │ └── test │ └── groovy │ └── net │ └── je2sh │ └── examples │ └── commands │ ├── EchoTests.groovy │ └── HelloTests.groovy ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── shell ├── build.gradle └── src │ └── main │ ├── java │ └── net │ │ └── je2sh │ │ └── shell │ │ └── StandaloneShell.java │ └── kotlin │ └── net │ └── je2sh │ └── shell │ └── ShellPlugin.kt ├── spring ├── build.gradle └── src │ └── main │ ├── java │ └── net │ │ └── je2sh │ │ └── spring │ │ ├── JeeshRestAutoConfiguration.java │ │ ├── JeeshSshAutoConfiguration.java │ │ ├── rest │ │ ├── CommandRequest.java │ │ ├── CommandResponse.java │ │ ├── JeeshRestOptions.java │ │ ├── JeeshRestSpring.java │ │ └── RestTerminalProvider.java │ │ └── ssh │ │ ├── JeeshSshOptions.java │ │ └── JeeshSshSpring.java │ └── resources │ ├── META-INF │ └── spring.factories │ └── application.yaml ├── ssh ├── build.gradle └── src │ ├── main │ ├── java │ │ └── net │ │ │ └── je2sh │ │ │ └── ssh │ │ │ └── SshPlugin.java │ └── resources │ │ └── jeesh │ │ ├── hostkey.pem │ │ └── jeesh.properties │ └── test │ └── groovy │ └── net │ └── je2sh │ └── ssh │ └── SshPluginTests.groovy └── test ├── build.gradle └── src └── main └── java └── net └── je2sh └── test └── TestUtils.java /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | 13 | ### IntelliJ IDEA ### 14 | .idea 15 | *.iws 16 | *.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | before_cache: 5 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 6 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 7 | cache: 8 | directories: 9 | - $HOME/.gradle/caches/ 10 | - $HOME/.gradle/wrapper/ 11 | script: ./gradlew build 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 JeeSh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is JeeSh 2 | 3 | [![Build Status](https://travis-ci.org/jeeshell/je2sh.svg?branch=master)](https://travis-ci.org/jeeshell/je2sh) 4 | [![CodeFactor](https://www.codefactor.io/repository/github/hartimer/je2sh/badge)](https://www.codefactor.io/repository/github/hartimer/je2sh) 5 | 6 | JeeSh stands for "JVM Extensible & Embeddable Shell". It aims at being a reboot of the famous 7 | [Common Reusable SHell (CRaSH)](https://github.com/crashub/crash). 8 | 9 | 10 | ## Motivation 11 | 12 | CRaSH is no longer maintained and [Spring Boot](https://projects.spring.io/spring-boot/) is removing support 13 | for it with release 2.0. Having the ability to access Spring features through SSH is very handy, especially 14 | for administrative tasks. 15 | 16 | 17 | We envisioned three common use-cases: 18 | * **Standalone shell**. Useful for encoding utilities 19 | * **SSH Server**. If you want to have JeeSh running on your server providing functionality not available 20 | on a normal *nix shell or for embedding it into a [Spring](https://spring.io/) application (common use case for CRaSH) 21 | * **REST Endpoints**. Allows you to expose your commands through a REST API, making it easy to create both 22 | a healthy repertoire of features and a client layer (other than SSH) to interact with it. The *examples* project 23 | provides a sample on how to use [JQueryTerminal](http://terminal.jcubic.pl/) for this purpose. 24 | 25 | 26 | # First Steps 27 | 28 | You can check JeeSh in action right after checking out the project. The **examples** module is a Spring Boot app 29 | that bootstraps JeeSh as both a SSH server and a REST API endpoint. Just run `./gradlew :examples:bootRun` 30 | and navigate to [http://localhost:8080/](http://localhost:8080/) or run `ssh -p 2003 admin@localhost` (admin:admin). 31 | 32 | 33 | Try the `help` command. 34 | 35 | 36 | ## First Command 37 | 38 | JeeSh just takes advantage of [JCommander](http://jcommander.org/) for command specification. Here is 39 | the entire code of the built in `hello` command 40 | 41 | ```java 42 | @Parameters(commandNames = {"hello", "hi"} , commandDescription = "Just says hello") 43 | public class Hello extends AbstractCommand { 44 | 45 | @Parameter(names = {"-n", "--name"}, description = "Your name") 46 | private String name; 47 | 48 | @Override 49 | public void execute(@NotNull CommandContext context) { 50 | context.println("Hello " + Optional.ofNullable(name).orElse("World")); 51 | } 52 | 53 | } 54 | ``` 55 | 56 | The requirements: 57 | * Annotate your command with `@Parameters` 58 | * Implement `Command`. You will typically extend `net.je2sh.core.AbstractCommand` for convenience. 59 | 60 | 61 | # Architecture 62 | 63 | JeeSh is broken down into multiple modules, allowing users to only import what they need. 64 | By design, and for experimentation purposes, it is built using Java 8 and Kotlin. 65 | 66 | 67 | The base idea is that you can just define your commands using [JCommander](http://jcommander.org/) and those 68 | will be automatically picked up by JeeSh (if [*annotations*](#annotations) module is used). 69 | 70 | 71 | For parsing and rendering we use [jline3](https://github.com/jline/jline3), which allows using ANSI encodings 72 | and overall makes life easier. 73 | 74 | 75 | ## Core 76 | 77 | The core module provides the basis for everything else. Necessary interfaces and glue to interconnect 78 | the different components that build JeeSh. 79 | 80 | ## Annotations 81 | 82 | Enables *the magic* of interpreting `@Parameters`. 83 | 84 | 85 | This module provides an annotation processor that creates a `CommandProvider` for you and makes it 86 | available as a `ServiceProvider` (`META-INF/services`) which is how **core** loads the available commands. 87 | 88 | 89 | Mode on this subject later on. 90 | 91 | 92 | ## Base Plugins 93 | 94 | Small set of builtin plugins. Function mostly as an reference but also includes the `help` command. 95 | 96 | ## Shell 97 | 98 | Standalone shell instant that can be executed from the terminal 99 | 100 | ## SSH 101 | 102 | Pretty much self-explanatory. 103 | 104 | ## Spring (Boot) 105 | 106 | When included this module autoconfigures JeeSh according the the properties you define. By default 107 | both a SSH Server and a REST API will be enabled. These however can be controlled through the properties: 108 | `jeesh.ssh.enabled` and `jeesh.rest.enabled` respectively (to be added on your `application.[properties|yaml]`). 109 | 110 | # Disclaimer 111 | 112 | This is definitely a work in progress so there is definitely a lot of room for improvement. If you want 113 | to contribute go ahead and grab an issued or submitted your awesome new feature! 114 | 115 | Licensed under the MIT License ([LICENSE](LICENSE.md)) 116 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker -------------------------------------------------------------------------------- /annotations/build.gradle: -------------------------------------------------------------------------------- 1 | import javax.tools.ToolProvider 2 | 3 | dependencies { 4 | compile project(':core') 5 | compile group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc3' 6 | compile 'com.squareup:javapoet:1.9.0' 7 | 8 | testCompile 'com.google.testing.compile:compile-testing:0.8' 9 | testCompile group: 'junit', name: 'junit', version: '4.12' 10 | testCompile files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs()) 11 | } 12 | -------------------------------------------------------------------------------- /annotations/src/main/java/net/je2sh/annotations/CommandProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.annotations; 26 | 27 | import java.io.IOException; 28 | import java.util.Arrays; 29 | import java.util.Collections; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Objects; 34 | import java.util.Set; 35 | 36 | import com.beust.jcommander.Parameters; 37 | import com.google.auto.service.AutoService; 38 | import com.squareup.javapoet.AnnotationSpec; 39 | import com.squareup.javapoet.ClassName; 40 | import com.squareup.javapoet.JavaFile; 41 | import com.squareup.javapoet.MethodSpec; 42 | import com.squareup.javapoet.ParameterizedTypeName; 43 | import com.squareup.javapoet.TypeName; 44 | import com.squareup.javapoet.TypeSpec; 45 | import com.squareup.javapoet.WildcardTypeName; 46 | import javax.annotation.processing.AbstractProcessor; 47 | import javax.annotation.processing.Processor; 48 | import javax.annotation.processing.RoundEnvironment; 49 | import javax.annotation.processing.SupportedAnnotationTypes; 50 | import javax.annotation.processing.SupportedSourceVersion; 51 | import javax.lang.model.SourceVersion; 52 | import javax.lang.model.element.Element; 53 | import javax.lang.model.element.Modifier; 54 | import javax.lang.model.element.Name; 55 | import javax.lang.model.element.PackageElement; 56 | import javax.lang.model.element.TypeElement; 57 | import javax.lang.model.type.TypeMirror; 58 | import javax.tools.Diagnostic; 59 | import net.je2sh.core.Command; 60 | import net.je2sh.core.CommandProvider; 61 | 62 | 63 | 64 | /** 65 | * Annotation processor capable of understanding classes annotated with {@link Parameters} and 66 | * generating a {@link CommandProvider} for such commands. 67 | *

68 | * This processor uses JavaPoet to build 69 | * the new class(es). 70 | */ 71 | @SupportedAnnotationTypes("com.beust.jcommander.Parameters") 72 | @SupportedSourceVersion(SourceVersion.RELEASE_8) 73 | @AutoService(Processor.class) 74 | public class CommandProcessor extends AbstractProcessor { 75 | 76 | @Override 77 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 78 | 79 | Map types = new HashMap<>(); 80 | TypeMirror expectedInterfaceType = 81 | processingEnv.getElementUtils().getTypeElement(Command.class.getCanonicalName()) 82 | .asType(); 83 | PackageElement pkg = null; 84 | for (TypeElement a : annotations) { 85 | for (Element element : roundEnv.getElementsAnnotatedWith(a)) { 86 | Parameters parameters = 87 | Objects.requireNonNull(element.getAnnotation(Parameters.class)); 88 | 89 | TypeMirror typeMirror = element.asType(); 90 | 91 | if (pkg == null) { 92 | pkg = processingEnv.getElementUtils().getPackageOf(element); 93 | } 94 | 95 | if (!processingEnv.getTypeUtils().isAssignable(typeMirror, expectedInterfaceType)) { 96 | processingEnv.getMessager() 97 | .printMessage(Diagnostic.Kind.ERROR, 98 | String.format("Class %s does not implement %s", 99 | element.getSimpleName(), 100 | Command.class.getCanonicalName())); 101 | } 102 | 103 | for (String commandName : extractCommandNames(element.getSimpleName(), 104 | parameters.commandNames())) { 105 | types.put(commandName, typeMirror); 106 | } 107 | } 108 | } 109 | 110 | if (!types.isEmpty()) { 111 | try { 112 | writeFile(types, pkg); 113 | } 114 | catch (IOException e) { 115 | processingEnv.getMessager() 116 | .printMessage(Diagnostic.Kind.ERROR, 117 | "Unable to process jeesh annotations: " + 118 | e.getMessage()); 119 | } 120 | } 121 | return false; 122 | } 123 | 124 | private void writeFile(Map types, 125 | PackageElement pkg) throws IOException 126 | { 127 | TypeName wildcardClass = ParameterizedTypeName.get(ClassName.get(Class.class), 128 | WildcardTypeName 129 | .subtypeOf(Command.class)); 130 | ParameterizedTypeName returnType = 131 | ParameterizedTypeName.get(ClassName.get(Map.class), 132 | ClassName.get(String.class), wildcardClass); 133 | 134 | MethodSpec.Builder methodBuilder = 135 | MethodSpec.methodBuilder("getCommands") 136 | .addAnnotation(Override.class) 137 | .addModifiers(Modifier.PUBLIC) 138 | .returns(returnType) 139 | .addStatement("$T knownTypes = new $T<>()", 140 | returnType, ClassName.get(HashMap.class)); 141 | 142 | for (Map.Entry entry : types.entrySet()) { 143 | methodBuilder.addStatement("knownTypes.put($S, $T.class)", entry.getKey(), 144 | entry.getValue()); 145 | } 146 | 147 | MethodSpec getcommands = methodBuilder.addStatement("return knownTypes") 148 | .build(); 149 | 150 | AnnotationSpec autoServiceAnnotation = 151 | AnnotationSpec.builder(AutoService.class) 152 | .addMember("value", "$T.class", CommandProvider.class) 153 | .build(); 154 | 155 | TypeSpec commandProvider = TypeSpec.classBuilder("CommandProviderImpl") 156 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL) 157 | .addSuperinterface(CommandProvider.class) 158 | .addAnnotation(autoServiceAnnotation) 159 | .addMethod(getcommands) 160 | .build(); 161 | 162 | JavaFile javaFile = JavaFile.builder(pkg.getQualifiedName().toString() + ".provider", 163 | commandProvider).build(); 164 | javaFile.writeTo(processingEnv.getFiler()); 165 | } 166 | 167 | private List extractCommandNames(Name elementName, String[] commandNames) { 168 | if (commandNames.length == 0) { 169 | String className = elementName.toString(); 170 | StringBuilder builder = new StringBuilder() 171 | .append(Character.toLowerCase(className.charAt(0))) 172 | .append(className.substring(1, className.length())); 173 | 174 | return Collections.singletonList(builder.toString()); 175 | } 176 | return Arrays.asList(commandNames); 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /annotations/src/test/java/net/je2sh/annotations/CommandProcessorTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.annotations; 26 | 27 | import static com.google.common.truth.Truth.assertAbout; 28 | import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; 29 | 30 | import com.google.testing.compile.JavaFileObjects; 31 | import javax.tools.JavaFileObject; 32 | import org.junit.Test; 33 | 34 | 35 | 36 | public class CommandProcessorTests { 37 | 38 | @Test 39 | public void commandWithoutAnnotationShouldFailt() { 40 | assertAbout(javaSource()) 41 | .that(CLASS_WITHOUT_SUPER) 42 | .processedWith(new CommandProcessor()) 43 | .failsToCompile() 44 | .withErrorContaining("Class TestingCommand does not implement"); 45 | } 46 | 47 | @Test 48 | public void commandWithoutNameUsesClassName() { 49 | assertAbout(javaSource()) 50 | .that(CLASS_WITHOUT_NAMES) 51 | .processedWith(new CommandProcessor()) 52 | .compilesWithoutError() 53 | .and() 54 | .generatesSources(JavaFileObjects.forSourceString( 55 | "net.je2sh.annotations.provider.CommandProviderImpl.java", 56 | "package net.je2sh.annotations.provider;\n" + 57 | "\n" + 58 | "import com.google.auto.service.AutoService;\n" + 59 | "import java.lang.Class;\n" + 60 | "import java.lang.Override;\n" + 61 | "import java.lang.String;\n" + 62 | "import java.util.HashMap;\n" + 63 | "import java.util.Map;\n" + 64 | "import net.je2sh.annotations.TestingCommand;\n" + 65 | "import net.je2sh.core.Command;\n" + 66 | "import net.je2sh.core.CommandProvider;\n" + 67 | "\n" + 68 | "@AutoService(CommandProvider.class)\n" + 69 | "public final class CommandProviderImpl implements CommandProvider {\n" + 70 | " @Override\n" + 71 | " public Map> getCommands() {\n" + 72 | " Map> knownTypes = new HashMap<>();\n" + 73 | " knownTypes.put(\"testingCommand\", TestingCommand.class);\n" + 74 | " return knownTypes;\n" + 75 | " }\n" + 76 | "}")); 77 | } 78 | 79 | @Test 80 | public void commandWithAnnotationShoudSucceed() { 81 | assertAbout(javaSource()) 82 | .that(CLASS_WITH_ANNOTATION) 83 | .processedWith(new CommandProcessor()) 84 | .compilesWithoutError() 85 | .and() 86 | .generatesSources(JavaFileObjects.forSourceString( 87 | "net.je2sh.annotations.provider.CommandProviderImpl.java", 88 | "package net.je2sh.annotations.provider;\n" + 89 | "\n" + 90 | "import com.google.auto.service.AutoService;\n" + 91 | "import java.lang.Class;\n" + 92 | "import java.lang.Override;\n" + 93 | "import java.lang.String;\n" + 94 | "import java.util.HashMap;\n" + 95 | "import java.util.Map;\n" + 96 | "import net.je2sh.annotations.TestingCommand;\n" + 97 | "import net.je2sh.core.Command;\n" + 98 | "import net.je2sh.core.CommandProvider;\n" + 99 | "\n" + 100 | "@AutoService(CommandProvider.class)\n" + 101 | "public final class CommandProviderImpl implements CommandProvider {\n" + 102 | " @Override\n" + 103 | " public Map> getCommands() {\n" + 104 | " Map> knownTypes = new HashMap<>();\n" + 105 | " knownTypes.put(\"testing\", TestingCommand.class);\n" + 106 | " return knownTypes;\n" + 107 | " }\n" + 108 | "}")); 109 | } 110 | 111 | /* ### Utility constants ### */ 112 | 113 | private static final JavaFileObject CLASS_WITH_ANNOTATION = 114 | JavaFileObjects.forSourceLines("net.je2sh.annotations.TestingCommand", 115 | "package net.je2sh.annotations;\n" + 116 | "\n" + 117 | "import com.beust.jcommander.Parameters;\n" + 118 | "import net.je2sh.core.AbstractCommand;\n" + 119 | "import net.je2sh.core.CommandContext;\n" + 120 | "import org.jetbrains.annotations.NotNull;\n" + 121 | "\n" + 122 | "\n" + 123 | "@Parameters(commandNames = \"testing\")\n" + 124 | "public class TestingCommand extends AbstractCommand {\n" + 125 | "\n" + 126 | " @Override\n" + 127 | " public void execute(@NotNull CommandContext context) {\n" + 128 | " // noop\n" + 129 | " }\n" + 130 | "}"); 131 | 132 | private static final JavaFileObject CLASS_WITHOUT_SUPER = 133 | JavaFileObjects.forSourceLines("net.je2sh.annotations.TestingCommand", 134 | "package net.je2sh.annotations;\n" + 135 | "\n" + 136 | "import com.beust.jcommander.Parameters;\n" + 137 | "import net.je2sh.core.AbstractCommand;\n" + 138 | "import net.je2sh.core.CommandContext;\n" + 139 | "import org.jetbrains.annotations.NotNull;\n" + 140 | "\n" + 141 | "@Parameters(commandNames = \"testing\")\n" + 142 | "public class TestingCommand {\n" + 143 | "\n" + 144 | " public void execute(@NotNull CommandContext context) {\n" + 145 | " // noop\n" + 146 | " }\n" + 147 | "}\n" + 148 | "\n" + 149 | ""); 150 | 151 | private static final JavaFileObject CLASS_WITHOUT_NAMES = 152 | JavaFileObjects.forSourceLines("net.je2sh.annotations.TestingCommand", 153 | "\n" + 154 | "package net.je2sh.annotations;\n" + 155 | "\n" + 156 | "import com.beust.jcommander.Parameters;\n" + 157 | "import net.je2sh.core.AbstractCommand;\n" + 158 | "import net.je2sh.core.CommandContext;\n" + 159 | "import org.jetbrains.annotations.NotNull;\n" + 160 | "\n" + 161 | "@Parameters\n" + 162 | "public class TestingCommand extends AbstractCommand {\n" + 163 | "\n" + 164 | " @Override\n" + 165 | " public void execute(@NotNull CommandContext context) {\n" + 166 | " // noop\n" + 167 | " }\n" + 168 | "}"); 169 | } 170 | -------------------------------------------------------------------------------- /base-plugins/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compileOnly project(':core') 3 | compileOnly project(':annotations') 4 | 5 | } 6 | -------------------------------------------------------------------------------- /base-plugins/src/main/java/net/je2sh/base/Help.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.base; 26 | 27 | import java.util.Arrays; 28 | import java.util.Collections; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Optional; 32 | import java.util.TreeMap; 33 | import java.util.stream.Collectors; 34 | 35 | import com.beust.jcommander.Parameters; 36 | import net.je2sh.asciitable.AnsiContentParser; 37 | import net.je2sh.asciitable.JTable; 38 | import net.je2sh.asciitable.style.JPadding; 39 | import net.je2sh.asciitable.style.JTheme; 40 | import net.je2sh.core.AbstractCommand; 41 | import net.je2sh.core.Command; 42 | import net.je2sh.core.CommandContext; 43 | import org.fusesource.jansi.Ansi; 44 | import org.jetbrains.annotations.NotNull; 45 | import org.jline.utils.AttributedStringBuilder; 46 | import org.jline.utils.AttributedStyle; 47 | 48 | 49 | 50 | @Parameters(commandNames = "help", commandDescription = "Shows the available commands") 51 | public class Help extends AbstractCommand { 52 | 53 | @Override 54 | public void execute(@NotNull CommandContext context) { 55 | context.print("List of available commands. "); 56 | 57 | AttributedStringBuilder headerBuilder = 58 | new AttributedStringBuilder() 59 | .append("To find more about a specific command run ") 60 | .style(AttributedStyle.BOLD.foreground(AttributedStyle.RED)) 61 | .append(" -h"); 62 | 63 | context.println(headerBuilder.toAnsi(context.getTerminal())); 64 | 65 | Map> commandMap = 66 | context.getPluginContext().getCommandManager().getCommands(); 67 | 68 | AnsiContentParser ansiContentParser = new AnsiContentParser(); 69 | 70 | JTable table = JTable.of() 71 | .width(60) 72 | .contentParser(ansiContentParser) 73 | .theme(JTheme.NO_LINE); 74 | 75 | Map helpMap = new TreeMap<>(); 76 | int maxCommandLength = 1; 77 | 78 | for (Class command : commandMap.values()) { 79 | String cmds = getCommandNames(command).stream() 80 | .map(s -> Ansi.ansi().fgRed().bold().a(s).reset() 81 | .toString()) 82 | .collect(Collectors.joining(", ")); 83 | 84 | helpMap.put(cmds, getDescription(command)); 85 | maxCommandLength = Math.max(maxCommandLength, ansiContentParser.getLength(cmds)); 86 | } 87 | 88 | for (Map.Entry entry : helpMap.entrySet()) { 89 | table.row() 90 | .col().width(maxCommandLength + 6) 91 | .padding(new JPadding(5, 0, 0, 0, ' ')) 92 | .content(Ansi.ansi().fgRed().bold().a(entry.getKey()).reset()) 93 | .done() 94 | .col().content(entry.getValue()); 95 | } 96 | 97 | table.render().forEach(context::println); 98 | } 99 | 100 | private static List getCommandNames(Class tClass) { 101 | Parameters paramAnn = tClass.getAnnotation(Parameters.class); 102 | if (paramAnn.commandNames().length == 0) { 103 | String simpleName = tClass.getClass().getSimpleName(); 104 | simpleName = Character.toLowerCase(simpleName.charAt(0)) + 105 | simpleName.substring(1, simpleName.length()); 106 | return Collections.singletonList(simpleName); 107 | } 108 | return Arrays.asList(paramAnn.commandNames()); 109 | } 110 | 111 | private static String getDescription(Class tClass) { 112 | return Optional.ofNullable(tClass.getAnnotation(Parameters.class)) 113 | .map(Parameters::commandDescription) 114 | .orElse(""); 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | description 'JVM Extensible Embeddable SHell' 2 | 3 | apply plugin: 'java' 4 | apply plugin: 'net.researchgate.release' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | buildscript { 9 | ext.kotlin_version = '1.1.2-5' 10 | repositories { 11 | mavenCentral() 12 | } 13 | dependencies { 14 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 15 | classpath 'net.researchgate:gradle-release:2.4.0' 16 | 17 | } 18 | } 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | subprojects { 25 | apply plugin: 'java' 26 | apply plugin: 'kotlin' 27 | 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version" 34 | } 35 | } 36 | 37 | configure(subprojects.findAll { 38 | it.name in ['core', 'annotations', 'base-plugins', 'ssh', 'shell', 'spring'] 39 | }) { 40 | apply plugin: 'maven' 41 | apply plugin: 'signing' 42 | 43 | task javadocJar(type: Jar) { 44 | classifier = 'javadoc' 45 | from javadoc 46 | } 47 | 48 | task sourcesJar(type: Jar) { 49 | classifier = 'sources' 50 | from sourceSets.main.allSource 51 | } 52 | 53 | artifacts { 54 | archives javadocJar, sourcesJar 55 | } 56 | 57 | if (project.hasProperty('ossrhUsername')) { 58 | signing { 59 | sign configurations.archives 60 | } 61 | 62 | uploadArchives { 63 | repositories { 64 | mavenDeployer { 65 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 66 | 67 | repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") { 68 | authentication(userName: ossrhUsername, password: ossrhPassword) 69 | } 70 | 71 | snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") { 72 | authentication(userName: ossrhUsername, password: ossrhPassword) 73 | } 74 | 75 | pom.project { 76 | name 'JVM Extensible & Embeddable SHell' 77 | packaging 'jar' 78 | // optionally artifactId can be defined here 79 | description 'Reboot of the CRaSH project' 80 | url 'https://jeeshell.github.io/je2sh/' 81 | 82 | scm { 83 | connection 'scm:https://github.com/jeeshell/je2sh.git' 84 | developerConnection 'scm:https://github.com/jeeshell/je2sh.git' 85 | url 'git@github.com:jeeshell/je2sh.git' 86 | } 87 | 88 | licenses { 89 | license { 90 | name 'MIT License' 91 | url 'https://github.com/jeeshell/je2sh/blob/master/LICENSE.md' 92 | } 93 | } 94 | 95 | developers { 96 | developer { 97 | id 'hartimer' 98 | name 'Joao Peixoto' 99 | email 'joao.hartimer@gmail.com' 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | 3 | dependencies { 4 | compile group: 'org.jline', name: 'jline', version: '3.3.1' 5 | compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' 6 | compile group: 'com.beust', name: 'jcommander', version: '1.69' 7 | compile group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc3' 8 | compile 'com.squareup:javapoet:1.9.0' 9 | compile 'net.je2sh:asciitable:1.0.0' 10 | 11 | testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4' 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /core/src/main/java/net/je2sh/core/AbstractCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core; 26 | 27 | import java.util.Optional; 28 | 29 | import com.beust.jcommander.Parameter; 30 | import com.beust.jcommander.Parameters; 31 | import org.jetbrains.annotations.NotNull; 32 | 33 | 34 | 35 | /** 36 | * Abstract implementations of a {@link Command}. Most/All commands should extend this class. 37 | *

38 | * Automatically adds the command switches {@code -h} and {@code --help} to extending command. 39 | *

40 | * The contents of "help" are the ones specified in {@link Parameters#commandDescription()} or 41 | * empty string if none. 42 | * 43 | * @see Command 44 | */ 45 | public abstract class AbstractCommand implements Command { 46 | 47 | @Parameter(names = { "-h", "--help" }, help = true, description = "Shows this help") 48 | private boolean help; 49 | 50 | @Override 51 | public boolean getHelp() { 52 | return help; 53 | } 54 | 55 | @NotNull 56 | @Override 57 | public String getDescription() { 58 | return Optional.ofNullable(this.getClass().getAnnotation(Parameters.class)) 59 | .map(Parameters::commandDescription) 60 | .orElse(""); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/net/je2sh/core/CommandProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core; 26 | 27 | import java.util.Map; 28 | 29 | 30 | 31 | /** 32 | * Base interface to define a command provider. 33 | *

34 | * We provide the raw {@link Class}es each command execution, which is parsed by 35 | * {@link com.beust.jcommander.JCommander}, does not leave previously parsed arguments behind 36 | *

37 | * Implementations of this interface are automatically generated if the "annotations" module is 38 | * included and there are classes in the classpath annotated 39 | * with {@link com.beust.jcommander.Parameters}. 40 | * 41 | * @see AbstractCommand 42 | */ 43 | public interface CommandProvider { 44 | 45 | Map> getCommands(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/net/je2sh/core/plugins/PluginContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.plugins; 26 | 27 | import java.io.IOException; 28 | import java.security.Principal; 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | import java.util.concurrent.ExecutorService; 32 | 33 | import net.je2sh.core.Bootstrap; 34 | import net.je2sh.core.CommandManager; 35 | import net.je2sh.core.impl.DefaultCommandManager; 36 | 37 | 38 | 39 | /** 40 | * Context holder used by plugins. 41 | *

42 | * Provides relevant information and objects to the plugins. 43 | * 44 | * @see CommandManager 45 | */ 46 | public class PluginContext { 47 | 48 | private final DefaultCommandManager commandManager; 49 | private final ExecutorService executor; 50 | private final Map attributes; 51 | 52 | private Principal principal; 53 | 54 | public PluginContext() throws IOException { 55 | this(new DefaultCommandManager(new Bootstrap()), null); 56 | } 57 | 58 | public PluginContext(ExecutorService executor) throws IOException { 59 | this(new DefaultCommandManager(new Bootstrap()), executor); 60 | } 61 | 62 | public PluginContext(DefaultCommandManager commandManager, ExecutorService executor) 63 | { 64 | this.commandManager = commandManager; 65 | this.executor = executor; 66 | this.attributes = new HashMap<>(); 67 | } 68 | 69 | public DefaultCommandManager getCommandManager() { 70 | return commandManager; 71 | } 72 | 73 | public ExecutorService getExecutor() { 74 | return executor; 75 | } 76 | 77 | public Map getAttributes() { 78 | return attributes; 79 | } 80 | 81 | public void attribute(String name, Object val) { 82 | attributes.put(name, val); 83 | } 84 | 85 | public Principal getPrincipal() { 86 | return principal; 87 | } 88 | 89 | public void setPrincipal(Principal principal) { 90 | this.principal = principal; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/Bootstrap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | import java.util.* 28 | 29 | class Bootstrap { 30 | 31 | val runnablePlugins = ServiceLoader.load(net.je2sh.core.plugins.RunnablePlugin::class.java).toList() 32 | val commandProviders = ServiceLoader.load(CommandProvider::class.java).toList() 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/Command.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | interface Command { 28 | 29 | fun execute(context: CommandContext) 30 | fun getHelp(): Boolean 31 | fun getDescription(): String 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/CommandContext.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | import net.je2sh.core.plugins.PluginContext 28 | import org.jline.terminal.Terminal 29 | 30 | class CommandContext(val pluginContext: PluginContext, val terminal: Terminal) { 31 | 32 | fun print(t: Any) = terminal.writer().print(t) 33 | fun println(t: Any) = terminal.writer().println(t) 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/CommandManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | interface CommandManager { 28 | 29 | fun runCommand(name: String, context: CommandContext, vararg args: String) 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/JeeShell.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | import net.je2sh.core.plugins.PluginContext 28 | import org.jline.reader.EndOfFileException 29 | import org.jline.reader.LineReaderBuilder 30 | import org.jline.reader.UserInterruptException 31 | import org.jline.terminal.Terminal 32 | import org.jline.terminal.TerminalBuilder 33 | import java.lang.Exception 34 | 35 | class JeeShell(val context: PluginContext, 36 | val terminal: Terminal = TerminalBuilder.terminal(), 37 | val commandManager: CommandManager = context.commandManager) { 38 | private val EXIT_CMD = "exit" 39 | private var running = false 40 | 41 | fun run() { 42 | val prompt: String = "${context.attributes["prompt"]?.toString() ?: context.principal?.name ?: "jeesh"} $ " 43 | val reader = LineReaderBuilder.builder() 44 | .terminal(terminal) 45 | .build() 46 | running = true 47 | 48 | while (running) { 49 | 50 | try { 51 | val line = reader.readLine(prompt, null, null, null).trim { it <= ' ' } 52 | 53 | if (line.isEmpty()) { 54 | continue 55 | } 56 | 57 | val args = line.split(" ") 58 | val cmdStr = args[0] 59 | 60 | if (EXIT_CMD == cmdStr) { 61 | break 62 | } 63 | 64 | commandManager.runCommand(cmdStr, CommandContext(context, terminal), *args.toTypedArray()) 65 | 66 | } catch (e: UserInterruptException) { 67 | // Ignore 68 | } catch (e: EndOfFileException) { 69 | break 70 | } catch (e: Exception) { 71 | terminal.writer().println("Command failed with \"" + e.message + "\"") 72 | } 73 | 74 | } 75 | 76 | terminal.writer().println("See you next time") 77 | terminal.writer().flush() 78 | terminal.close() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/annotations/CommandProcessor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.annotations 26 | 27 | import javax.annotation.processing.AbstractProcessor 28 | import javax.annotation.processing.RoundEnvironment 29 | import javax.lang.model.element.TypeElement 30 | 31 | 32 | class CommandProcessor : AbstractProcessor() { 33 | override fun process(annotations: MutableSet?, roundEnv: RoundEnvironment?): Boolean { 34 | return false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/errors/DuplicateCommandException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.errors 26 | 27 | class DuplicateCommandException(message: String?) : Exception(message) { 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/impl/DefaultCommandManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.impl 26 | 27 | import com.beust.jcommander.JCommander 28 | import com.beust.jcommander.ParameterException 29 | import net.je2sh.core.Bootstrap 30 | import net.je2sh.core.Command 31 | import net.je2sh.core.CommandContext 32 | import net.je2sh.core.CommandManager 33 | 34 | class DefaultCommandManager(bootstrap: Bootstrap) : CommandManager { 35 | 36 | val commands: Map> 37 | 38 | init { 39 | commands = bootstrap.commandProviders.map { it.commands } 40 | .reduce { acc, mutableMap -> acc + mutableMap } 41 | } 42 | 43 | 44 | override fun runCommand(name: String, context: CommandContext, vararg args: String) { 45 | val cmdClass = commands[name] 46 | if (cmdClass != null) { 47 | val cmdInstance = cmdClass.newInstance() 48 | try { 49 | val jCommander = JCommander.newBuilder() 50 | .addObject(cmdInstance) 51 | .programName(name) 52 | .build() 53 | 54 | if (args.size > 1) { 55 | jCommander.parse(*args.sliceArray(IntRange(1, args.size - 1))) 56 | } 57 | 58 | if (cmdInstance.getHelp()) { 59 | val usageStr = StringBuilder() 60 | jCommander.usage(usageStr) 61 | context.println(usageStr) 62 | } else { 63 | cmdInstance.execute(context) 64 | } 65 | } catch (e: ParameterException) { 66 | context.println("Unable to parse command: ${e.message}. Try \"$name -h\" for a list of the available options") 67 | } 68 | } else { 69 | context.println("Command \"$name\" not found. Try \"help\" for a list of available commands") 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/impl/EmptyCommandManager.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.impl 26 | 27 | import net.je2sh.core.CommandContext 28 | import net.je2sh.core.CommandManager 29 | 30 | class EmptyCommandManager : CommandManager { 31 | override fun runCommand(name: String, context: CommandContext, vararg args: String) { 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/plugins/Plugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.plugins 26 | 27 | import net.je2sh.core.CommandManager 28 | 29 | interface Plugin { 30 | 31 | fun configureCommands(commandManager: CommandManager) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/plugins/PluginLifeCycle.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.plugins 26 | 27 | import net.je2sh.core.JeeShell 28 | import java.io.Closeable 29 | import java.util.concurrent.Future 30 | 31 | abstract class PluginLifeCycle(val context: PluginContext) : Closeable, Runnable { 32 | 33 | private val jeeShell = JeeShell(context) 34 | private var future: Future<*>? = null 35 | 36 | fun start() { 37 | if (context.executor == null) { 38 | run() 39 | } else { 40 | future = context.executor.submit(this) 41 | } 42 | } 43 | 44 | fun stop() { 45 | if (future != null) { 46 | context.executor.shutdownNow() 47 | } 48 | } 49 | 50 | override fun close() = stop() 51 | 52 | override fun run() = jeeShell.run() 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/kotlin/net/je2sh/core/plugins/RunnablePlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core.plugins 26 | 27 | import net.je2sh.core.CommandManager 28 | 29 | abstract class RunnablePlugin(val name: String, val interactive: Boolean = false) : Runnable { 30 | 31 | var commandManager: CommandManager? = null 32 | 33 | open fun configureCommands(commandManager: CommandManager) { 34 | this.commandManager = commandManager 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/groovy/net/je2sh/core/CommandTests.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.core 26 | 27 | import com.beust.jcommander.Parameters 28 | import org.jetbrains.annotations.NotNull 29 | import spock.lang.Specification 30 | 31 | class CommandTests extends Specification { 32 | 33 | static class NoParametersCommand extends AbstractCommand { 34 | @Override 35 | void execute(@NotNull CommandContext context) { 36 | // noop 37 | } 38 | } 39 | 40 | @Parameters 41 | static class NoDescriptionCommand extends AbstractCommand { 42 | @Override 43 | void execute(@NotNull CommandContext context) { 44 | // noop 45 | } 46 | } 47 | 48 | @Parameters(commandDescription = 'A sample command') 49 | static class WithDescriptionCommand extends AbstractCommand { 50 | @Override 51 | void execute(@NotNull CommandContext context) { 52 | // noop 53 | } 54 | } 55 | 56 | def 'Command without @Parameters should return empty string'() { 57 | expect: 58 | new NoParametersCommand().description == '' 59 | } 60 | 61 | def 'Command without @Parameters description should return empty string'() { 62 | expect: 63 | new NoDescriptionCommand().description == '' 64 | } 65 | 66 | def 'Command with @Parameters description should return description'() { 67 | expect: 68 | new WithDescriptionCommand().description == 'A sample command' 69 | } 70 | 71 | 72 | } 73 | -------------------------------------------------------------------------------- /examples/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.5.4.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | apply plugin: 'java' 14 | apply plugin: 'groovy' 15 | apply plugin: 'eclipse' 16 | apply plugin: 'org.springframework.boot' 17 | 18 | version = '0.0.1-SNAPSHOT' 19 | sourceCompatibility = 1.8 20 | 21 | repositories { 22 | mavenCentral() 23 | } 24 | 25 | 26 | dependencies { 27 | compile('org.springframework.boot:spring-boot-starter-web') 28 | compile("org.springframework.boot:spring-boot-starter-thymeleaf") 29 | 30 | compile project(":spring") 31 | 32 | compileOnly project(":annotations") 33 | 34 | testCompile project(':test') 35 | testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4' 36 | } 37 | -------------------------------------------------------------------------------- /examples/src/main/java/net/je2sh/examples/DemoApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples; 26 | 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | 30 | 31 | 32 | @SpringBootApplication 33 | public class DemoApplication { 34 | 35 | public static void main(String[] args) { 36 | SpringApplication.run(DemoApplication.class, args); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/main/java/net/je2sh/examples/DemoController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples; 26 | 27 | import org.springframework.stereotype.Controller; 28 | import org.springframework.web.bind.annotation.GetMapping; 29 | 30 | 31 | 32 | @Controller 33 | public class DemoController { 34 | 35 | @GetMapping("/") 36 | public String home() { 37 | return "home"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/src/main/java/net/je2sh/examples/commands/Echo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples.commands; 26 | 27 | import java.util.List; 28 | 29 | import com.beust.jcommander.Parameter; 30 | import com.beust.jcommander.ParameterException; 31 | import com.beust.jcommander.Parameters; 32 | import net.je2sh.core.AbstractCommand; 33 | import net.je2sh.core.CommandContext; 34 | import org.jetbrains.annotations.NotNull; 35 | 36 | 37 | 38 | @Parameters(commandNames = "echo", commandDescription = "Echos what you pass in") 39 | public class Echo extends AbstractCommand { 40 | 41 | @Parameter(description = "Message to echo", required = true) 42 | private List message; 43 | 44 | @Override 45 | public void execute(@NotNull CommandContext context) { 46 | if (message == null) throw new ParameterException("No message provided"); 47 | 48 | context.println(String.join(" ", message)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/src/main/java/net/je2sh/examples/commands/Hello.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples.commands; 26 | 27 | import java.util.Optional; 28 | 29 | import com.beust.jcommander.Parameter; 30 | import com.beust.jcommander.Parameters; 31 | import net.je2sh.core.AbstractCommand; 32 | import net.je2sh.core.CommandContext; 33 | import org.jetbrains.annotations.NotNull; 34 | 35 | 36 | @Parameters(commandNames = {"hello", "hi"} , commandDescription = "Just says hello") 37 | public class Hello extends AbstractCommand { 38 | 39 | @Parameter(names = {"-n", "--name"}, description = "Your name") 40 | private String name; 41 | 42 | @Override 43 | public void execute(@NotNull CommandContext context) { 44 | context.println("Hello " + Optional.ofNullable(name).orElse("World")); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /examples/src/main/java/net/je2sh/examples/plugin/SpringPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples.plugin; 26 | 27 | import com.beust.jcommander.Parameters; 28 | import net.je2sh.core.AbstractCommand; 29 | import net.je2sh.core.CommandContext; 30 | import net.je2sh.examples.DemoController; 31 | import org.jetbrains.annotations.NotNull; 32 | import org.springframework.context.ApplicationContext; 33 | 34 | 35 | 36 | @Parameters(commandNames = "names") 37 | public class SpringPlugin extends AbstractCommand { 38 | 39 | @Override 40 | public void execute(@NotNull CommandContext context) { 41 | context.println("Running from spring"); 42 | 43 | ApplicationContext beanFactory = 44 | (ApplicationContext) context.getPluginContext().getAttributes().get("spring.context"); 45 | 46 | DemoController bean = beanFactory.getBean(DemoController.class); 47 | 48 | context.println("The bean holds " + bean); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # 4 | # Copyright (c) 2017 JeeSh 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | logging.level.net.je2sh=DEBUG 26 | 27 | security.user.name=admin 28 | security.user.password=admin1 29 | 30 | jeesh.ssh.port=2003 31 | 32 | -------------------------------------------------------------------------------- /examples/src/main/resources/static/js/home.js: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | jQuery(function ($, undefined) { 26 | $('#term_demo').terminal('/jeesh/exec', { 27 | greetings: 'Jeesh REST', 28 | name: 'jeesh', 29 | memory: true, 30 | height: 400, 31 | width: 950, 32 | prompt: 'jeesh > ', 33 | onAjaxError: function (xhr, status, error) { 34 | switch (xhr.status) { 35 | case 403: 36 | this.echo('Login first'); 37 | break; 38 | case 500: 39 | this.echo('Something went wrong on the server side... try again later please'); 40 | break; 41 | default: 42 | this.echo('Unexpected error. Please try again later'); 43 | console.log("Ajax error boy", status, 'and', error, 'and', xhr); 44 | break; 45 | } 46 | } 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /examples/src/main/resources/static/js/unix_formatting.js: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | /* global jQuery */ 25 | (function($) { 26 | if (!$.terminal) { 27 | throw new Error('$.terminal is not defined'); 28 | } 29 | // --------------------------------------------------------------------- 30 | // :: Replace overtyping (from man) formatting with terminal formatting 31 | // --------------------------------------------------------------------- 32 | $.terminal.overtyping = function overtyping(string) { 33 | return string.replace(/((?:_\x08.|.\x08_)+)/g, function(full) { 34 | var striped = full.replace(/_x08|\x08_|_\u0008|\u0008_/g, ''); 35 | return '[[u;;]' + striped + ']'; 36 | }).replace(/((?:.\x08.)+)/g, function(full) { 37 | return '[[b;#fff;]' + full.replace(/(.)(?:\x08|\u0008)\1/g, 38 | function(full, g) { 39 | return g; 40 | }) + ']'; 41 | }); 42 | }; 43 | // --------------------------------------------------------------------- 44 | // :: Html colors taken from ANSI formatting in Linux Terminal 45 | // --------------------------------------------------------------------- 46 | $.terminal.ansi_colors = { 47 | normal: { 48 | black: '#000', 49 | red: '#A00', 50 | green: '#008400', 51 | yellow: '#A50', 52 | blue: '#00A', 53 | magenta: '#A0A', 54 | cyan: '#0AA', 55 | white: '#AAA' 56 | }, 57 | faited: { 58 | black: '#000', 59 | red: '#640000', 60 | green: '#006100', 61 | yellow: '#737300', 62 | blue: '#000087', 63 | magenta: '#650065', 64 | cyan: '#008787', 65 | white: '#818181' 66 | }, 67 | bold: { 68 | black: '#000', 69 | red: '#F55', 70 | green: '#44D544', 71 | yellow: '#FF5', 72 | blue: '#55F', 73 | magenta: '#F5F', 74 | cyan: '#5FF', 75 | white: '#FFF' 76 | }, 77 | // XTerm 8-bit pallete 78 | palette: [ 79 | '#000000', '#AA0000', '#00AA00', '#AA5500', '#0000AA', '#AA00AA', 80 | '#00AAAA', '#AAAAAA', '#555555', '#FF5555', '#55FF55', '#FFFF55', 81 | '#5555FF', '#FF55FF', '#55FFFF', '#FFFFFF', '#000000', '#00005F', 82 | '#000087', '#0000AF', '#0000D7', '#0000FF', '#005F00', '#005F5F', 83 | '#005F87', '#005FAF', '#005FD7', '#005FFF', '#008700', '#00875F', 84 | '#008787', '#0087AF', '#0087D7', '#0087FF', '#00AF00', '#00AF5F', 85 | '#00AF87', '#00AFAF', '#00AFD7', '#00AFFF', '#00D700', '#00D75F', 86 | '#00D787', '#00D7AF', '#00D7D7', '#00D7FF', '#00FF00', '#00FF5F', 87 | '#00FF87', '#00FFAF', '#00FFD7', '#00FFFF', '#5F0000', '#5F005F', 88 | '#5F0087', '#5F00AF', '#5F00D7', '#5F00FF', '#5F5F00', '#5F5F5F', 89 | '#5F5F87', '#5F5FAF', '#5F5FD7', '#5F5FFF', '#5F8700', '#5F875F', 90 | '#5F8787', '#5F87AF', '#5F87D7', '#5F87FF', '#5FAF00', '#5FAF5F', 91 | '#5FAF87', '#5FAFAF', '#5FAFD7', '#5FAFFF', '#5FD700', '#5FD75F', 92 | '#5FD787', '#5FD7AF', '#5FD7D7', '#5FD7FF', '#5FFF00', '#5FFF5F', 93 | '#5FFF87', '#5FFFAF', '#5FFFD7', '#5FFFFF', '#870000', '#87005F', 94 | '#870087', '#8700AF', '#8700D7', '#8700FF', '#875F00', '#875F5F', 95 | '#875F87', '#875FAF', '#875FD7', '#875FFF', '#878700', '#87875F', 96 | '#878787', '#8787AF', '#8787D7', '#8787FF', '#87AF00', '#87AF5F', 97 | '#87AF87', '#87AFAF', '#87AFD7', '#87AFFF', '#87D700', '#87D75F', 98 | '#87D787', '#87D7AF', '#87D7D7', '#87D7FF', '#87FF00', '#87FF5F', 99 | '#87FF87', '#87FFAF', '#87FFD7', '#87FFFF', '#AF0000', '#AF005F', 100 | '#AF0087', '#AF00AF', '#AF00D7', '#AF00FF', '#AF5F00', '#AF5F5F', 101 | '#AF5F87', '#AF5FAF', '#AF5FD7', '#AF5FFF', '#AF8700', '#AF875F', 102 | '#AF8787', '#AF87AF', '#AF87D7', '#AF87FF', '#AFAF00', '#AFAF5F', 103 | '#AFAF87', '#AFAFAF', '#AFAFD7', '#AFAFFF', '#AFD700', '#AFD75F', 104 | '#AFD787', '#AFD7AF', '#AFD7D7', '#AFD7FF', '#AFFF00', '#AFFF5F', 105 | '#AFFF87', '#AFFFAF', '#AFFFD7', '#AFFFFF', '#D70000', '#D7005F', 106 | '#D70087', '#D700AF', '#D700D7', '#D700FF', '#D75F00', '#D75F5F', 107 | '#D75F87', '#D75FAF', '#D75FD7', '#D75FFF', '#D78700', '#D7875F', 108 | '#D78787', '#D787AF', '#D787D7', '#D787FF', '#D7AF00', '#D7AF5F', 109 | '#D7AF87', '#D7AFAF', '#D7AFD7', '#D7AFFF', '#D7D700', '#D7D75F', 110 | '#D7D787', '#D7D7AF', '#D7D7D7', '#D7D7FF', '#D7FF00', '#D7FF5F', 111 | '#D7FF87', '#D7FFAF', '#D7FFD7', '#D7FFFF', '#FF0000', '#FF005F', 112 | '#FF0087', '#FF00AF', '#FF00D7', '#FF00FF', '#FF5F00', '#FF5F5F', 113 | '#FF5F87', '#FF5FAF', '#FF5FD7', '#FF5FFF', '#FF8700', '#FF875F', 114 | '#FF8787', '#FF87AF', '#FF87D7', '#FF87FF', '#FFAF00', '#FFAF5F', 115 | '#FFAF87', '#FFAFAF', '#FFAFD7', '#FFAFFF', '#FFD700', '#FFD75F', 116 | '#FFD787', '#FFD7AF', '#FFD7D7', '#FFD7FF', '#FFFF00', '#FFFF5F', 117 | '#FFFF87', '#FFFFAF', '#FFFFD7', '#FFFFFF', '#080808', '#121212', 118 | '#1C1C1C', '#262626', '#303030', '#3A3A3A', '#444444', '#4E4E4E', 119 | '#585858', '#626262', '#6C6C6C', '#767676', '#808080', '#8A8A8A', 120 | '#949494', '#9E9E9E', '#A8A8A8', '#B2B2B2', '#BCBCBC', '#C6C6C6', 121 | '#D0D0D0', '#DADADA', '#E4E4E4', '#EEEEEE' 122 | ] 123 | }; 124 | // --------------------------------------------------------------------- 125 | // :: Replace ANSI formatting with terminal formatting 126 | // --------------------------------------------------------------------- 127 | $.terminal.from_ansi = (function() { 128 | var color_list = { 129 | 30: 'black', 130 | 31: 'red', 131 | 32: 'green', 132 | 33: 'yellow', 133 | 34: 'blue', 134 | 35: 'magenta', 135 | 36: 'cyan', 136 | 37: 'white', 137 | 138 | 39: 'inherit' // default color 139 | }; 140 | var background_list = { 141 | 40: 'black', 142 | 41: 'red', 143 | 42: 'green', 144 | 43: 'yellow', 145 | 44: 'blue', 146 | 45: 'magenta', 147 | 46: 'cyan', 148 | 47: 'white', 149 | 150 | 49: 'transparent' // default background 151 | }; 152 | function format_ansi(code) { 153 | var controls = code.split(';'); 154 | var num; 155 | var faited = false; 156 | var reverse = false; 157 | var bold = false; 158 | var styles = []; 159 | var output_color = ''; 160 | var output_background = ''; 161 | var _8bit_color = false; 162 | var _8bit_background = false; 163 | var process_8bit = false; 164 | var palette = $.terminal.ansi_colors.palette; 165 | function set_styles(num) { 166 | switch (num) { 167 | case 1: 168 | styles.push('b'); 169 | bold = true; 170 | faited = false; 171 | break; 172 | case 4: 173 | styles.push('u'); 174 | break; 175 | case 3: 176 | styles.push('i'); 177 | break; 178 | case 5: 179 | process_8bit = true; 180 | break; 181 | case 38: 182 | _8bit_color = true; 183 | break; 184 | case 48: 185 | _8bit_background = true; 186 | break; 187 | case 2: 188 | faited = true; 189 | bold = false; 190 | break; 191 | case 7: 192 | reverse = true; 193 | break; 194 | default: 195 | if (controls.indexOf('5') === -1) { 196 | if (color_list[num]) { 197 | output_color = color_list[num]; 198 | } 199 | if (background_list[num]) { 200 | output_background = background_list[num]; 201 | } 202 | } 203 | } 204 | } 205 | for (var i in controls) { 206 | if (controls.hasOwnProperty(i)) { 207 | num = parseInt(controls[i], 10); 208 | if (process_8bit && (_8bit_background || _8bit_color)) { 209 | if (_8bit_color && palette[num]) { 210 | output_color = palette[num]; 211 | } 212 | if (_8bit_background && palette[num]) { 213 | output_background = palette[num]; 214 | } 215 | } else { 216 | set_styles(num); 217 | } 218 | } 219 | } 220 | if (reverse) { 221 | if (output_color || output_background) { 222 | var tmp = output_background; 223 | output_background = output_color; 224 | output_color = tmp; 225 | } else { 226 | output_color = 'black'; 227 | output_background = 'white'; 228 | } 229 | } 230 | var colors, color, background, backgrounds; 231 | if (bold) { 232 | colors = backgrounds = $.terminal.ansi_colors.bold; 233 | } else if (faited) { 234 | colors = backgrounds = $.terminal.ansi_colors.faited; 235 | } else { 236 | colors = backgrounds = $.terminal.ansi_colors.normal; 237 | } 238 | if (_8bit_color) { 239 | color = output_color; 240 | } else if (output_color === 'inherit') { 241 | color = output_color; 242 | } else { 243 | color = colors[output_color]; 244 | } 245 | if (_8bit_background) { 246 | background = output_background; 247 | } else if (output_background === 'transparent') { 248 | background = output_background; 249 | } else { 250 | background = backgrounds[output_background]; 251 | } 252 | return [styles.join(''), color, background]; 253 | } 254 | return function from_ansi(input) { 255 | //merge multiple codes 256 | /*input = input.replace(/((?:\x1B\[[0-9;]*[A-Za-z])*)/g, function(group) { 257 | return group.replace(/m\x1B\[/g, ';'); 258 | });*/ 259 | var splitted = input.split(/(\x1B\[[0-9;]*[A-Za-z])/g); 260 | if (splitted.length === 1) { 261 | return input; 262 | } 263 | var output = []; 264 | //skip closing at the begining 265 | if (splitted.length > 3) { 266 | var str = splitted.slice(0, 3).join(''); 267 | if (str.match(/^\[0*m$/)) { 268 | splitted = splitted.slice(3); 269 | } 270 | } 271 | var prev_color, prev_background, code, match; 272 | var inside = false; 273 | for (var i = 0; i < splitted.length; ++i) { 274 | match = splitted[i].match(/^\x1B\[([0-9;]*)([A-Za-z])$/); 275 | if (match) { 276 | switch (match[2]) { 277 | case 'm': 278 | if (+match[1] !== 0) { 279 | code = format_ansi(match[1]); 280 | } 281 | if (inside) { 282 | output.push(']'); 283 | if (+match[1] === 0) { 284 | //just closing 285 | inside = false; 286 | prev_color = prev_background = ''; 287 | } else { 288 | // someone forget to close - move to next 289 | code[1] = code[1] || prev_color; 290 | code[2] = code[2] || prev_background; 291 | output.push('[[' + code.join(';') + ']'); 292 | // store colors to next use 293 | if (code[1]) { 294 | prev_color = code[1]; 295 | } 296 | if (code[2]) { 297 | prev_background = code[2]; 298 | } 299 | } 300 | } else if (+match[1] !== 0) { 301 | inside = true; 302 | code[1] = code[1] || prev_color; 303 | code[2] = code[2] || prev_background; 304 | output.push('[[' + code.join(';') + ']'); 305 | // store colors to next use 306 | if (code[1]) { 307 | prev_color = code[1]; 308 | } 309 | if (code[2]) { 310 | prev_background = code[2]; 311 | } 312 | } 313 | break; 314 | } 315 | } else { 316 | output.push(splitted[i]); 317 | } 318 | } 319 | if (inside) { 320 | output.push(']'); 321 | } 322 | return output.join(''); //.replace(/\[\[[^\]]+\]\]/g, ''); 323 | }; 324 | })(); 325 | $.terminal.defaults.formatters.unshift($.terminal.overtyping); 326 | $.terminal.defaults.formatters.unshift($.terminal.from_ansi); 327 | })(jQuery); 328 | -------------------------------------------------------------------------------- /examples/src/main/resources/templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | 27 | 28 | Jeesh Web-base Terminal Demo 29 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 38 | 39 | 40 |

41 | 42 | 43 | -------------------------------------------------------------------------------- /examples/src/test/groovy/net/je2sh/examples/commands/EchoTests.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples.commands 26 | 27 | import static net.je2sh.test.TestUtils.mockitoAssert 28 | import static org.mockito.Matchers.eq 29 | import static org.mockito.Mockito.verify 30 | 31 | import com.beust.jcommander.ParameterException 32 | import net.je2sh.core.CommandContext 33 | import net.je2sh.core.plugins.PluginContext 34 | import net.je2sh.test.TestUtils 35 | import spock.lang.Specification 36 | 37 | class EchoTests extends Specification { 38 | 39 | CommandContext commandContext 40 | Echo cmd 41 | 42 | def setup() { 43 | commandContext = new CommandContext(Mock(PluginContext), TestUtils.mockedTerminal()) 44 | cmd = new Echo() 45 | } 46 | 47 | def 'No arguments should fail'() { 48 | when: 49 | cmd.execute(commandContext) 50 | 51 | then: 52 | thrown(ParameterException) 53 | } 54 | 55 | def 'Message "#arguments" should echo values'() { 56 | given: 57 | TestUtils.parseCommand('echo', cmd, arguments) 58 | 59 | when: 60 | cmd.execute(commandContext) 61 | 62 | then: 63 | mockitoAssert(verify(commandContext.terminal.writer()).println(eq(arguments))) 64 | 65 | where: 66 | arguments << [ 67 | 'something', 68 | 'multiple spaces are seen', 69 | '-o option non-existent' 70 | ] 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /examples/src/test/groovy/net/je2sh/examples/commands/HelloTests.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.examples.commands 26 | 27 | import static net.je2sh.test.TestUtils.mockitoAssert 28 | import static org.mockito.Matchers.eq 29 | import static org.mockito.Mockito.verify 30 | 31 | import com.beust.jcommander.ParameterException 32 | import net.je2sh.core.CommandContext 33 | import net.je2sh.core.plugins.PluginContext 34 | import net.je2sh.test.TestUtils 35 | import spock.lang.Specification 36 | import spock.lang.Unroll 37 | 38 | @Unroll 39 | class HelloTests extends Specification { 40 | 41 | CommandContext commandContext 42 | Hello cmd 43 | 44 | def setup() { 45 | commandContext = new CommandContext(Mock(PluginContext), TestUtils.mockedTerminal()) 46 | cmd = new Hello() 47 | } 48 | 49 | def 'Wrong command "#cmdStr #arguments" should fail'() { 50 | when: 51 | TestUtils.parseCommand(cmdStr, cmd, (arguments as String).split(' ')) 52 | cmd.execute(commandContext) 53 | 54 | then: 55 | thrown(ParameterException) 56 | 57 | where: 58 | [cmdStr, arguments] << [ 59 | ['hello', 'hi'], 60 | [ 61 | '-n', 62 | '-a', 63 | 'something', 64 | '-n arg with spaces', 65 | '--name arg with spaces', 66 | ] 67 | ].combinations() 68 | } 69 | 70 | def 'No argument should print world'() { 71 | when: 72 | cmd.execute(commandContext) 73 | 74 | then: 75 | mockitoAssert(verify(commandContext.terminal.writer()).println(eq('Hello World'))) 76 | } 77 | 78 | def 'Command "#cmdStr #arguments" should succeed'() { 79 | given: 80 | TestUtils.parseCommand(cmdStr, cmd, (arguments as String).split(' ')) 81 | 82 | when: 83 | cmd.execute(commandContext) 84 | 85 | then: 86 | mockitoAssert(verify(commandContext.terminal.writer()).println(eq("Hello $arg" as String))) 87 | 88 | where: 89 | [cmdStr, param, arg] << [ 90 | ['hello', 'hi'], 91 | ['-n', '--name'], 92 | ['myself'] 93 | ].combinations() 94 | arguments = "$param $arg" 95 | } 96 | 97 | } 98 | 99 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | version=1.4.0-SNAPSHOT 2 | group=net.je2sh 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeeshell/je2sh/dc7f722950f7571ec8220cfaaad2a552abae6ac0/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # MIT License 3 | # 4 | # Copyright (c) 2017 JeeSh 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | #Tue Jun 20 08:34:51 PDT 2017 26 | distributionBase=GRADLE_USER_HOME 27 | distributionPath=wrapper/dists 28 | zipStoreBase=GRADLE_USER_HOME 29 | zipStorePath=wrapper/dists 30 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip 31 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jeesh' 2 | 3 | include 'core', 'shell', 'ssh', 'base-plugins', 'annotations', 'spring', 'examples', 'test' 4 | 5 | -------------------------------------------------------------------------------- /shell/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | //apply plugin: 'kotlin-kapt' 3 | 4 | dependencies { 5 | compile project(':core') 6 | 7 | runtime project(':base-plugins') 8 | 9 | // kapt 'com.squareup:javapoet:1.9.0' 10 | } 11 | 12 | mainClassName = 'net.je2sh.core.Main' 13 | -------------------------------------------------------------------------------- /shell/src/main/java/net/je2sh/shell/StandaloneShell.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.shell; 26 | 27 | public class StandaloneShell { 28 | 29 | public static void main(String[] args) throws InterruptedException { 30 | 31 | try (ShellPlugin shellPlugin = new ShellPlugin()) { 32 | shellPlugin.start(); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /shell/src/main/kotlin/net/je2sh/shell/ShellPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.shell 26 | 27 | import net.je2sh.core.plugins.PluginContext 28 | import net.je2sh.core.plugins.PluginLifeCycle 29 | 30 | class ShellPlugin : PluginLifeCycle(PluginContext()) 31 | -------------------------------------------------------------------------------- /spring/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '1.5.2.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" 11 | } 12 | } 13 | 14 | apply plugin: 'io.spring.dependency-management' 15 | apply plugin: 'org.springframework.boot' 16 | 17 | dependencyManagement { 18 | imports { 19 | mavenBom 'io.spring.platform:platform-bom:Brussels-SR1' 20 | } 21 | } 22 | 23 | dependencies { 24 | compile project(':ssh') 25 | compile 'org.springframework.boot:spring-boot-autoconfigure' 26 | compile group: 'org.springframework', name: 'spring-webmvc' 27 | 28 | 29 | compileOnly group: 'org.springframework.security', name: 'spring-security-config' 30 | compileOnly group: 'org.springframework.security', name: 'spring-security-web' 31 | compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1' 32 | 33 | compileOnly group: 'org.projectlombok', name: 'lombok' 34 | } 35 | 36 | bootRepackage { 37 | enabled = false 38 | } 39 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/JeeshRestAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring; 26 | 27 | import java.io.InputStream; 28 | import java.io.OutputStream; 29 | import java.nio.charset.StandardCharsets; 30 | 31 | import net.je2sh.spring.rest.JeeshRestOptions; 32 | import net.je2sh.spring.rest.RestTerminalProvider; 33 | import org.jline.terminal.Terminal; 34 | import org.jline.terminal.impl.ExternalTerminal; 35 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 36 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 37 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 38 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 39 | import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; 40 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 41 | import org.springframework.context.annotation.Bean; 42 | import org.springframework.context.annotation.ComponentScan; 43 | import org.springframework.context.annotation.Configuration; 44 | import org.springframework.core.annotation.Order; 45 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 46 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 47 | import org.springframework.security.core.context.SecurityContextHolder; 48 | 49 | 50 | 51 | @Configuration 52 | @AutoConfigureAfter(WebMvcAutoConfiguration.class) 53 | @ConditionalOnWebApplication 54 | @EnableConfigurationProperties(JeeshRestOptions.class) 55 | @ComponentScan("net.je2sh.spring.rest") 56 | public class JeeshRestAutoConfiguration { 57 | 58 | @Configuration 59 | @ConditionalOnClass(SecurityContextHolder.class) 60 | @Order(1001) 61 | public static class SecurityConfig extends WebSecurityConfigurerAdapter { 62 | @Override 63 | protected void configure(HttpSecurity http) throws Exception { 64 | http.csrf().ignoringAntMatchers("/jeesh/**"); 65 | } 66 | } 67 | 68 | @Bean 69 | @ConditionalOnMissingBean(RestTerminalProvider.class) 70 | public RestTerminalProvider terminalProvider() { 71 | return (InputStream in, OutputStream out) -> new ExternalTerminal("Je2sh Terminal", "TERM", 72 | in, out, 73 | StandardCharsets.UTF_8 74 | .name(), 75 | Terminal.SignalHandler.SIG_DFL); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/JeeshSshAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring; 26 | 27 | import java.io.IOException; 28 | 29 | import net.je2sh.spring.ssh.JeeshSshOptions; 30 | import net.je2sh.spring.ssh.JeeshSshSpring; 31 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 32 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 33 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 34 | import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 35 | import org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration; 36 | import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; 37 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 38 | import org.springframework.context.annotation.Bean; 39 | import org.springframework.context.annotation.ComponentScan; 40 | import org.springframework.context.annotation.Configuration; 41 | import org.springframework.security.core.context.SecurityContextHolder; 42 | 43 | 44 | 45 | @Configuration 46 | @AutoConfigureAfter({ WebMvcAutoConfiguration.class, SpringBootWebSecurityConfiguration.class }) 47 | @ConditionalOnWebApplication 48 | @ConditionalOnMissingBean(JeeshSshSpring.class) 49 | @ConditionalOnClass(SecurityContextHolder.class) 50 | @EnableConfigurationProperties(JeeshSshOptions.class) 51 | @ComponentScan("net.je2sh.spring.ssh") 52 | public class JeeshSshAutoConfiguration { 53 | 54 | @Bean 55 | public JeeshSshSpring jeeshSshSpring(JeeshSshOptions jeeshSshOptions) throws IOException { 56 | return new JeeshSshSpring(jeeshSshOptions); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/rest/CommandRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.rest; 26 | 27 | import java.util.List; 28 | 29 | import lombok.Data; 30 | 31 | 32 | 33 | @Data 34 | public class CommandRequest { 35 | 36 | private Integer id; 37 | private String josnrpc; 38 | private String method; 39 | private List params; 40 | } 41 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/rest/CommandResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.rest; 26 | 27 | import lombok.AllArgsConstructor; 28 | import lombok.Getter; 29 | import lombok.NoArgsConstructor; 30 | import lombok.Setter; 31 | 32 | 33 | 34 | @Getter 35 | @Setter 36 | @NoArgsConstructor 37 | @AllArgsConstructor 38 | public class CommandResponse { 39 | 40 | private Integer id; 41 | private String result; 42 | private String error; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/rest/JeeshRestOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.rest; 26 | 27 | import lombok.Getter; 28 | import lombok.Setter; 29 | import org.springframework.boot.context.properties.ConfigurationProperties; 30 | 31 | 32 | 33 | @ConfigurationProperties(prefix = "jeesh.rest") 34 | @Getter 35 | @Setter 36 | public class JeeshRestOptions { 37 | 38 | /** 39 | * Enable exposing Jeesh commands through a REST endpoint. Defaults to {@code true}. 40 | */ 41 | private boolean enabled; 42 | 43 | } 44 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/rest/JeeshRestSpring.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.rest; 26 | 27 | import java.io.ByteArrayOutputStream; 28 | import java.io.IOException; 29 | import java.nio.charset.StandardCharsets; 30 | import java.util.ArrayList; 31 | import java.util.List; 32 | 33 | import net.je2sh.core.CommandContext; 34 | import net.je2sh.core.plugins.PluginContext; 35 | import org.jline.terminal.Terminal; 36 | import org.slf4j.Logger; 37 | import org.slf4j.LoggerFactory; 38 | import org.springframework.beans.BeansException; 39 | import org.springframework.beans.factory.annotation.Autowired; 40 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 41 | import org.springframework.context.ApplicationContext; 42 | import org.springframework.context.ApplicationContextAware; 43 | import org.springframework.web.bind.annotation.RequestBody; 44 | import org.springframework.web.bind.annotation.RequestMapping; 45 | import org.springframework.web.bind.annotation.RestController; 46 | 47 | 48 | 49 | @ConditionalOnProperty(prefix = "jeesh.rest", name = "enabled") 50 | @RestController 51 | @RequestMapping("/jeesh") 52 | public class JeeshRestSpring extends PluginContext implements ApplicationContextAware { 53 | 54 | private static final Logger log = LoggerFactory.getLogger(JeeshRestSpring.class); 55 | 56 | @Autowired 57 | private RestTerminalProvider restTerminalProvider; 58 | 59 | public JeeshRestSpring() throws IOException { 60 | } 61 | 62 | @RequestMapping("/exec") 63 | public CommandResponse exec(@RequestBody CommandRequest commandRequest) 64 | throws IOException 65 | { 66 | log.trace("Received request: {}", commandRequest); 67 | ByteArrayOutputStream resultOutputStream = new ByteArrayOutputStream(); 68 | 69 | Terminal cmdTerminal = restTerminalProvider.getTerminal(System.in, resultOutputStream); 70 | 71 | List args; 72 | if (commandRequest.getParams() == null) { 73 | args = new ArrayList<>(); 74 | } 75 | else { 76 | args = new ArrayList<>(commandRequest.getParams()); 77 | } 78 | args.add(0, commandRequest.getMethod()); 79 | 80 | getCommandManager().runCommand(commandRequest.getMethod(), 81 | new CommandContext(this, cmdTerminal), 82 | args.toArray(new String[args.size()])); 83 | cmdTerminal.writer().flush(); 84 | return new CommandResponse(commandRequest.getId(), 85 | resultOutputStream.toString(StandardCharsets.UTF_8.name()), 86 | null); 87 | } 88 | 89 | @Override 90 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 91 | attribute("spring.context", applicationContext); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/rest/RestTerminalProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.rest; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.io.OutputStream; 30 | 31 | import org.jline.terminal.Terminal; 32 | 33 | 34 | @FunctionalInterface 35 | public interface RestTerminalProvider { 36 | 37 | Terminal getTerminal(InputStream in, OutputStream out) throws IOException; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/ssh/JeeshSshOptions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.ssh; 26 | 27 | import lombok.Getter; 28 | import lombok.Setter; 29 | import org.springframework.boot.context.properties.ConfigurationProperties; 30 | import org.springframework.context.annotation.Configuration; 31 | 32 | 33 | 34 | @Configuration 35 | @ConfigurationProperties(prefix = "jeesh.ssh") 36 | @Getter 37 | @Setter 38 | public class JeeshSshOptions { 39 | 40 | /** 41 | * Enable exposing Jeesh commands through SSH. Defaults to {@code true}. 42 | */ 43 | private boolean enabled; 44 | 45 | /** 46 | * Port on which to bind the SSH server 47 | */ 48 | private Integer port; 49 | 50 | /** 51 | * IP address on which to bind the SSH server 52 | */ 53 | private String ip; 54 | } 55 | -------------------------------------------------------------------------------- /spring/src/main/java/net/je2sh/spring/ssh/JeeshSshSpring.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.spring.ssh; 26 | 27 | import java.io.IOException; 28 | import java.util.Properties; 29 | 30 | import net.je2sh.ssh.SshPlugin; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | import org.springframework.beans.BeansException; 34 | import org.springframework.context.ApplicationContext; 35 | import org.springframework.context.ApplicationContextAware; 36 | import org.springframework.context.SmartLifecycle; 37 | import org.springframework.security.core.context.SecurityContextHolder; 38 | 39 | 40 | 41 | public class JeeshSshSpring implements SmartLifecycle, ApplicationContextAware { 42 | 43 | private static final Logger log = LoggerFactory.getLogger(JeeshSshSpring.class); 44 | 45 | private final SshPlugin sshPlugin; 46 | private boolean running = false; 47 | 48 | public JeeshSshSpring(JeeshSshOptions options) throws IOException { 49 | Properties props = new Properties(); 50 | props.put(SshPlugin.IP_KEY, options.getIp()); 51 | props.put(SshPlugin.PORT_KEY, options.getPort()); 52 | this.sshPlugin = new SshPlugin(props, SecurityContextHolder.getContext().getAuthentication()); 53 | } 54 | 55 | @Override 56 | public boolean isAutoStartup() { 57 | return true; 58 | } 59 | 60 | @Override 61 | public void stop(Runnable callback) { 62 | stop(); 63 | callback.run(); 64 | } 65 | 66 | @Override 67 | public void start() { 68 | sshPlugin.start(); 69 | running = true; 70 | } 71 | 72 | @Override 73 | public void stop() { 74 | sshPlugin.stop(); 75 | running = false; 76 | } 77 | 78 | @Override 79 | public boolean isRunning() { 80 | return running; 81 | } 82 | 83 | @Override 84 | public int getPhase() { 85 | return 0; 86 | } 87 | 88 | @Override 89 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 90 | this.sshPlugin.getContext().attribute("spring.context", applicationContext); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /spring/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | net.je2sh.spring.JeeshSshAutoConfiguration,\ 3 | net.je2sh.spring.JeeshRestAutoConfiguration 4 | -------------------------------------------------------------------------------- /spring/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | jeesh: 2 | ssh: 3 | enabled: true 4 | port: 2002 5 | ip: 127.0.0.1 6 | rest: 7 | enabled: true 8 | root-protection: true 9 | command-protection: true 10 | -------------------------------------------------------------------------------- /ssh/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'application' 2 | apply plugin: 'groovy' 3 | 4 | dependencies { 5 | compile project(':core') 6 | compile group: 'org.jline', name: 'jline-remote-ssh', version: '3.3.1' 7 | compile ('org.apache.sshd:apache-sshd:1.4.0') { 8 | exclude group: 'org.slf4j' 9 | } 10 | 11 | runtime project(':base-plugins') 12 | 13 | testCompile project(':test') 14 | testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4' 15 | testCompile group: 'com.jcraft', name: 'jsch', version: '0.1.54' 16 | testCompile group: 'org.apache.commons', name: 'commons-io', version: '1.3.2' 17 | 18 | testCompile group: 'org.apache.commons', name: 'commons-lang3', version: '3.6' 19 | 20 | testRuntime group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25' 21 | 22 | } 23 | 24 | mainClassName = 'net.je2sh.core.Main' 25 | 26 | -------------------------------------------------------------------------------- /ssh/src/main/java/net/je2sh/ssh/SshPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.ssh; 26 | 27 | import java.io.File; 28 | import java.io.IOException; 29 | import java.security.Principal; 30 | import java.util.Objects; 31 | import java.util.Properties; 32 | import java.util.concurrent.Executors; 33 | 34 | import net.je2sh.core.CommandContext; 35 | import net.je2sh.core.JeeShell; 36 | import net.je2sh.core.plugins.PluginContext; 37 | import net.je2sh.core.plugins.PluginLifeCycle; 38 | import org.apache.sshd.server.SshServer; 39 | import org.apache.sshd.server.auth.password.PasswordAuthenticator; 40 | import org.apache.sshd.server.auth.password.PasswordChangeRequiredException; 41 | import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; 42 | import org.apache.sshd.server.session.ServerSession; 43 | import org.jline.builtins.ssh.ShellCommand; 44 | import org.jline.builtins.ssh.ShellFactoryImpl; 45 | import org.jline.builtins.ssh.Ssh; 46 | import org.jline.terminal.Terminal; 47 | import org.jline.terminal.TerminalBuilder; 48 | import org.slf4j.Logger; 49 | import org.slf4j.LoggerFactory; 50 | 51 | 52 | 53 | /** 54 | * Plugin that allows access to the available commands through SSH. 55 | *

56 | * This plugin creates a {@link SshServer} and manages its lifecycle. 57 | */ 58 | public class SshPlugin extends PluginLifeCycle implements PasswordAuthenticator { 59 | 60 | private static final Logger log = LoggerFactory.getLogger(SshPlugin.class); 61 | public static final String PROPERTIES_FILE = "jeesh/jeesh.properties"; 62 | public static final String KEY_FILE = "jeesh/hostkey.pem"; 63 | public static final String PORT_KEY = "jeesh.ssh.port"; 64 | public static final String IP_KEY = "jeesh.ssh.ip"; 65 | 66 | private final SshServer server; 67 | private Principal principal; 68 | 69 | public SshPlugin() throws IOException { 70 | this(null, null); 71 | } 72 | 73 | public SshPlugin(Properties properties, Principal principal) throws IOException { 74 | super(new PluginContext(Executors.newSingleThreadExecutor())); 75 | getContext().setPrincipal(principal); 76 | 77 | ClassLoader classLoader = this.getClass().getClassLoader(); 78 | 79 | if (properties == null) { 80 | properties = new Properties(); 81 | properties.load(classLoader.getResourceAsStream(PROPERTIES_FILE)); 82 | } 83 | 84 | SimpleGeneratorHostKeyProvider keyPairProvider = 85 | new SimpleGeneratorHostKeyProvider(new File( 86 | classLoader.getResource(KEY_FILE).getPath())); 87 | Object port = properties.getOrDefault(PORT_KEY, 2022); 88 | Object ip = properties.getOrDefault(IP_KEY, "127.0.0.1"); 89 | log.debug("Binding SSH Server to {}:{}", ip, port); 90 | 91 | server = SshServer.setUpDefaultServer(); 92 | server.setPort(Integer.valueOf(port.toString())); 93 | server.setHost((String) ip); 94 | server.setShellFactory(new ShellFactoryImpl(this::shell)); 95 | server.setCommandFactory(command -> new ShellCommand(this::execute, command)); 96 | server.setKeyPairProvider(keyPairProvider); 97 | server.setPasswordAuthenticator(this); 98 | } 99 | 100 | @Override 101 | public boolean authenticate(String username, String password, ServerSession session) 102 | throws PasswordChangeRequiredException 103 | { 104 | return Objects.equals(username, "admin") && Objects.equals(password, "admin"); 105 | } 106 | 107 | @Override 108 | public void run() { 109 | try { 110 | log.debug("Starting SSH Server"); 111 | server.start(); 112 | } 113 | catch (IOException e) { 114 | log.error("Failure running the SSH Server", e); 115 | throw new RuntimeException(e); 116 | } 117 | } 118 | 119 | @Override 120 | public void close() { 121 | try { 122 | log.debug("Stopping SSH Server"); 123 | server.stop(true); 124 | } 125 | catch (IOException e) { 126 | log.error("Unable to gracefully stop SSH server", e); 127 | } 128 | finally { 129 | super.close(); 130 | } 131 | } 132 | 133 | private void shell(Ssh.ShellParams params) { 134 | log.info("Starting shell"); 135 | new JeeShell(getContext(), params.getTerminal(), getContext().getCommandManager()) 136 | .run(); 137 | params.getCloser().run(); 138 | } 139 | 140 | private void execute(Ssh.ExecuteParams params) { 141 | log.info("Execute received {}", params); 142 | try { 143 | Terminal terminal = TerminalBuilder.builder() 144 | .streams(params.getIn(), params.getOut()) 145 | .build(); 146 | String[] args = params.getCommand().split(" "); 147 | getContext().getCommandManager().runCommand(args[0], 148 | new CommandContext(getContext(), terminal), 149 | args); 150 | terminal.flush(); 151 | } 152 | catch (IOException e) { 153 | log.error("Unable to execute command", e); 154 | } 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /ssh/src/main/resources/jeesh/hostkey.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQDdfIWeSV4o68dRrKSzFd/Bk51E65UTmmSrmW0O1ohtzi6HzsDP 3 | jXgCtlTt3FqTcfFfI92IlTr4JWqC9UK1QT1ZTeng0MkPQmv68hDANHbt5CpETZHj 4 | W5q4OOgWhVvj5IyOC2NZHtKlJBkdsMAa15ouOOJLzBvAvbqOR/yUROsEiQIDAQAB 5 | AoGBANG3JDW6NoP8rF/zXoeLgLCj+tfVUPSczhGFVrQkAk4mWfyRkhN0WlwHFOec 6 | K89MpkV1ij/XPVzU4MNbQ2yod1KiDylzvweYv+EaEhASCmYNs6LS03punml42SL9 7 | 97tOmWfVJXxlQoLiY6jHPU97vTc65k8gL+gmmrpchsW0aqmZAkEA/c8zfmKvY37T 8 | cxcLLwzwsqqH7g2KZGTf9aRmx2ebdW+QKviJJhbdluDgl1TNNFj5vCLznFDRHiqJ 9 | wq0wkZ39cwJBAN9l5v3kdXj21UrurNPdlV0n2GZBt2vblooQC37XHF97r2zM7Ou+ 10 | Lg6MyfJClyguhWL9dxnGbf3btQ0l3KDstxMCQCRaiEqjAfIjWVATzeNIXDWLHXso 11 | b1kf5cA+cwY+vdKdTy4IeUR+Y/DXdvPWDqpf0C11aCVMohdLCn5a5ikFUycCQDhV 12 | K/BuAallJNfmY7JxN87r00fF3ojWMJnT/fIYMFFrkQrwifXQWTDWE76BSDibsosJ 13 | u1TGksnm8zrDh2UVC/0CQFrHTiSl/3DHvWAbOJawGKg46cnlDcAhSyV8Frs8/dlP 14 | 7YGG3eqkw++lsghqmFO6mRUTKsBmiiB2wgLGhL5pyYY= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /ssh/src/main/resources/jeesh/jeesh.properties: -------------------------------------------------------------------------------- 1 | jeesh.ssh.port=2001 2 | jeesh.ssh.ip=127.0.0.1 3 | -------------------------------------------------------------------------------- /ssh/src/test/groovy/net/je2sh/ssh/SshPluginTests.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.ssh 26 | 27 | import com.jcraft.jsch.ChannelExec 28 | import com.jcraft.jsch.JSch 29 | import com.jcraft.jsch.Session 30 | import groovy.util.logging.Slf4j 31 | import net.je2sh.test.TestUtils 32 | import org.apache.commons.io.IOUtils 33 | import spock.lang.Ignore 34 | import spock.lang.Shared 35 | import spock.lang.Specification 36 | 37 | @Slf4j 38 | class SshPluginTests extends Specification { 39 | 40 | @Shared 41 | SshPlugin sshPlugin 42 | 43 | @Shared 44 | Integer sshdPort 45 | 46 | Session session 47 | ChannelExec channel 48 | 49 | def setupSpec() { 50 | sshdPort = TestUtils.findAvailablePort() 51 | log.info("Starting test server on port {}", sshdPort) 52 | def props = new Properties() 53 | props.putAll([(SshPlugin.PORT_KEY): sshdPort]) 54 | sshPlugin = new SshPlugin(props, null) 55 | sshPlugin.start() 56 | Thread.sleep(500) 57 | } 58 | 59 | def cleanupSpec() { 60 | sshPlugin?.close() 61 | } 62 | 63 | def setup() { 64 | String host = '127.0.0.1' 65 | String user = 'admin' 66 | JSch jsch = new JSch(); 67 | session = jsch.getSession(user, host, sshdPort) 68 | session.password = 'admin' 69 | session.setConfig("StrictHostKeyChecking", "no"); 70 | 71 | session.connect() 72 | 73 | channel = session.openChannel('exec') 74 | } 75 | 76 | def cleanup() { 77 | session.disconnect() 78 | } 79 | 80 | @Ignore('This test is currently flapping. Need to investigate why') 81 | def 'Running "help" command should succeed'() { 82 | when: 83 | channel.command = 'help' 84 | channel.inputStream = null 85 | channel.connect() 86 | 87 | def lines = IOUtils.readLines(channel.inputStream) 88 | 89 | channel.disconnect() 90 | 91 | then: 92 | lines[0].contains('List of available commands.') 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /test/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(':core') 3 | compile group: 'cglib', name: 'cglib-nodep', version: '3.2.5' 4 | compile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' 5 | 6 | } 7 | -------------------------------------------------------------------------------- /test/src/main/java/net/je2sh/test/TestUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2017 JeeSh 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | package net.je2sh.test; 26 | 27 | import static org.mockito.Mockito.mock; 28 | import static org.mockito.Mockito.when; 29 | 30 | import java.io.IOException; 31 | import java.io.PrintWriter; 32 | import java.net.ServerSocket; 33 | 34 | import com.beust.jcommander.JCommander; 35 | import net.je2sh.core.AbstractCommand; 36 | import org.jline.terminal.Terminal; 37 | 38 | 39 | 40 | public class TestUtils { 41 | 42 | public static void parseCommand(String name, AbstractCommand cmd, String... args) { 43 | JCommander.newBuilder() 44 | .addObject(cmd) 45 | .programName(name) 46 | .build() 47 | .parse(args); 48 | } 49 | 50 | public static Terminal mockedTerminal() { 51 | return mockedTerminal(mockedWriter()); 52 | } 53 | 54 | public static Terminal mockedTerminal(PrintWriter writer) { 55 | Terminal mockedTerminal = mock(Terminal.class); 56 | when(mockedTerminal.writer()).thenReturn(writer); 57 | return mockedTerminal; 58 | } 59 | 60 | public static PrintWriter mockedWriter() { 61 | return mock(PrintWriter.class); 62 | } 63 | 64 | public static boolean mockitoAssert(Object result) { 65 | return result == null; 66 | } 67 | 68 | public static Integer findAvailablePort() { 69 | while(true) { 70 | try (ServerSocket socket = new ServerSocket(0)) { 71 | return socket.getLocalPort(); 72 | } 73 | catch (IOException e) { 74 | // noop 75 | } 76 | } 77 | } 78 | } 79 | --------------------------------------------------------------------------------