Gosh is an "incubator" project that provides an alternate way to run command-line Groovy scripts, as well as a simplified way to write Windows Services (WinNT Services) using Groovy. Gosh treats scripts the same, whether they are run from the command-line or as a service (including bindings). You can debug a service by running it from the command-line, and you can also run standard command-line Groovy scripts. There is very little difference between a Groovy script that is intended to be run as a Windows Service and any other Groovy script.
Gosh uses JavaService to do the "heavy lifting" of running a Windows Service. Gosh provides the framework that integrates command-line and service calls with Groovy, and a utility to configure JavaService. The end result is something that is a lot easier to develop with, and ultimately a lot easier to maintain, than Windows Services based on pure Java using JavaService.
Because this is an incubation project, many of the details documented here are likely to change over time. As of this writing, the following items still need to be addressed (these will be documented in Jira eventually):
- Need an installer. Manual setup is still a little more complex than I would like.
- Needs to use a native runner for command-line. Currently uses a batch file.
- Setting up a new Windows Service requires a batch file. Should support either a GUI or Groovy script.
- Classloader structure works well, but still needs peer review.
- Dynamic-loading design (including folder structure conventions) need peer review.
- Dynamic loading of DLLs is still experimental at this point, and has not been proven in non-sun JVMs.
- Need to roll in basic logging support.
- Gosh uses an older version of JavaService.exe to run Windows Services. Needs to be updated and tested.
Building from Source
The Gosh project structure is designed to accommodate complex projects. This may include projects containing multiple JAR libraries and JNI DLLs, where different versions of the same library may exist on one machine. Gosh also supports convenient programmable runtime configuration using - drumroll please - a Groovy script.
One of the key differences between Gosh and the Groovy command-line tools is the emphasis on local, dynamic (runtime) project configuration. The Gosh project model follows a convention similar to what you would see in an application server, like a Tomcat web application. The standard Groovy command-line tools use a global configuration file that applies to all scripts. Although these tools support dynamically changing the classpath, the support is not obvious and somewhat awkward.
Conversely, in Gosh, there is no global configuration at all. JAR files (and also DLLs) are local to the project, and are loaded dynamically. Gosh also supports a local configuration script, written in Groovy, that lets you conditionally define external dependencies. This makes it easy to use different library versions in different projects on the same machine, something which Groovy isn't particularly good at at them moment. It also makes it almost foolproof to distribute an entire Groovy-based application to different machines (xcopy or zip the project).
Note that you can run multiple scripts from a single Gosh project. That includes Windows Services. So you can define, for example, 5 Windows Services and 20 utility scripts in the same project, all using the same set of JAR files and DLLs.
A Gosh project consists of a root folder containing the following:
- Uncompiled Groovy scripts, class files, properties files, and other resources.
- (optional) A .\GROOVY-INF folder, containing any of the following:
- (optional) A .\lib folder containing JAR files. These will automatically be added to the classloader.
- (optional) A .\bin folder containing additional Windows *.DLL files. This path is added to the beginning of the java.library.path, so DLLs in this folder override other DLLs on the local machine.
- (optional) A .\config.groovy script. This lets you do any additional configuration, like loading external JAR files or setting up logging.
When you run a Groovy script using Gosh, this is what happens:
- Gosh "discovers" the project root folder. Gosh searches upwards from the current folder, looking for a .\GROOVY-INF folder. The project root is handled by a single instance of GroovyClassLoader.
- If it is found, the folder containing it becomes the project root. This affects the assumed package hierarchy of the project.
- If it is not found, the folder containing the script is assumed to be the project root. Note that this means you can't reliably run Groovy scripts from within packages unless you define .\GROOVY-INF!
- If it exists, Gosh loads all the JAR files in .\GROOVY-INF\lib into the system classloader.
- If it exists, Gosh adds the .\GROOVY-INF\bin folder to the java.library.path.
- If it exists, Gosh runs the .\GROOVY-INF\config.groovy script (config.groovy is compiled and cached, so this step actually costs very little).
- Finally, Gosh runs your script.
Although it looks like a lot is going on here, the logic is actually very streamlined and fast. We've taken great pains to minimize the startup time, and the performance is generally acceptable.
Configuring a WinNT Service
The following are known limitations of Gosh:
- You can't typically use this to bootstrap complex application servers, like Tomcat and JBoss, due to the modified classloader structure and potential conflicts with Groovy JAR library dependencies. If anyone has success doing this, please contact me.