We want to be able to start up a groovy command line shell, so that we can quickly get feedback from our application. For example:
$ gradle bootConsole
> context.getBean("userRepository").findOne("123").getName()
=> "John Smith"
This kind of REPL would be really valuable to have for development and debugging purposes.
We've been experimenting with groovysh and have tried to load a Groovysh with our application as the context. There probably is a better way to do this.
Application.main(args);
Binding binding = new Binding();
binding.setVariable("context", Application.context);
new org.codehaus.groovy.tools.shell.Groovysh(
binding,
new IO()
);
Did you look at Boot's integration for CRaSH? It comes with the spring-boot-starter-remote-shell starter pom and allows you to ssh into your app and run commands. There are various commands available out of the box. But you can write your own commands too. Take a look at the spring-boot-sample-actuator.
This is not a full-blow Groovy shell though. Let me know what you think.
We'll take a look at it. Can you point me to some good documentation on how to get started?
See here http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#production-ready-remote-shell for some docs.
The remote shell is actually pretty close to what we're looking for:
|ruby-1.9.3-p392| cliff in ~
○ → ssh user@localhost -p 2000
Password authentication
Password:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.0.BUILD-SNAPSHOT) on cliff
> repl groovy
Using repl groovy
> cats.Application.context.getBean("greetingRepository").findAll().collect() { thing -> thing.message }
[meow, meow, yoyoyo]
>
It fits my usecase, to be able to boot up our application and poke around at the data. What's missing is readline (the ability to re-visit/edit previous commands). Also, it would be nice if it dropped the shell into a Groovy repl.
I think there's an opportunity for a tool (spring-boot-cli, gradle, maven?) to start up this remote shell, drop me into a groovy repl, without having to copy-paste the password from the log, and provide me my application context as a variable. For example:
$ spring console
> Loaded Spring Boot :: (v1.1.0.BUILD-SNAPSHOT)
> context.getBean("greetingRepository").findAll().collect() { thing -> thing.message }
[meow, meow, yoyoyo]
>
Thanks to Cédric Champeau, we have a working prototype of a shell using Gradle.
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Console {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Console.class, args);
Binding binding = new Binding();
binding.setProperty("app", context);
new Groovysh(binding, new IO()).run((String) null);
}
}
task('console', type: JavaExec) {
main = 'cats.Console'
classpath = project.sourceSets.main.runtimeClasspath
standardInput = System.in
}
$ gradle -q console
Groovy Shell (2.3.0, JVM: 1.8.0_05)
Type ':help' or ':h' for help.
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
groovy:000> app.getBean("greetingRepository")
===> org.springframework.data.jpa.repository.support.SimpleJpaRepository@6cc64028
groovy:000> app.getBean("greetingRepository").findAll()
Hibernate: select greeting0_.id as id1_0_, greeting0_.message as message2_0_ from greeting greeting0_
===> [cats.Greeting@3c18942, cats.Greeting@39c87b42, cats.Greeting@47fcefb3, cats.Greeting@236c098, cats.Greeting@68e2d03e, cats.Greeting@120aa40b, cats.Greeting@32ab408e, cats.Greeting@6ad6443, cats.Greeting@78b03788, cats.Greeting@3f5dfe69]
groovy:000>
One note: gradle requires the -q flag because its output collides with the REPL otherwise.
Example project here:
https://github.com/berlin-ab/cats/tree/feature/groovy-shell
Note: use the 'feature/groovy-shell' branch
Switched to using a SpringApplicationBuilder to turn off the web server and lessen the output in the logs:
ApplicationContext context = new SpringApplicationBuilder()
.main(Console.class)
.sources(Console.class)
.showBanner(false)
.headless(true)
.web(false)
.run();
When I wrote the original shell this was something I really wanted to see so I'm glad someone's taken it on because I think it could be very valuable. At the beginning I was approaching it from the idea that the ApplicationContext would be the this context. By that I mean a non-qualified reference to a variable would reference a bean in the context if it existed. I couldn't really find an instance where collapsing the top "namespace" conflicted with what I wanted to do in a shell and it makes commands a lot shorter to type, which is presumably of primary benefit when using a shell.
It should be possible to do this easily by implementing method missing and getter methods on a root context object that kept transitive assignments you might make in the shell away from your application context while still making it easy to do:
myRepo.findAll().each { pojo -> }
I had also considered things like command completion of bean names and even introspecting methods of beans to do command completion, etc...
Yes please! Those are all great ideas.
You can do something similar with scala's console:
$ mvn scala:console
scala> import com.mycompany.myapp.StandardApp
import com.mycompany.myapp.StandardApp
scala> StandardApp.main(Array[String]())
2016-09-19 18:33:10.464 INFO 7373 --- [ main] com.mycompany.myapp.StandardApp : Starting StandardApp on vagrant-ubuntu-trusty-64 with PID 7373 (/home/vagrant/standard/target/classes started by vagrant in /home/vagrant/standard)
2016-09-19 18:33:10.478 DEBUG 7373 --- [ main] com.mycompany.myapp.StandardApp : Running with Spring Boot v1.4.0.RELEASE, Spring v4.3.2.RELEASE
... app starts
scala> StandardApp.context.getBean("userRepository").asInstanceOf[com.mycompany.myapp.repository.UserRepository].findAll()
Hibernate: select user0_.id as id1_5_, user0_.created_by as created_2_5_, user0_.created_date as created_3_5_, user0_.last_modified_by as last_mod4_5_, user0_.last_modified_date as last_mod5_5_, user0_.activated as activate6_5_, user0_.activation_key as activati7_5_, user0_.email as email8_5_, user0_.first_name as first_na9_5_, user0_.lang_key as lang_ke10_5_, user0_.last_name as last_na11_5_, user0_.login as login12_5_, user0_.password_hash as passwor13_5_, user0_.reset_date as reset_d14_5_, user0_.reset_key as reset_k15_5_ from jhi_user user0_
res9: java.util.List[com.mycompany.myapp.domain.User] = [User{login='system', firstName='System', lastName='System', email='system@localhost', activated='true', langKey='en', activationKey='null'}, User{login='anonymoususer', firstName='Anonymous', lastName='User', email='anonymous@localhost', activated='true', langKey='en', activationKey='null'}, User{login='admin', firstName='Administrator', lastName='Administrator', email='admin@localhost', activated='true', langKey='en', activationKey='null'}, User{login='user', firstName='User', lastName='User', email='user@localhost', activated='true', langKey='en', activationKey='null'}]
Drawback is you lose the devtools hot-reload...
I think this issue might become a bit more critical due to upcoming Remote Shell deprecation https://github.com/spring-projects/spring-boot/issues/7006.
Also relates to https://github.com/spring-projects/spring-boot/issues/7445
Critical for application frameworks relying on dynamic changes from the admin personnel.
After some discussion we've decided this is something we don't want to put in the core of the project. If anyone wants to take this on outside of Spring Boot we'll happily add any extension points that are needed.
Is it actually removed the support in the new version 5 of Spring and Spring Boot 2 the Remote Shell?
Too disappointed to hear that.
Meanwhile, for those that use the previous versions of Spring, I made a "plugin" that allows to access and invoke your bean objects through the Groovy repl whithin the Remote Shell: https://github.com/grayshirts/spring-ctx-groovy
It's so simple like this: ctx.App.myUserService.getById(100123). Take a look how it works:

This is cool!
@martin-g don't forget to give a star to the project if it's useful for you :-)
Most helpful comment
Is it actually removed the support in the new version 5 of Spring and Spring Boot 2 the Remote Shell?
Too disappointed to hear that.
Meanwhile, for those that use the previous versions of Spring, I made a "plugin" that allows to access and invoke your bean objects through the Groovy repl whithin the Remote Shell: https://github.com/grayshirts/spring-ctx-groovy
It's so simple like this:
ctx.App.myUserService.getById(100123). Take a look how it works: