The object
keyword in Kotlin creates singleton in a very convenient way. It can be used for example as a state of an operation. Spock Framework is one of the most expressive Read more
Tag: groovy
Karaf configuration as Groovy file
Introduction
By deafault, Apache Karaf keeps configuration for bundles in the etc
directory as flat properties files. We can override configuration for the storing Read more
Tired of exporting your OSGI metatype to client manually?
Feel my pain
We use OSGi, but we don’t deploy our bundles further than testing environment. It is our client who deploys it to production. However, they rarely read Read more
Using Kotlin extensions in Groovy
OSGi Blueprint visualization
What is blueprint?
Blueprint is a dependency injection framework for OSGi bundles. It could be written by hand or generated using Blueprint Maven Plugin. Blueprint file is only an XML describing beans, services and references. Each OSGi bundle could have one or more blueprint files.
Blueprint files represent architecture of our bundle. Let's visualize it using groovy script and graphviz available in my github repository and analyze.
Example generation
Pre: All you need is groovy and graphviz installed on your OS
I am working mostly with bundles with generated blueprint, so I will use blueprint file generated from Blueprint Maven Plugin tests as example. All examples are included in github repository.
Generation could be invoked by running run.sh
script with given destination file prefix (png extension will be added to it) and path to blueprint file:
mkdir -p target
./run.sh target/fullBlueprint fullBlueprint.xml
Visualization is available here.
Separating domains
First if you look at the image, you see that some beans are grouped. You could easily extract such domains with tree roots: beanWithConfigurationProperties
and beanWithCallbackMethods
to separate blueprint files and bundles in future and generate images from them:
./run.sh target/beanWithCallbackMethods example/firstCut/beanWithCallbackMethods.xml
./run.sh target/beanWithConfigurationProperties example/firstCut/beanWithConfigurationProperties.xml
./run.sh target/otherStuff example/firstCut/otherStuff.xml
Now we have three, a bit cleaner, images: beanWithConfigurationProperties.png
, beanWithCallbackMethods.png
and otherStuff.png
.
We also could generate image from more than one blueprint:
./run.sh target/joinFirstCut example/firstCut/otherStuff.xml example/firstCut/beanWithConfigurationProperties.xml example/firstCut/beanWithCallbackMethods.xml
And the result is here. The image contains beans grouped by file, but if you do not like it, you could force generation without such separation using option --no-group-by-file
:
./run.sh target/joinFirstCutGrouped example/firstCut/otherStuff.xml example/firstCut/beanWithConfigurationProperties.xml example/firstCut/beanWithCallbackMethods.xml --no-group-by-file
It will generate image with all beans from all files.
Exclusion
Sometimes it is difficult to spot and extract other domains. It will be easier to do some experiments on blueprint. For example, bean my1
is a dependency for too many other beans. You could consider converting my1
bean to OSGi service and extracting it to another bundle.
Let's exclude my1
bean from generation via -e
option and see what happens:
./run.sh target/otherStuffWithoutMy example/firstCut/otherStuff.xml -e my1
Result is available here. Now we see, that tree with root bean myFactoryBeanAsService
could be separated and my1
could be inject to it as osgi service in another bundle.
You could exclude more than one bean adding -e
switch for each of them, e. g. -e my1 -e m2 -e myBean123
.
Conclusion
Blueprint is great for dependency injection for OSGi bundles, but it is easy to create quite big context containing many domains. It is much easier to recognize or search for such domains using blueprint visualizer script.
YOUR CODE HRER
Do not use AllArgsConstructor in your public API
Introduction
Do you think about compatibility of your public API when you modify classes from it? It is especially easy to miss out that something incompatibly changed when you are using Lombok. If you useAllArgsConstructor
annotation it will cause many problems.
What is the problem?
Let's define simple class withAllArgsConstructor
:
@Data @AllArgsConstructor public class Person { private final String firstName; private final String lastName; private Integer age; }Now we can use generated constructor in spock test:
def 'use generated allArgsConstructor'() { when: Person p = new Person('John', 'Smith', 30) then: with(p) { firstName == 'John' lastName == 'Smith' age == 30 } }And the test is green. Let's add new optional field to our Person class -
email
:
@Data @AllArgsConstructor public class Person { private final String firstName; private final String lastName; private Integer age; private String email; }Adding optional field is considered compatible change. But our test fails...
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.github.alien11689.allargsconstructor.Person(java.lang.String, java.lang.String, java.lang.Integer)
How to solve this problem?
After adding field add previous constructor
If you still want to useAllArgsConstructor
you have to ensure compatibility by adding previous version of constructor on your own:
@Data @AllArgsConstructor public class Person { private final String firstName; private final String lastName; private Integer age; private String email; public Person(String firstName, String lastName, Integer age) { this(firstName, lastName, age, null); } }And now our test again passes.
Annotation lombok.Data
is enough
If you use only Data
annotation, then constructor, with only mandatory (final
) fields, will be generated. It is because Data
implies RequiredArgsConstructor
:
@Data public class Person { private final String firstName; private final String lastName; private Integer age; }
class PersonTest extends Specification { def 'use generated requiredFieldConstructor'() { when: Person p = new Person('John', 'Smith') p.age = 30 then: with(p) { firstName == 'John' lastName == 'Smith' age == 30 } } }After adding new field
email
test still passes.
Use Builder
annotation
Annotation Builder
generates for us PersonBuilder
class which helps us create new Person
:
@Data @Builder public class Person { private final String firstName; private final String lastName; private Integer age; }
class PersonTest extends Specification { def 'use builder'() { when: Person p = Person.builder() .firstName('John') .lastName('Smith') .age(30).build() then: with(p) { firstName == 'John' lastName == 'Smith' age == 30 } } }After adding email field test still passes.
Conclusion
If you useAllArgsConstructor
you have to be sure what are you doing and know issues related to its compatibility. In my opinion the best option is not to use this annotation at all and instead stay with Data
or Builder
annotation.
Sources are available here. Primitives and its wrapped types compatibility
Introduction
How often do you think about possible changes in your API? Do you consider that something required could become optional in future? How about compatibility of such change? One of this changes is going from primitive (e. g. int
) to its wrapped type (e. g. Integer
). Let's check it out.
API - first iteration
Let's start with simple DTO class Dep
in our public API.
public class Dep {
private int f1;
public int getF1(){
return f1;
}
public void setF1(int f1){
this.f1 = f1;
}
// other fields and methods omitted
}
f1
is obligatory field that never will be null.
Let's use it in Main
class:
public class Main {
public static void main(String... args) {
Dep dep = new Dep();
dep.setF1(123);
System.out.println(dep.getF1());
}
}
compile it:
$ javac depInt/Dep.java
$ javac -cp depInt main/Main.java
and run:
$ java -cp depInt:main Main
123
It works.
API - obligatory field become optional
Now suppose our business requirements have changed. f1
is not longer obligatory and we want possibility to set it to null
.
So we provide next iteration of Dep
class where f1
field has type Integer
.
public class Dep {
private Integer f1;
public Integer getF1(){
return f1;
}
public void setF1(Integer f1){
this.f1 = f1;
}
// other fields and methods omitted
}
We compile only the new Dep
class because we do not want to change the Main
class:
$ javac depInteger/Dep.java
and run it with old Main
:
$ java -cp depInteger:main Main
Exception in thread "main" java.lang.NoSuchMethodError: Dep.setF1(I)V
at Main.main(Main.java:4)
Wow! It does not work...
Why does it not work?
We can use javap
tool to investigate Main
class.
$ javap -c main/Main.class
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String...);
Code:
0: new #2 // class Dep
3: dup
4: invokespecial #3 // Method Dep."<init>":()V
7: astore_1
8: aload_1
9: bipush 123
11: invokevirtual #4 // Method Dep.setF1:(I)V
14: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
17: aload_1
18: invokevirtual #6 // Method Dep.getF1:()I
21: invokevirtual #7 // Method java/io/PrintStream.println:(I)V
24: return
}
The most important are 11th and 18th instructions of main
method. Main
lookups for methods which use int
(I
in method signature).
Next let's compile the Main
class with Dep
which has f1
of type Integer
:
$ javac -cp depInteger main/Main.java
and use javap
on this class:
$ javap -c main/Main.class
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String...);
Code:
0: new #2 // class Dep
3: dup
4: invokespecial #3 // Method Dep."<init>":()V
7: astore_1
8: aload_1
9: bipush 123
11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: invokevirtual #5 // Method Dep.setF1:(Ljava/lang/Integer;)V
17: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_1
21: invokevirtual #7 // Method Dep.getF1:()Ljava/lang/Integer;
24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
27: return
}
Now we see the difference. The main
method:
- converts
int
toInteger
in instruction 11th, - invokes method
setF1
which takes parameter of typeInteger
(Ljava/lang/Integer;
) in instruction 14th, - invokes method
getF1
which returnsInteger
in instruction 21st.
These differences do not allow us to use the Main
class with Dep
without recompilation if we change f1
.
How about Groovy?
We have GroovyMain
class which do the same as Main
class written in Java.
class GroovyMain {
static void main(String... args) {
Dep dep = new Dep(f1: 123)
println(dep.f1)
}
}
We will compile GroovyMain
class only with Dep
which uses int
:
$ groovyc -cp lib/groovy-all-2.4.5.jar:depInt -d main main/GroovyMain.groovy
It runs great as expected with int
:
$ java -cp lib/groovy-all-2.4.5.jar:depInt:main GroovyMain
123
but with Integer
... It works the same!
$ java -cp lib/groovy-all-2.4.5.jar:depInteger:main GroovyMain
123
Groovy is immune to such change.
With CompileStatic
But what if we compile groovy with CompileStatic
annotation? This annotation instructs groovy compiler to compile class with type checking and should produce bytecode similar to javac
output.
GroovyMainCompileStatic
class is GroovyMain
class with only CompileStatic
annotation:
import groovy.transform.CompileStatic
@CompileStatic
class GroovyMainCompileStatic {
static void main(String... args) {
Dep dep = new Dep(f1: 123)
println(dep.f1)
}
}
When we compile this with Dep
with int
field:
$ groovyc -cp lib/groovy-all-2.4.5.jar:depInt -d main main/GroovyMainCompileStatic.groovy
then of course it works:
$ java -cp lib/groovy-all-2.4.5.jar:depInt:main GroovyMainCompileStatic
123
but with Dep
with Integer
field it fails like in Java:
$ java -cp lib/groovy-all-2.4.5.jar:depInteger:main GroovyMainCompileStatic
Exception in thread "main" java.lang.NoSuchMethodError: Dep.setF1(I)V
at GroovyMainCompileStatic.main(GroovyMainCompileStatic.groovy:6)
Conclusion
Change from primitive to its wrapped java type is not compatible change. Bytecode which uses dependent class assumes that there will be method which consumes or returns e. g. int
and cannot deal with the same class which provides such method with Integer
in place of int
.
Groovy is much more flexible and could handle it, but only if we do not use CompileStatic
annotation.
The source code is available here.
Easy configuration usage with ConfigSlurper
What's the problem?
We have to deal with properties in almost every projects that we write. Properties class, which we use in these cases, is just mapping key to value. Sometimes it is fine, but in many cases properties look like tree. Example of properties file is shown below:systemName=test
endpoint.first.protocol=http
endpoint.first.address=localhost
endpoint.first.port=8080
endpoint.first.path=test
endpoint.second.protocol=ftp
endpoint.second.address=localhost
endpoint.second.port=21
endpoint.second.user=admin
endpoint.second.password=pass
Here we have simple properties like systemName and also complex endpoints definition (all properties which start with endpoint) and single endpoints definition (each endpoint properties starts with endpoint.<ENDPOINT_NAME>).
How simple could it be to treat this properties like a tree and simply extract subset of them?
The answer is using ConfigSlurper.
ConfigSlurper from properties
To use ConfigSlurper just parse properties object:def 'should import configuration from properties'() {
given:
Properties p = new Properties()
p.load(ConfigSlurperTest.getResourceAsStream('/configuration.properties'))
expect:
new ConfigSlurper().parse(p).systemName as String == 'test'
}
Parse method returns ConfigObject which is just very clever map Map.
Now you could get property using dot notation:
def 'should get nested property'() {
expect:
fromProperties.endpoint.first.protocol == 'http'
}
But there is a deal. If you use ConfigObject then you cannot use it like normal Properties and get property with dots.
def 'should not used nested property as one string'() {
expect:
fromProperties.'endpoint.first.protocol' != 'http'
}
ConfigObject allows you to extract subtree as Properties:
def 'should get first endpoint info from properties'() {
expect:
fromProperties.endpoint.first.toProperties() == [
protocol: 'http',
address : 'localhost',
port : '8080',
path : 'test'
]
}
and even:
def 'should allow for nested property as one string when toProperties called'() {
expect:
fromProperties.endpoint.toProperties()['first.protocol'] == 'http'
}
If you want to know how many endpoint you have and how they are named you could use keySet method:
def 'should get list of endpoints'() {
expect:
fromProperties.endpoint.keySet() == ['first', 'second'] as Set
}
ConfigSlurper do not return null even if property is not found, so you could get nested property without fear:
def 'should not throw exception when missing property'() {
expect:
fromProperties.endpoint.third.port.toProperties() == [:] as Properties
}
You have only to be careful, when have property named like begining of another property:
def 'should throw exception when asking for too nested property'() {
when:
fromProperties.endpoint.first.port.test
then:
thrown(MissingPropertyException)
}
fromProperties.endpoint.first.port returns String and do not have test property.
You could also print properties from ConfigObject:
println fromProperties.prettyPrint()
The output looks like this:
endpoint {
first {
path='test'
port='8080'
protocol='http'
address='localhost'
}
second {
password='pass'
protocol='ftp'
address='localhost'
port='21'
user='admin'
}
}
systemName='test'
Hmm... It looks like DSL. Why do not keep your configuration in this manner?
ConfigSlurper from script
Your configuration could be a groovy script.systemName = 'test'
endpoint {
first {
path = 'test'
port = 8080
protocol = 'http'
address = 'localhost'
}
second {
password = 'pass'
protocol = 'ftp'
address = 'localhost'
port = 21
user = 'admin'
}
}
test.key = ['really': 'nested?'] as Properties
You could pass such configuration as resource stream or file content:
def 'should get config from script as url'() {
given:
ConfigObject config = new ConfigSlurper().parse(ConfigSlurperTest.getResource('/configuration.groovy'))
expect:
config.systemName == 'test'
}
def 'should get config from script as string'() {
given:
ConfigObject config = new ConfigSlurper().parse(ConfigSlurperTest.getResource('/configuration.groovy').text)
expect:
config.systemName == 'test'
}
What interesting all your properties do not have to be strings. It could be any object: String, long, int, etc.
def 'should get nested properties from script as int'() {
expect:
fromScript.endpoint.first.port == 8080
}
def 'should get really nested properties from script and continue digging'() {
expect:
fromScript.test.key.really == 'nested?'
}
Conclusion
You could deal with properties like simple Map, but why if you could instead use it like tree of properties? Sources are available here.Groovy, Callable and ExecutorService
The Baroque version
You could create a class that implements Callable:class MyJob implements Callable<Integer> {
@Override
Integer call() throws Exception {
return 42
}
}
and give it to the executor service:
def 'submit callable as MyJob object'() {
expect:
executorService.submit(new MyJob()).get() == 42
}
The response is, as expected, 42.
Map as Callable version
You want to use this job only in one place so why not inline this class:def 'submit callable as map'() {
expect:
executorService.submit([call: { 42 }] as Callable).get() == 42
}
The response is again 42.
Groovy closure version
Why not use closure instead of map?def 'submit callable as closure'(){
expect:
executorService.submit { 42 }.get() == 42
}
The response is ... null.
Condition not satisfied:
executorService.submit { 42 }.get() == 42
| | | |
| | null false
| java.util.concurrent.FutureTask@21de60b4
java.util.concurrent.Executors$FinalizableDelegatedExecutorService@1700915
Why? It is because Groovy treats this closure as Runnable, not Callable and Future#get returns null when task is complete.
Groovy closure version with cast
We have to cast our closure before submiting to executor service:def 'submit callable as closure with cast'() {
when:
int result = executorService.submit({ return 42 } as Callable<Integer>).get()
then:
result == 42
}
The response is, as expected, again 42.What interesting, the same test with inlined result variable fails... Strange... It could be Spock framework error.
Source code is available here.
Writing JAXB in Groovy
Suppose you want write a jaxb class in groovy. Why? Because you do not have to write these all getters, setters and other methods. You only have to write your fields down. Read more