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
task using Groovy, for instance.
Inexpressiveness nevermore
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 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
and archiving binaries with
and
, with the rich expressiveness of the Ruby programming language.
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
.
Add
to your
environment variable.
Verify your installation by opening a command prompt and entering
. If everything was installed correctly, you should see something like
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
task (which
is actually just a Ruby method, provided by Raven, called
), which you can see in Listing 1:
Listing 1. Creating a classpath with Raven
The
dependency task, as defined in Listing 1, includes all of
the libraries in my lib directory using the
class and
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
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
As you can see, I'm performing compilation via Raven's
task. The task is called
and it has a dependency to the
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
attribute of the
element in Ant).
In between the
and the
of my
task is where all the magic happens. By default, the
task uses
as the default value of
. But, because my Java project does not follow this convention (remember Figure 2
?), I need to modify the
property, which in my case is
because that's where my source code resides.
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:
.
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
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
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
task with the name
,
which, by default, creates a WAR file with the same name. The default location of
is src/main/webapp, but because my directory structure is different
than what Raven assumes, I need to modify this with the line
.
Listing 4. Creating a WAR to deploy to a Web container
Note how I've declared that before a WAR file can be created, Raven must first run the
task, followed by the
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
and
wouldn't be required!).
Believe it or not, that's all there is to it — Raven's
task handles directory creation, file placement, and war file
generation. Very efficient, don't you think?
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
Note that once again because my project doesn't follow Raven's assumed model, I have to
modify the
property of the
task. In addition, the task depends upon the
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
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.
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, manyof 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 (orDSL) 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 |
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 |
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?
Youmight 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> |
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 |
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. Ravenuses 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 |
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 task | Description |
---|---|
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. |
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.
相关文章推荐
- The project was not built since its build path is incomplete. Cannot find the class file for java.lang.Object
- Cannot find the class file for java.lang.Object. Fix the build path then try building this project
- Automation for the people: Improving code with Eclipse plugins
- The project was not built since its build path is incomplete. Cannot find the class file for java.lang.Object. Fix the build pat
- The project was not built since its build path is incomplete. Cannot find the class file for java.lang.Object. Fix the build pat
- Automation for the people: Remove the smell from your build scripts
- 存档: Automation for the people: Improving code with Eclipse plugins
- Automation for the people: Manage dependencies with Ivy
- The project was not built since its build path is incomplete. Cannot find the class file for java.la
- JAVA Error:The project was not built since its build path is incomplete. Cannot find the class file for java.util.Map$Entry.....
- Automation for the people: Speed deployment with automation
- JAVA Error:The project was not built since its build path is incomplete. Cannot find the class file for java.util.Map$Entry.....
- The project was not built since its build path is incomplete. Cannot find the class file for java.la
- Cannot find the class file for java.lang.Object. Fix the build path then try building this project
- The project was not built since its build path is incomplete. Cannot find the class file for java.la
- The project was not built since its build path is incomplete. Cannot find the class file for java.la
- Java中The project cannot be built until build path errors are resolved错误解决方法
- The SDK Build Tools revision (23.0.3) is too low for project ':app'. Minimum required is 25.0.0
- The resource is not on the build path of a java project
- java包名前面有个红色的大叹号(A cycle was detected in the build path of project )