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 添加一些资源。

