Avoid Binding Variables in Groovy Scripts
This blog is part of a series that shall assist Integration Developers to properly address non-functional aspects like resource consumption, performance, and reliability. The previous contribution in this series is Stream the XMLSlurper input in Groovy Scripts.
This contribution relates to Groovy scripts, as they are used in the Script step of Integration Flows. It focuses on potential issues due to accidentally created binding variables, as explained in the following.
In the Groovy programming language it is possible to define variables in a typed or untyped fashion.
Example for a typed definition:
String name = "Bob" // Typed definition
Example for an untyped definition:
def name = "Bob" // Untyped definition
For Groovy scripts, there is still a third type of definition:
name = "Bob" // Bad practice !!
This implies that name
is defined as a so-called binding variable. The binding is defined in the Groovy API documentation as follows: “Represents the variable bindings of a script which can be altered from outside the script object or created outside of a script and passed into it.”. An example for a binding variable is the messageLogFactory
, which is implicitly passed into your script, and lets you write to the Message Processing Log.
Now it can happen that you just forget the “def” or the type in the definition for a variable, so that it accidentally becomes a binding variable. This can cause subtle issues which can impact message processing, even though your script seems to work as expected. These issues are twofold:
On the one hand, the lifetime of that variable will be much longer: while local variables prefixed by “def” or the respective type are subject to garbage collection as soon as the script execution has finished, binding variables stay in memory until the Integration Flow is undeployed or redeployed. This is problematic if a large amount of memory is allocated for the binding variable. For example, if large message bodies or message attachments are stored as java.lang.String in binding variables, this could eventually result in a java.lang.OutOfMemoryError
, which would interrupt message processing.
On the other hand, since the binding variable is shared between script executions, unintended interactions can occur, which can impact message processing and are hard to track down. Note that binding variables are not thread-safe, as pointed out in the Groovy API documentation: “Binding instances are not supposed to be used in a multi-threaded context.”
With this blog we wish to make you aware of the fact that creation of binding variables is discouraged, of the risks related to their accidental creation, and would recommend you to watch out for forgotten “def” keywords or type declarations.
The next contribution in this series is How to Guard Against Huge Messages.
Great under the hood insights. Many thanks
Markus, I'm so glad I found this blog post. It helped solve the errors I've experienced with parallel processing from JMS Queue with "Number of Concurrent Processes" greater than 1, which resulted in some messages fail randomly. Many thanks!