您的位置:首页 > 产品设计 > UI/UE

Automation for the people: Build Java projects with Raven

2009-09-17 06:09 525 查看
Say "nevermore" to inexpressiveness and use Ruby to build your Java applications

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



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.

Back to top

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



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.

Back to top

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!

Back to top

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
javac

Compiles Java source files and places the resulting class files in a
target/classes directory by default.
jar

Generates a JAR file, which is often a collection of .class files.
war

Generates a Web application archive that is capable of deployment to a Java Web
container.
javadoc

Generates Javadocs from source code.
junit

Executes JUnit tests
jar_source

Creates a JAR file from Java source files.
gem_wrap_inst

Transforms 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!

Back to top

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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐