Simple Weather 命令行应用程序包含五个 Java 类。
org.sonatype.mavenbook.weather.Main
-
这个类包含了一个静态的
main()
函数,即系统的入口。 org.sonatype.mavenbook.weather.Weather
-
Weather
类是个很简单的 Java Bean,它保存了天气报告的地点和其它一些关键元素,如气温和湿度。 org.sonatype.mavenbook.weather.YahooRetriever
-
YahooRetriever
连接到 Yahoo! Weather 并且返回来自数据源数据的InputStream
。 org.sonatype.mavenbook.weather.YahooParser
-
YahooParser
解析来自 Yahoo! Weather 的 XML,返回Weather
对象。 org.sonatype.mavenbook.weather.WeatherFormatter
-
WeatherFormatter
接受Weather
对象,创建VelocityContext
,根据 Velocity 模板生成结果。
这里我们不是想要详细阐述样例中的代码,但解释一下程序中使之运行的核心代码还是必要的。
我们假设大部分读者已经下载的本书的源码,但也不会忘记那些按着书一步一步往下看的读者。 本小节列出了
simple-weather
项目的类,这些类都放在同一个包下面,org.sonatype.mavenbook.weather 。
让我们删掉由 archetype:create
生成
App
类和 AppTest
类,然后加入我们新的包。在
Maven 项目中,所有项目的源代码都存储在 src/main/java
目录。
在新项目的基础目录下,运行下面的命令:
$ 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
你已经创建了一个新的包 org.sonatype.mavenbook.weather 。
现在,我们需要把那些类放到这个目录下面。 用你最喜欢的编辑器,创建一个新文件,名字为
Weather.java
,内容如下:
Example 4.4. Simple Weather 的 Weather 模型对象
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; } }
Weather
类定义了一个简单的 bean ,用来存储由 Yahoo! Weather
数据源解析出来的天气信息。天气数据源提供了丰富的信息,从日出日落时间,到风速和风向。 为了让这个例子保持简单,
Weather
模型对象只保存温度,湿度和当前天气情况的文字描述等信息。
在同一目录下,创建 Main.java
文件。Main
这个类有一个静态的 main()
函数——样例程序的入口。
Example 4.5. Simple Weather 的 Main 类
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) int zipcode = 60202; try { zipcode = Integer.parseInt(args[0]); } catch( Exception e ) {} // Start the program new Main(zipcode).start(); } private int zip; public Main(int 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 ) ); } }
上例中的 main()
函数通过获取 classpath 中的资源文件来配置 Log4J
,之后它试图从命令行读取邮政编码。 如果在读取邮政编码的时候抛出了异常,程序会设置默认邮政编码为 60202 。 一旦有了邮政编码,它初始化一个
Main
对象,调用该对象的 start()
方法。而
start()
方法会调用 YahooRetriever
来获取天气的 XML 数据。 YahooRetriever
返回一个 InputStreem
,传给
YahooParser
。 YahooParser
解析
XML 数据并返回 Weather
对象。
最后,WeatherFormatter
接受一个
Weather
对象并返回一个格式化的 String
,打印到标准输出。
在相同目录下创建文件 YahooRetriever.java
,内容如下:
Example 4.6. Simple Weather 的 YahooRetriever 类
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(); } }
这个简单的类打开一个连接到 Yahoo! Weather API 的
URLConnection
并返回一个
InputStream
。 我们还需要在该目录下创建文件
YahooParser.java
用以解析这个数据源。
Example 4.7. Simple Weather 的 YahooParser 类
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; } }
YahooParser
是本例中最复杂的类,我们不会深入 Dom4J 或者 Jaxen
的细节,但是这个类还是需要一些解释。YahooParser
的
parse()
方法接受一个 InputStrem
然后返回一个 Weather
对象。 为了完成这一目标,它需要用 Dom4J 来解析
XML 文档。因为我们对 Yahoo! Weather XML
命名空间的元素感兴趣,我们需要用 createXmlReader()
方法创建一个包含命名空间信息的 SAXReader
。 一旦我们创建了这个 reader
并且解析了文档,得到了返回的 org.dom4j.Document
,只需要简单的使用 XPath
表达式来获取需要的信息,而不是遍历所有的子元素。 本例中 Dom4J 提供了 XML 解析功能,而 Jaxen
提供了 XPath 功能。
我们已经创建了 Weather
对象,我们需要格式化输出以供人阅读。
在同一目录中创建一个名为 WeatherFormatter.java
的文件。
Example 4.8. Simple Weather 的 WeatherFormatter 类
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(); } }
WeatherFormatter
使用 Veloticy
来呈现一个模板。format()
方法接受一个
Weather
bean 然后返回格式化好的
String
。format()
方法做的第一件事是从 classpath 载入名字为 output.vm
的 Velocity 模板。
然后我们创建一个 VelocityContext
,它需要一个
Weather
对象来填充。
一个StringWriter
被创建用来存放模板生成的结果数据。通过调用
Velocity.evaluate()
,给模板赋值,结果作为 String 返回。
在我们能够运行该样例程序之前,我们需要往 classpath 添加一些资源。