The Java approach, part 2: Maven

Một phần của tài liệu Making java groovy (Trang 132 - 138)

I’m going to confess up front that Maven is hard to talk about rationally. Its best fea- tures (establishing a conventional project layout, managing dependencies, providing a rich plugin architecture) are also considered some of its worst features (difficult to work outside its conventions, hard to manage transitive dependencies, the whole

“download the internet” problem). I can honestly say I’ve never encountered a tech- nology that’s both common in the industry and yet loathed with the white-hot inten- sity of a thousand suns.6 Bring up Maven in a group of developers, and someone will refuse to discuss “the M word.” Yet, at the same time, somebody else will quietly say that they can make it do anything and don’t understand what all the fuss is about.

My own experience isn’t so black-and-white. I find that if a project was designed using Maven from the beginning, it tends to work well with the system. It’s also hard to use that system without Maven. On the other hand, adding Maven to a system that wasn’t started with it can be quite painful. In addition, friends have also assured me that once a system gets above a certain size, the whole process becomes an unmanage- able mess.

Perhaps the best way to stay above the fray is to say that Maven has a highly opin- ionated API. To be successful you have to do things the Maven way. Plus, like Ant, you’re coding your build in XML, which is never easy. The multi-project build capabil- ities are awkward, too.7

I will note that the standard Maven project layout (shown in figure 5.2) has become common throughout the industry. Also, people may complain about Maven’s approach to dependency management, but I haven’t seen anything dramatically bet- ter. Gradle (the proposed replacement, discussed later in this chapter) uses Maven repositories and Ivy dependency management and suffers from the same “download the internet” problem. Dependency management is just hard, no matter how you approach it.

Returning (at last) to the core theme of this book, the goal of this section is to show you how to incorporate Groovy into Maven builds. There are two ways to do that.

I’ll start with the Groovy-Eclipse plugin and then build the same application using the GMaven project.

(continued)

The built-in Ant tasks are very low level. As a result, Ant build files quickly grow long and complex and involve a lot of repetition.

For all these reasons and others Ant was ripe for a higher-level replacement. That role was filled by the Maven project, which is either a blessing or a curse depending on your experiences with it.

6 Except possibly for every Microsoft technology ever.

7 Admittedly that doesn’t sound terribly “above the fray,” but at least I’m trying.

5.4.1 The Groovy-Eclipse plugin for Maven

The Groovy-Eclipse compiler plugin (http://mng.bz/2rHY) is a standard com- piler plugin for Maven. It emerged from the effort to build a good Eclipse plugin for Groovy that worked with combined Groovy and Java projects. The Maven plugin is a way to take advantage of that effort, whether you plan to use the Eclipse IDE or not.

To demonstrate its use I’ll build a small project that accesses the Yahoo! Weather web service and reports on the current conditions. This is easy enough to do in Java but becomes particularly simple in Groovy.

The Yahoo! Weather web service (http://developer.yahoo.com/weather/) pro- vides weather information in the form of an RSS feed. The web service is accessed from a URL of the form

http://weather.yahooapis.com/forecastrss

The URL has two parameters, one required and one optional. The required parame- ter is w, a so-called WOEID (Where On Earth ID), that Yahoo uses to identify a location.

The other parameter is u, which is used to specify the temperature units in Fahrenheit (f, the default) or Celsius (c). For unknown reasons, there’s no way to programmati- cally look up a WOEID. Instead Yahoo! directs you to its own weather page and sug- gests you search for your city.

A simple HTTPGET request to the proper URL returns an XML response in RSS form. A sample is included on Yahoo!’s web page.

Suppose I decided to build a simple application to retrieve the current weather conditions based on this service. Maven recommends that you specify a particular arti- fact to begin the project, so I’ll start with the classic maven-archetype-quickstart:

> mvn archetype:generate –DgroupId=mjg –DartifactId=weather –DarchetypeArtifactId=maven-archetype-quickstart

-Dversion=1.0-SNAPSHOT –Dpackage=mjg

MAVEN ARCHETYPES The Groovy-Eclipse plugin uses regular Java archetypes and adds Groovy functionality. The GMaven approach in the next section includes a basic archetype to get started.

Figure 5.2 Standard Maven project structure used for the application in this section. Compiled sources are in src/main/

java, and tests reside in src/test/java.

107 The Java approach, part 2: Maven

This generates a Java project with the standard layout, meaning the source code direc- tory is src/main/java and the testing directory is src/test/java. The quick start arche- type includes a trivial App.java and AppTest.java in those directories, respectively. The generator also adds a standard Maven POM file in the root directory, whose only dependency is on JUnit, as shown in the next listing.

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>mjg</groupId>

<artifactId>weather</artifactId>

<packaging>jar</packaging>

<version>1.0-SNAPSHOT</version>

<name>weather</name>

<url>http://maven.apache.org</url>

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

<scope>test</scope>

</dependency>

</dependencies>

</project>

The only change I’ve made so far from the standard is to upgrade the JUnit depen- dency to 4.10 from 3.8.1.

To do the actual work I need a class to send the request to Yahoo and parse the response, and a POJO to hold the resulting weather information. Starting with the POJO, for a given city, region, and country I want to store the condition, temperature, wind chill, and humidity. The web service returns a lot more information than this, but this will suffice to get started.

POJOs are simple containers for data, so the constructors, getter and setter meth- ods, and any necessary overrides are mostly clutter. I can therefore simplify my life if I use a POGO instead, as shown in the following listing.

package mjg class Weather { String city String region String country String condition String temp

Listing 5.8 The Maven pom.xml file for a standard Java project

Listing 5.9 Weather.groovy, a POGO to hold weather results from the web service

String chill String humidity String toString() { """

Weather for $city, $region, $country:

Condition : $condition Temperature: $temp Wind Chill : $chill Humidity : $humidity """

} }

The toString method is a way to produce formatted output. Groovy’s multiline string makes it particularly easy.

The other class I need is a parser for the web service. Because all I need is a GET request I can use the parse method in the XmlSlurper class as usual and drill down the resulting DOM tree to get the results I want. That’s pretty simple, too, as shown in the following listing.

package mjg

class YahooParser {

final static String BASE = 'http://weather.yahooapis.com/forecastrss?' Weather getWeather(String woeid) {

def root = new XmlSlurper().parse(BASE + "w=$woeid") Weather w = new Weather(

city:root.channel.location.@city, region:root.channel.location.@region, country:root.channel.location.@country, condition:root.channel.item.condition.@text, temp:root.channel.item.condition.@temp, chill:root.channel.wind.@chill,

humidity:root.channel.atmosphere.@humidity )

} }

Given a WOEID, the service builds the URL and accesses the web service, parses the resulting RSS, and returns an instance of the Weather class with all the relevant fields populated.

To complete the program I need a driver, which I can write as a Groovy script.

That’s a one-liner, unless I want to allow the client to specify a WOEID on the com- mand line:

def woeid = args.size() ? args[0] : '2367105' println new YahooParser().getWeather(woeid)

Listing 5.10 YahooParser.groovy, which accesses and parses the weather service

109 The Java approach, part 2: Maven

The default WOEID in the script is for Boston, MA, and it’s stored in RunDemo.groovy. In order to demonstrate the differences when both Java and Groovy sources are pres- ent together, I also added a Java class to access the web service in the file RunIn- Java.java:

public class RunInJava {

public static void main(String[] args) { String woeid = "2367105";

if (args.length > 0) woeid = args[0];

YahooParser yp = new YahooParser();

System.out.println(yp.getWeather(woeid));

} }

Now comes the interesting part: how do I get Maven to handle all the Groovy code?

The Groovy-Eclipse plugin requires two additions to the POM file. First I need to add Groovy as a dependency:

<dependencies>

...

<dependency>

<groupId>org.codehaus.groovy</groupId>

<artifactId>groovy-all</artifactId>

<version>2.1.5</version>

</dependency>

</dependencies>

Next I need to add the Groovy-Eclipse plugin in a build section below the dependencies:

<build>

<plugins>

<plugin>

<artifactId>maven-compiler-plugin</artifactId>

<version>2.3.2</version>

<configuration>

<compilerId>groovy-eclipse-compiler</compilerId>

</configuration>

<dependencies>

<dependency>

<groupId>org.codehaus.groovy</groupId>

<artifactId>groovy-eclipse-compiler</artifactId>

<version>2.7.0-01</version>

</dependency>

</dependencies>

</plugin>

</plugins>

</build>

With both of these additions Maven will compile and use Groovy code appropriately, except for one rather strange oddity. Normally I would add my Groovy classes to src/

main/groovy and any Groovy tests to src/test/groovy. According to the plugin docu- mentation, I can do that only if (1) there’s at least one Java class in src/main/java or (2) I add a lot more XML to specify the additional source directories.

SOURCE DIRECTORIES For the Groovy-Eclipse plugin, put Java and Groovy sources in the src/main/java and src/test/java directories by default.

I put my Groovy files in src/main/java and src/test/java. Now I can build the proj- ect using

mvn clean install

I can even execute the project using the exec:java (!) task, both using the default WOEID and with a supplied command-line argument:

> mvn exec:java -Dexec.mainClass=mjg.RunDemo ...

Weather for Boston, MA, United States:

Condition : Cloudy Temperature: 58 Wind Chill : 58 Humidity : 84

I can supply a command-line argument using –Dexec.args:

> mvn exec:java -Dexec.mainClass=mjg.RunDemo -Dexec.args='44418' ...

Weather for London, , United Kingdom:

Condition : Cloudy Temperature: 54 Wind Chill : 54 Humidity : 82

A guiding principle in this book is that Java is good at tools, libraries, and (existing) infrastructure, and that Groovy is good at everything else. It’s hard to imagine a better demonstration of that than the current example. The entire application was written in Groovy, at a code savings on the order of 10 to 1. The infrastructure treated the code as though it was all Java, and I was even able to use the Java exec task to execute the Groovy script to drive the application.

The Groovy-Eclipse compiler plugin is a funded project, because it’s used inside the IDEs provided by SpringSource (a division of VMware).8 The quality of the plugin, especially for cross-compilation, is therefore quite high. Just because it has the name

“Eclipse” wired into it, there’s no reason not to use it in a Maven project. There’s no implication that the plugin is exclusive to the IDE. You can use it anywhere, as I did with the Maven project in this section.

The other way to add Groovy to a project built with Maven is to use the GMaven project, discussed in the next section.

5.4.2 The GMaven project

GMaven is an alternative approach for adding Groovy into Maven projects. It works with combined Java and Groovy sources by generating stubs for the Groovy files as part of the build sequence.

8 Now part of Pivotal, which is owned by VMware, which is owned by EMC…

111 The Java approach, part 2: Maven

To help users get started, the project provides a Maven archetype called gmaven- archetype-basic. To use the archetype, execute the following at the command line:

> mvn archetype:generate –DgroupId=mjg –DartifactId=weather –DarchetypeArtifactId=gmaven-archetype-basic

-Dversion=1.0-SNAPSHOT –Dpackage=mjg

This again produces a project in standard Maven structure, in which the sources are in src/main/groovy and the tests are in src/test/groovy. The plugin expects both Java and Groovy sources to reside in those directories.

The generated POM is shown in the following listing, with some modifications dis- cussed in the listing.

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>mjg</groupId>

<artifactId>weather</artifactId>

<name>weather project</name>

<version>1.0-SNAPSHOT</version>

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.codehaus.groovy</groupId>

<artifactId>groovy-all</artifactId>

<version>2.1.5</version>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.codehaus.gmaven</groupId>

<artifactId>gmaven-plugin</artifactId>

<version>1.4</version>

<configuration>

<providerSelection>2.0</providerSelection>

</configuration>

<executions>

<execution>

<goals>

<goal>generateStubs</goal>

<goal>compile</goal>

<goal>generateTestStubs</goal>

Listing 5.11 The Maven pom.xml file produced by the GMaven project

Một phần của tài liệu Making java groovy (Trang 132 - 138)

Tải bản đầy đủ (PDF)

(369 trang)