Groovy as a dynamic language
Groovy can be used to a great benefit to capture branching logic and business rules validation that change frequently.
Groovy scripts are interpreted within the JVM, they can provide ability to dynamically modify behavior of a program at runtime.
Runtime logic adjustment is an important feature in many business apps. E.g. validation, conditional flows can evolve quickly and require faster promotion to production than your sprint schedules.
With Groovy, you just need to change the script and drop it into your standard location where the application will re-read it and execute.
Groovy benefits are:
- Conciseness in code;
- Smooth learning curve;
- Easily readable code, especially for Java developers, very good for coding validation and branching logic
- Seamless integration with java apps, for example using Spring’s dynamic language support
As an example, we can look at a financial app that will need to receive trade representation and has to validate the trade.
First, we add groovy support to our pom file.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>test</groupId> <artifactId>spring1</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <spring-ver>4.3.7.RELEASE</spring-ver> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring-ver}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring-ver}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring-ver}</version> </dependency> <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.4.7</version> </dependency> </dependencies> </project>
In our spring xml we will add support for dynamic languages.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- this is the bean definition for the Groovy-backed SecurityValidator implementation --> <lang:groovy id="secValidator" refresh-check-delay="5000" script-source="classpath:SecurityValidator.groovy"> <lang:property name="dao" ref="dao"/> </lang:groovy> <!-- this is the bean definition for the Groovy-backed SkipValidator implementation --> <lang:groovy id="skipValidator" refresh-check-delay="5000" script-source="classpath:SkipValidator.groovy"/> <bean id="dao" class="springgroovy.Dao"/> <bean id="tradeProcessor" class="springgroovy.TradeProcessor"> <property name="validators"> <util:list value-type="springgroovy.Validator"> <ref bean="secValidator" /> <ref bean="skipValidator" /> </util:list> </property> </bean> </beans>
Now let’s show the implementation of the above configuration.
Our validator interface will be as follows, defining only two methods.
package springgroovy; public interface Validator { String getName(); boolean validate(TradeBean t) throws ValidationException; }
Our trade processor will need a list of these validators, they will be called from the process() method.
package springgroovy; import java.util.List; public class TradeProcessor { private List<Validator> validators; public void process(TradeBean t) { try { for (Validator v: getValidators()) { if (!v.validate(t)) { System.err.println("Trade did not pass validation in " + v.getName()); t.setStatus(STATUS.INVALID); return; } } t.setStatus(STATUS.VALIDATED); System.out.println("Trade processed: "+t.getSecurityName()+", asset class=" + t.getAssetClass()); } catch (ValidationException e) { System.err.println("Trade failed to process: error=" + e.getMessage()); } finally { saveToDb(t); } } private void saveToDb(TradeBean t) { System.out.println("Trade saved: sec id=" + t.getSecurityId()); } public List<Validator> getValidators() { return validators; } public void setValidators(List<Validator> validators) { this.validators = validators; } public enum STATUS { INVALID, VALIDATED } }
And the dynamic validation rules will be coded in Groovy scripts.
package springgroovy; import java.util.List; public class TradeProcessor { private List<Validator> validators; public void process(TradeBean t) { try { for (Validator v: getValidators()) { if (!v.validate(t)) { System.err.println("Trade did not pass validation in " + v.getName()); t.setStatus(STATUS.INVALID); return; } } t.setStatus(STATUS.VALIDATED); System.out.println("Trade processed: "+t.getSecurityName()+", asset class=" + t.getAssetClass()); } catch (ValidationException e) { System.err.println("Trade failed to process: error=" + e.getMessage()); } finally { saveToDb(t); } } private void saveToDb(TradeBean t) { System.out.println("Trade saved: sec id=" + t.getSecurityId()); } public List<Validator> getValidators() { return validators; } public void setValidators(List<Validator> validators) { this.validators = validators; } public enum STATUS { INVALID, VALIDATED } }
package pkg1.groovy; // import the Validator interface (written in Java) that is to be implemented import springgroovy.Validator import springgroovy.TradeBean import springgroovy.Dao import springgroovy.ValidationException // define the implementation in Groovy class PortfolioValidator implements Validator { def skipPortfolios = ['IGNORE','SKIP']; public boolean validate(TradeBean t) { // write sophisticated validation logic here return !(t.portfolio in skipPortfolios); } String getName() { getClass().getName(); } }
And the main class with a few trades getting processed.
package springgroovy; import java.io.IOException; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); TradeProcessor proc = ctx.getBean(TradeProcessor.class); TradeBean t1 = new TradeBean("20170404","10001","Sell",100,"IBM","PORTF1"); System.out.println(" >> trade: "+t1); proc.process(t1); TradeBean t2 = new TradeBean("20170404","10002","Buy",2000,"US912828V988","PORTF1"); System.out.println(" >> trade: "+t2); proc.process(t2); TradeBean t3 = new TradeBean("20170404","10003","Buy",3000,"MMM","PORTF1"); System.out.println(" >> trade: "+t3); proc.process(t3); TradeBean t4 = new TradeBean("20170404","10004","Sell",3000,"IBM","SKIP"); System.out.println(" >> trade: "+t4); proc.process(t4); } }
The complete example of the code can be downloaded from here: