WebWork的result實現非常實用,它很好的解決了View渲染的靈活性問題。這才是MVC模式的優勢所在,而像JSF那樣幫定JSP的MVC就吃不到這個甜頭了。說WebWork2是Model 2 MVC的巔峰就在這些靈活的地方。
閑扯這個不是主要目的。現在Rome是Java下最常用的RSS包,最近消息似乎要轉入Apache的Abdera合并變成更強大的聚合引擎。用Rome生成和解析RSS都很方便。今天討論一下使用ROME給網站生成RSS,并通過WebWork2的Result機制渲染。
最初是從WebWork的Cookbook上看到的RomeResult的文章,一看就會,我這里其實不過是舉個詳細點的例子,注意我使用的是WebWork 2.2.2和Rome 0.8:
http://wiki.opensymphony.com/display/WW/RomeResult
參考了和東的這篇Blog,利用rome寫rss feed生成程序:
http://hedong.3322.org/newblog/archives/000051.html
首先創建RomeResult類:
?*?
?*/
package?com.goldnet.framework.webwork.result;
import?java.io.Writer;
import?org.apache.log4j.Logger;
import?com.opensymphony.webwork.ServletActionContext;
import?com.opensymphony.xwork.ActionInvocation;
import?com.opensymphony.xwork.Result;
import?com.sun.syndication.feed.synd.SyndFeed;
import?com.sun.syndication.io.SyndFeedOutput;
/**
?*?A?simple?Result?to?output?a?Rome?SyndFeed?object?into?a?newsfeed.
?*?@author?Philip?Luppens
?*?
?*/
public?class?RomeResult?implements?Result?{
?private?static?final?long?serialVersionUID?=?-6089389751322858939L;
?private?String?feedName;
?private?String?feedType;
?private?final?static?Logger?logger?=?Logger.getLogger(RomeResult.class);
?/*
??*?(non-Javadoc)
??*?
??*?@see?com.opensymphony.xwork.Result#execute(com.opensymphony.xwork.ActionInvocation)
??*/
?public?void?execute(ActionInvocation?ai)?throws?Exception?{
??if?(feedName?==?null)?{
???//?ack,?we?need?this?to?find?the?feed?on?the?stack
???logger
?????.error("Required?parameter?'feedName'?not?found.?"
???????+?"Make?sure?you?have?the?param?tag?set?and?"
???????+?"the?static-parameters?interceptor?enabled?in?your?interceptor?stack.");
???//?no?point?in?continuing?..
???return;
??}
??//?don't?forget?to?set?the?content?to?the?correct?mimetype
??ServletActionContext.getResponse().setContentType("text/xml");
??//?get?the?feed?from?the?stack?that?can?be?found?by?the?feedName
??SyndFeed?feed?=?(SyndFeed)?ai.getStack().findValue(feedName);
??if?(logger.isDebugEnabled())?{
???logger.debug("Found?object?on?stack?with?name?'"?+?feedName?+?"':?"
?????+?feed);
??}
??if?(feed?!=?null)?{
???if?(feedType?!=?null)?{
????//?Accepted?types?are:?rss_0.90?-?rss_2.0?and?atom_0.3
????//?There?is?a?bug?though?in?the?rss?2.0?generator?when?it?checks
????//?for?the?type?attribute?in?the?description?element.?It's?has?a
????//?big?'FIXME'?next?to?it?(v.?0.7beta).
????feed.setFeedType(feedType);
???}
???SyndFeedOutput?output?=?new?SyndFeedOutput();
???//we'll?need?the?writer?since?Rome?doesn't?support?writing?to?an?outputStream?yet
???Writer?out?=?null;
???try?{
????out?=?ServletActionContext.getResponse().getWriter();
????output.output(feed,?out);
???}?catch?(Exception?e)?{
????//?Woops,?couldn't?write?the?feed??
????logger.error("Could?not?write?the?feed",?e);
???}?finally?{
????//close?the?output?writer?(will?flush?automatically)
????if?(out?!=?null)?{
?????out.close();
????}
???}
??}?else?{
???//?woops?..?no?object?found?on?the?stack?with?that?name??
???logger.error("Did?not?find?object?on?stack?with?name?'"?+?feedName
?????+?"'");
??}
?}
?public?void?setFeedName(String?feedName)?{
??this.feedName?=?feedName;
?}
?public?void?setFeedType(String?feedType)?{
??this.feedType?=?feedType;
?}
}
程序很簡單。實現了Result接口,尋找一個與feedName參數匹配的SyndFeed實例,然后轉換為指定的feedType類型,然后通過rome的SyndFeedOutput輸出到Response去。
然后我們給我們的WebWork配置romeResult。
在xwork.xml中配置:






這樣我們就給xwork配置了一個叫做feed的result,它就是我們的romeResult。
然后我們實現一個類,來測試一下這個romeResult。
?*
?*/
package?com.goldnet.webwork.action.news;
import?com.opensymphony.xwork.ActionSupport;
import?com.sun.syndication.feed.synd.SyndCategory;
import?com.sun.syndication.feed.synd.SyndCategoryImpl;
import?com.sun.syndication.feed.synd.SyndContent;
import?com.sun.syndication.feed.synd.SyndContentImpl;
import?com.sun.syndication.feed.synd.SyndEntry;
import?com.sun.syndication.feed.synd.SyndEntryImpl;
import?com.sun.syndication.feed.synd.SyndFeed;
import?com.sun.syndication.feed.synd.SyndFeedImpl;
import?org.apache.commons.logging.Log;
import?org.apache.commons.logging.LogFactory;
import?java.util.ArrayList;
import?java.util.Date;
import?java.util.List;
/**
?*?@author?Tin
?*
?*/
public?class?TestFeedCreateAction?extends?ActionSupport?{
????private?static?final?long?serialVersionUID?=?-2207516408313865979L;
????private?transient?final?Log?log?=?LogFactory.getLog(TestFeedCreateAction.class);
????private?int?maxEntryNumber?=?25;
????private?String?siteUrl?=?"http://127.0.0.1";
????private?SyndFeed?feed?=?null;
????public?TestFeedCreateAction()?{
????????super();
????}
????@Override
????public?String?execute()?{
????????List<News>?newsList?=?getNewsList();
????????if?(log.isDebugEnabled())?{
????????????log.debug("Geting?feed!?and?got?news?"?+?newsList.size()?+
????????????????"?pieces.");
????????}
????????feed?=?new?SyndFeedImpl();
????????feed.setTitle(converttoISO("測試中的新聞系統"));
????????feed.setDescription(converttoISO("測試中的新聞系統:測試Rome?Result"));
????????feed.setAuthor(converttoISO("測試Tin"));
????????feed.setLink("http://www.justatest.cn");
????????List<SyndEntry>?entries?=?new?ArrayList<SyndEntry>();
????????feed.setEntries(entries);
????????for?(News?news?:?newsList)?{
????????????SyndEntry?entry?=?new?SyndEntryImpl();
????????????entry.setAuthor(converttoISO(news.getAuthor()));
????????????SyndCategory?cat?=?new?SyndCategoryImpl();
????????????cat.setName(converttoISO(news.getCategory()));
????????????List<SyndCategory>?cats?=?new?ArrayList<SyndCategory>();
????????????cats.add(cat);
????????????entry.setCategories(cats);
????????????SyndContent?content?=?new?SyndContentImpl();
????????????content.setValue(converttoISO(news.getContent()));
????????????List<SyndContent>?contents?=?new?ArrayList<SyndContent>();
????????????contents.add(content);
????????????entry.setContents(contents);
????????????entry.setDescription(content);
????????????entry.setLink(siteUrl?+?"/common/news/displayNews.action?id="?+
????????????????news.getId());
????????????entry.setTitle(converttoISO(news.getTitle()));
????????????entry.setPublishedDate(news.getPublishDate());
????????????entries.add(entry);
????????}
????????return?SUCCESS;
????}
????private?static?String?converttoISO(String?s)?{
????????try?{
????????????byte[]?abyte0?=?s.getBytes("UTF-8");
????????????return?new?String(abyte0,?"ISO-8859-1");
????????}?catch?(Exception?exception)?{
????????????return?s;
????????}
????}
????private?List<News>?getNewsList()?{
????????List<News>?newsList?=?new?ArrayList<News>();
????????for?(int?i?=?0;?i?<?maxEntryNumber;?i++)?{
????????????News?news?=?new?News();
????????????news.setTitle("測試標題"?+?i);
????????????news.setContent(
????????????????"<p>測試內容測試內容<span?style=\"color:red\">測試內容</span></p>");
????????????news.setPublishDate(new?Date());
????????????news.setId(new?Long(i));
????????????news.setAuthor("Tin");
????????????newsList.add(news);
????????}
????????return?newsList;
????}
????/**
?????*?@return?Returns?the?maxEntryNumber.
?????*/
????public?long?getMaxEntryNumber()?{
????????return?maxEntryNumber;
????}
????/**
?????*?@param?maxEntryNumber?The?maxEntryNumber?to?set.
?????*/
????public?void?setMaxEntryNumber(int?maxEntryNumber)?{
????????this.maxEntryNumber?=?maxEntryNumber;
????}
????/**
?????*?@param?siteUrl?The?siteUrl?to?set.
?????*/
????public?void?setSiteUrl(String?siteUrl)?{
????????this.siteUrl?=?siteUrl;
????}
????/**
?????*?@return?Returns?the?feed.
?????*/
????public?SyndFeed?getFeed()?{
????????return?feed;
????}
????private?class?News?{
????????private?Long?id;
????????private?String?title;
????????private?String?content;
????????private?Date?publishDate;
????????private?String?author;
????????private?String?category;
????????/**
??*?Getter/Setter都省略了,使用了內部類,就是圖個方便
??*?本意是模仿我們常常使用的Pojo,大家的實現都不一樣,我突簡單,里面其實可以有復雜類型的
??*/
????}
}
真是不好意思,Getter/Setter占了大部分地方我省略去了。邏輯很簡單,就是把我們的POJO影射到Feed的模型上面,過程很簡單。我留下了幾個參數可以在外面設置:
maxEntryNumber顯示的feed的條數,鏈接生成時使用的SiteUrl,當然也可以通過request獲取。
下面我們配置我們的Action,注意平時我們可能使用DAO生成newsList,而不是我這個寫死的getNewsList()方法,此時可能需要配合Spring進行IOC的設置,我們這里省略掉。
下面是我們這個Action的xwork配置:
















OK,配置完畢后訪問/news/feed.action就可以訪問到這個feed了。倒入你的feedDeamon,看看,是不是非常簡單?
不過需要考慮兩個地方,一個是編碼問題,看了和東說的中文問題,本沒當回事,結果生成亂碼(我們項目全部使用UTF-8),然后還是轉了一下。沒有研究ROME源代碼,感覺xml不應該有UTF-8還會亂碼的問題呀,也許還需要看看是否是設置不到位。還有就是對于feed如果增加了權限認證則訪問比較麻煩,用feedDeamon這樣的客戶端就無法訪問到了,因為它不會顯示登陸失敗后顯示的登陸頁面,也許放feed就要開放一點吧(當然還是有變通放案的)。
和動例子里面的rome 0.7和現在的rome 0.8相比,Api已經發生了不少變化,唉,開源要代碼穩定還真難。
就這些,就到這里,粗陋了:D