2015-09-20

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.

2015-09-07

Groovy, Callable and ExecutorService

Suppose you want submit job to 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.

2015-09-06

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.

@XmlRootElement
@HashCodeAndEquals
@ToString
class Person {
 String firstName
 String lastName
 Integer age
}


Lets check if we could unmarshal xml to Person class:

def 'should unmarshall person xml to object'(){
    given:
        JAXBContext jc = JAXBContext.newInstance(Person)
        String xml = 'JohnSmith20' 
    expect:
        jc.createUnmarshaller().unmarshal(new StringReader(xml)) == new Person(firstName: 'John', lastName: 'Smith', age: 20)
}


When we try this, then we obtain an eception:

com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
groovy.lang.MetaClass is an interface, and JAXB can't handle interfaces.
 this problem is related to the following location:
  at groovy.lang.MetaClass
  at public groovy.lang.MetaClass com.blogspot.przybyszd.jaxbingroovy.Person.getMetaClass()
  at com.blogspot.przybyszd.jaxbingroovy.Person

 at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:91)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:445)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:277)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.(JAXBContextImpl.java:124)
 at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$JAXBContextBuilder.build(JAXBContextImpl.java:1123)
 at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:147)
 at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:247)
 at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:234)
 at javax.xml.bind.ContextFinder.find(ContextFinder.java:462)
 at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:641)
 at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:584)
 at com.blogspot.przybyszd.jaxbingroovy.PersonJaxbTest.should unmarshall person xml to object(PersonJaxbTest.groovy:10)



It is because groovy defines getMetaClass method for us. Marshaller and Unmarshaller use by default XmlAccessType.PUBLIC_MEMBER what means that public getters and setters should be used during marshalling/unmarshalling.

To solve this just add XmlAccessorType annotatnio with XmlAccessType.FIELD on jaxb class:

@XmlRootElement
@EqualsAndHashCode
@XmlAccessorType(XmlAccessType.FIELD)
class Person {
    String firstName
    String lastName
    Integer age
}


Of course if you want to apply this rule for each jaxb class in package, then you could put XmlAccessorType in pacakge-info.java file.

@XmlAccessorType(XmlAccessType.FIELD)
package com.blogspot.przybyszd.jaxbingroovy;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;


Great, it works.

Now let's check out marshaller:


def 'should marshall person'() {
    given:
        JAXBContext jc = JAXBContext.newInstance(Person)
        Person p = new Person(firstName: 'John', lastName: 'Smith', age: 20)
        StringWriter sw = new StringWriter()
    when:
        jc.createMarshaller().marshal(p, sw)
    then:
        String xml = sw.toString()
        GPathResult gPathResult = new XmlSlurper().parseText(xml)
        gPathResult.name() == 'person'
        gPathResult.firstName == 'John'
        gPathResult.lastName == 'Smith'
        gPathResult.age == '20'
}


And it also works.

Source is available here.