论坛首页 Java企业应用论坛

使用WebWork和Rome轻松暴露RSS

浏览 10556 次
该帖已经被评为精华帖
作者 正文
   发表时间:2006-06-05  
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类:
/**
 * 
 */
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中配置:
<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。
/**
 *
 */
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配置:
<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已经发生了不少变化,唉,开源要代码稳定还真难。
就这些,就到这里,粗陋了:D
   发表时间:2006-06-06  
very good!
谢谢楼主!
我正想做这方面的工作,你已经把路都铺好了。
再次感谢!
1 请登录后投票
   发表时间:2007-06-06  
好东西 用你的东西调了一下 发现在RomeResult中设置feed.setEncoding("iso-8859-1");就行了 不用转换也能看到中文。
1 请登录后投票
   发表时间:2007-06-06  
我这里试验的时候,在这行会出错:
SyndFeedOutput output = new SyndFeedOutput();
Rome 0.9 和webwork2.24.
---------------------------------------------------------
可以了,原来是rome需要jdom包的支持,只是出错的时候没有任何提示,感觉很不爽啊。
一开始试的时候也有乱码,按照楼上的说的改了后,果然是立竿见影, 中文可以显示了, 只是不明白为什么用iso-8859-1编码的。
1 请登录后投票
   发表时间:2007-06-11  
请问想让这个生成的rss在ie中正常显示要怎么做 rome好像不能写入xsl的引用 这样ie是无法浏览的
1 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics