Message-ID: <668192826.7474.1422586606295.JavaMail.firstname.lastname@example.org> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_7473_1388167462.1422586606294" ------=_Part_7473_1388167462.1422586606294 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
With this new 2.1 release, Groovy:
With Groovy 2.0, we introduced support for JDK 7=E2=80=99s =
=E2=80=9Cinvoke dynamic=E2=80=9D bytecode instruction and API to benefit fr=
om the dedicated support and performance improvements for dynamic languages=
starting with JDK 7. Groovy 2.1 brings
In Groovy 2.0, most method calls were using the =E2=80=9Cin= voke dynamic=E2=80=9D instruction, but there have been exceptions: construc= tor calls or =E2=80=9Cspread calls=E2=80=9D (where you pass arguments with the =E2=80=9Cspread operator=E2=80=9D). Gro= ovy 2.1 completes the implementation started in 2.0. Now, code compiled wit= h the =E2=80=9Cinvoke dynamic=E2=80=9D JAR on JDK 7 will not be using the o= ld =E2=80=9Ccall site caching=E2=80=9D code which served us well for gettin= g good performance for Groovy prior to JDK 7. If you are lucky enough to be= using JDK 7 in production, be sure to use the Groovy 2.1 =E2=80=9Cindy=E2= =80=9D JAR to benefit from the full =E2=80=9Cinvoke dynamic=E2=80=9D suppor= t. The =E2=80=9Cindy=E2=80=9D version is bundled with the binary download p= ackage and can be obtained via Maven (all JARs with =E2=80=9Cinvoke dynamic= =E2=80=9D support are postfixed with =E2=80=9C-indy=E2=80=9D).
Groovy 2.1=E2=80=99s distribution bundles the recently released GPars 1.0, the one-st= op shop for all your concurrency needs. This new version comes with various= enhancements in the asynchronous functions, promises, parall= el collections, actors, dataflow support, Google App Engine s= upport, etc.
Authoring Domain-Specific Languages (DSLs) has always been = a sweet spot for Groovy, and the availability of closures and the malleable= syntax of the language has allowed DSL implementors to build nice mini-lan= guages like =E2=80=9Cbuilders=E2=80=9D, to represent configuration or hiera= rchical data.
Thanks to the various delegation strategies of the
The very popular and powerful Gradle build automation system uses its ow=
n DSL for build script specifications. On the DSL implementation layer are =
various methods taking closures as arguments, and with special delegation s=
trategies delegating to some other parameter passed to them. Providing good=
IDE support for Groovy DSLs =E2=80=94 like the one in Gradle =E2=80=94 has=
presented a few challenges. Hence the need for the
Groovy 2.1 introduces the
tation as a documentat=
ion mechanism for DSL users and maintainers, as an&nbs=
p;IDE hint for providing better coding assistanc=
e, and as additional information that can be taken into account by the static type checker and static =
compilation introduced in Groovy 2.0. Let=E2=80=99s se=
e that in action with some examples.
A closure delegate based method usage might look like the f= ollowing:
;method takes a closure as parameter, and the actual
launch() call inside that closure is delegated t=
o some particular object (the closure delegate), instead of being dispatche=
d to the enclosing class. The above code would only fail at runtime (not at=
compile-time!), as the
launch() method can not be found in the closure context. In order to delegate=
method calls within the closure=E2=80=99s code block to another object ins=
tance, we need to set the closure delegate.
Setting a closure delegate is as easy as invoking
The delegate can be set to an arbitrary object instance (he=
re, an instance of an
nbsp;class that has a
nbsp;method). When the delegate is set accordingly, we can execute the clos=
Note that usually, to avoid odd behavior if the closure is = used in multiple threads, we tend to clone that closure.
The problem with delegate objects are IDEs not knowing abou=
t them. Given our example, most IDEs will underline the
launch() method as being an unknown method in=
This is where
@DelegatesTo comes into play. By adding the
@DelegatesTo annotation to DSL methods like
A future update might let GroovyDoc show the details about = the annotation usage to help users know what methods they can call, what pr= operties they can access, etc.
Here=E2=80=99s what your
() method will look like with the annotation:
Besides specifying the actual delegate type,
can be used to hint at the=
actual resolve strategy. The resolve strategy determines the order in whic=
h non-closure method / property calls are looked up. In our example, <=
code>Closure.DELEGATE_FIRST will be u=
sed. This indicates the closure will attempt to resolve against the given d=
elegate object in first place, followed by the owner object:
IDE support is not the only reason to use
The static type checker and static compiler=
take the additional meta-data specified by the @DelegatesTo annotation into account<=
/span>. If there is a typo in the closure code block, the type che=
cker will complain. And if you use the static compilation capability introd=
uced in Groovy 2.0, the calls will be compiled statically.
Let=E2=80=99s say we wouldn=E2=80=99t call
launchr() in the closure code block, we would get a mes=
Static type checks for custom Do= main-Specific Languages is a very convenient feature i= n Groovy 2.1!
In addition, Groovy 2.1 features other abilities for even f= urther type checking your DSLs, as you shall see in the following section.<= /p>
Before moving on, let=E2=80=99s mention a few closing detai=
lows to specify the receiver calls are delegated to. For instance, when a d=
elegate calls a method or property on another method parameter. Imagine our=
exec() method taking the&=
Executor argument instance=
In this example, the information is lost that the call is d=
elegated to the
er. Thanks to the
/code> annotation we can specify ex as target for being the delegate object:
What if we had several
or parameters, how would we differentiate which one we=
The delegation =E2=80=9Ctarget=E2=80=9D can be specified wi=
th an arbitrary id. In the example above it is
One last very nice little feature: if you are using static =
type checking, you can omit the type of the parameter and
@DelegatesTo combined with =E2=80=9Cflow ty=
ping=E2=80=9D (the ability of following the current type of an untyped vari=
able) would still know if method calls are valid:
We=E2=80=99ve seen that the
DelegatesTo helps documenting, tooling, and checking Dom=
ain-Specific Languages in the specific context of closure delegate based me=
thods, but we hinted at the fact we can go beyond, in terms of static type =
checking for your DSLs.
For more details take a look at the @DelegatesTo documentation.
Static type checking was introduced in Groovy 2.0, but Groo= vy 2.1 goes beyond built-in type checks and offers a way to create type checker extensions. This is = great news for Groovy scripts, configuration files, or Domain-Specific Lang= uages implementations as they can can be =E2=80=9Ctype checked=E2=80=9D wit= h more advanced, domain-specific rules. As an example, it would be possible= to create a custom DSL type checker that throws compilation errors when ce= rtain verbs of the DSL are not recognized, or tells this other noun is allo= wed even if it=E2=80=99s a dynamic name bound at runtime, or type checks li= teral strings containing SQL code to see if the syntax is correct, and more= .
Imagine a script, where we define a small robot class and i= nstantiate it:
And we want to operate our robot in the
operate() method, but we want this method to =
be type checked:
The static type checker will complain as it doesn=E2=80=99t=
understand where the
robot variable is coming from, as it=E2=
=80=99s going through the binding of the script =E2=80=94 note that we coul=
d teach the type checker to figure out binding-bound variables. It will thr=
ow an error telling us that the robot v=
ariable was undeclared.
But by utilizing type checker extensions, we can hook into =
the type checking process to teach it how to handle unresolved variables! I=
n order to do that, we=E2=80=99ll specify an extension script through the n=
;annotation parameter of the
Now it=E2=80=99s time to define the type checker extension =
The type checker extension script is written by applying a new DSL =E2=80=
=94 the =E2=80=9Ctype checking DSL=E2=80=9D. The DSL provides various hooks=
for type checker extensions to register to. Going back to the example abov=
e, we register for unresolved variables using the
The type checker extension script needs to be on the classp=
ath. If this is the case, the script gets notified during compile-time when=
the static type checker encounters an unresolved variable. The unresolved =
variable closure is handed over a
is an object directly from Groovy=E2=80=99s AST (Abstract Synt=
ax Tree). It is a representation of the unresolved variable expression. The=
script checks if the variable is named
, if this is the case, we lookup a
ClassNode representing the
Robot class, and store the type of that variable ba=
ck in the AST. At the end, the
property is set to true, to indicate the type checker already =
managed that variable. As a consequence, you won=E2=80=99t get the compilat=
ion error about that undeclared variable.
To continue the journey, let=E2=80=99s consider the case wh= ere the user enters a wrong direction string. We could of course use an enu= m or some other class containing direction constants, but for the sake of t= he example, we=E2=80=99ll have a look at how we can teach the type checker = to inspect strings and how you can actually throw your own compilation erro= rs.
For that purpose, let=E2=80=99s say a robot can only move l= eft, right, forward and backward. And now, let=E2=80=99s change our robot m= ove instruction to:
The robot is not allowed to move sideways, so we should ins=
truct the type checker to throw a compilation error if it encounters a dire=
ction the robot will not be able to understand. Here=E2=80=99s how we can a=
chieve our goal, by adding a new event handler to our
This handler receives a
dCall expression. We are using the
getTargetMethod() utility method to retrieve th=
MethodNode. We =
check that the method call is a call to our
obot, and that the name of the method corresponds to themove method. Then, we fetch the arguments passed =
to that method call, and if we=E2=80=99re passed a direction in the form of=
a string constant, we are checking that the direction is an actual allowed=
direction. If this is not the case, we are adding a new static typing comp=
ilation error into the mix, so that the compiler will yell at the poor user=
because he used a direction which is forbidden and not understood by our r=
This second example is also interesting in a way that it sh= ows how you can even add compilation checks on things like literal strings = on a domain-specific level, paving the way for possible checks on sprintf s= trings, on SQL or HQL code in strings, etc, allowing you to go even further= that what the Java compiler actually checks.
The extension script can make use of various event oriented=
extension points and utility methods coming from the
TypeCheckingExtension class from Groovy, such a=
The two examples are just the tip of the iceberg, but we wi= ll work out more complete documentation of the various extension points and= utility methods going forward.
For more details take a look at the type checking extensions documentation.
Annotations are a great way to add supplementary meta-data = to classes, methods, fields, and other source code elements, thus framework= s, libraries, and even Groovy=E2=80=99s homegrown AST transformations can t= ake advantage of them to do some special treatments to the correspondi= ng AST nodes. Every now and then the use case arises to reuse a combin= ation of annotations, potentially at the expense of a galore of at-signs th= at obscure the general intent of that particular combination.
To group annotations together, to make the intent clearer o= r to streamline your code, Groovy 2.1 offers a meta-annotation system, which allows to combine other annotations into = one =E2=80=9Calias=E2=80=9D annotation.
Imagine we are using some annotations defining constraints =
on properties of your class, like
@Length, or <=
code>@Pattern, which could be defined as f=
An example of how to annotate an
with those annotations could look like this:
For a single property, that=E2=80=99s quite a bit of annota= tion overload! And it could be the case of other domain classes with proper= ties having the same validation rules as the ISBN property, where we would = need to duplicate that pattern.
As of Groovy 2.1,
nsform.AnnotationCollector can be used to solve code dup=
lication for this use case.
tor can be specified on annotation types and acts as met=
a-annotation. Whenever an annotation marked with it is found, it is replace=
d with its own annotations. Let=E2=80=99s illustrate this with our ISBN exa=
We will create a new annotation combination for the 13-digi=
t ISBN standard, but this time, using the
@ISBN13 as a si=
ngle annotation can now be applied on code e=
lements, instead of applying the entire annotation gang::
What is particularly interesting with such
you would count 3 (
@Pattern). Thus, your underlying framework doesn=E2=
=80=99t need to know about that meta-annotation solution and act accordingl=
In our example above, we annotated our meta-annotation with= the annotations that are then combined together. But for annotations for w= hich you don=E2=80=99t need to specify arguments, you could have also passe= d the names of the annotations to combine as parameters to the annotation c= ollector:
In the above case, we combine the
ing transformation into a meta-annotation called
If you need to pass some specific parameter to one of the u= nderlying annotations which are combined, you can still do so by passing th= e parameter to the meta-annotation.
Let=E2=80=99s assume we need to combine the following annot= ations:
We define the meta-annotation combining both the above anno= tations:
But we want to change the propagation strategy for the unde=
tation, we do so by passing the parameter to the meta-annotation:
Note that if two combined annotations share the same parame= ter name, the last annotation declared wins and gets the parameter passed t= o the meta-annotation.
If you need even more flexibility, meta-annotations allow y= ou to define custom processors. The role of the custom processor is to go b= eyond the simple exchange of the meta-annotation with the combined annotati= ons, to further customize the logic of that transformation.
Custom processors must be precompiled to take action, so we=
=E2=80=99ll create our processor, and then evaluate our final example with =
GroovyShell, but first, let=E2=80=99s talk about the use case.=
We have two validation annotations for defining a minimum a= nd maximum value for an integer property:
If we want to define a range of values, with a lower and an= upper bound, we could define a new annotation and implement the associated= validation logic, or we could use custom meta-annotation processors to&nbs= p;replace a range annotation with a minimum= and a maximum one.
So instead of writing:
We could write:
With the normal replacement logic, there=E2=80=99s no way w= e can map the lower and upper bound values to the minimum and maximum annot= ation element default values. That is where custom processors come into pla= y.
Our meta-annotation definition will look like this:
Notice how we specify that the
@Range annotation is a combination of
x, and more importantly, how we pass a processor parameter to=
to instruct it about our custom meta-annotation processing logic.
In order to create a custom processor, you have to extend t=
class and override the
A few words about the parameters : the
collector corresponds to the
@Range annotation definition,
to the actual usage of the <=
is the annotated class, and&n=
src is script being compile=
We start our implementation of the processor by retrieving =
the numeric expressions of the bounds defined as the from and to annotation pa=
rameters, because we=E2=80=99ll pass those values back to the underlying&nb=
@Max combined annotations. In order to do tha=
t, we retrieve the
@Max combined annotation=
s thanks to the
getTargetAnnotationList() method. We then set the values of the
@Range meta-annotation sinc=
e those parameters aren=E2=80=99t really&nb=
sp;defined on a real annotation but on a meta-annotation. And last, we retu=
rn the two
@Min and <=
code>@Max annotations. If you wanted =
the Groovy compiler to do its usual replacement logic, you could have also =
super.visit(...), but in=
our case it wasn=E2=80=99t needed.
The full example can be found in this Gist on Github: = https://gist.github.com/4= 563430
Additional details can be found in the meta-anno= tations documentation.
When integrating and evaluating Groovy scripts in an applic= ation for business rules or Domain-Specific Languages, it is often valuable= to define a base script class, in order to add various utility methods, pr= operties, or interception mechanisms for missing methods or properties.
CompilerConfiguration object, that you can pass to
GroovyShell and other integration mechanisms, allows you=
to specify a base script class with the
As of Groovy 2.1, we introduce the ability to define a base=
script class reference for your scripts via an additional command-line opt=
--basescript for the
groovyc command, as well as for the
Here=E2=80=99s an example using a script called
In the above script, we notice two things: the usage of a&n=
lookupRate() method, and tw=
o undeclared variables:
USD. Neither the method, =
nor the variables have been defined in our script. Instead, they are provid=
ed by a base script class, which can look like the following
lookupRate() method used in our script is declared in the the base class, and the=
two currencies are retrieved via the
Now it=E2=80=99s time to wire them together, by instructing=
the groovyc compiler or the
groovy command line launcher to use =
our base script class for all
Similarly to the
flag, there=E2=80=99s another new option for the <=
groovyc commands: the
--configscript flag. Its purpose is to let you furt=
her configure the compiler, in a configuration script, by parameterizing th=
ject used for the compilation.
/span>, you can customize the various aspects of the Groovy compilat=
ion process. For example, you can specify various compilation customizers i=
ntroduced in Groovy 1.8. Imagine you want to add a new default import to yo=
ur classes, like importing all
functions and constants, so that your scripts and class=
es don=E2=80=99t have to prefix those functions and constants all the time,=
and to avoid having to do that import wherever needed. Here=E2=80=99s how =
you can proceed.
At first, your script,
rmula.groovy, contains the following lines:
For evaluating such math expressions, you wish to make the = static import implicit, so that the final script will actually look like th= is:
If you=E2=80=99d run it as is, you=E2=80=99d get an error m= essage saying:
We need to use
uration to do add an
tCustomizer. We=E2=80=99ll create ai
mportConfigurer.groovy script with the content below:=
We import and then instantiate an
ImportCustomizer, on which we ask for a static star impo=
rt of the methods and constants of the
ang.Math class. Eventually, we pass that customizer to t=
which is an instance of
n that will be used for the compilation of your math for=
Now, we are able to execute your formula with the following= command-line:
If you use the groovy compiler to compile all your classes,=
one drawback of the approach above is that the customization applies globa=
lly to all classes that are going to be compiled. You may want to add certa=
in default imports only in certain classes (ie. scripts containing math), b=
ut you might want to do something different for other classes, like adding =
to all the domain classes of your application. For that purpose, a new cus=
tomizer was created, the
r, to let you filter which classes should be impacted by part=
icular compilation customizations, such as filtering by class name, by file=
extension, or by a custom logic.
Coming back to our previous example, let=E2=80=99s add the =
default import to our
mathFormula.groovy script, but add a
@ToString transformation to the
The more complex the customization becomes, the more cumber= some the above configuration becomes to write too, that=E2=80=99s why Groov= y 2.1 also provides a builder for building these types of configurations.= p>
The builder allows you to use a familiar declarative syntax= and saves you from adding manually various imports. Let=E2=80=99s adapt ou= r example above with the builder:
The configuration code is easier to read and maintain, than= ks to the clarity brought by the builder ap= proach. But we=E2=80=99ve only seen a couple examples of customization, and= you should have a look at the other customizations available in the org.codehaus.groovy.control.customizers.builder pa= ckage to learn more about them.
More details can be found in the = advanced compiler configuration documentation.
There are now
There are now methods for cre= ating temporary directories and determining the total size of all files in = a directory.
There is now a
collectMany for maps (has bee=
n backported to earlier versions of Groovy too).
There is now a
closeStreams() method for
You ca now explicitly set a file encoding.
There is support for using a "jar:" prefix when running a scri= pt from a URL, in addition to the "file:" and "http:".<= /p>
There is a method for escaping / encoding XML entities in Strings.
= There is a convenience method for serializing
You can now clone
The name() method now works for all
bjects, not just
Multiple environments blocks are now supported and merged.
Can now carry over annotations if desired for methods and method paramet= ers.
You can now cache the
toString value. This is useful for im=
You can now cache the calculated hashCode value. This is useful for immu= table objects.
You can now specify
knownImmutables. This is useful when yo=
u know you are using an immutable object, but its type isn't one of the kno=
wn immutable types.
There is now a
AutoCloneStyle which avo=
ids some annoyances with Java's cloning behavior from
Those who need to clone Grails domain objects might find this useful.