使用WebWork和Rome轻松暴露RSS

本文介绍如何使用WebWork框架结合Rome库生成RSS Feed。通过自定义Result类,实现RSS Feed的动态生成,并配置Action以指定Feed的具体参数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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. }  
/**
 * 
 */
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中配置:
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配置等 -->  
<package name="default" extends="webwork-default">
		<result-types>
			<result-type name="feed" class="com.goldnet.framework.webwork.result.RomeResult"/>
		</result-types>
		<interceptors>
		<!-- 然后是你的那些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. }  
/**
 *
 */
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配置:
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>  
<package name="news" extends="default" namespace="/news">
		<action name="feed" class="com.goldnet.webwork.action.news.TestFeedCreateAction">
			<!-- 每次生成15条rss feed -->
			<param name="maxEntryNumber">15</param>
			<!-- 链接的前缀,我们使用Weblogic是7001,也许你的是8080 -->
			<param name="siteUrl">http://127.0.0.1:7001</param>
			<!-- result是feed -->
			<result name="success" type="feed">
				<!-- feed名字是feed,对应我们这个Action中的那个SyndFeed的实例的名字feed,别忘记写getter -->
				<param name="feedName">feed</param>
				<!-- 制定生成的feed的类型,我这里选择rss_2.0 -->
				<!-- 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 -->
				<param name="feedType">rss_2.0</param>
			</result>
		</action>
</package>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值