Introduction

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. We will, of course, attempt to maintain backward-compatibility as much as possible going forward. 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.

Building from Source

Everything you need to build Gosh is included in the download. Navigate to .\gosh\build and run make.bat.

Project Structure

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.

(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 Windows Service

You can initially configure a Windows Service using a simple DOS batch file. Additional configuration is accomplished using Windows administration tools and by directly editing the Windows Registry. The tools are a little crude still, but none of this is very difficult. The documentation here should give you a clear understanding of what is necessary to configure and maintain a Gosh-based Windows Service.

1. Initial Configuration: DOS Batch File

To create (or recreate) a Windows Service, you will need to define a batch file. The batch file will call another batch file, goshservice.bat, with a set of parameters, as in the code example below. Goshservice.bat wraps the call to JavaService.exe, which requires relatively complex command line syntax.

The parameters you pass to goshservice.bat are defined as follows:

Parameter

Description

JAVASERVICE_STUB

Each service needs a renamed copy of JavaService.exe to function. The batch file makes a copy for you, in the location you specify, and uses it to run the service.

SERVICE_NAME

The name of the service. Must be unique.

GROOVY_SCRIPT

The groovy script to run as a service.

LOGS_FOLDER

Standard out and standard err streams will be written to this folder, as files. Note that each service needs a unique folder!

After you run your batch file, the new service will be accessible for additional configuration from the Windows Services tool. You can rerun the batch file with different parameters, but remember that you will have to redo the manual configuration steps each time.

2. Additional Configuration: Windows Tools

Most Windows Services require some additional setup. Open the Services tool to finish configuring the service.

start → Settings → Control Panel → Administrative Tools → Services

At a very minimum, you should verify the configuration settings for the following items:

Description - A description of the service.

Startup Type - Manual or Automatic?

Log On - The account determines what rights the service has.

Once you are done, you can start the service.

3. Advanced Configuration: The Windows Registry

Writing a Service

At this point, you are probably wondering what the difference is between a regular Groovy script and Groovy script that runs as a Windows Service.

There is a difference. A Groovy script does a job, and when it is finished, it ends. With a Windows Service, the program doesn't end until Windows asks it to. You can implement this - very simply - by creating a loop that checks a flag to exit.

That is really all there is to it. The service instance is one of the bindings. service.shutdownRequested returns true when Windows has requested that the service be shutdown. service.sleep is a variation on Thread.sleep() that returns immediately if the service has been requested to shut down.

Examples

There are several examples in the .\gosh\sample project, included in the build.

The winntservice example implements a complete Windows Service, ready to install and run. To use:

Run installsampleservice.bat from a command-line.

Using the Services tool, start the service.

Check the log files to verify that the service is running.

There are also a few command-line examples in the same project.

Bindings

Gosh automatically creates a number of bindings. All of these are available to your script as variables, both when run as a service and when run from the command-line.

Name

Type

Description

args

List

When run from the command-line, this contains the command-line arguments. When run as a service, this is an empty list. Gosh does not support passing parameters to a service.

service

ServiceRunner

Provides state information about the service. When running from a command line, the state information never changes, so you have to shut down the process manually.

systemClassLoader

GoshClassLoader

The Java system classloader. This classloader allows you add add JAR files and new classpaths at runtime.

groovyClassLoader

GroovyClassLoader

The Groovy classloader. Note that the parent of the groovyClassLoader is the systemClassLoader.

fs

FileSystem

Supports finding the root path of your project, creating files from the root of your project (rather than the current folder) using relative paths, and changing the java.library.path, among other things.

scriptFile

File

The canonical path to the initial Groovy script.

Classloader Architecture

The classloader architecture in Gosh differs somewhat from the architecture found in other the other command-line runners for Groovy. Gosh replaces the RootClassLoader with the GoshClassLoader, which is a very simple override of UrlClassLoader.

Gosh is broken into two JAR files. The first, dasboot.jar, contains the minimal number of classes to boot Gosh. This includes the main entry class, the GoshClassLoader, and a small number of supporting classes. Once dasboot.jar is loaded, the system classloader gets set to an instance of GoshClassLoader, and all the other class libraries, including gosh.jar, are added to it.

Once that is done, Gosh creates a GroovyClassLoader, which is a child of the system classloader. This classloader is pointed to the root folder of the project, where it interprets uncompiled Groovy scripts.

In all cases, we have attempted to use the simplest, tightest classloader structure that works. This results in optimal loading and execution times.

Limitations

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.