Automation for the people: Build Java projects with Raven

Say "nevermore" to inexpressiveness and use Ruby to build your Java applications

Ant is arguably the de facto build tool for the Java™ platform; however, other build tools, which support a more expressive paradigm that XML lacks, are entering the scene. In this installment of Automation for the people, automation expert Paul Duvall describes how Raven, a build platform built on top of Ruby, leverages the power of a full-featured programming language with the simplicity of a build-centric Domain Specific Language.

Share:

Paul Duvall (paul.duvall@stelligent.com), CTO, Stelligent Incorporated

Paul DuvallPaul Duvall is the CTO of Stelligent Incorporated, a consulting firm and thought leader in helping development teams optimize Agile software production. He is the co-author of the Addison-Wesley Signature Series book, Continuous Integration: Improving Software Quality and Reducing Risk (Addison-Wesley, 2007). He also contributed to the UML 2 Toolkit (Wiley, 2003) and the No Fluff Just Stuff Anthology (Pragmatic Programmers, 2007).



06 November 2007

Also available in Russian

I have an existing Java project, and over the past few years, I've meticulously maintained an Ant build script for it. I like the plethora of tasks available for Ant; however, I often find that the XML syntax of Ant scripts is somewhat onerous to write. Further, Ant's XML heritage is occasionally limiting when it comes to expressiveness. In fact, when I find myself needing a higher degree of flexibility (say with conditional logic), I'm often forced to write a custom task or embed logic within Ant's script task using Groovy, for instance.

Inexpressiveness nevermore

About this series

As developers, we work to automate processes for end-users; yet, many of us overlook opportunities to automate our own development processes. To that end, Automation for the people is a series of articles dedicated to exploring the practical uses of automating software development processes and teaching you when and how to apply automation successfully.

A few years ago, in my pursuit to find more expressive tools for building software, I learned of Rake. Rake is a Domain Specific Language (or DSL) for building software using the full-featured Ruby programming language. With Rake, I have the expressiveness I'm looking for without having to jump through hoops. I don't need to write custom tasks, for instance, because a Rake build script is Ruby (as opposed to XML); therefore, if I need some conditional logic, I can easily code it where I need it. What's more, Rake employs a dependency-based tasking system similar to what you find in Ant and its venerable progenitor, make. For instance, if I type rake deploy, Rake would call the dependent tasks I've set up, such as compilation, running tests, and so on. And Rake is smart enough to run dependent tasks only once (just like Ant, by the way).

By itself, Rake isn't all that helpful for most Java projects; however, a relatively new project, dubbed Raven, has married the power and expressiveness of Rake with tasks specific for building Java applications. With Raven, Java developers have a truly viable build platform that provides Java-specific features, such as compilation with javac and archiving binaries with jar and war, with the rich expressiveness of the Ruby programming language.

What is a DSL?

A Domain Specific Language (or DSL) is a programming language designed for a specific task. The Java language and Ruby are general-purpose programming languages, while Ant and Rake are DSLs dedicated to building software. Rake is considered an internal DSL because it's written in Ruby and can be extended using Ruby. Ant, on the other hand, is an external DSL because it is parsed and then executed in another language (that is, the XML is parsed and then acted upon via Java programming).

Raven is Rake and Rake is Ruby

Before jumping into using Raven for Java projects, it helps to understand the relationship between Raven and Rake. Figure 1 shows a logical diagram of Raven, and as you can see, Raven uses Rake, RubyGems, and Ruby. Accordingly, native Rake tasks are available to you when using Raven; plus, you can also use Ruby within Raven scripts. RubyGems is a package manager similar to CPAN, RPM, or APT that can be used to download necessary Ruby packages (and Java jars) and any transitive dependencies as well.

Figure 1. Logical diagram of Raven
Logical architecture for Raven

While the jury is still out as to whether Raven is the best platform for building Java projects, I think you'll find that Raven provides a simple, yet powerful combination in moving toward a more natural programming language for building software. If you ask me, not having to rely on XML to build Java projects is reason enough to take a look at Raven.


Raven's tapping at your chamber door

There are two approaches to getting Raven installed and configured, the first being the Ruby way and the second being more Java in style, which makes use of JRuby. I prefer the Java way because it's much simpler to configure. Getting Raven up and running entails a few steps:

  • Download the Raven/JRuby distribution (see Resources).
  • Unzip the file to a location on your workstation.
  • Create an environment variable called JRUBY_HOME.
  • Add JRUBY_HOME/bin to your PATH environment variable.
  • Verify your installation by opening a command prompt and entering jruby -version. If everything was installed correctly, you should see something like ruby 1.8.5 (0) [java] in the command window.

Hello, Raven

Of course, downloading and installing Raven isn't all that interesting. The real meat of this beast is its flexibility when creating build scripts. In Raven speak, build files are called Rakefiles and they have a simple format; in fact, if you've ever programmed in Ruby (or seen some Ruby code), they'll look quite familiar. That's the beauty, by the way, of Raven: it is Ruby — with some special words inserted here and there.

To get started using Raven quickly, I'll do arguably the most basic (and important) task first, compilation. The directory structure in Figure 2 represents my Java project's layout. Knowing where my third-party libraries and source code live will come in handy shortly.

Figure 2. Directory Structure of existing Java project
Directory Structure of existing Java project

Note the lib directory in Figure 2. This is where third-party JARs live. Before I can compile, I must create a path, which couldn't be easier with Raven because it supports the ability to create a classpath via its dependency task (which is actually just a Ruby method, provided by Raven, called dependency), which you can see in Listing 1:

Listing 1. Creating a classpath with Raven
require 'raven'
require 'rake'

dependency 'deps' do | task |
  task.libs = Dir.glob('lib/**/*.*')  
end

The deps dependency task, as defined in Listing 1, includes all of the libraries in my lib directory using the Dir class and glob() method. I can also use the dependency task to download JARs (using RubyGems) from a repository (which is similar to Maven's dependency management mechanism).

In Listing 1, I've specifically included anything found in the lib directory as part of my path. I've named this task deps and, as you'll see next in Listing 2, I can then make this task execute explicitly before I attempt to compile anything:

Listing 2. Compilation is a breeze
javac 'compile' => 'deps' do |task|
  task.build_path << "src"
end

As you can see, I'm performing compilation via Raven's javac task. The task is called compile and it has a dependency to the deps task from Listing 1. The symbol => means that Raven will run any task (or tasks) to the right of this operator prior to the current task (much like the depends attribute of the target element in Ant).

In between the do and the end of my javac task is where all the magic happens. By default, the javac task uses "src/main/java" as the default value of build_path. But, because my Java project does not follow this convention (remember Figure 2?), I need to modify the build_path property, which in my case is src because that's where my source code resides.

Is Raven just Ruby for Maven?

You might think that Raven is just a Ruby version of Maven, but that's not quite accurate. Although there are similarities in default naming conventions and dependency management, Raven's creator explains that it was just easier to do so.

To test this Raven script, I need to open a command prompt and type: rake compile. The .class files that are generated as a result of compilation will be placed into a directory called target/classes, which is automatically generated by the javac Raven task.

Less is more

One thing should be clear at this point. I didn't have to write a lot of code to get things compiled because Raven handled a few things behind the scenes, such as creating a target/classes directory and copying the resulting class files into this location. In fact, you may have already forgotten what the equivalent Ant script for Listing 1 and Listing 2 looks like. As a friendly reminder, Listing 3 shows a typical compilation using an Ant build script:

Listing 3. Compile Java source code with Ant
<?xml version="1.0" encoding="iso-8859-1"?>
<project name="compile-code" default="all" basedir=".">
...
  <target name="compile-src">
    <mkdir dir="${classes.dir}"/>
    <javac destdir="${classes.dir}" debug="true">
      <src path="${src.dir}" />
      <classpath refid="project.class.path"/>
    </javac>
  </target>
<project>

Comparing Listing 3 with Listing 1 and Listing 2 offers some striking differences, don't you think? Not only does Raven do away with XML, but it assumes some basic attributes, thus allowing you to specify less when it comes to defining tasks. What's more, as Listing 2 shows, Raven allows you to override aspects that Raven assumes if you need to.

While Listings 1 and 2 are certainly interesting from a comparison standpoint, compilation is just the beginning of the fun that is build scripting. Let me show you a more interesting feature: packaging a Java Web application.


Simple war file generation

A Web Archive (WAR) file is used to package Web applications and contains various assets such as servlets, third-party libraries, and images, for instance. These archives use a standard directory naming scheme and expect certain files, such as web.xml (which maps servlet names and other configuration aspects), to reside in the WEB-INF directory.

Raven makes the creation of a WAR file quite simple. In Listing 4, I'm using the war task with the name 'brewery.war', which, by default, creates a WAR file with the same name. The default location of webapp_dir is src/main/webapp, but because my directory structure is different than what Raven assumes, I need to modify this with the line task.webapp_dir = 'src/web'.

Listing 4. Creating a WAR to deploy to a Web container
war 'brewery.war' => ['clean', 'compile', 'java-doc'] do |task|
  task.webapp_dir = 'src/web'
end

Note how I've declared that before a WAR file can be created, Raven must first run the clean task, followed by the compile task, followed by a task to create some documentation. After those tasks successfully run, Raven can then create an archive, which as you can see requires very little typing. In fact, if my project followed the default directory layout that Raven assumes, I wouldn't have needed to write anything beyond those dependent tasks (that is, the code between the do and end wouldn't be required!).

Believe it or not, that's all there is to it — Raven's war task handles directory creation, file placement, and war file generation. Very efficient, don't you think?

Is it Raven, Rake, or Ruby?

All of the above! Rake is a DSL for building projects using Ruby. Raven uses Rake to provide Java-specific functionality for building Java projects. With Raven, just as with Rake, you've got access to the full Ruby programming language within your build scripts.

Generating documentation the easy way

Generating project documentation is often helpful in disseminating helpful information to team members. If you find yourself writing framework code or utilities that others plan to use, you'll eventually need to publish Javadocs, which are HTML files describing the classes, methods, and public instance variables found in related source code.

Generating documentation using the Javadoc mechanism is a snap using Raven, as you can see in Listing 5:

Listing 5. Generating JavaDoc documentation using Raven
javadoc 'java-doc' => 'deps' do |task|
  task.build_path << "src"
end

Note that once again because my project doesn't follow Raven's assumed model, I have to modify the build_path property of the javadoc task. In addition, the task depends upon the deps task (from Listing 1) to include any class references.

At this point, you should probably realize that if your project follows Raven's desired layout, your build scripts will be even simpler than mine!


Gimme more!

Although I've covered just a few of the features available in Raven, there's so much more you can do with this innovative build platform. In particular, there's extensive support in Raven for handling build dependencies (that is, third-party libraries) using RubyGems, a mechanism for auto-downloading of versioned binaries at build time rather than referring to shared drives. RubyGems handles third-party libraries in a similar manner to Maven or Ivy.

Besides RubyGems, Raven also can run JUnit tests, build normal JAR files, and clean directories. Table 1 lists a few of the more popular tasks available in Raven that facilitate building Java projects:

Table 1. Popular (and useful) Raven tasks
Raven taskDescription
javacCompiles Java source files and places the resulting class files in a target/classes directory by default.
jarGenerates a JAR file, which is often a collection of .class files.
warGenerates a Web application archive that is capable of deployment to a Java Web container.
javadocGenerates Javadocs from source code.
junitExecutes JUnit tests
jar_sourceCreates a JAR file from Java source files.
gem_wrap_instTransforms a JAR file into a RubyGem and then installs the file into a repository.

As Raven matures, the list in Table 1 will likely grow. Besides, if there's something missing in Raven that you need, there's nothing to stop you from writing it yourself!


In conclusion

I hope I've demonstrated that the beauty of Raven is that it enables you to utilize the power and flexibility of the Ruby language within a build script. Whether or not Raven becomes popular for building Java projects is not for me to decide, but I believe Raven moves the industry in the right direction. In particular, Raven enables dependency-based tasking with a full-featured imperative programming language (rather than a declarative one like XML). Give it a try and see what I mean.

Resources

Learn

  • Raven: Scripting Java Builds with Ruby (Matthieu Riou, Apress, 2007): This is the definitive guide for Raven, written by Raven's creator.
  • "Using the Rake Build Language" (Martin Fowler, martinfowler.com, August 2005): Using Ruby to build software couldn't be more fun.
  • "Ruby off the Rails" (Andrew Glover, developerWorks, October 2005): Get to know Ruby before you hop on (or off) the Rails bandwagon.
  • "Introduction to Apache Maven 2" (Sing Li, developerWorks, December 2006): This tutorial gets you started with Maven 2.
  • "Practically Groovy: Ant scripting with Groovy" (Andrew Glover, developerWorks, December 2004): Andrew Glover introduces Groovy's builder utility, which makes it especially easy to combine Groovy with Ant and Maven for more expressive and controllable builds.
  • "What's after Ant?" (Andrew Glover, thediscoblog.com, April 2007): Andrew Glover discusses what's on the horizon in the future of build languages.
  • Automation for the people (Paul Duvall, developerWorks): Read the complete series.
  • developerWorks: Hundreds of articles about every aspect of Java programming.

Get products and technologies

  • Raven: Download Raven and JRuby.
  • Ruby: Download the Ruby one-click installer.
  • RubyGems: Download RubyGems package management system.
  • Example Code: Raven script from the article.

Discuss

  • Improve Your Code Quality discussion forum: Regular developerWorks contributor Andrew Glover brings his considerable expertise as a consultant focused on improving code quality to this moderated discussion forum.
  • Accelerate development space: Regular developerWorks contributor Andrew Glover hosts a one stop portal for all things related to developer testing, Continuous Integration, code metrics, and refactoring.

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=266795
ArticleTitle=Automation for the people: Build Java projects with Raven
publish-date=11062007