As part of these plugins, the rake-maven-plugin
allows you to call Rake tasks in a project, which can be useful if the build tool of your comapny is based on Java, but your project is Ruby-based (and you don’t want to use JRuby). To illustrate its use, we’ll use a very straightforward Rakefile:
task :do_that_thing do
puts " *** RAKE RUNNING ***"
end
It has a unique task, do_that_thing
, that we’ll call from Maven. In the pom, you need to add the rake-maven-plugin
:
<plugin>
<groupId>de.saumya.mojo</groupId>
<artifactId>rake-maven-plugin</artifactId>
<version>1.0.0-rc3</version>
We are now going to define its execution, and tie it to the verify
lifecycle step (you can choose what step you want):
<executions>
<execution>
<id>run-spec</id>
<phase>verify</phase>
<goals>
<goal>rake</goal>
</goals>
<configuration>
<args>do_that_thing</args>
</configuration>
</execution>
</executions>
</plugin>
The goal we are calling on the Rake plugin is rake
, and the name of the Rake task is passed in args
. As we are using Rake, we need to define a gem
dependency to rake
. To do so, you first need to add the rubygems
Maven repo provided by TorqueBox:
<repositories>
<repository>
<id>rubygems-releases</id>
<url>http://rubygems-proxy.torquebox.org/releases</url>
</repository>
</repositories>
You can then add the dependency to the rake
gem:
<dependencies>
<dependency>
<groupId>rubygems</groupId>
<artifactId>rake</artifactId>
<version>10.1.0</version>
<type>gem</type>
</dependency>
</dependencies>
Running mvn verify
will then execute the Rake task.
The full example can be found on my GitHub repo, rake-maven-example.
To get more details about the Rake maven plugin (or any plugin at all in general), the help
plugin comes, as usual, quite handy:
mvn help:describe -Dplugin=de.saumya.mojo:rake-maven-plugin:1.0.0-rc3 -Ddetail
]]>
The “heart” of the app is the Spring MVC controller which must be annotated with the Controller
annotation, and its request path defined with the RequestMapping
annotation:
We also put the class in the com.weblogism.myapp
package, we’ll see why in a second.
The Spring MVC configuration is pretty much “standard”:
It defines the component-scan
tag that will look for all the classes annotated with Controller
in the com.weblogism.myapp
; you now see why we used java_package
for our controller.
The compiling and packaging is done by Maven. Again, nothing really extraordinary in the pom.xml
. The only unconventional feature is the use of the jruby-maven-plugin
to compile our JRuby class into a Java class:
It generates its output into target/generate-sources/jruby
, and compiles out class into target/classes
, like any other Java class. The output of the build is a war file that can be deployed in a Java EE container.
To see our amazing app in action, run Jetty:
mvn jetty:run
Once Jetty is up, you can access the app at http://localhost:8080/welcome.html
]]>.jrubyrc
, which can prove very useful when working on JRuby devs:
]]>
JMenuBar
support to Rubeus. It is not released yet, but you can clone the git repo and play with it. The implementation is rather naive, but it works well for my needs.
]]>
If only I had received it before my DnB JRuby presentation…
]]>An excellent starting point is the set of plugins called JRuby Maven plugins (not to be confused with jruby-maven-plugin to write JRuby mojos, rather than using Jelly). The information is a bit sparse and outdated, but after while tinkering with them, I found them pretty easy to use. To use them, you must first have a JRuby on Rails project:
sebastien@greystones:/tmp$ rvm list
rvm rubies
=> ruby-1.9.2-head [ x86_64 ] jruby-1.5.6 [ amd64-java ]
sebastien@greystones:/tmp$ rvm jruby sebastien@greystones:/tmp$ ruby -v jruby 1.5.6 (ruby 1.8.7 patchlevel 249) (2010-12-03 9cf97c3) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_20) [amd64-java] sebastien@greystones:/tmp$ rails new myapp create create README create Rakefile create config.ru create .gitignore create Gemfile create app create app/controllers/application_controller.rb . . .
Once the rails project is created, you can now use the jruby-maven-archetype
:
sebastien@greystones:/tmp$ mvn archetype:generate -DrailsVersion=3.0.3
(Documentation says it is -Drails.version=3.0.3
, but it is incorrect). The jruby-maven-archetype
currently is number 21, so you can enter its number at the prompt, as well as its current version number (0.23.0), and then the groupId
, the artifactId
of your app (here myapp
, like the rails app), the version, and the package name.
Once the application is set up, you should be able to run it with the following command:
sebastien@greystones:/tmp$ cd myapp
sebastien@greystones:/tmp$ mvn rails3:server
You should then be able to access your app at http://localhost:3000/.
bundle is a great addition to rails 3; unfortunately, it is not available from maven commands provided by JRuby maven plugins yet. That’s why I wrote the following addition to these plugins, bundle-maven-plugin. It is still ongoing work, so it is still a bit rough, but it should do the trick for bundle
install
, update
, package
, exec
and config
commands.
This is the next step in the process… and I haven’t had the chance to get to it yet. However, it looks like warbler is the tool for this task, so I’m guessing this will be another post.
]]>One extra thing to mention (as this has caused me some issues) is that when using rvm, you should avoid installing jruby as the first ruby, as it looks like it tries to use it to compile the following ones — causing troubles to install 1.9.2-head
. If you have installed jruby as the first ruby, you can delete it with rvm remove
, then install MRI, or ree, and only then install jruby.
(It is very likely that this is caused by the fact my “system” ruby was actually a ruby I had compiled myself…)
]]>As a Java developer, you need to know more than Java to be able to work efficiently:
Why the scripting language? Because there are lots of stuff you need to do on a daily basis, and Java is far too big a tool to do them (understand, far too slow to code, far too slow to compile, far too slow to deploy, etc.):
Ant (or even more pathological, Maven) uses XML to alleviate some of these issues, but XML is just not “appropriate” for these tasks: the build scripts size explodes and are very hard to maintain, unless you resort to some macrodef
trickery that are just symptomatic of the fact that you actually really need a proper scripting language…
The two recent examples I have is actually producing reports in a quick fashion. The first example is actually something I wrote, as I was asked to create a list of users of an application by querying the LDAP directory. On the very first day of work in my very first company, I worked with LDAP, and have been ever since, so doing an ldapsearch
for this kind of query was easy stuff; formatting the report is always a bit more tricky, because you have to sed the hell out of the output, and let’s face it, I don’t speak sed very well. The Java/JNDI option is far far too heavy for a simple report (and it does make you look a bit stupid if it takes you like half a day to produce a report listing all your users…), so the scripting option is ideal.
Having never used the ruby-ldap extension, I was a bit worried that its installation would be hard, especially behind an overzealous proxy, but it went very smoothly on Cygwin — here are the command in the unpacked archive:
$ ruby extconf
$ make
$ make install
Once installed, the script itself was a pleasure:
require 'ldap'
ldap_host = 'ldap_server' ldap_port = 389 ldap_base_dn = 'dc=myformercompany,dc=ie' ldap_filter='(objectclass=appusers)' ldap_attrs=nil
def print_entry(entry) puts "#{entry.vals('uid')},#{entry.vals('givenName')}, #{entry.vals('sn')}, #{entry.vals('mail')}" end
puts "uid, first name, surname, email" ldap_conn = LDAP::Conn.new(ldap_host, ldap_port) ldap_conn.bind('cn=Directory Manager', 'xxxxx') { ldap_conn.search(ldap_base_dn, LDAP::LDAP_SCOPE_SUBTREE, ldap_filter, ldap_attrs){ |entry| print_entry(entry) } }
All put together in 5 min. The cool thing about this was that the people who were requesting this report asked for different versions with different attributes, and the turnaround was almost instantaneous!
The second example is a script I came across whilst browsing snipplr, which solves problems I have seen solved with XML/XSL in the past: extracting the substantifique moëlle of an XML report to produce a summary on a CI report. Using Nokogiri, it just becomes trivial to parse an XML document (here a JUnit XML log) to produce a report. Think of all the applications! Creating code coverage reports, creating dashboards, providing querying features to end users via DSLs (piece of cake with ruby)…
Ruby (and more specifically JRuby) has really become for me one of the most useful tools, and it has that extra-special feature that it makes me feel like McGyver whenever I come up with a solution to a problem within minutes in a very minimal amount of code. And Holy Richard Dean Anderson knows that if the following were to happen, life would be even happier:
And there you go, I just cannot resist:
]]>Here is the situation: when using FitNesse, developers sometimes have to develop what is called fixtures, that is, Java classes that can be used (usually by a business analyst) to write tests in the FitNesse wiki. Fixtures then perform the actual testing and return the result so that it can be displayed on the wiki. They can be very straightforward, or they can interact with web pages (via Selenium, for instance) or even call web services. As they are to be used by non-developers, they have to be properly documented. One way to do this is simply by publishing the JavaDoc – but that would kind of ruin my JRuby example1!
So let’s use annotations to document our fixtures. Here is an example of an annotation that could be used:
package com.weblogism.jruby; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface FixtureHint { String usage(); String description(); }
The RetentionPolicy
indicates that this annotation will be available at runtime.
Fixtures can then be documented as follows2:
package com.weblogism.jruby; public class TheFixture { @FixtureHint(usage="| click the | _button_id_ | button |", description="clicks _button_id_") public boolean clickTheButton() { System.out.println("Click"); return true; } }
These classes are nicely packaged in a jar called test.jar
. So how to use JRuby to find the annotations? Extremely simple:
require 'java' require 'lib/test.jar' include_class 'com.weblogism.jruby.TheFixture' include_class 'com.weblogism.jruby.FixtureHint'
You first import all the Java stuff, such as your classes. Don’t forget that these include_class
can be called dynamically, and therefore you could potentially search for all the relevant classes in the jar, and then import them all. Here, the jar is explicitly imported (it is located in the lib
directory of the current working dir), but another way to make it “visible” to the script is add it to the $CLASSPATH
environment var.
annotations = Hash.new TheFixture.java_class.declared_instance_methods.each do |m| if m.annotation_present?(FixtureHint.java_class) annotation = m.annotation(FixtureHint.java_class) annotations[m.name] = annotation end end annotations.values.each do |a| puts "#{a.usage()}\t#{a.description()}" end
And that’s as simple as that: the Java methods isAnnotationPresent
and getAnnotation
become annotation_present?
and annotation
(à la ruby), and once they have been found, they can be manipulated like ruby objects.
JRuby version:
sebastien@greystones:~/workspace/sandbox$ jruby -v jruby 1.6.0.dev (ruby 1.8.7 patchlevel 249) (2010-08-10 f740f78) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_20) [amd64-java]
1 The example might be a bit convoluted, but it illustrates the use of annotations through a real-life requirement.
2 Fixtures would usually extend a fixture class, e.g. DoFixture
, ColumnFixture
, etc. but here it isn’t to keep things simple.