Spring and Groovy are old friends. While you can use POGOs as Spring beans as though they were POJOs, that's not the focus of this article. Instead, I'm going to demonstrate one of the capabilities that is unique (more or less) to Groovy: refreshable beans. They allow you to deploy source code to production that you change while the system is still running. Hey, what could go wrong?
Return with me now, boys and girls, to those thrilling days of yesteryear, way back to 2008. It was a simpler time, when:
It’s that last one that I wish to address now.
Consider a combined Java / Groovy implementation of a mortgage application that uses Spring. First, I’ll start with the MortgageApplication class itself:
MortgageApplication.groovy, a POGO
import groovy.transform.* @Canonical class MortgageApplication { BigDecimal amount BigDecimal rate int years }
The MortgageApplication
class is a Plain Old Groovy Object (POGO). The @Canonical
annotation triggers an Abstract Syntax Tree (AST) transformation. The result is a class that has:
amount
, rate
, and years
)toString
method that shows the fully qualified class name, followed by the attributes in order, top downequals
method that checks the values of all three attributeshashCode
method consistent with equals
Not bad for 7 lines of code, including the import
statement. There is the minor issue that the mortgage application doesn't include anything about the applicant, but that wouldn't change refreshable bean discussion.
The Spring framework is quite happy to use this POGO as a regular Spring bean. Just declare it in the XML application context file, add a component-based annotation, or use a Java-based config class, and you're good.
When you plan on using Java and Groovy together, one way to minimize integration problems (not that there tend to be many in the first place) is to use Java interfaces. So if the plan is to show a bean with one version in Java and another in Groovy, I might as well use a Java interface that they can both implement.
Here is the Evaluator
interface (in Java):
Evaluator.java, an interface implemented by both evaluators
public interface Evaluator { boolean approve(MortgageApplication application); }
The bank simply invokes the approve method with an application, and it returns true or false based on whatever business logic they care to implement. Of course, this being early in 2008, we know what they're effectively doing. Here’s the Java implementation.
JavaEvaluator.java
@Component public class JavaEvaluator implements Evaluator { public boolean approve(MortgageApplication application) { return true; } }
Not much of a surprise there, given the way things eventually worked out. That also shows why nothing was added to the MortgageApplication
class about the person applying?—?it didn’t matter anyway.
The @Component
annotation is there so that Spring can find it automatically on a “component scan.” What is a component scan? Funny you should ask. This is a Spring application, so we need some metadata. Like all the cool kids, I’ll use the Java configuration approach. Here's the relevant class:
AppConfig.groovy, a Spring bean configuration file
@Configuration @ComponentScan class AppConfig {}
(There’s something truly Zen in the minimalist nature of this class, isn’t there? Truly, we live in magical times.)
The @Configuratio
n annotation indicates that this is a class used to declare beans, rather than being a bean itself. I want it to automatically find all the Spring beans (anything annotated with @Component
or one of the annotations based on it) automatically, which is what the @ComponentScan
does. It searches the current directory and all of its descendants and finds all the components.
To show the evaluator in action, I'll create a simple Java class that fires up the Spring container and acquires the bean.
Here's my JavaDemo
class to do that:
JavaDemo.java
public class JavaDemo { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); // 1 Evaluator evaluator = ctx.getBean("javaEvaluator", Evaluator.class); // 2 boolean result = evaluator.approve(null); // 3 System.out.println((result ? "approved" : "denied")); } }
? Start up the Spring container
? Get the Java evaluator bean
? Use the bean to approve a mortgage application
In the main method, the AnnotationConfigApplicationContext
class is used to read the AppConfig
configuration metadata and instantiate the bean. The getBean
method retrieves the bean whose id is javaEvaluator
(the default name of a bean is the class name with a lowercase first letter) and casts it to the proper type. The evaluator is used to approve a null mortgage application, because why not?
So far, this is all standard Spring, except for the fact that I've been mixing in Groovy implementation files. Spring doesn't care where the byte codes come from, as long as it can resolve all the dependencies.
That reminds me: how do I tell Spring about the dependencies? I use a Gradle build, of course. Here's the build file:
build.gradle
apply plugin: 'groovy' repositories { jcenter() // 1 } ext.springVersion = '4.2.0.RELEASE' // 2 ext.spockVersion = '1.0-groovy-2.4' dependencies { compile "org.codehaus.groovy:groovy-all:2.4.4" // 3 compile "org.springframework:spring-context:$springVersion" testCompile "org.spockframework:spock-core:$spockVersion" testCompile "org.springframework:spring-test:$springVersion" testCompile "org.spockframework:spock-spring:$spockVersion" }
? Use JCenter for dependencies
? Latest release version of Spring core
? Latest version of the Groovy compiler
The build tells Gradle to use the JCenter bintray repository to resolve all the dependencies, download them, and cache them locally. The “external” properties (designated as “ext” in the build file) are used to assign the current release versions of the Spring framework and the Spock testing framework to project variables, which are then used in the rest of the dependencies.
After building the project, running the JavaDemo
application prints out, naturally enough, true
.
This is all well and good, but what happens when we change our mind? We now move from the Spring (no pun intended) of 2008 to the Fall of the same year. To the considerable astonishment of the banking industry, all of those bad mortgages turned out to be, well, bad. The stock market crashed, Lehman Brothers collapsed, and the entire world financial system teetered on the brink of ruin.
Those were fun days, weren’t they? (Spoiler alert: No. No they were not. Yuck.)
For our part, we have to find some way to stop the bleeding. What we really need to do is to shut down our system and change the implementation of the approve method so that we're not giving away money we don't have.
The problem is, shutting down the system could itself cause a panic. As Mr. Potter (Henry, not Harry) said to George Bailey during a bank run in It’s a Wonderful Life, “George, you close those doors you’ll never open them again!”
Figure 1. The more things change…
Yikes again. If the implementation is in Java, we have no choice. If, on the other hand, we’re willing to use Groovy, we can take actually change our implementation while the system is still running.
Spring is perfectly happy to use compiled Groovy classes anywhere it normally expects to see compiled Java classes, as demonstrated earlier. But for a limited number of dynamic languages, Spring provides an alternative, known as refreshable beans. The idea is that you can deploy actual source code, rather than compiled byte codes, and tell Spring to periodically check to see if the source code has changed. If it has, Spring will detect the change, reload the bean, and add it to the system while it is still in operation.
That’s pretty awesome, but arguably a rather dangerous idea. How would you like the ability to modify running code in production? Hey, what could go wrong?
(The answer is pretty much everything, which is why we watch.)
In this particular case, now I want to add Groovy source code to my system in a way that does not result in compiled code in the classpath. To do this, I need to use the lang
namespace.
The Java configuration class approach is great for most things, but adding tags from alternative namespaces isn’t necessarily the easiest thing to do. In this case, it turns out that using XML is actually helpful.
Here is an applicationContext.xml
file that declares the lang
namespace, and then adds a Groovy evaluator as a refreshable bean.
applicationContext.xml, in the src/main/resources directory
<?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" 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"> <lang:groovy id="groovyEvaluator" script-source="file:src/main/resources/GroovyEvaluator.groovy" refresh-check-delay="1000" /> </beans>
Declaring the lang
namespace makes the lang:groovy
tag available. This tag is used much the same way a standard bean tag is used, except that instead of a class, the script-source
attribute is assigned to the location of the actual Groovy source code.
Note, by the way, that the name script-source
sounds like it implies that Groovy “scripts” are not compiled. That’s not true?—?the code will still be compiled after it’s loaded. Also, it sounds like it’s designed for a Groovy script, meaning code that isn’t inside a class. That isn’t necessary either. The script-source
attribute here is assigned to a class called GroovyEvaluator
.
The refresh-check-delay
attribute represents the amount of time, in milliseconds, that the container waits before checking to see if the source has been changed. Here it’s set to one second.
What does the GroovyEvaluator class look like? Here it is:
GroovyEvaluator.groovy, deployed as source code
class GroovyEvaluator implements Evaluator { boolean approve(MortgageApplication application) { true } }
Standard Groovy idioms apply here. For example, (1) the class is public by default, (2) the approve
method is public by default, and (3) I don’t need a return keyword inside the approve
method. Also, Groovy classes can implement Java interfaces without a problem.
Now, however, I have Spring bean configuration files in both Groovy and XML. One way to tie them together is to add an @ImportResource
annotation to the Groovy configuration file:
AppConfig.groovy, modified to import the XML configuration file
@Configuration @ComponentScan @ImportResource("classpath:applicationContext.xml") // 1 class AppConfig {}
? Loads the beans in the XML file
(Still pretty Zen, but not as much. The lesson, as always: XML ruins everything.)
A regular @Import
annotation is used to combine Java or Groovy configuration files. Here @ImportResource
loads the applicationContext.xml
file from the classpath and adds the bean definitions to the context. The nice part about using this approach is that you don’t have to change the client code (from the main
method in JavaDemo
) in any way.
Before proceeding, it’s probably a good idea to test the evaluators, though honestly there isn’t much in them by way of business logic. Still, it’s a good practice to test it anyway. Spring has great testing capabilities built in, involving its TestContext
mechanism. Best of all, I can use the “Spock-Spring” dependency from the Gradle build file to write my test in Spock and still take advantage of Spring's dependency injection.
Here is a Spock test that checks the approve
method in both the Java and Groovy evaluators.
EvaluatorSpec.groovy, a Spock test for the evaluators
@ContextConfiguration(classes = AppConfig) class EvaluatorSpec extends Specification { @Resource(name = 'javaEvaluator') Evaluator javaEvaluator @Resource(name = 'groovyEvaluator') Evaluator groovyEvaluator def 'dependency injection works'() { expect: "injected references should not be null" javaEvaluator groovyEvaluator } def 'all Java mortgage applications are approved'() { expect: javaEvaluator.approve(null) } def 'all Groovy mortgage applications are approved'() { expect: groovyEvaluator.approve(null) } }
Normally I use the @Autowire
annotation to bring my class being tested into the test itself, but since @Autowire
actually uses “autowire by type” and both evaluators implement the same interface, I would have a conflict. I could resolve that with a @Qualifier
annotation, but there's an easy alternative. The @Resource
annotation from Java does “autowire by name”, so here I’m using it to bring in both of the evaluators individually.
The three Spock tests use an expect
block. The Spock testing framework evaluates every statement in the expect
block according to the Groovy truth (i.e., not null is true, non-zero is true, non-empty collections or strings are true, etc), and the test passes only if that condition is satisfied. In this case, the injected references are not null and both evaluators return true for any mortgage application, so we’re good, test-wise if not financially.
Returning now to the application, it’s time to demonstrate the refreshable bean. Here is a Groovy script (a real one this time) to do that, though I could have just as easily used Java for the same purpose:
groovy_demo.groovy, a script to run the refreshable bean
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig) Evaluator evaluator = ctx.getBean("groovyEvaluator", Evaluator) 10.times { boolean result = evaluator.approve(null) println result ? 'approved' : 'denied' sleep 1000 }
After loading the container and instantiating all the beans, the script finds the groovyEvaluator
bean and casts it to type Evaluator
. The times
method on integer invokes its closure argument the integer number of times. In this case, it executes the approve
method on the evaluator 10 times, sleeping for 1 second in between each. That gives me enough time to change the value in the source GroovyEvalutor
source code to this:
GroovyEvaluator.groovy, updated to stop printing money
class GroovyEvaluator implements Evaluator { boolean approve(MortgageApplication application) { false } }
This change is made some time during the execution of the script.
I’ll show that in a moment, but before that there’s one last minor complication. As you may have noticed from the XML application context, the GroovyEvaluator.groovy
file is not in src/main/groovy
, it’s in src/main/resources
. That’s because it doesn't need to be in the classpath, and I don’t want it to be in the classpath because I don't want it to be compiled. Instead, by putting it in the resources
folder, I can still use it without compiling it. This does, however, confuse a poor IDE, which isn’t sure how to handle that situation at all.
(Btw, IntelliJ IDEA was confused for a while, but eventually figured it out.)
The easy work-around is to use Gradle to execute the script. That can be done by adding a custom task to the Gradle build file.
build.gradle task to execute the Groovy demo
task demo(type:JavaExec) { main = 'com.kousenit.demo.groovy_demo' classpath sourceSets.main.runtimeClasspath }
The task is of type JavaExec
, ironically enough, even though I'm calling a Groovy script. All I need to do is set the classpath to be the runtime classpath while executing the script, and everything runs properly.
If I run this by typing >gradle demo at the command line, the result is similar to:
:compileJava UP-TO-DATE :compileGroovy :processResources UP-TO-DATE :classes :demo Jul 31, 2015 10:46:28 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@548ad73b: startup date [Fri Jul 31 22:46:28 CDT 2015]; root of context hierarchy Jul 31, 2015 10:46:28 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [applicationContext.xml] approved approved approved approved approved // 1 denied denied denied denied denied
? Change the implementation from true
to false
There you have it. The GroovyEvaluator
source code is deployed into the container, which checks every second to see whether or not it has been modified. When it is, Spring reloads and recompiles the bean and adds to the already running system. You don't even need to retrieve it again (note that the getBean
method was invoked outside the loop). The changes are picked up automatically.
That’s all well and good, but most companies are understandably reluctant to deploy source code into production that can be changed while the system is running. So what good is this in practice?
Some problems simply aren't detectable unless the system is under significant load. Refreshable beans allow developers in a controlled environment to deploy what is effectively an intelligent probe into the system, which can be adjusted in any way needed. You might decide to log some other package, or process some data as it’s being collected, or, as here, change the actual behavior of a running system. The details are up to you.
This is a very powerful technique, and as such is rather dangerous. As in most things, the Spider-Man corollary applies?—With Great Power Comes Great Responsibility.
(I'd link to the Spiderman movie here, but with all the reboots and bad sequels, I’ve pretty much given up on the franchise. So I’ll leave it to the late, great, Michael Conrad to have the final word: Let’s be careful out there.)
Written by Ken Kousen
Our live, instructor-led lectures are far more effective than pre-recorded classes
If your team is not 100% satisfied with your training, we do what's necessary to make it right
Whether you are at home or in the office, we make learning interactive and engaging
We accept check, ACH/EFT, major credit cards, and most purchase orders
Alabama
Birmingham
Huntsville
Montgomery
Alaska
Anchorage
Arizona
Phoenix
Tucson
Arkansas
Fayetteville
Little Rock
California
Los Angeles
Oakland
Orange County
Sacramento
San Diego
San Francisco
San Jose
Colorado
Boulder
Colorado Springs
Denver
Connecticut
Hartford
DC
Washington
Florida
Fort Lauderdale
Jacksonville
Miami
Orlando
Tampa
Georgia
Atlanta
Augusta
Savannah
Hawaii
Honolulu
Idaho
Boise
Illinois
Chicago
Indiana
Indianapolis
Iowa
Cedar Rapids
Des Moines
Kansas
Wichita
Kentucky
Lexington
Louisville
Louisiana
New Orleans
Maine
Portland
Maryland
Annapolis
Baltimore
Frederick
Hagerstown
Massachusetts
Boston
Cambridge
Springfield
Michigan
Ann Arbor
Detroit
Grand Rapids
Minnesota
Minneapolis
Saint Paul
Mississippi
Jackson
Missouri
Kansas City
St. Louis
Nebraska
Lincoln
Omaha
Nevada
Las Vegas
Reno
New Jersey
Princeton
New Mexico
Albuquerque
New York
Albany
Buffalo
New York City
White Plains
North Carolina
Charlotte
Durham
Raleigh
Ohio
Akron
Canton
Cincinnati
Cleveland
Columbus
Dayton
Oklahoma
Oklahoma City
Tulsa
Oregon
Portland
Pennsylvania
Philadelphia
Pittsburgh
Rhode Island
Providence
South Carolina
Charleston
Columbia
Greenville
Tennessee
Knoxville
Memphis
Nashville
Texas
Austin
Dallas
El Paso
Houston
San Antonio
Utah
Salt Lake City
Virginia
Alexandria
Arlington
Norfolk
Richmond
Washington
Seattle
Tacoma
West Virginia
Charleston
Wisconsin
Madison
Milwaukee
Alberta
Calgary
Edmonton
British Columbia
Vancouver
Manitoba
Winnipeg
Nova Scotia
Halifax
Ontario
Ottawa
Toronto
Quebec
Montreal
Puerto Rico
San Juan