Skip to content

Overview

Concepts and Principles

Development

Overview

IDEs

API Explorer

Releases

Release Notes

TORO Integrate

Coder Studio

Coder Cloud

Bug Reports

Search

Creating and Registering Custom Groovy Extension Modules

Overview

An Groovy Extension Module allows you to add new methods to existing classes, including classes which are precompiled, like classes from the JDK.1 With extension modules, you can conveniently extend the behavior of an existing class without changing the definition of the class.

Groovy itself ships with a default extension module and it is because of this extension module that you are able to use code like the following:

1
2
3
assert [1, 2, 3, 4, 5, 6].sum() == 21
assert ['a', 'b', 'c', 'd', 'e'].sum() == 'abcde'
assert [['a', 'b'], ['c', 'd']].sum() == ['a', 'b', 'c', 'd']

The sum() method is not defined in the ArrayList class

At first glance, sum() may look like an instance method but it is in fact a public static method with multiple implementations found in the DefaultGroovyMethods class, where many default extension methods are defined.

Groovy's default extension module adds a suite of extra methods to Files, Collections, Maps, Strings, primitive arrays, and more.

Groovy's enhancements on the JDK

Learn more about Groovy's changes to the JDK by visiting their API documentation.

In addition to what Groovy offers by default, TORO Integrate also provides other extension modules intended to help you code and integrate faster. These extension modules contain the so-called 'one-liners' – handy utility methods that help solve common integration problems in just one line of code. But aside from the extension modules provided by Groovy and TORO Integrate, you can also add your own. Continue reading on to the following section to learn how.

Procedures

To define and register your own extension module, you must:

  1. Create a new project2 and then create the class which will contain your extension methods. You can create multiple classes if you want.
  2. Create the extension module descriptor file.
  3. Pack the files you have created in a JAR.
  4. Store the JAR in the package's META-INF/services directory3.

Alternatively, you can opt to keep your extension module classes (their source code) in the Integrate Package4 where you will use it so you won't have to create a JAR file; all you need is to place the descriptor file in the META-INF/services directory.

Reusing Groovy extension modules from other projects!

Groovy extension modules are not just a TORO Integrate thing. You can grab the extension module JAR of another project and plug it to an Integrate Package so you can use it in TORO Integrate! Likewise, you can get a copy of your extension module JAR from TORO Integrate and use it for your projects outside of TORO Integrate (assuming it has no dependencies on TORO Integrate classes).

Creating the Extension Module's Classes and Methods

All you need to do is create a class containing the methods you want to add. These methods must:

  • Have the modifiers public and static
  • Have at least one parameter
  • Ensure that the first parameter's type is also the class that will be extended

Check out other guides!

We recommend reading Hubert Klein Ikkink's (mrhaki) and Andre Steingress' blog posts about Groovy extension modules. Both articles thoroughly explain the process of creating a Groovy extension module.

To help you get started, here's an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package io.toro.integrate

/**
  * A class containing functions for common math operations.
  */
class MathMethods {

    /**
     * <p>Computes the greatest common factor of two numbers.</p>
     * @param x the first number
     * @param y the second number
     * @return the greatest common factor
     */
    static int gcf(int x, int y) {
        return (y == 0) ? x.abs() : gcf(y, x % y);
    }

    /**
     * <p>Computes the greatest common factor of a given set of numbers.</p>
     * @param self the set of numbers
     * @return the greatest common factor
     * @throws NullPointerException if {@code self == null}
     * @throws IllegalArgumentException if {@code self.length < 2} or if all numbers are zeros
     */
    static int gcf(int... self) {
        Objects.requireNonNull(self, "Must provide numbers")
        if (self.length < 2) {
            throw new IllegalArgumentException("Must have at least two numbers")
        }

        return self.inject(0) { int result, int current -> gcf(result, current) }
    }

}

Where's the public modifier?

By default, Groovy considers classes and methods public. So you don’t have to use the public modifier everywhere something is public. Only if it’s not public, you should put a visibility modifier.5

The class above defines two extension methods:

  • MathMethods.gcf(int, int)

    An extra method for the int class (inferred from the first parameter's type) for finding the greatest common factor of two integers.

  • MathMethods.gcf(int...)

    An extra method for the int[] class (inferred from the first parameter's type) for finding the greatest common factor of an array of integers.

Common practices

When creating extension module classes, it is common practice to:

  • Not define static and instance extension methods together in a single class
  • Create separate extension modules per target type

Creating the Descriptor File

In the META-INF/services directory of your module archive or classpath, create the org.codehaus.groovy.runtime.ExtensionModule file. We will use this file to define the extension module, like so:

1
2
3
4
moduleName = extras-module
moduleVersion = 1.0
extensionClasses = io.toro.integrate.MathMethods
staticExtensionClasses=

Multiple extension classes

You can add multiple extension classes on a single extension module. Simply delimit each class name with a comma.

If you want to add instance methods to the target type6, then use the extensionClasses property; if you want to add static methods instead, use the staticExtensionClasses property.

Creating the JAR

Typically, the JAR file creation process is tied to the project's lifecycle.

Usage

After moving the JAR or extension module descriptor file under your Integrate Package's META-INF/services directory, you may now use your extension methods.

To use instance extension methods (like that seen in this example), call the method on an instance of the first parameter's type, like so:

1
2
144.gcf(32)
[144, 64, 32].gcf()

For static extension methods, you must call the method on the initial parameter's type (like you would normally call static methods); for example:

1
<AffectedClass>.method(<arguments...>)

  1. See extension modules

  2. A separate project that does not reside in TORO Integrate. 

  3. Typically created under the resources directory. 

  4. Particularly in the code directory. 

  5. See public by default

  6. The target type is the type of the extension method's first parameter.