使用WebWork和Rome轻松暴露RSS

来源:互联网 发布:深圳潮流网络怎么样 编辑:程序博客网 时间:2024/05/12 22:28
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.2Rome 0.8
http://wiki.opensymphony.com/display/WW/RomeResult
参考了和东的这篇Blog,利用rome写rss feed生成程序:
http://hedong.3322.org/newblog/archives/000051.html

首先创建RomeResult类:
Java代码 复制代码
  1. /**  
  2.  *   
  3.  */  
  4. package com.goldnet.framework.webwork.result;   
  5.   
  6. import java.io.Writer;   
  7.   
  8. import org.apache.log4j.Logger;   
  9.   
  10. import com.opensymphony.webwork.ServletActionContext;   
  11. import com.opensymphony.xwork.ActionInvocation;   
  12. import com.opensymphony.xwork.Result;   
  13. import com.sun.syndication.feed.synd.SyndFeed;   
  14. import com.sun.syndication.io.SyndFeedOutput;   
  15.   
  16. /**  
  17.  * A simple Result to output a Rome SyndFeed object into a newsfeed.  
  18.  * @author Philip Luppens  
  19.  *   
  20.  */  
  21. public class RomeResult implements Result {   
  22.     private static final long serialVersionUID = -6089389751322858939L;   
  23.   
  24.     private String feedName;   
  25.   
  26.     private String feedType;   
  27.   
  28.     private final static Logger logger = Logger.getLogger(RomeResult.class);;   
  29.   
  30.     /*  
  31.      * (non-Javadoc);  
  32.      *   
  33.      * @see com.opensymphony.xwork.Result#execute(com.opensymphony.xwork.ActionInvocation);  
  34.      */  
  35.     public void execute(ActionInvocation ai); throws Exception {   
  36.         if (feedName == null); {   
  37.             // ack, we need this to find the feed on the stack   
  38.             logger   
  39.                     .error("Required parameter 'feedName' not found. "  
  40.                             + "Make sure you have the param tag set and "  
  41.                             + "the static-parameters interceptor enabled in your interceptor stack.");;   
  42.             // no point in continuing ..   
  43.             return;   
  44.         }   
  45.   
  46.         // don't forget to set the content to the correct mimetype   
  47.         ServletActionContext.getResponse();.setContentType("text/xml");;   
  48.         // get the feed from the stack that can be found by the feedName   
  49.         SyndFeed feed = (SyndFeed); ai.getStack();.findValue(feedName);;   
  50.   
  51.         if (logger.isDebugEnabled();); {   
  52.             logger.debug("Found object on stack with name '" + feedName + "': "  
  53.                     + feed);;   
  54.         }   
  55.         if (feed != null); {   
  56.   
  57.             if (feedType != null); {   
  58.                 // Accepted types are: rss_0.90 - rss_2.0 and atom_0.3   
  59.                 // There is a bug though in the rss 2.0 generator when it checks   
  60.                 // for the type attribute in the description element. It's has a   
  61.                 // big 'FIXME' next to it (v. 0.7beta);.   
  62.                 feed.setFeedType(feedType);;   
  63.             }   
  64.             SyndFeedOutput output = new SyndFeedOutput();;   
  65.             //we'll need the writer since Rome doesn't support writing to an outputStream yet   
  66.             Writer out = null;   
  67.             try {   
  68.                 out = ServletActionContext.getResponse();.getWriter();;   
  69.                 output.output(feed, out);;   
  70.             } catch (Exception e); {   
  71.                 // Woops, couldn't write the feed ?   
  72.                 logger.error("Could not write the feed", e);;   
  73.             } finally {   
  74.                 //close the output writer (will flush automatically);   
  75.                 if (out != null); {   
  76.                     out.close();;   
  77.                 }   
  78.             }   
  79.   
  80.         } else {   
  81.             // woops .. no object found on the stack with that name ?   
  82.             logger.error("Did not find object on stack with name '" + feedName   
  83.                     + "'");;   
  84.         }   
  85.     }   
  86.   
  87.     public void setFeedName(String feedName); {   
  88.         this.feedName = feedName;   
  89.     }   
  90.   
  91.     public void setFeedType(String feedType); {   
  92.         this.feedType = feedType;   
  93.     }   
  94.   
  95. }  


程序很简单。实现了Result接口,寻找一个与feedName参数匹配的SyndFeed实例,然后转换为指定的feedType类型,然后通过rome的SyndFeedOutput输出到Response去。
然后我们给我们的WebWork配置romeResult。
在xwork.xml中配置:
Java代码 复制代码
  1. <package name="default" extends="webwork-default">   
  2.         <result-types>   
  3.             <result-type name="feed" class="com.goldnet.framework.webwork.result.RomeResult"/>   
  4.         </result-types>   
  5.         <interceptors>   
  6.         <!-- 然后是你的那些inteceptor配置等 -->  
这样我们就给xwork配置了一个叫做feed的result,它就是我们的romeResult。
然后我们实现一个类,来测试一下这个romeResult。
Java代码 复制代码
  1. /**  
  2.  *  
  3.  */  
  4. package com.goldnet.webwork.action.news;   
  5.   
  6. import com.opensymphony.xwork.ActionSupport;   
  7.   
  8. import com.sun.syndication.feed.synd.SyndCategory;   
  9. import com.sun.syndication.feed.synd.SyndCategoryImpl;   
  10. import com.sun.syndication.feed.synd.SyndContent;   
  11. import com.sun.syndication.feed.synd.SyndContentImpl;   
  12. import com.sun.syndication.feed.synd.SyndEntry;   
  13. import com.sun.syndication.feed.synd.SyndEntryImpl;   
  14. import com.sun.syndication.feed.synd.SyndFeed;   
  15. import com.sun.syndication.feed.synd.SyndFeedImpl;   
  16.   
  17. import org.apache.commons.logging.Log;   
  18. import org.apache.commons.logging.LogFactory;   
  19.   
  20. import java.util.ArrayList;   
  21. import java.util.Date;   
  22. import java.util.List;   
  23.   
  24.   
  25. /**  
  26.  * @author Tin  
  27.  *  
  28.  */  
  29. public class TestFeedCreateAction extends ActionSupport {   
  30.     private static final long serialVersionUID = -2207516408313865979L;   
  31.     private transient final Log log = LogFactory.getLog(TestFeedCreateAction.class);;   
  32.     private int maxEntryNumber = 25;   
  33.     private String siteUrl = "http://127.0.0.1";   
  34.     private SyndFeed feed = null;   
  35.   
  36.     public TestFeedCreateAction(); {   
  37.         super();;   
  38.     }   
  39.   
  40.     @Override  
  41.     public String execute(); {   
  42.         List<News> newsList = getNewsList();;   
  43.   
  44.         if (log.isDebugEnabled();); {   
  45.             log.debug("Geting feed! and got news " + newsList.size(); +   
  46.                 " pieces.");;   
  47.         }   
  48.   
  49.         feed = new SyndFeedImpl();;   
  50.   
  51.         feed.setTitle(converttoISO("测试中的新闻系统"););;   
  52.         feed.setDescription(converttoISO("测试中的新闻系统:测试Rome Result"););;   
  53.         feed.setAuthor(converttoISO("测试Tin"););;   
  54.         feed.setLink("http://www.justatest.cn");;   
  55.   
  56.         List<SyndEntry> entries = new ArrayList<SyndEntry>();;   
  57.         feed.setEntries(entries);;   
  58.   
  59.         for (News news : newsList); {   
  60.             SyndEntry entry = new SyndEntryImpl();;   
  61.             entry.setAuthor(converttoISO(news.getAuthor();););;   
  62.   
  63.             SyndCategory cat = new SyndCategoryImpl();;   
  64.             cat.setName(converttoISO(news.getCategory();););;   
  65.   
  66.             List<SyndCategory> cats = new ArrayList<SyndCategory>();;   
  67.             cats.add(cat);;   
  68.             entry.setCategories(cats);;   
  69.   
  70.             SyndContent content = new SyndContentImpl();;   
  71.             content.setValue(converttoISO(news.getContent();););;   
  72.   
  73.             List<SyndContent> contents = new ArrayList<SyndContent>();;   
  74.             contents.add(content);;   
  75.             entry.setContents(contents);;   
  76.             entry.setDescription(content);;   
  77.             entry.setLink(siteUrl + "/common/news/displayNews.action?id=" +   
  78.                 news.getId(););;   
  79.             entry.setTitle(converttoISO(news.getTitle();););;   
  80.             entry.setPublishedDate(news.getPublishDate(););;   
  81.             entries.add(entry);;   
  82.         }   
  83.   
  84.         return SUCCESS;   
  85.     }   
  86.   
  87.     private static String converttoISO(String s); {   
  88.         try {   
  89.             byte[] abyte0 = s.getBytes("UTF-8");;   
  90.   
  91.             return new String(abyte0, "ISO-8859-1");;   
  92.         } catch (Exception exception); {   
  93.             return s;   
  94.         }   
  95.     }   
  96.   
  97.     private List<News> getNewsList(); {   
  98.         List<News> newsList = new ArrayList<News>();;   
  99.   
  100.         for (int i = 0; i < maxEntryNumber; i++); {   
  101.             News news = new News();;   
  102.             news.setTitle("测试标题" + i);;   
  103.             news.setContent(   
  104.                 "<p>测试内容测试内容<span style=/"color:red/">测试内容</span></p>");;   
  105.             news.setPublishDate(new Date(););;   
  106.             news.setId(new Long(i););;   
  107.             news.setAuthor("Tin");;   
  108.             newsList.add(news);;   
  109.         }   
  110.   
  111.         return newsList;   
  112.     }   
  113.   
  114.     /**  
  115.      * @return Returns the maxEntryNumber.  
  116.      */  
  117.     public long getMaxEntryNumber(); {   
  118.         return maxEntryNumber;   
  119.     }   
  120.   
  121.     /**  
  122.      * @param maxEntryNumber The maxEntryNumber to set.  
  123.      */  
  124.     public void setMaxEntryNumber(int maxEntryNumber); {   
  125.         this.maxEntryNumber = maxEntryNumber;   
  126.     }   
  127.   
  128.     /**  
  129.      * @param siteUrl The siteUrl to set.  
  130.      */  
  131.     public void setSiteUrl(String siteUrl); {   
  132.         this.siteUrl = siteUrl;   
  133.     }   
  134.   
  135.     /**  
  136.      * @return Returns the feed.  
  137.      */  
  138.     public SyndFeed getFeed(); {   
  139.         return feed;   
  140.     }   
  141.   
  142.     private class News {   
  143.         private Long id;   
  144.         private String title;   
  145.         private String content;   
  146.         private Date publishDate;   
  147.         private String author;   
  148.         private String category;   
  149.   
  150.         /**  
  151.      * Getter/Setter都省略了,使用了内部类,就是图个方便  
  152.      * 本意是模仿我们常常使用的Pojo,大家的实现都不一样,我突简单,里面其实可以有复杂类型的    
  153. */  
  154.     }   
  155. }  


真是不好意思,Getter/Setter占了大部分地方我省略去了。逻辑很简单,就是把我们的POJO影射到Feed的模型上面,过程很简单。我留下了几个参数可以在外面设置:
maxEntryNumber显示的feed的条数,链接生成时使用的SiteUrl,当然也可以通过request获取。
下面我们配置我们的Action,注意平时我们可能使用DAO生成newsList,而不是我这个写死的getNewsList()方法,此时可能需要配合Spring进行IOC的设置,我们这里省略掉。
下面是我们这个Action的xwork配置:
Java代码 复制代码
  1. <package name="news" extends="default" namespace="/news">   
  2.         <action name="feed" class="com.goldnet.webwork.action.news.TestFeedCreateAction">   
  3.             <!-- 每次生成15条rss feed -->   
  4.             <param name="maxEntryNumber">15</param>   
  5.             <!-- 链接的前缀,我们使用Weblogic是7001,也许你的是8080 -->   
  6.             <param name="siteUrl">http://127.0.0.1:7001</param>   
  7.             <!-- result是feed -->   
  8.             <result name="success" type="feed">   
  9.                 <!-- feed名字是feed,对应我们这个Action中的那个SyndFeed的实例的名字feed,别忘记写getter -->   
  10.                 <param name="feedName">feed</param>   
  11.                 <!-- 制定生成的feed的类型,我这里选择rss_2.0 -->   
  12.                 <!-- rome 0.8支持atom_0.3、atom_1.0、rss_1.0、rss_2.0、rss_0.90、rss_0.91、rss_0.91、rss_0.91U、rss_0.92、rss_0.93、rss_0.94 -->   
  13.                 <param name="feedType">rss_2.0</param>   
  14.             </result>   
  15.         </action>   
  16. </package>  

OK,配置完毕后访问/news/feed.action就可以访问到这个feed了。倒入你的feedDeamon,看看,是不是非常简单?
不过需要考虑两个地方,一个是编码问题,看了和东说的中文问题,本没当回事,结果生成乱码(我们项目全部使用UTF-8),然后还是转了一下。没有研究ROME源代码,感觉xml不应该有UTF-8还会乱码的问题呀,也许还需要看看是否是设置不到位。还有就是对于feed如果增加了权限认证则访问比较麻烦,用feedDeamon这样的客户端就无法访问到了,因为它不会显示登陆失败后显示的登陆页面,也许放feed就要开放一点吧(当然还是有变通放案的)。
和动例子里面的rome 0.7和现在的rome 0.8相比,Api已经发生了不少变化,唉,开源要代码稳定还真难。
就这些,就到这里,粗陋了