[TOC] ---------------------------------------------------------------------------------------------------- # About this section In this section we are going to implement *Groovy console* that allows: - executing Groovy code inside running Spring application; - manipulating with the available Java code artifacts and Spring context (beans). !!! warning This solution may be convenient for a running web application diagnostic and manipulation. Still this makes the application unstable and vulnerable as the actions performed via the console are not properly controlled. The safer solution would be certain manipulation actions defined via corresponding endpoints with appropriate restrictions and checks. But the certain endpoints approach is harder to implement and it doesn't provide so much flexibility. ---------------------------------------------------------------------------------------------------- # Materials A more extensive variation may be found at []. ---------------------------------------------------------------------------------------------------- # Spring project setup Go to [Spring Initializr site](https://start.spring.io/) and define the following configuration: ![](groovy_console_spring_boot_config_init.png){.img-frame} Alternatively the following link may be used: Delete some unnecessary files (like `mvnw` and `mvnw.cmd`), do some other code cleaning and reformatting. The generated artifact is going to be a JAR file with dependencies. It is massive enough, so we are going to bring the target location out of the working project directory. To do that, the `build/directory` element is added into the `pom.xml`. The environment variable `TMP_MAVEN_BUILDS_DIR` must be defined in the system. Building (as Java 17 is not set by default we need to set the corresponding environment variables): ````shell >set JAVA_HOME=%JAVA_17_HOME% >set PATH=%JAVA_17_HOME%\bin;%PATH% >mvn clean package ... [INFO] --------------------< groovy.study:groovy-console >--------------------- [INFO] Building groovy-console 0.0.1-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- ... [INFO] ------------------------------------------------------- [INFO] T E S T S [INFO] ------------------------------------------------------- ... [INFO] Results: [INFO] [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 7.698 s [INFO] Finished at: 2024-08-14T09:04:21+02:00 [INFO] ------------------------------------------------------------------------ ```` Running: ````shell >cd /d %TMP_MAVEN_BUILDS_DIR%\groovy-console\ >set PATH=%JAVA_17_HOME%\bin;%PATH% >java -jar groovy-console-0.0.1-SNAPSHOT.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.3.2) ... ... : Tomcat started on port 8080 (http) with context path '/' ... : Started Application in 1.584 seconds (process running for 1.913) ```` To stop press `Ctrl`+`C` (but not now please). Alternatively the project may be run in an IDE by executing the class `Application.java`. Testing the result in a browser: . The result must look like this: ![](groovy_console_first_result.png){.img-frame} The following commit represents the newly automatically generated project with the above cleanup and customization: Dependency tree report: ````shell >mvn dependency:tree > dependency_tree.txt ```` The result is in the file [`dependency_tree.txt`]() (note that this is the latest version, not what it was at the current step, see the commit above to see the the actual version at this point of time). ---------------------------------------------------------------------------------------------------- # Adding UI and REST > The full dependency tree file is [`dependency_tree.txt`](). !!! note This is a very simple implementation that lacks a lot of important features (like security) required in production. Here are some explanations. The UI is created as simple as possible so no template engine is used. A single static HTML page is used for demonstration. **Ajax** is used to execute the scripts and to update the displayed application state (see the `src/main/resources/static/console.html` file). The actual script execution is not implemented by now so the returned result is fake. Here's the UI snapshot: ![](groovy_console_ui.png){.img-frame} The following configuration adds a logic-less controller to serve the static page:
src/main/java/groovyconsole/MvcConfig.java
````java @Configuration public class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("console.html"); } } ```` The `groovyconsole/State.java` Spring bean is used for holding the application state that we are going to manipulate later. There are two REST endpoints created: ````wrapped-shell >curl -s -X POST -H "Content-Type:application/text" -d "2+2" http://localhost:8080/console | python -m json.tool { "error": null, "result": "The script is: 2+2", "output": "Fake output" } >curl -s http://localhost:8080/state | python -m json.tool { "consoleRequestCount": 5, "someProperty": null } ```` The Python `json.tool` module is used just for pretty-printing the output. ---------------------------------------------------------------------------------------------------- # Implementing Groovy console > The full dependency tree file is [`dependency_tree.txt`](). The following library is used for the actual Groovy scripts execution: ````xml org.codehaus.groovy groovy 3.0.22 ```` The `src/main/java/groovyconsole/SpringBinding.java` class makes the web application Spring context available inside the Groovy scripts. The `groovyconsole.Controller#console` endpoint method was rewritten to perform real Groovy scripts execution. The `groovyconsole.Greeter` class was created to demonstrate usage of arbitrary Java code artifacts inside the Groovy scripts. Some properties were added to the `src/main/resources/application.properties` file to check access to them from the Groovy scripts. ---------------------------------------------------------------------------------------------------- # Demo Here the UI snapshot is repeated for convenience: ![](groovy_console_ui.png){.img-frame}
ScriptResultComments
println "Hello world!"
RESULT: null
OUTPUT: Hello world!
The consoleRequestCount property value incremented.
System.out.println("Hello " + 
    "from Groovy script!")
RESULT: null
The specified message printed to the web application's standard output.
state.someProperty = "LALALA"
RESULT: LALALA
The someProperty property value updated. Here a bean from the Spring context is manipulated.
import groovyconsole.Greeter
var greeter = new Greeter("Bob")
greeter.greet()
RESULT: Hello, Bob!
Arbitrary Java code artifact is manipulated.
environment
    .getProperty("some.property")
RESULT: This is Groovy console
Spring environment property is accessed.
println "Some message"
1 / 0
ERROR: Division by zero
OUTPUT: Some message
Errors are processed correctly.