• google plus

How to Use the Intercept-Cache-Invoke Design Pattern in Groovy

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:

  1. Open your text editor and type in the following Groovy statements:
    // 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())
    }	
    
    
    The 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.
  2. Save your file as UseGroovyInterceptCacheInvoke.groovy.
  3. In the command prompt, type in the command to interpret and run your script:
    Run Intercept script
    The output shows the interceptor correctly determines if the express shipping method should be allowed to execute.
Author: Stephen Withrow

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.

About Webucator

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.