Monday, November 12, 2012

Using WSDLToJava with Gradle

I currently need to generate some java sources from a wsdl with Gradle. In Maven, I've done this using the cxf-codegen-plugin but I want to avoid using anything that's maven in my new build. And there's several resources out there who will either tell you to call WSDLToJava via cxf's ant task or directly. The 'direct' way still leaves you with several possibilities like calling ProcessBuilder from your Gradle Task or using a JavaExec Task. I went for the last.

So I started piecing everything together and suddenly the build ran trough my generateJavaFromWsdlDeps and failed at compiling my sources that use the generated ones. Great, I thought but wait! I haven't even supplied a wsdl location to the JavaExec task yet. Scroll up a few lines and there's a nice error message from WSDLToJava where it complains about that exact point.

Hm, so Gradle surely ignores the exit value of a JavaExec task? Oh no it doesn't! JavaExec and subsequently the whole build always failed until I've fixed the classpath for this task (frontend and databinding were missing in the beginning). So after a little println of "configurations.generateJavaFromwsdlDepsDeps.asPath" and running the "java" command myself and then looking at the exit value of the java process via "echo $?", which left me with a "0" - it was clear that this is a WSDLToJava bug (Or I'd consider it to be a bug).

But I won't wait for the next cxf version with a fix in it, I want a solution now and this is what I've come up with: JavaExec allows to change the standard In/Out/Err and so I set the StdErr to be a TeeOutputStream that goes to System.err and to a ByteArrayOutputStream of my own. Then parse my BAOS for errors and fail the build if there are any.

So here's the solution I went with, I think it's pretty nice. The "parsing" of stderr could probably be better (for example fail even if a single byte is written to stderr? since I don't know WSDLToJava's internals I don't know how it behaves exactly.

The whole thing does require some extra boilerplate since commons-io which I use here, must be on the build script's own classpath or else this won't work.

Loading ....

You can do this on the 'root' level of your script. I use it for a subproject so imagine the rest of the code below is wrapped in a "project(':some:sub:project')".

We first need to set up a configuration, this means we just access it inside a configurations closure and then set the dependencies like this:

Loading ....

Now we need to specify our generate task. The configuration part just configures the task itself. And in the "doLast" part we do the actual verification of the standart error stream (If you don't understand the difference, have a look at Anatomy of a Gradle Task: Execution Time). One thing I'd like to point out is, that the TaskExecutionException seems to be the only Exception that properly stops the build and it requires a reference to the failing task and a cause. To get the failing task, which is basically 'this' or 'self' or however you'd want to call it, we can't use this but need to locate the task via its name in the project as outlined in the chapter "Locating Tasks" of the gradle docs (or you could hardcode it as ':some:sub:project:generateJavaFromWsdl but that will backfire once you change the name of your task).

Loading ....

And that's it! The build fails with a nice error message. Now it's time to move on and actually make the WSDLToJava task generate some sources.

No comments:

Post a Comment