At times we might want to bypass the invocation of a method in a Groovy class. For example, a method in an Order class might be used to specify express shipping, but the shipper conceivably could impose a limit based on weight or size, and therefore the method specifying express shipping should be bypassed. Furthermore, the exact weight (or size) might vary depending on fuel costs, availability of vehicles, etc. and consequently the Groovy class would have to be constantly maintained to reflect the latest weight/size restriction. That adds cost to maintaining our business layer! A better approach is to implement the Intercept-Cache-Invoke design pattern by creating an "interceptor", a Java class that will determine if one or more methods of a class should be bypassed. Using an interceptor obviates the need to constantly maintain our business classes.
To learn how to use the Intercept-Cache-Invoke design pattern in Groovy, follow these 3 steps:
// Define Order class
class Order {
String orderItems
String customer
Date orderDate
int estimatedWeight
public String toString() {
"Items: $orderItems estimated weight $estimatedWeight"
}
public String expressShipping() {
"Preferred express shipping vendor for $orderItems"
}
}
// Define Interceptor implementation
class OrderInterceptor implements Interceptor {
int WEIGHT_LIMIT=100
boolean methodCanBeCalled=true
int weight=0
String orderitems=""
boolean doInvoke() {
methodCanBeCalled
}
Object beforeInvoke (Object object, String methodName, Object[] args) {
// if method is constructor then remember the estimated weight:
if (methodName=="ctor") {
orderitems=args[0]['orderItems']
weight=args[0]['estimatedWeight']
}
if (methodName == 'expressShipping' && weight > WEIGHT_LIMIT) {
methodCanBeCalled = false
}
}
Object afterInvoke (Object object, String methodName, Object[] args, Object message) {
if (methodName == 'expressShipping' && weight > WEIGHT_LIMIT) {
methodCanBeCalled = true
message="Order $orderitems too heavy for express shipping"
}
message
}
}
// Create a proxy object and attach our interceptor implementation
def myProxy=ProxyMetaClass.getInstance(Order)
def myInterceptor=new OrderInterceptor()
myProxy.interceptor=myInterceptor
// Apply interceptor
myProxy.use {
def heavyOrder = new Order(orderItems : "Big Thing", customer : "Acme", orderDate: new Date(), estimatedWeight: 101)
// try express shipping (will fail!)
println(heavyOrder.expressShipping())
def lightOrder = new Order(orderItems : "Smaller Thing", customer : "Jones Supply", orderDate: new Date(), estimatedWeight: 99)
// try express shipping (will succeed!)
println(lightOrder.expressShipping())
}
expressShipping
method is the focus of this demonstration as the estimated weight of the order items might require this method, which requests express
shipping for an order, to be bypassed.
I've supplied comments to describe the overall flow of the script. Notice that we have four pieces to the script: 1) Order class, 2) Interceptor interface implementation,
3) proxy object creation and 4) use of the proxy. The proxy supports an interceptor
property that is assigned to the interceptor implementation. To kick in the
interceptor, we use the use
method of the ProxyMetaClass
class. Now, any call to the Order class methods will automatically trigger the
callback methods. The beforeInvoke
method will be called before each method of the Order
class, including the constructor. Note that the method name is passed to each
callback method and the constructor name is ctor
. When the constructor is intercepted, we have access to all arguments using args[0]
. This array
element points to a map of the arguments. The order items and estimated weight are stored. The order items will be printed in the message generated by the call back methods.
The estimated weight is required to check for compliance. Note that the doInvoke
method determines if an Order method will be invoked. If the return value is
false
then the method is bypassed, else the method will be allowed to execute.
UseGroovyInterceptCacheInvoke.groovy
.Webucator provides instructor-led training to students throughout the US and Canada. We have trained over 90,000 students from over 16,000 organizations on technologies such as Microsoft ASP.NET, Microsoft Office, XML, Windows, Java, Adobe, HTML5, JavaScript, Angular, and much more. Check out our complete course catalog.
Stephen has over 30 years' experience in training, development, and consulting in a variety of technology areas including Java, C, C++, XML, JavaScript, AJAX, Tomcat, JBoss, Oracle, and DB2. His background includes design and implementation of business solutions on client/server, Web, and enterprise platforms. Stephen is a published writer in both technical and non-technical endeavors. Stephen received an undergraduate degree in Computer Science and Physics from Florida State University.