小妹大年初四上班,发博啦
Server端的持久化
到目前为止,在RSSReader项目里,还没有任何持久化的操作。为了让我们项目更具有真实性,现在我们要添加持久化的代码。但是为了让我们更专注于client-side,持久化的逻辑仅仅是最基本的操作——实现真实的持久化逻辑之前先加入持久化的接口,这样就可以为了需要,可以在不改变接口调用的基础上,改用另外一种持久化操作的内容既可(比如hibernate)。
目前,实现最初的持久化版本,步骤如下:
- 当添加新的feed实体时候,在server-side使用xml文件来存储。
- xml文件只有一个,我们会在这个文件里维护feed的列表。
持久化的代码不属于GXT的犯愁,因此我们可以把他以一个黑盒对待。在我们RSSReader项目里,要加入Persistence接口和实现此接口的FilePersistence类,他们的功能就是负责存储和获得RSS feed信息的。
持久化一个存在的Feed
在第二章,我们新建一个Link feed button,目的是用来从internet里导入一个已经存在的RSS feed网络地址。接下来我们要在FeedService接口里,定义addExistingFeed方法,用作持久层存储RSS feed的URL地址。此功能要被绑定到LinkFeedPopup的add button上。
- 在FeedService接口里,定义addExistingFeed方法,当然要传入一个URL 参数
void addExistingFeed(String feedUrl);
- 同样的会在FeedServiceAsync接口里,定义与之相关的异步回调方法
void addExistingFeed(String feedUrl, AsyncCallback<Void>
callback);
- 修改LinkFeedPopup的addFeed方法,加入addExistingFeed回调方法的调用,具体逻辑一看便知
public void addFeed(final String feedUrl) {
final FeedServiceAsync feedService = Registry
.get(RSSReaderConstants.FEED_SERVICE);
feedService.addExistingFeed(feedUrl, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
Info.display("RSS Reader", "Failed to add feed at: " + feedUrl);
}
@Override
public void onSuccess(Void result) {
tfUrl.clear();
Info.display("RSS Reader", "Feed at " + feedUrl
+ " added successfully");
hide();
}
});
}
- FeedServiceImpl类里,我们要加入日志,这里用简单的java.util.logging.Logger
private final static Logger LOGGER = Logger.getLogger(FeedServiceImpl.class
.getName());
- 在FeedServiceImpl类里,加入private的方法loadFeed,用来根据feed的URL,在JDOM规范下,从internet里获得RSS的XML内容,返回Feed实体bean
private Feed loadFeed(String feedUrl) {
Feed feed = new Feed(feedUrl);
try {
SAXBuilder parser = new SAXBuilder();
Document document = parser.build(new URL(feedUrl));
Element eleRoot = document.getRootElement();
Element eleChannel = eleRoot.getChild("channel");
feed.setTitle(eleChannel.getChildText("title"));
feed.setDescription(eleChannel.getChildText("description"));
feed.setLink(eleChannel.getChildText("link"));
return feed;
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "IO Error loading feed", e);
return feed;
} catch (JDOMException e) {
LOGGER.log(Level.SEVERE, "Error parsing feed", e);
return feed;
}
}
- 在FeedServiceImpl类里,加入HashMap的属性,用来存储Feed对象
private Map<String, Feed> feeds = new HashMap<String, Feed>();
- 我们目前的持久化功能是依赖与jdom-1.1.2.jar包功能的实现,具体的持久化代码如下,全部贴出来
持久化的配置文件:rssreader.properties(放在src\下)
feed.file=feeds.txt
data.folder=data
base.url=http://127.0.0.1:8888
package com.danielvaughan.rssreader.server.utils;
import java.util.Set;
import org.jdom.Document;
public interface Persistence {
public Set<String> loadFeedList();
public void saveFeedList(Set<String> feedUrls);
public void saveFeedXml(String uuid, Document document);
public String getUrl(String uuid);
}
package com.danielvaughan.rssreader.server.utils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;
import org.jdom.Document;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
public class FilePersistence implements Persistence {
private final static Logger LOGGER = Logger.getLogger(FilePersistence.class
.getName());
private String dataFolder;
private String feedFilePath;
private String baseUrl;
private final Properties properties = new Properties();
public FilePersistence() {
// Read properties file.
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
InputStream in = loader.getResourceAsStream("rssreader.properties");
properties.load(in);
dataFolder = (String) properties.get("data.folder");
String feedFile = (String) properties.get("feed.file");
feedFilePath = dataFolder + "\\" + feedFile;
baseUrl = (String) properties.get("base.url");
initDataFolder();
} catch (IOException e) {
LOGGER.severe("Unable to load properties file");
}
}
private void initDataFolder() {
try {
File dataFolderFile = new File(dataFolder);
if (!dataFolderFile.exists()) {
dataFolderFile.mkdir();
}
File feedFile = new File(feedFilePath);
if (!feedFile.exists()) {
feedFile.createNewFile();
}
} catch (IOException e) {
LOGGER.severe("Error initialising data folder");
}
}
@Override
public void saveFeedList(Set<String> feedUrls) {
try {
FileWriter fileWriter = new FileWriter(feedFilePath);
BufferedWriter out = new BufferedWriter(fileWriter);
for (String feedUrl : feedUrls) {
String line = feedUrl.concat("\n");
out.write(line);
}
out.close();
} catch (Exception e) {
LOGGER.severe("Error: " + e.getMessage());
}
}
@Override
public Set<String> loadFeedList() {
Set<String> feedUrls = new HashSet<String>();
try {
FileInputStream fstream = new FileInputStream(feedFilePath);
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
while ((strLine = br.readLine()) != null) {
feedUrls.add(strLine);
}
in.close();
return feedUrls;
} catch (Exception e) {
LOGGER.severe("Error: " + e.getMessage());
return feedUrls;
}
}
@Override
public void saveFeedXml(String feedId, Document document) {
try {
File file = new File(generateFilePath(feedId));
XMLOutputter serializer = new XMLOutputter();
Format prettyFormat = Format.getPrettyFormat();
serializer.setFormat(prettyFormat);
FileWriter writer = new FileWriter(file);
serializer.output(document, writer);
writer.close();
} catch (IOException e) {
LOGGER.severe("Error: " + e.getMessage());
}
}
private String generateFilePath(String feedId) {
return dataFolder + "/" + feedId + ".xml";
}
@Override
public String getUrl(String feedId) {
String fileName = generateFilePath(feedId);
return baseUrl + "/" + fileName;
}
}
- 最后在FeedServiceImpl类里,实现addExistingFeed方法,调用刚才创建的loadFeed方法,根据feedUrl返回Feed对象,再调用上面持久化类的saveFeedList方法,完成持久化操作
@Override
public void addExistingFeed(String feedUrl) {
Feed loadResult = loadFeed(feedUrl);
if (loadResult.getTitle() != null) {
feeds.put(feedUrl, loadFeed(feedUrl));
persistence.saveFeedList(feeds.keySet());
}
}
- 创建本地的RSS xml文件用来读取:http://127.0.0.1:8888/rss2sample.xml。在WebContent\下创建rss2sample.xml,内容如下:
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title>Liftoff News</title>
<link>http://liftoff.msfc.nasa.gov/</link>
<description>Liftoff to Space Exploration.</description>
<language>en-us</language>
<pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
<lastBuildDate>Tue, 10 Jun 2003 09:41:01 GMT</lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>Weblog Editor 2.0</generator>
<managingEditor>editor@example.com</managingEditor>
<webMaster>webmaster@example.com</webMaster>
<item>
<title>Star City</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
<description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>.</description>
<pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
</item>
<item>
<description>Sky watchers in Europe, Asia, and parts of Alaska and Canada will experience a <a href="http://science.nasa.gov/headlines/y2003/30may_solareclipse.htm">partial eclipse of the Sun</a> on Saturday, May 31st.</description>
<pubDate>Fri, 30 May 2003 11:06:42 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/30.html#item572</guid>
</item>
<item>
<title>The Engine That Does More</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-VASIMR.asp</link>
<description>Before man travels to Mars, NASA hopes to design new engines that will let us fly through the Solar System more quickly. The proposed VASIMR engine would do that.</description>
<pubDate>Tue, 27 May 2003 08:37:32 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/27.html#item571</guid>
</item>
<item>
<title>Astronauts' Dirty Laundry</title>
<link>http://liftoff.msfc.nasa.gov/news/2003/news-laundry.asp</link>
<description>Compared to earlier spacecraft, the International Space Station has many luxuries, but laundry facilities are not one of them. Instead, astronauts have other options.</description>
<pubDate>Tue, 20 May 2003 08:56:02 GMT</pubDate>
<guid>http://liftoff.msfc.nasa.gov/2003/05/20.html#item570</guid>
</item>
</channel>
</rss>
- 运行程序,会读取rssreader.properties配置文件初始化持久化的配置。当你通过Link feed button 成功添加一个真实的URL link(http://127.0.0.1:8888/rss2sample.xml)的时候,会在项目WebContent\data\feeds.txt文件里存储一行内容http://127.0.0.1:8888/rss2sample.xml