The simple weather command-line application consists of the following five Java classes:
org.sonatype.mavenbook.weather.Main
The
Main
class contains a staticmain()
function, and is the entry point for this system.org.sonatype.mavenbook.weather.Weather
The
Weather
class is a straightforward Java bean that holds the location of our weather report and some key facts, such as the temperature and humidity.org.sonatype.mavenbook.weather.YahooRetriever
The
YahooRetriever
class connects to Yahoo! Weather and returns anInputStream
of the data from the feed.org.sonatype.mavenbook.weather.YahooParser
The
YahooParser
class parses the XML from Yahoo! Weather, and returns aWeather
object.org.sonatype.mavenbook.weather.WeatherFormatter
The
WeatherFormatter
class takes aWeather
object, creates aVelocityContext
, and evaluates a Velocity template.
Although we wonât dwell on the code here, we will provide all
the necessary code for you to get the example working. We assume that
most readers have downloaded the examples that accompany this book,
but weâre also mindful of those who may wish to follow the example in
this chapter step-by-step. The sections that follow list classes in
the simple-weather
project. Each of these classes
should be placed in the same package:
org.sonatype.mavenbook.weather.
Letâs remove the App
and the
AppTest
classes created by
archetype:create
and add our new package. In a
Maven project, all of a projectâs source code is stored in src/main/java. From the base directory of
the new project, execute the following commands:
$ cd src/test/java/org/sonatype/mavenbook $ rm AppTest.java $ cd ../../../../../.. $ cd src/main/java/org/sonatype/mavenbook $ rm App.java $ mkdir weather $ cd weather
This creates a new package named org.sonatype.mavenbook.weather. Now we need to put some classes in this directory. Using your favorite text editor, create a new file named Weather.java with the contents shown in Example 4-4.
Example 4-4. simple-weatherâs Weather model object
package org.sonatype.mavenbook.weather; public class Weather { private String city; private String region; private String country; private String condition; private String temp; private String chill; private String humidity; public Weather() {} public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getRegion() { return region; } public void setRegion(String region) { this.region = region; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getCondition() { return condition; } public void setCondition(String condition) { this.condition = condition; } public String getTemp() { return temp; } public void setTemp(String temp) { this.temp = temp; } public String getChill() { return chill; } public void setChill(String chill) { this.chill = chill; } public String getHumidity() { return humidity; } public void setHumidity(String humidity) { this.humidity = humidity; } }
The Weather
class defines a simple bean
that is used to hold the weather information parsed from the Yahoo!
Weather feed. This feed provides a wealth of information, from the
sunrise and sunset times to the speed and direction of the wind. To
keep this example as simple as possible, the
Weather
model object keeps track of only the
temperature, chill, humidity, and a textual description of current
conditions.
Now, in the same directory, create a file named Main.java. This Main
class will hold the static main()
functionâthe entry point for this example. See Example 4-5.
Example 4-5. simple-weatherâs Main class
package org.sonatype.mavenbook.weather; import java.io.InputStream; import org.apache.log4j.PropertyConfigurator; public class Main { public static void main(String[] args) throws Exception { // Configure Log4J PropertyConfigurator.configure(Main.class.getClassLoader() .getResource("log4j.properties")); // Read the Zip Code from the Command-line (if none supplied, use 60202) String zipcode = "60202"; try { zipcode = args[0]); } catch( Exception e ) {} // Start the program new Main(zipcode).start(); } private String zip; public Main(String zip) { this.zip = zip; } public void start() throws Exception { // Retrieve Data InputStream dataIn = new YahooRetriever().retrieve( zip ); // Parse Data Weather weather = new YahooParser().parse( dataIn ); // Format (Print) Data System.out.print( new WeatherFormatter().format( weather ) ); } }
The main()
function shown in this
example configures Log4J by retrieving a resource from the classpath.
It then tries to read a zip code from the command line. If an
exception is thrown while it is trying to read the zip code, the
program will default to a zip code of 60202. Once it has a zip code,
it instantiates an instance of Main
and calls
the start()
method on an instance of
Main
. The start()
method calls out to the YahooRetriever
to retrieve the
weather XML. The
YahooRetriever
returns an InputStream
, which is then passed to
the YahooParser
. The
YahooParser
parses the Yahoo! Weather
XML and returns a Weather
object. Finally, the WeatherFormatter
takes a
Weather
object and spits out a formatted
String
, which is printed to standard
output.
Create a file named YahooRetriever.java in the same directory with the contents shown in Example 4-6.
Example 4-6. simple-weatherâs YahooRetriever class
package org.sonatype.mavenbook.weather; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import org.apache.log4j.Logger; public class YahooRetriever { private static Logger log = Logger.getLogger(YahooRetriever.class); public InputStream retrieve(int zipcode) throws Exception { log.info( "Retrieving Weather Data" ); String url = "http://weather.yahooapis.com/forecastrss?p=" + zipcode; URLConnection conn = new URL(url).openConnection(); return conn.getInputStream(); } }
This simple class opens a URLConnection
to the Yahoo! Weather API and returns an
InputStream
. To create something to parse this
feed, weâll need to create the YahooParser.java file in the same
directory. See Example 4-7.
Example 4-7. simple-weatherâs YahooParser class
package org.sonatype.mavenbook.weather; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import org.dom4j.Document; import org.dom4j.DocumentFactory; import org.dom4j.io.SAXReader; public class YahooParser { private static Logger log = Logger.getLogger(YahooParser.class); public Weather parse(InputStream inputStream) throws Exception { Weather weather = new Weather(); log.info( "Creating XML Reader" ); SAXReader xmlReader = createXmlReader(); Document doc = xmlReader.read( inputStream ); log.info( "Parsing XML Response" ); weather.setCity( doc.valueOf("/rss/channel/y:location/@city") ); weather.setRegion( doc.valueOf("/rss/channel/y:location/@region") ); weather.setCountry( doc.valueOf("/rss/channel/y:location/@country") ); weather.setCondition( doc.valueOf("/rss/channel/item/y:condition/@text") ); weather.setTemp( doc.valueOf("/rss/channel/item/y:condition/@temp") ); weather.setChill( doc.valueOf("/rss/channel/y:wind/@chill") ); weather.setHumidity( doc.valueOf("/rss/channel/y:atmosphere/@humidity") ); return weather; } private SAXReader createXmlReader() { Map<String,String> uris = new HashMap<String,String>(); uris.put( "y", "http://xml.weather.yahoo.com/ns/rss/1.0" ); DocumentFactory factory = new DocumentFactory(); factory.setXPathNamespaceURIs( uris ); SAXReader xmlReader = new SAXReader(); xmlReader.setDocumentFactory( factory ); return xmlReader; } }
The YahooParser
is the most complex class
in this example. Weâre not going to dive into the details of Dom4J or
Jaxen here, but the class deserves some explanation.
YahooParser
âs parse()
method takes an InputStream
and returns a
Weather
object. To do this, it needs to parse
an XML document with Dom4J. Since weâre interested
in elements under the Yahoo! Weather XML namespace,
we need to create a namespace-aware SAXReader
in the createXmlReader()
method. Once we
create this reader and parse the document, we get an
org.dom4j.Document
object back. Instead of
iterating through child elements, we simply address each piece of
information we need using an XPath expression. Dom4J provides the
XML parsing in this example, and Jaxen provides the
XPath capabilities.
Once weâve created a Weather
object, we
need to format our output for human consumption. Create a file named
WeatherFormatter.java in the same
directory as the other classes. See Example 4-8.
Example 4-8. simple-weatherâs WeatherFormatter class
package org.sonatype.mavenbook.weather; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import org.apache.log4j.Logger; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; public class WeatherFormatter { private static Logger log = Logger.getLogger(WeatherFormatter.class); public String format( Weather weather ) throws Exception { log.info( "Formatting Weather Data" ); Reader reader = new InputStreamReader( getClass().getClassLoader() .getResourceAsStream("output.vm")); VelocityContext context = new VelocityContext(); context.put("weather", weather ); StringWriter writer = new StringWriter(); Velocity.evaluate(context, writer, "", reader); return writer.toString(); } }
The WeatherFormatter
uses Velocity to
render a template. The format()
method takes
a Weather
bean and spits out a formatted
String
. The first thing the
format()
method does is load a Velocity
template from the classpath named output.vm. We then create a
VelocityContext
, which is populated with a
single Weather
object named
weather
. A StringWriter
is
created to hold the results of the template merge. The template is
then evaluated with a call to
Velocity.evaluate()
, and the results are
returned as a String
.
Before we can run this example, weâll need to add some resources to our classpath.
Get Maven: The Definitive Guide now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.