The Groovy Script Server plugin is a server plugin that allow you to dynamically execute Groovy scripts on the RHQ server. Server plugins have access to the internal RHQ server APIs and as such Groovy scripts executed by this plugin have access to the RHQ server APIs as well. This plugin allows you to extend the server functionality in very dynamic ways. You can any number of things including,
- Generate reports - Write a script to generate some reports for various metrics that are collected across the inventory.
- Perform a customized work flow - Write a script that provisions a group of machines with EAP, creates some alert definitions for those EAP instances, and then deploys an application.
- Integrate with external systems - Write a script that pulls metrics out of RHQ and feeds them into an external system through some web services (e.g., SOAP, REST) API.
The plugin provides a number of features to facilitate writing scripts. These features and capabilities, as well as planned features, are discussed in the following sections.
You can define the classpath to be used for each script that you execute. The screenshot below illustrates how you specify the classpath.
The Classpath field allows you to specify the classpath in its entirety which each entry being placed on a new line. You can specify class directories, script directories, and JAR files. With the Library Directories field, you can specify multiple paths with each entry on its own line. Each path should be should be a directory. All of the JAR files found in the directory will be added to the classpath. Note that both of these properties are optional, and even without anything specified, all of the RHQ server APIs are available at runtime as well as the core Groovy APIs.
Most business logic in the RHQ server is implemented in stateless session EJBs (SLSBs). The SLSBs conform to the following naming conventions.
- Local interface - XXXManagerLocal
- Remote interface - XXXManagerRemote
- Implementing class - XXXManagerBean
Each of the SLSBs has its local interface exposed as a dynamic property using the naming convention, XXXManager. Let's look at an example to illustrate this.
In the script we access two manager SLSBs - ResourceManager and SubjectManager. This frees us from having to do any kind of JNDI look up to access the EJBs. Because these manager properties are dynamic, as new EJBs are added to system, they too will be available to your scripts without requiring changes to the plugin.
The entity criteria API is one of the central features in the CLI. A good overview can be found here. The great thing about the criteria API is that it enables you to define your own queries. You are not limited to a set of statically defined query methods. Let's look at an example that we might use in the CLI.
This is a non-trivial query that is looking for JBoss servers that are down and that are being managed by myagent. The criteria also specifies that the child resources and resource configuration updates should be loaded as well. Finally, we order the results by resource category and then by resource type. While this example demonstrates some of the flexibility provided by the criteria API, the example also shows that the API can be rather verbose. The script provides a mini-DSL for specifying criteria queries in a more concise manner while maintaining and even improving readability. Here is how the above code could be written using the criteria DSL.
The criteria() takes two arguments. The first is the entity class which determines what type of criteria to generate. The second argument is a closure in which you specify the actual criteria to use. Any filters you want to use can be specified as a map which should be bound to the variable filter. It should be pointed out that in Groovy, map keys are implicitly treated as strings so each of the map keys you see in the example is in fact of type java.lang.String. And notice that each map key is a property name of the Resource class. Each of the entries in the map is translated into a corresponding addFilterXXX() call. If you specify a property in the map for which no corresponding addFilterXXX() method exists, a script exception is thrown.
In the fetch block we specify properties that we want eagerly loaded. For each of the properties specified here, the corresponding fetchXXX() method is called. If the method is not found or does not exist, a script exception is thrown. Notice in this block that each property is listed on a separate line. If you want to specify multiple properties on a single line, they need to be delimited by semi-colons.
The sort block is very similar to the fetch block in that we specify a list of properties. In the example sort order is specified using a property access notation. For ascending order, we can do myproperty.asc, and for descending order we can do myproperty.desc. If the sort order is not specified, it defaults to ascending order. For each property listed, the corresponding addSortXXX() method is called. If the method does not exist, a script exception is thrown.
This DSL does not currently support the full criteria API. The criteria() method however returns the underlying criteria object that is generated; so, if you need something that is not available through the DSL you still have full access to actual criteria object.
This leads us to the exec() method call. The exec() is made against the returned criteria object which in this example is a ResourceCriteria object. The exec() method is not defined in the ResourceCriteria class. It is dynamically added to the class through the DSL. The method internally makes the call to ResourceManager.findResourcesByCriteria() like we saw in the first CLI example. The first argument to exec() is a Subject object, and the second argument is a closure that is called for each resource that is returned.