Test Coverage Using JMockit

January 21, 2012

The JMockit Unit Testing library continues to astound. One new thing I discovered is its Coverage reporting.

Code Coverage
Code coverage is simply a measurement of what code has been actually run when tests are executed. There are many such measures, ramifications, and tools. Like testing itself, code coverage measurement is probably not done enough, or misused.

“Code coverage tells you what you definitely haven’t tested, not what you have.” — Mark Simpson in comment

Path Coverage
Plenty of coverage reporting tools out there. What this one also includes is Path coverage. This is different then branch coverage. Paths are possible execution paths from entry points to exit points. If you visualize a methods statements in a directed graph, paths are a enumeration of the possible edges traversed when that method is invoked. So, Path coverages is inclusive of Branch coverage. Well, I’m not a testing expert, so this may be way off.

Very surprising results. For example, you run a coverage report with a tool such as Cobertura or Emma and feel very happy that you exercised every line and branch with your tests. Then you run the same tests but use JMockit Coverage and discover your tests didn’t cover all the paths! Not only that your line coverage wasn’t so great either.

Report
JMockit explicitly gives you a report showing:

Path
Measures how many of the possible execution paths through method/constructor bodies were actually executed by tests.
The percentages are calculated as 100*NPE/NP, where NP is the number of possible paths and NPE the number of fully executed paths.

Line
Measures how much of the executable production code was exercised by tests. An executable line of code contains one or more executable segments.
The percentages are calculated as 100*NE/NS, where NS is the number of segments and NE the number of executed segments.

Data
Measures how many of the instance and static non-final fields were fully exercised by the test run. To be fully exercised, a field must have the last value assigned to it read by at least one test. The percentages are calculated as 100*NFE/NF, where NF is the number of non-final fields and NFE the number of fully exercised fields.

– from the JMockit coverage report HTML page

Other information is found by using the full HTML output option.

Example
A sample JMockit coverage report is here. Of course you can drill down into various parts of the html page. Like when you click on an exercised line you will get a list of what invoked that line.

Worth it?
Are the metrics such as Path coverage that this tool generates accurate? Is JMockit coverage a replacement for other tools such as Cobertura? I don’t know. For most projects, the resources would probably make the use of coverages generated by multiple tools prohibitive.

Evaluation
One possible approach to evaluating coverage tools is to just use actual real results of the target application. Use the list of bugs and correlate to a coverage tool report. Where were the bugs? Which tool gave the least measure for this location?

Further Reading


Eliades Ochoa – Siboney


Unit Testing what will never happen?

December 4, 2011

Some developers refuse or are slow to test units for certain values or situations because, they claim, those values or situations will not occur.

Some reasons given are that the presentation tier or UI will prevent certain values; that other modules in the call chain already have error handling and validation. So why test something that won’t happen?

Let’s take an example. In the method/function below, the type will never be blank or null. Looking at the actual source code for the application will show this. So should a unit test be written that will invoke this method with a null, “”, or ” “?

public boolean service(final String type){
    // do stuff
    return result;
}

Yes!
1. Things change.
2. Bad stuff happens.
3. Development process will use invalid values.
4. More complete testing. Regression testing, path coverage, etc.
5. The method could now be used in a different call chain.
6. Its a public method, anything can invoke it, even a business partner via some remoting technology.
7. When invoked as part of other unit tests, it could have invalid values.

#3 is, I think, the most important. Did you ever do development and something not work and it turn out to be existing code that did not handle arguments correctly? That is wasted time and an aggravation. Sure, in the current production code, a blank string won’t be used, but during development, especially TDD, you scaffold code. Sometimes you don’t have values yet, so you just use a blank string.

Just the other day I tested a deployed production method that correctly tested the argument for an empty string, “”, and did the right thing. However, the code did not check for a blank string, ” “, and throws an exception. Unit testing would have shown this.

And, yes, this will never happen in our application. :)

Ok, you still don’t want to test something for null or invalid values? At least put this in writing in the Javadoc: “I (insert your name and address) am a great developer and stake my professional career that this method will never be invoked with invalid values.” The address is needed so us mediocre developers can hunt you down when the server crashes.

Further Reading

  1. Duplicate blog post: Unit testing what will never happen
  2. FindBugs and JSR-305

Off topic, some music …
” If ” – Oregon (2009)


JMockit

November 19, 2011

Yesterday at work I gave a presentation on Unit Testing. It went well. 160 slides! And, no one passed out and hit the floor.

One thing I mentioned was mocking frameworks and how JMockit is very useful. Perhaps JMockIt represents the state of the art in Java based Mocking tools.

There are plenty of good reasons for using mocks:

“JMockit allows developers to write unit/integration tests without the testability issues typically found with other mocking APIs. Tests can easily be written that will mock final classes, static methods, constructors, and so on. There are no limitations.” — JMockit

JMockit is ” a collection of tools and APIs for use in developer testing, that is, tests written by developers using a testing framework such as JUnit or TestNG.”

I’ve used it for some tests. Since it uses Java instrumentation it can mock almost anything, especially those legacy untestable great OO classes. Best of all it has a very good tutorial.

The only ‘negative’, so far, is that JMockit does not, afaik, have many developers working on the project. That could also be a plus, of course.

Another mock tool is PowerMock.

Seems to me there are too many mock frameworks and they do pretty much the same things. Time for consolidation so that an API and a body of practice can shake out?

Further reading

    Duplicate of this post on new blog: JMockit

  1. Mock object
  2. MockingToolkitComparisonMatrix
  3. Beyond EasyMock and JMock, try JMockIt !
  4. The Difference Between Mocks and Stubs
  5. The JMockit Testing Toolkit
  6. The Concept of Mocking
  7. PowerMock
  8. Unit Testing Using Mocks – Testing Techniques 5
  9. Making a mockery of CQ5 with JMockit


Off topic, some music …

Stefano Cantini – Blowin in the wind


Exception verification using fault injection via AOP

November 13, 2011

A simple example is used to show how to use Aspect Oriented Programming to provide fault injection.

Intro

A few months ago I was looking at Java code and thinking there must be a better way to see if this will really work, if there are any issues in how the program would react to exceptional conditions.  I could write some unit tests or force some problems to see if the exception handling is correct and the log file output is really useful.  But is there another way?

I got a solution after a while of thinking: Insert faults.  Now how to do that in an easy and  maintainable way? Not a new idea of course. After doing a web search I found some interesting references.

Funny, today at work I was extending an application and one of the support objects was failing. Turned out to be an index out of bounds problem. The method had adequate exception handling but, of course, an index out of bounds scenario was not tested. In this case the problem would not occur in production (hmmm), but it definitely occurred during development, and was not handled correctly.

Exception Handling

There are many references of correct exception handling strategies and best practices.  However, when creating applications, the developer must still make critical creative decisions about how to handle an exception.    That decision may not be a wise one.  And even if it is, may later prove to have been very unwise since the system requirements and implementation may have changed due to maintenance or requirement evolution.

Testing Approaches

Using various testing methodologies a subsystem can be exhaustively tested for exception handling.  Yet, most testing methods require that the subsystems be isolated in some manner.   This, though very valuable, can give fatal false confidence that the system will behave in a predictable manner in responding to exceptional conditions.

The prime example of this is within the Unit Testing methodologies.  Since Unit Tests are tests of isolated components, they do not test actual exception handling in the live system.  A perfect component can still contribute to system failure when it is used incorrectly or uses other components incorrectly.    For example, a unit test can show that class X’s methods correctly handle and when necessary throw the perfect exception.   That means nothing if components that interact with X don’t correctly use those exceptions:  they swallow, mask, or mistakenly catch them when there is nothing they can do.

Thus, one needs to use Integration Testing to test assemblages of components.  But, we are still back with the same shortcoming as with Unit Testing. Thus we would next need various forms of system testing.  And, that system testing must be a White Box test.  A Black Box test wherein an interface is exercised with various inputs (such as in web test systems or by QA personnel), will not necessarily invoke the full internal API/SPI between components that could be part of a complicated call chain.  Why?  Because, the interface, such as a browser client, will have (we hope) the validation, security, and logging layers implemented so that by the time a system test workflow enters the deep system components within the client or middleware, there is no way to influence the target component state into programmatic intentional failure mode.

If there is no way to exercise a run time trajectory, then why bother? The reason is that exceptional conditions are exceptional. Resources can be exhausted, systems not available, and so forth. The rule of Fail Fast may not be enough if collaborating components are not exceptionally responsive.

Fault Injection

To see if a system is responding to exceptional conditions, one must wait for those conditions or create them.    Analogously to Fuzz Testing we can dynamically and randomly insert faults.  In Fuzz Testing, inputs are manipulated to a system under test.  In Fault Injection we have to manipulate inputs inside the real system, within the components themselves.  And as in Fuzz Testing, we can employ various strategies to do so, such as randomized faults, etc.   Of course, this system is not the deployed production system, it is the real system in a test environment, an internal full deployment.

Some approaches are: source modification, source macros, annotations, dependency injection (IOC), and Aspect Oriented Programming (AOP). Of course, there are many other techniques as found in open/closed projects, commercial products, and the research community.

Fault Injection using AOP

Since using the real system is required, a viable approach is to use the Instrumentation already available in the Java system. We could dynamically insert code that forces exceptions. AOP as implemented by the AspectJ language can already do this.

The major advantage of using AOP is that the source is not changed in any way. AspectJ is the state of the art in the Java ecosystem for using AOP.

Other approaches

Monitoring

BTrace (a Java oriented approach comparable to DTrace) is very interesting. However, since, like DTrace, it is meant to be safe, defect injection may not be doable. But, see this post on an unsafe mode for BTrace.

Source modification

One way of injecting failure is just to edit the source code and add strategic code that will cause the instigating failure.  An advanced form of this is Mutation Testing.   Code changes could be:  arguments with wrong values, nulls, deliberately thrown exceptions, etc.  For example, one can simply create a method that throws a runtime exception:


     *** DON'T DO THIS ***

    public static void REMOVE_XXX_FROM_XXX_SOURCE(){
       if(true){
          throw new NullPointerException(
             "\n\t****** DELIBERATE RUNTIME EXCEPTION*****\n");
       }
    }
    

Then insert invocations to this method at strategic points in the code, deploy to a test environment, and see if the expected results are obtained, such as logging output that can identify the cause, or that no side effects are recorded, such as incorrect data storage.

This is a low tech and error prone approach.  Most importantly, this is dangerous.  In the mad development rush to meet deadlines and also eat lunch, this code could make it into production!  Even with that horrific method name, it will wind up in production!  One would have to create deployment filters that stop the process if any “fault” inducing code is included.  Another reason why this is not a good approach is that it doesn’t scale very well.   Forcing exceptions is just one type of verification.  Another is setting values outside of expected ranges or states.  Thus, one would need many different kinds of source code insertions. 

Of course, Unit and Behavior based testing tools sets can supply these required verifications if included into the system as Built-In Self-Tests (BIST).

Built-In Self-Test

In the hardware realm, BIST as found in, for example, IEEE 1149.1 JTAG, has been very successful. On the software side, there is ongoing research on how to implement BIST-like capability. This would make Brad Cox’s concept of the “Software IC” even more powerful.

Macros

A somewhat viable approach to source code fault insertion is instead of inserting the faulting code,  insert “include” macros in the original source code that indicates what fault insertion should be done at a location.  A fault injection preprocessor can scan and insert the fault framework’s invocations to accomplish the requirement.  The source code build process can then simply enable or disable the use of the preprocessor.  This would however still require source code modification and an extra maintenance nightmare.  When the code changes the macros may also require change.

Annotations

Instead of macros, we could also use Annotations.  Annotations could explicitly state the runtime exceptional behavior “service level agreements” at the component level. These SLA could then be systematically manipulated to test if they really hold at the system level.

Dependency Injection

One can also inject exceptional behavior by dependency injection, using programmatic, declarative, or annotations, faulted components could be inserted (or created via Aspect Oriented Programming) into the target system.

AOP Demonstration

In listing one below, an ATM class uses a service to do some banking.

/**
 * Driver class for example. 
 * For a real system see:  https://bitbucket.org/aragost/javahg/
 * @author jbetancourt
 */
public class ATM {
	private BankService service = new BankService(1000);

	/** 
	 * Perform a banking action.
	 */
	public void transaction(){	
		{
			service.deposit(100L);
			service.withdraw(200L);
		}
		
		service.statement();		
	}
	
	/** Application entry point	 */
	public static void main(String[] args) {
		new ATM().transaction();
	}
}

Listing two is the BankService being used. Of course, a real service would be more complex.

/**
 * Example service.
 */
public class BankService {
	// Use Long to allow example's null for failure
	// injection.

	private BigDecimal balance;
	
	public BankService(long savings){
		this.balance = new BigDecimal(savings);
	}
	
	/** */
	public void deposit(Long amount){
		balance = balance.add(new BigDecimal(amount));
		System.out.println("Deposit:  " + amount);
	}
	
	/** */
	public void withdraw(Long amount){
		balance = balance.subtract(new BigDecimal(amount));
		System.out.println("Withdraw: " + amount);
	}
	
	/** */
	public void statement() {
		System.out.println("Balance:  " + balance);		
	}	
	
} // end BankService

A example Non-faulted execution result is:

Report: pointcut usage
  withdrawCut:   true ->  false
  depositCut:  false ->  false
       Deposit: 100
      Withdraw: 200
       Savings: 900

When we run the demo program we get:

Exception at the withdraw method.

Report: pointcut usage
withdrawCut:   true ->   true
depositCut:  false ->  false
Deposit: 100
Exception in thread "main" java.lang.NullPointerException
   at BankService.withdraw_aroundBody2(BankService.java:15
   at BankService.withdraw_aroundBody3$advice(BankService.java:81)
   at BankService.withdraw(BankService.java:1)
   at Main.main(Main.java:16)

Exception at the deposit method.

Report: 
pointcut usage
withdrawCut:   true ->  false
depositCut:  false ->   true
Exception in thread "main" java.lang.NullPointerException
   at BankService.deposit_aroundBody0(BankService.java:9)
   at BankService.deposit_aroundBody1$advice(BankService.java:81)
   at BankService.deposit(BankService.java:1)
   at Main.main(Main.java:15)

All pointcuts enabled.

Report: 
pointcut usagewithdrawCut:   true ->   true
depositCut:  false ->   true
Exception in thread "main" java.lang.NullPointerException
   at BankService.deposit_aroundBody0(BankService.java:9)
   at BankService.deposit_aroundBody1$advice(BankService.java:81)
   at BankService.deposit(BankService.java:1)
   at Main.main(Main.java:15)

Implementation

Now to test the exceptional behavior we want to fault the deposit and withdraw methods.
First we have a way of specifying what we want to fault by using a JSON configuration file:

{
	"__comments":[
		"File: properties.json"
	],
	"settings":{
		"random":true
	},
	
	"cuts":{
		"depositCut":false,
		"withdrawCut":true
	}	
}

The “cuts” settings indicate which AspectJ “pointcut” to turn on. The “random” setting indicates if we want the exceptions to be randomly inserted into the code base at the enabled pointcuts.

The Abstract failure injection aspect. Subclasses (aspects) will supply the actual pointcuts to specify where to ‘do’ the injection.

/**
 * FailureInjectionAspect.aj 
 * @author jbetancourt
 * 
 */

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Abstract Aspect that determines if failure should occur.
 * @author jbetancourt
 * 
 */
public abstract aspect FailureInjectionAspect {
	private Map<String, Boolean> pointcutFlags = new HashMap<String, Boolean>();
	private volatile boolean initialized =false;
	
	/** 
	 * Initialize Failure injection aspect.
	 * @throws Exception 
	 * 
	 */
	protected void init() throws Exception {
		Config config = new Config();
		config.configure();
		pointcutFlags = config.getPointcutFlags();		
		initialized = true;
	}	

	/**
	 * Get boolean value for pointcut name.
	 * 
	 * @param name pointcut name.
	 * @return true if pointcut enabled.
	 * @throws IOException 
	 */
	protected boolean isPointcutEnabled(String name) {
		if(!initialized){
			try {
				init();
			} catch (Exception e) {
				throw new IllegalStateException(
						"Could not initialize object",e);
			}
		}
		
		boolean f = false;	
		Object val = pointcutFlags.get(name);
		if (null != val) {
			f = ((Boolean) val).booleanValue();
		}	

		return f;
	}
	
} // end FailureInjectionAspect.aj

Here is a aspect that uses a nulling type of injection.

/**
 * 
 * Example of an aspect for nulling an argument to a method.
 * @author jbetancourt
 *
 */
public aspect AmountFailureAspect extends FailureInjectionAspect {

	/** fault the deposit */
	private pointcut depositCut(Long amount) :
		execution(public void BankService.deposit(Long)) 
		&& if(AmountFailureAspect.aspectOf().isPointcutEnabled("depositCut"))
		&& args(amount) 
		&& !within(FailureInjectionAspect)
	;

	/** fault the withdrawal */
	private pointcut withdrawCut(Long amount) :
		execution(public void BankService.withdraw(Long)) 
		&& if(AmountFailureAspect.aspectOf().isPointcutEnabled("withdrawCut"))
		&& args(amount) 
		&& !within(FailureInjectionAspect)
	;

	/** Null the amount arg */
	void around(Long amount) : depositCut(amount)|| withdrawCut(amount) {
		amount = null;
		proceed(amount);
	}
}

Here is how to read the configuation JSON file. I use the JSON-Simple library.

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

/**
 * 
 * @author jbetancourt
 *
 */
public class Config {
	private static final Logger logger = 
          Logger.getLogger(Config.class.getName());
	private String configFile = "\\src\\properties.json";
	private Map<String, Boolean> pointcutFlags = new HashMap<String, Boolean>();
	private volatile boolean initialized = false;
	private String basePath;

	/**
	 * 
	 * @param configFile2 
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void configure() throws Exception {
		basePath = new File(".").getAbsolutePath();

		String path = basePath + configFile;

		Object obj = (JSONObject) JSONValue
				.parse(new FileReader(new File(path)));
		
		Map<String, Map<String, ?>> map = (Map<String, Map<String, ?>>) obj;
		pointcutFlags = (Map<String, Boolean>) map.get("cuts");
		Map<String, Object> settings = (Map<String, Object>) map
				.get("settings");
		
		Object r = settings.get("random");
		boolean randomize = (r != null && ((Boolean)r)) ;
		
		if(randomize){
			println(String.format(
			    "Pointcut usage\n%16s %6s    %6s","Name        ",
			    "Prior","Current"));
			
			for(Map.Entry<String, Boolean>entry : pointcutFlags.entrySet()){
				Boolean prior = entry.getValue();
				Boolean f = Math.random() > 0.65;
				entry.setValue(f);
				println(String.format(
					"%15s: %6s -> %6s", entry.getKey(),prior,f));
			}
			println("\n");		
		}		
		
		saveConfig();		
		initialized = true;		
		logger.log(Level.INFO,"Initialized: " + initialized);
	}
	
	/**
	 * Write the config settings to external JSON file.
	 * 
	 * Format is: { cutname : boolean , ... }
	 *   Example: {"withdrawCut":true,"depositCut":true}
	 * Will be used to set up behavior analysis service to 
	 * monitor response.
	 * 
	 * @throws IOException
	 */
	protected void saveConfig() throws IOException{
		String json = JSONValue.toJSONString(pointcutFlags);
		File file = new File(basePath + "\\runtimeProperties.json");
		FileWriter writer = new FileWriter(file);		
		writer.append(json);		
		writer.close();		
	}	

	/** getter */
	public Map<String, Boolean> getPointcutFlags() {
		return pointcutFlags;
	}

	/** setter */
	public void setPointcutFlags(Map<String, Boolean> cutFlags) {
		this.pointcutFlags = cutFlags;
	}

	/**
	 * Just a shortcut to System.out.println(String).
	 * @param s
	 */
	private void println(String s){
		System.out.println(s);
	}
}

Running at the command line

Using AspectJ is much easier in a supporting IDE like Eclipse. Below is the “mess”, unless you love the CLI, of compiling and running in a command shell. Could be made clean by creating aliases, scripts, etc.

cd src
src>javac -d ..\bin -cp ..\jars\json-simple-1.1.jar  *.java
src>java -cp ..\bin Main
Jun 9, 2011 3:18:24 PM Main main
INFO: Starting Main.....
   Deposit:  100
   Withdraw: 200
   Balance:  900

Now we change the flag from false to true in the runtimeInjection.json file:

src>type runtimeInjection.json | sed "s/false/true/" > temp
src>copy /Y temp runtimeInjection.json
src>del temp

Compile the aspects using the Aspectj “ajc” compiler.  Here we use 1.6 compliance level; destination of output to ..\bin folder; and give the classpath.

c:\Users\jbetancourt\Documents\projects\dev\AspectsForNullTesting\src&gt;\java\aspectj1.6\bin\ajc -1.6 -d ..\bin -cp "c:\java\aspectj1.6\lib\aspectjrt.
jar;c:\java\aspectj1.6\lib\aspectjtools.jar;c:\java\aspectj1.6\lib\aspectjweaver.jar;..\jars\json-simple-1.1.jar" -sourceroots .

Now we run the same Main program.  Since the pointcut flag is true, the advise is invoked and the list argument to the service(List) is set to null.

c:\Users\jbetancourt\Documents\projects\dev\AspectsForNullTesting&gt;java -cp "c:\java\aspectj1.6\lib\aspectjrt.jar;c:\java\aspectj1.6\lib\aspectjtools
.jar;c:\java\aspectj1.6\lib\aspectjweaver.jar;jars\json-simple-1.1.jar;bin;." Main
Jun 9, 2011 3:25:04 PM Main main
INFO: Starting Main.....
Pointcut usage
Name          Prior    Current
withdrawCut:   true -&gt;  false
depositCut:  false -&gt;  false
c:\Users\jbetancourt\Documents\projects\dev\AspectsForNullTesting&gt;java -cp "c:\java\aspectj1.6\lib\aspectjrt.jar;c:\java\aspectj1.6\lib\aspectjtools
.jar;c:\java\aspectj1.6\lib\aspectjweaver.jar;jars\json-simple-1.1.jar;bin;."   Main
Jun 9, 2011 3:25:10 PM Main main
INFO: Starting Main.....
Pointcut usage
Name          Prior    Current
withdrawCut:   true -&gt;   true
depositCut:  false -&gt;   true
Jun 9, 2011 3:25:10 PM Config exec
INFO: Initialized: true
Exception in thread "main" java.lang.NullPointerException
at BankService.deposit_aroundBody0(BankService.java:19)
at BankService.deposit_aroundBody1$advice(BankService.java:26)
at BankService.deposit(BankService.java:1)
at Main.main(Main.java:16)

c:\Users\jbetancourt\Documents\projects\dev\AspectsForNullTesting&gt;java -cp "c:\java\aspectj1.6\lib\aspectjrt.jar;c:\java\aspectj1.6\lib\aspectjtools
.jar;c:\java\aspectj1.6\lib\aspectjweaver.jar;jars\json-simple-1.1.jar;bin;."   Main
Jun 9, 2011 3:25:13 PM Main main
INFO: Starting Main.....
Pointcut usage
Name          Prior    Current
withdrawCut:   true -&gt;   true
depositCut:  false -&gt;  false
Jun 9, 2011 3:25:13 PM Config exec
INFO: Initialized: true
Deposit:  100
Exception in thread "main" java.lang.NullPointerException
at BankService.withdraw_aroundBody2(BankService.java:25)
at BankService.withdraw_aroundBody3$advice(BankService.java:26)
at BankService.withdraw(BankService.java:1)
at Main.main(Main.java:17)

Updates

  • Feb 1, 2012: Just learned about Byteman.

Further Reading



Nicolas Lens – Sumus Vicinae (Flamma Flamma)


Proxy A Groovy Ant BuildListener?

August 20, 2011

In my last post BuildListener using Groovy In Ant Scriptdef, I got a scripted BuildListener to work. Was wondering if there were a way to use a Proxy to stand in for the listener.

See update below for another attempt that succeeds.

In short, I can’t. The first problem was classloader issues, but I think I fixed that by clearing the CLASSPATH:
set CLASSPATH =

Then a host of issues, until finally I give up. I think the big complexity is that BuildListener is an Interface. That means you have to jump hoops to proxy it. I’m sure Groovy can do this, but I don’t see it yet. May need more info on Groovy’s MOP.

Perhaps there is too much indirection being used. Is modern software engineering just ingenious ways to hide a GOTO? Just kidding.

Using commons-proxy library
Apache’s commons-proxy puts a more usable framework around different proxy factories.

Listing 1 – Proxy using Javassist library

import org.apache.commons.proxy.factory.javassist.*
import java.lang.reflect.*
import org.apache.commons.proxy.*
import org.apache.tools.ant.*

class MyListener implements Invoker  {
    static int count = 0;

    Object invoke(Object proxy, Method method, Object[] arguments) 
      throws Throwable{
	new File("finished.run").setText("count: ${count++}")   
    }

}

MyListener proxy = new MyListener()		
java.lang.ClassLoader loader = proxy.class.getClassLoader()

JavassistProxyFactory factory = new JavassistProxyFactory()

Object listener = factory.createInvokerProxy(  
	loader,
	proxy,
	[BuildListener.class] as Class[]
);

project.addBuildListener(listener)		

// end source

Listing 2 – build script

<project name="listener" default="default" basedir=".">
<!--
File: build.xml, author: J.Betancourt,
    date:2011-08-18T2137
-->

<path id="libs">
    <fileset dir="lib">
	<include name="commons-proxy-1.0.jar" />
	<include name="javassist.jar"/>
	<include name="groovy-all-1.8.0.jar"/>
    </fileset>
</path>

<!-- Groovy library -->
<typedef resource="org/codehaus/groovy/antlib.xml" classpathref="libs" />    

<!-- sets a BuildListener to the project -->
<scriptdef name="set-listener" language="Groovy" 
   classpathref="libs" src="lib/BuildListener.groovy">

  <attribute name="endTarget"/>

</scriptdef>

<!-- install the listener -->
<set-listener  endTarget="list"/>    

<target name="default">
    <fail message="doh!"/>    
</target>

</project>
    
   

Listing 3 – stacktrace snippet

C:\temp\ant\groovyListener>ant
Buildfile: C:\temp\ant\groovyListener\build.xml

BUILD FAILED
java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
        at MyListener.invoke(Script1.groovy:10)
        at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java)
        at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254)
        at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290)


Update

This works, but just as wordy as the original using a BuildListener class implementation.

import org.apache.tools.ant.*

listenerMap = [
  theTarget : attributes.get("endtarget"),
  buildFinished: { event ->	
  new File("finished.run").
    setText(
     "Build Finished:  message='${event.message}' exception='${event.exception}'"
    )
	
    project.executeTarget(listenerMap.theTarget)
	
  },

  buildStarted : {},
  messageLogged : {},
  targetFinished : {},
  targetStarted : {},
  taskFinished : {},
  taskStarted : {},
]

listener = listenerMap as BuildListener

project.addBuildListener(listener )		

Uses the technique outlined at:
Groovy Way to implement interfaces

Conclusion

Updates

Further Reading

Listing 4 – Full stacktrace

C:\temp\ant\groovyListener>ant
Buildfile: C:\temp\ant\groovyListener\build.xml

BUILD FAILED
java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
        at MyListener.invoke(Script1.groovy:10)
        at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java)
        at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254)
        at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290)
        at org.apache.tools.ant.Project.log(Project.java:470)
        at org.apache.tools.ant.Project.log(Project.java:459)
        at org.apache.tools.ant.AntClassLoader.log(AntClassLoader.java:396)
        at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1310)
        at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1064)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
        at MyListener.invoke(Script1.groovy:10)
        at JavassistUtilsGenerated_0.taskFinished(JavassistUtilsGenerated_0.java)
        at org.apache.tools.ant.Project.fireTaskFinished(Project.java:2206)
        at org.apache.tools.ant.Task.perform(Task.java:364)
        at org.apache.tools.ant.Target.execute(Target.java:390)
        at org.apache.tools.ant.helper.ProjectHelper2.parse(ProjectHelper2.java:180)
        at org.apache.tools.ant.ProjectHelper.configureProject(ProjectHelper.java:82)
        at org.apache.tools.ant.Main.runBuild(Main.java:793)
        at org.apache.tools.ant.Main.startAnt(Main.java:217)
        at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
        at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)

Total time: 1 second
java.lang.ClassCircularityError: groovy/runtime/metaclass/java/io/FileMetaClass
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
        at MyListener.invoke(Script1.groovy:10)
        at JavassistUtilsGenerated_0.messageLogged(JavassistUtilsGenerated_0.java)
        at org.apache.tools.ant.Project.fireMessageLoggedEvent(Project.java:2254)
        at org.apache.tools.ant.Project.fireMessageLogged(Project.java:2290)
        at org.apache.tools.ant.Project.log(Project.java:470)
        at org.apache.tools.ant.Project.log(Project.java:459)
        at org.apache.tools.ant.AntClassLoader.log(AntClassLoader.java:396)
        at org.apache.tools.ant.AntClassLoader.findClass(AntClassLoader.java:1310)
        at org.apache.tools.ant.AntClassLoader.loadClass(AntClassLoader.java:1064)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:169)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:127)
        at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
        at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
        at org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl.getMetaClass(MetaClassRegistryImpl.java:242)
        at org.codehaus.groovy.runtime.InvokerHelper.getMetaClass(InvokerHelper.java:751)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.createCallConstructorSite(CallSiteArray.java:71)
        at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:54)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:182)
        at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
        at MyListener.invoke(Script1.groovy:10)
        at JavassistUtilsGenerated_0.taskFinished(JavassistUtilsGenerated_0.java)
        at org.apache.tools.ant.Project.fireTaskFinished(Project.java:2206)
        at org.apache.tools.ant.Task.perform(Task.java:364)
        at org.apache.tools.ant.Target.execute(Target.java:390)
        at org.apache.tools.ant.helper.ProjectHelper2.parse(ProjectHelper2.java:180)
        at org.apache.tools.ant.ProjectHelper.configureProject(ProjectHelper.java:82)
        at org.apache.tools.ant.Main.runBuild(Main.java:793)
        at org.apache.tools.ant.Main.startAnt(Main.java:217)
        at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
        at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)
groovy/runtime/metaclass/java/io/FileMetaClass

 

 

 


 

“Timeless” solo 12 string guitar played by Ralph Towner on his “Solo Concert” CD. YouTube


BuildListener using Groovy In Ant Scriptdef

August 18, 2011

If at the end of an Ant build you must record the build status and, for example, send an email, the Ant manual recommends you add a BuildListener to your project.

A listener is added by running Ant with a -listener argument on the command line. Can it instead be done within the build script itself? Yes, and for rapid prototyping I show how to use Groovy to do so.

Using a Scriptdef
Since Ant executes top level (outside of any target) tasks first, we can execute a custom task that installs a BuildListener implementation. First we must create a task that does this.

Instead of using ‘taskdef’, using a ‘Scriptdef’ defined task allows use of the Groovy language to access the Project object and do this. Of course, there are other languages one could use in Ant, such as JavaScript, BeanShell, JRuby, JPython, etc.

In listing 1 below we also allow the BuildListener to invoke a specified target when the buildFinished event is processed. The example buildFinished event handler method just creates a file with the event info. Then the specified terminal target is invoked and the content of the that file is listed.

Saving the build results to a file allows other processes in a build pipeline to reuse that information.

This just might be what I need to finish my current project.

Listing 1 – Example

<project name="listener" default="default" basedir=".">
<!--
File: build.xml, author: J.Betancourt,
    date:2011-08-18T2137
-->

<path id="libs">
    <fileset dir="lib">
        <include name="*.jar" />
    </fileset>
</path>

<!-- Groovy library -->
<typedef resource="org/codehaus/groovy/antlib.xml" classpathref="libs" />    

<!-- sets a BuildListener to the project -->
<scriptdef name="set-listener" language="Groovy" 
   classpathref="libs" src="lib/BuildListener.groovy">

  <attribute name="endTarget"/>

</scriptdef>

<!-- install the listener -->
<set-listener  endTarget="list"/>    

<target name="default">
    <fail message="doh!"/>    
</target>

<target name="list">
    <echo>Content of 'finish.run' file:</echo>    
    <loadfile srcFile="finished.run" property="finishContent" failonerror="false"/>
    <echo message="${finishContent}"/>

    <!-- <exec executable="cmd.exe" >
        <arg line="/c type finished.run"/>
    </exec>  -->

</target>    

</project>
    
   

Listing 2 – the BuildListener Groovy implementation

   import org.apache.tools.ant.BuildEvent

   class MyListener implements
     org.apache.tools.ant.BuildListener {

       def theTarget
       def project            
            
       def runTargets(){                    
         project.executeTarget(theTarget)                
       }
            
       public void buildFinished(
         org.apache.tools.ant.BuildEvent event){
             new File("finished.run").
                setText(
                 "Build Finished:  message='${event.message}'
                           exception='${event.exception}'")
         runTargets()                    
       }
            
       public void buildStarted(BuildEvent event){}
       public void messageLogged(BuildEvent event){}
       public void targetFinished(BuildEvent event){}
       public void targetStarted(BuildEvent event){}
       public void taskFinished(BuildEvent event){}
       public void taskStarted(BuildEvent event){}    
   } // end class
        
   def targetName = attributes.get("endtarget")            
   def listener = new MyListener(
         theTarget:targetName,project:project)          

   project.addBuildListener(listener)       

// end source

That subclass with the empty methods is quite ugly. I think it’s time to read up on the new @Delegate annotation new with Groovy 1.8*. [update] Looked at it. @delegate won’t help. Using a proxy framework that works with Interfaces leads to classloader hell. I tried commons-proxy.

Listing 2 – Example run

C:\temp\ant\groovyListener>ant
Buildfile: C:\temp\ant\groovyListener\build.xml

default:

BUILD FAILED
C:\temp\ant\groovyListener\build.xml:51: doh!

Total time: 1 second

list:
     [echo] Content of 'finish.run' file:
     [exec] Build Finished:  message='null'  
              exception='C:\temp\ant\groovyListener\build.xml:51: doh!'
  
  

Seems to work. Haven’t tested enough.

I got this idea from a presentation by Steve Loughran. In those slides he uses the “internal” approach but by using a taskdef and external Java coding. He also executes a list of subtasks, whereas in my example, I invoke a target. Btw, the book “Ant In Action” is very good.

Practical Use?
There are certain caveats regarding a BuildListener, such as not advisable to do i/o with the standared console and error streams.

Also, as used here, when the buildlistener is added to the build script, very likely, most of the configuration and properties are not known. In my project, I got around this by using reflection on the listener object attached to the project. You get those by:

def vect = project.getBuildListeners()

... iterate and find the matching class

def clz = listener.getClass()
def fld = clz.getField(fieldName)
fld.set(listener, value)

Where fieldName and value are what your particular listener impl require to do its buildFinished handling. I’m sure there are better approaches.
<doh> Could have just used project.getProperties() </doh>

Conclusion
Presented was a simple method of using an Ant BuildListener via ScriptDef using the Groovy language.

But, is an Ant build script really finished if it is busy running targets or tasks? Perhaps it is finishing …

Updates
2011-08-20:

  • Another option to run code when build is finished (even with error?) is to use a CustomExitCode class. See Ant manual.

Further Reading


Groovy AntBuilder in Ant Scriptdef to Replace Props

August 17, 2011

Intro

In a build process, you have a properties file, the former one, and another one, the latter file.   Now you want to replace all properties in the former that exists in the latter.   A kind of SED for property files.

This can be done easily in any programming language, but Property files in Java follow a spec and we rather not deal with the complexities.  Ant has the PropertyFile Task for this.

Apache Ant provides an optional task for editing property files. This is very useful when wanting to make unattended modifications to configuration files for application servers and applications.

Need to iterate
The problem is that the PropertyFile task requires the explicit listing of each key value to edit (the task can do more then just replace). AFAIK there is no way to use a resource collection. Simple example:

Listing 1 – example of PropertyFile use

   <propertyfile file="old.properties">
      <entry key="tag.name" value="release_1"/>
   </propertyfile>

We instead require the creation of the elements while we iterate thru the new file.

Propertiesreplace scriptdef
Groovy is a perfect fit for this. The Builder DSL allows a more ‘natural’ approach. I thought it would be a challenge to code this, but in a few minutes, it was done!

The trick is to create a new AntBuilder in the script. However, this may be a bad or non-idiomatic approach. Perhaps a Groovy expert can comment on it?

Listing 2 – the scriptdef

<!-- Replace properties in oldfile 
whose key match those in newfile -->

<scriptdef name="propertiesreplace" language="groovy"
    classpathref="libs" uri="http://com.jbetancourt1.ant">

    <attribute name="oldfile"/>
    <attribute name="newfile"/>

    <![CDATA[
     def ant = new AntBuilder(project)
     ant.propertyfile( file:attributes.get("oldfile") ){
          def nps = new Properties()
          nps.load(
               new File( attributes.get("newfile") )
                     .newReader()
          )

          nps.each{ cur ->
               entry( key:cur.key,value:cur.value )
          }
     }
    ]]>
</scriptdef>

I should have named it replaceproperties. Note that it would be better to put the Groovy script external, such as:

<!-- Replace properties in oldfile 
whose key match those in newfile -->

<scriptdef name="propertiesreplace" language="groovy"
    classpathref="libs" uri="http://com.jbetancourt1.ant" 
    src="src\ReplaceProperties.groovy">

    the rest of the declarations from listing 2 ...
</scriptdef> 
This is cool; Ant’s XML declarative language is used to create a programmatic macro using a script language, Groovy, that in turn is using a DSL to generate Ant code. A code Ouroborous.

Listing 3 – the old properties file

one=1111
#four=4444
two=2222
five=5555
three=3333

Listing 4 – the new properties file

one=abcd
two=efgh
three=ijkl
four=mnop

Listing 5 – run example Ant script

C:\temp\ant\propMerge>ant
Buildfile: C:\temp\ant\propMerge\build.xml

init:
     [copy] Copying 1 file to C:\temp\ant\propMerge

merge:
[propertyfile] Updating property file: C:\temp\ant\propMerge\oldProps.properties

list:
     [exec] one=abcd
     [exec] #four=4444
     [exec] two=efgh
     [exec] five=5555
     [exec] three=ijkl
     [exec]
     [exec] four=mnop

all:

BUILD SUCCESSFUL
Total time: 1 second

We actually save these files as templates, so that as shown in the init task of listing 6, we can recreate the test files. Then in target ‘list’, we print the modified old properties file.

Listing 6 – Ant script for dev of solution

<project name="merge" default="all" basedir=".">
  <!-- File: build.xml, 
       author: J.Betancourt, 
       Date:2011-08-17T20:59-0500 -->

  <path id="libs">
    <fileset dir="lib">
      <include name="*.jar" />
        </fileset>
      </path>

      <!-- Groovy library -->
      <typedef resource="org/codehaus/groovy/antlib.xml" 
         classpathref="libs" />

      <!-- Replace properties in oldfile whose 
         key match those in newfile -->

  <scriptdef name="propertiesreplace" 
      language="groovy" classpathref="libs" 
        uri="http://com.jbetancourt1.ant">

  <attribute name="oldfile"/>
  <attribute name="newfile"/>

    <![CDATA[
    def ant = new AntBuilder(project)
    ant.propertyfile( file:attributes.get("oldfile") ){
      def nps = new Properties()
      nps.load(new File( 
        attributes.get("newfile")).newReader())
        nps.each{ cur ->
         entry( key:cur.key,value:cur.value )
      }
     }
  ]]>
</scriptdef>
	
	<!-- use the "propertiesreplace" scriptdef -->
	<target name="merge" depends="init" 
                       xmlns:s="http://com.jbetancourt1.ant">

		<s:propertiesreplace oldfile="oldProps.properties" 
                       newfile="newProps.properties"/>		
	</target>

	<target name="all" depends="init,merge,list"/>		
	
	<target name="init">
		<copy file="oldProps.properties-template" 
                  tofile="oldProps.properties"/>
	</target>	
	
	<target name="list">
	  <exec executable="cmd.exe" >
	    <arg line="/c type oldProps.properties"/>
	  </exec>	
	</target>	
	
</project>

Why version control?
I code this up, then I decided to share it for anyone who may appreciate some examples (and for my own dev notes). I edit a file. It stops working. Can’t figure out why quickly enough. My editor’s (notepad++) undo is not helping.

Fixed it, but to avoid this scenario again I do:
git init
git commit -a -m “initial commit”

Careful
Did you see the future human operator error waiting to happen? In the example, the modified properties file has two entries:
#four=4444
four=mnop

Lets say this file is, for example, involved in code controlling a 800 ton crane, and an admin removes comment mark from the first “four”. If the file is processed with Ant, the first ‘four’ entry wins. Control code is deployed and now crane is waiting to drop its crate at bad moment. A Road Runner coyote scenario. Beep beep.

Updates
2011-0819: Got stumped regarding exception invoking cvs task in Ant and losing all existing properties at the BuildFinished handler. Did a search and found that someone already created a merge properties task:
. Merging property files…
. A version with fixes
2012-04-02: It may be possible that the Groovy scriptdef taskdef already includes an AntBuilder.

Further Reading


Ant InputHandler GUI Using Groovy

August 16, 2011

Intro

With Apache Ant you can get input from the user using the “input” task. When you run Ant in a console window, the DefaultInputHandler is used. This handler just uses the console’s error and input streams.

When you run Ant in a graphical IDE, a Graphical User Interface based input handler may be used. This handler will use the graphics toolkit (on Eclipse that would be SWT) to pop up dialog boxes.


GUI in console?

Can you use GUI input dialogs when running in a command shell? Yes, and it is very easy. (Of, this is true if a gui environment is available to Java runtime, etc.).

First you create an InputHandler subclass that will be used with the Ant command option “-inputhandler class name”. If you use Ant 1.8* you can use a nested “inputputhandler” task in the “input” task within the build script.

Probably a much better approach is to just create a new custom Ant task, “ginput” for this, or even use a macrodef or scriptdef that uses Groovy inline or from a script file.

Another alternative is to use a browser interface for the Ant input. See “Java’s HTTP Server for browser-based Groovy app” for an example of using an embedded server to invoke browser UI.

Groovy?
Why Groovy for this? Just to be able to use the SwingBuilder that potentially could make the Gui creation much simpler. One can envision a full blown form being used for cases where Ant is used outside its traditional use-case.

GUI InputHandler subclass

Below in listing 1, there is an example Groovy source: AntGuiInputHandler.groovy

Much harder is actually getting Ant to use this handler.

Build file

First lets create an Ant build file that will get some input:

<project name="AntGui" default="all" basedir=".">
<target name="all">

    <input message="What is your name?"
       addproperty="got.response"/>		
    <echo>${got.response}>/echo>		
</target>

</project>

Compiling
Now, compile the Groovy source as follows:

groovyc AntGuiInputHandler.groovy

Running
There are many ways to actually run the Ant build. The simplest but not the best way is to set up your CLASSPATH and then run Ant as usual, for example:

C:\temp\AntGui&gt;set classpath=.;\java\groovy\embeddable\groovy-all-1.8.1.jar

C:\temp\AntGui&gt;ant -inputhandler AntGuiInputHandler
Buildfile: C:\temp\AntGui\build.xml

all:
[input] prompt=What is your name?
[input] Josef
[echo] Josef from Groovy InputHandler

BUILD SUCCESSFUL
Total time: 11 seconds

The resulting input dialog is:

Ant GUI InputHandler prompt

Listing 1
Note: I currently have no Swing mojo, so this is just reuse of code snippets. BTW, SwingBuilder could use more docs or examples.

import java.awt.event.WindowEvent
import javax.swing.ActionPropertyChangeListener;

import groovy.swing.SwingBuilder;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.input.InputHandler;
import org.apache.tools.ant.input.InputRequest;
import org.apache.tools.ant.input.MultipleChoiceInputRequest;

import javax.swing.*

/**
 * @author jbetancourt
 *
 */
class AntGuiInputHandler implements InputHandler {

	/* (non-Javadoc)
	 * @see org.apache.tools.ant.input.InputHandler#handleInput(org.apache.tools.ant.input.InputRequest)
	 */
	@Override
	public void handleInput(InputRequest request) throws BuildException {
		def response = ''
		println "prompt=${request.getPrompt()}"
		
		if(request instanceof org.apache.tools.ant.input.MultipleChoiceInputRequest){
			response = 	getMultipleChoiceInput(request)
		}else{
			response = getTextInput(request)
		}
		
		println response
		request.setInput("${response} from Groovy InputHandler");
	}
	
	String getTextInput(InputRequest request){
		//def swingBuilder = new SwingBuilder()
		def response = ''
                // how to do this with the builder?
		response = JOptionPane.showInputDialog(
                   null, 'Project Name','Enter name', JOptionPane.OK_OPTION)

		return response	
		
	}
	
	
	String getMultipleChoiceInput(InputRequest request){
		def swingBuilder = new SwingBuilder()
		def req = (MultipleChoiceInputRequest)request;
		
		def choices = req.getChoices()
		def defaultValue = req.getDefaultValue()		
		def prompt = request.getPrompt()
		
	    def pane = swingBuilder.optionPane(
                  message:prompt, 
                  selectionValues:choices as Object[], 
                  optionType:JOptionPane.CLOSED_OPTION)
		def dialog = pane.createDialog(null, 'dialog')
		dialog.show()
		
		def response = pane.getInputValue()
		
		println "input: ${response}"		
		
		return response
	}
}

Pretty groovy use of Groovy!

Previously posted at: “Ant InputHandler GUI Using Groovy

Updates
2011-08-16T2159: Just stumbled upon the AntForm project. If I knew about it, I wouldn’t even have tried doing the above. Oh well.


Using replaceAll with Groovy regular expressions

August 13, 2011

String.replaceAll can take regular expression pattern arguments. Tried it with a Groovy script and had a compile error.

Ooops! Figured it out. In a slashy string, those using ‘/’ as terminators, the last character cannot be a ‘\’ since that will escape the terminator character, ‘/’. Thus,

def pattern = ~/\\/

Will not compile. But, this:

def pattern = ~/\${''}/

will. It is documented, I just kept missing it.

That is why it is used here:

println /c:\a\b\c\d/.replaceAll((/\\${''}/),"/")

Alternatively, if your using Groovy 1.8* you can use the new $//$ slashy string:

println ( /c:\a\b\c\d/.replaceAll(($/\\/$),"/")  )

or

println ( (/c:\a\b\c\d/ =~ $/\\/$).replaceAll("/") )

I was reading this blog post on the need to sometimes not use regular expression in Java: “Tip #5 Avoid RegEx When Unnecessary.”

To take the string “c:\a\b\c\d” and convert it to “c:/a/b/c/d”, just do s.replace(‘\\’, ‘/’);. Don’t use regexp. And, I thought, it would be easy to still use regular expressions if Groovy were used, doesn’t the slashy string remove the backslash headache?

Example that gets compile error:

println "new is: " +  /c:\a\b\c\d/.replaceAll((/\\/),"/")
C:\temp>groovy test.groovy
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
C:\temp\test.groovy: 2: expecting anything but ''\n''; got it anyway @ line 2, column 58.
   \b\c\d/.replaceAll((/\\/),"/")
                                 ^
1 error

I also tried using a compiled pattern, pat = ~/\\/, but that did not work either, neither did a few other things. For example, this doesn’t work either:

println  (/c:\a\b\c\d/ =~ /\\/).replaceAll("/")

The first set of parenthesis should have returned a Matcher object, and then the replaceAll called on it. Instead, we get:
C:\temp\test.groovy: 1: unexpected char: 0xFFFF @ line 1, column 47.
\c\d/ =~ /\\/).replaceAll(“/”)
^

So I looked into the Groovy unit tests to see how they test the replaceAll function. Unit tests are sometimes a great way to learn how to use an API. True, some unit tests can be very obscure and complex.

The unit test I found was used the ${”} GString.

So, example that Compiles:

println "new is: " +  /c:\a\b\c\d/.replaceAll((/\\${''}/),"/")
C:\temp>groovy test.groovy
new is: c:/a/b/c/d

The command line inline script could then be:

C:\temp>groovy -e "println \"new is: \" +  /c:\a\b\c\d/.replaceAll(/\\${''}/,\"/\")"
new is: c:/a/b/c/d

Ok, so it was not easier in Groovy. Maybe a Groovy expert can make it as easy?

Now, why is it so hard?

Specs
Groovy version: 1.8.1
JVM: 1.6.0_25
OS: Windows 7 64bit

Further Reading

 


Ralph Towner — Tale of Saverio


Java File Change Notification

April 26, 2011

Just read a blog post on this. Such synchronicity, I was thinking about this the other day.

Yes, this can be done in Java.

References
Watching a Directory for Changes
JNotify
develop a java file watcher
How to write Java code to listen runtime changes in text file


Follow

Get every new post delivered to your Inbox.