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中配置:
- <package name="default" extends="webwork-default">
- <result-types>
- <result-type name="feed" class="com.goldnet.framework.webwork.result.RomeResult"/>
- </result-types>
- <interceptors>
- <!-- 然后是你的那些inteceptor配置等 -->
然后我们实现一个类,来测试一下这个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> newnewsList = new ArrayList<News>();
- for (int i = 0; i < maxEntryNumber; i++) {
- News newnews = 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已经发生了不少变化,唉,开源要代码稳定还真难。
前几天转一个rome的介绍过来,现在拿它写个rss feed生成程序。rome的安装包中,有三个例程,分别是解析已有的rss feed,转换rss feed的格式以及内容聚合方面的,这个程序可以做补充。 程序思路:在更新不很频繁的发布网站上,可以生成一个通过http可访问的静态rss feed文件。为了便于调用,做成一个bean,接一个带有绝对路径的文件名作参数。
可能会遇到一些编码问题,所以可能要用到转换函数,写了几个放在代码末尾
mport java.io.IOException;
import java.net.UnknownHostException;
import java.net.URL;
import java.util.List;
import java.util.Date;
import java.util.ArrayList;
import java.io.BufferedWriter;
import java.io.Writer;
import java.io.FileWriter;
import com.sun.syndication.feed.synd.SyndFeedI;
import com.sun.syndication.feed.synd.SyndContentI;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.io.SyndFeedOutput;
import com.sun.syndication.io.SyndFeedInput;
public class RssCreator
{
public RssCreator()
throws TRIPException, UnknownHostException, IOException
{
}
public boolean genRss(String filename)
{
try
{
。。。connect to database and locat the latest 15 records....
String outputType = "rss_1.0";
String outputFile = filename;
Writer outWriter = null;
try{
outWriter=new BufferedWriter(new FileWriter(outputFile));
}catch (Exception e){
//deal exception
}
SyndFeedI feed = new SyndFeed();
feed.setFeedType(outputType);
feed.setTitle(converttoISO(" 你的站点的名称 "));
feed.setDescription(converttoISO("描述一下站点吧。"));
feed.setAuthor("author name");
feed.setLink("http://www.yoursite.com");
List entries = new ArrayList();
feed.setEntries(entries);
for (int i=0;i<15;i++) {
SyndEntry entry = new SyndEntry();
record = search.record(hits-i);
tfield=record.field("AUTHORS");
sfld = chkValue(tfield.getValue());
entry.setAuthor( sfld );
tfield=record.field("SUBCLASS");
sfld = chkValue(tfield.getValue());
List cats=new ArrayList();
SyndCategory cat=new SyndCategory();
cat.setName(sfld);
cats.add(cat);
entry.setCategories(cats);
//entry.setContents("什么内容呢?");
tfield=record.field("ABS");
sfld = chkValue(tfield.getValue());
SyndContentI desc=new SyndContent();
desc.setValue(sfld);
entry.setDescription(desc);
tfield=record.field("URL");
sfld = chkValue(tfield.getValue());
entry.setLink(sfld);
tfield=record.field("TITLE");
sfld = chkValue(tfield.getValue());
entry.setTitle(sfld) ;
//long ldate=(long)1079511514653;
tfield=record.field("ID");//我数据库中,ID用的是系统时钟
sfld = chkValue(tfield.getValue());
Date pdate=null;
pdate=new Date(new Long(sfld).longValue());
entry.setPublishedDate(pdate);
entries.add(entry);
}
SyndFeedOutput output = new SyndFeedOutput();
output.output(feed,outWriter);
outWriter.close();
...close database....
return true;
}
catch(Exception exception)
{
close database
close file
return false
}
return true;
}
private String chkValue(String s){
if (s==null) return "";
return s;
}
public static String converttoGB(String s)
{
try
{
byte abyte0[] = s.getBytes("ISO-8859-1");
return new String(abyte0, "gb2312");
}
catch(Exception exception)
{
return s;
}
}
public static String converttoISO(String s)
{
try
{
byte abyte0[] = s.getBytes("gb2312");
return new String(abyte0, "ISO-8859-1");
}
catch(Exception exception)
{
return s;
}
}
}
by hedong
不要误会,这里介绍的rome为一个开源java框架,可以在www.java.net上找到它,它是为RSS聚合而开发的一个框架,让你可以快速的开发基于java的RSS阅读,发布器,支持
- RSS 0.91 Netscape
- RSS 0.91 Userland
- RSS 0.92
- RSS 0.93
- RSS 0.94
- RSS 1.0
- RSS 2.0
- Atom 0.3
- Atom 1.0
等标准。你也可以自己扩展他的Module,让他解析你自己定义的XML格式。可以说它是一种用途很广的框架,在使用它之前。你必须下载JDOM开发包,因为它是用JDOM解析XML的。
在Rome中主要包裹下面几个包
com.sun.syndication.feed
com.sun.syndication.feed.atom
与获得atom中各个节点的数据
com.sun.syndication.feed.module
modeule为各个数据模型层。可以自己扩展
com.sun.syndication.feed.rss
用与获得RSS中各个相对应的XML节点中的值
com.sun.syndication.feed.sse
com.sun.syndication.feed.synd
此包为写成RSS格式的XML设置个节点的属性
com.sun.syndication.io
此包为输入输出流。
它的UML图

图1
boolean ok = false;
if (args.length==2) {
try {
String feedType = args[0];
String fileName = args[1];
feed.setFeedType(feedType); //设置rss版本
feed.setLink(http://www.csdn.net); //<link>
feed.setDescription("This feed has been created using Rome (Java syndication utilities");
SyndEntry entry;
SyndContent description;
entry.setTitle("Rome v1.0");
entry.setLink(http://www.csdn.net);
entry.setPublishedDate(dateParser.parse("2004-06-08"));
description = new SyndContentImpl();
description.setType("text/plain");
description.setValue("Initial release of Rome");
entry.setDescription(description);
entries.add(entry);
entry.setTitle("Rome v2.0");
entry.setLink(http://ww.csdn.net);
entry.setPublishedDate(dateParser.parse("2004-06-16"));
description = new SyndContentImpl(); //描述
description.setType("text/xml");
description.setValue("Bug fixes, <xml>XML</xml> minor API changes and some new features");
entry.setDescription(description);
entries.add(entry);
entry.setTitle("Rome v3.0");
entry.setLink("http://www.csdn.net");
entry.setPublishedDate(dateParser.parse("2004-07-27"));
description = new SyndContentImpl();
description.setType("text/html");
description.setValue("<p>More Bug fixes, mor API changes, some new features and some Unit testing</p>"+
"<p>For details check the <a href=\"http://www.csdn.net\">Changes Log</a></p>");
entry.setDescription(description);
entries.add(entry);
SyndFeedOutput output = new SyndFeedOutput();
output.output(feed,writer); //写到文件中去
writer.close();
}
catch (Exception ex) {
ex.printStackTrace();
System.out.println("ERROR: "+ex.getMessage());
}
}
System.out.println();
System.out.println("FeedWriter creates a RSS/Atom feed and writes it to a file.");
System.out.println("The first parameter must be the syndication format for the feed");
System.out.println(" (rss_0.90, rss_0.91, rss_0.92, rss_0.93, rss_0.94, rss_1.0 rss_2.0 or atom_0.3)");
System.out.println("The second parameter must be the file name for the feed");
System.out.println();
}
}
我准备从How 和 Why 这两个角度来谈谈我对创建者模式的理解.
How to implement Factory and Abstract Factory
对于创建者模式, 有一点很容易被大多数人所忽视所误解, 就是Factory模式和Abstract Factory模式的区别.
他们的最重要的区别不是在于Abstract Factory是用于创建产品族, 而Factory仅仅用于创建一种类型的产品.
他们的区别在于一个很重要的思想: 是采用聚合还是继承? Favor Composition than inheritance.
谈到创建者模式大家不自然的就会想到他们的任务都是用于创建对象. 然而真是这样吗? 他们仅仅是用于创建对象?
让我们来看看Gof书中有关Factory的一个例子.
注意到了吗? Application就是一个Factory, 对于不同的Document我们需要扩展不同的Application然后override它们的CreateDocument方法. 然而Application仅仅是创建文档对象吗? 它还做了一系列的工作,将新建的文档对象添加到文档列表并打开文档. 并且还提供了一个完全和创建无关的打开文档(OpenDocument方法)的功能. 看看它和你想象中的创建者模式一样吗? 它可不是仅仅new 一个对象那么简单. 你注意到了吗? 我估计很多人都没有注意到这点, 甚至在吕震宇的文章中.(吕兄可别怪我 :P 谁叫你太有名了)
吕震宇文中的例图
我们只看到了factory方法. 然而在Gof中的图是这样的:
注意到那个 AnOperation() 了吗?
再来看看Gof对Factory模式的评价:
There are two common ways to parameterize a system by the classes of objects it creates. One way is to subclass the class that creates the objects; this corresponds to using the Factory Method pattern. The main drawback of this approach is that it can require creating a new subclass just to change the class of the product. Such changes can cascade. For example, when the product creator is itself created by a factory method, then you have to override its creator as well.
因为Factory模式不仅仅是创建一个对象那么简单,通常还要加上对创建出来的对象做一些操作(比如初始化一些对象的一些属性, 把对象和环境关联起来) 这样就算那些操作不变仅仅是因为创建的对象变了,你就需要新建一个Factory.
For example, when the product creator is itself created by a factory method, then you have to override its creator as well.
这句话有点让人费解,让我们把这句话的演示一下:
Gof的意思大概如图所示.就是说仅仅是因为文档变了我们不仅要为它造一个老爸(MyApp),还要连带为它造一个爷爷(MyAppCreator).怎样才能不造爷爷?使用Abstract Factory.
这时的工厂就完全是为了创建一个新的对象, 而不在工厂中对新建的对象做任何操作.如下图所示:
将创建对象的功能(DocCreator)从对象的其他操作中(Application)完全分离出来作为一个类, 这样我们就不需要为每种类型的文档造爷爷了.
这个是什么模式?
看看Gof的原话
The other way to parameterize a system relies more on object composition: Define an object that's responsible for knowing the class of the product objects, and make it a parameter of the system. This is a key aspect of the Abstract Factory, Builder, and Prototype patterns. All three involve creating a new "factory object" whose responsibility is to create product objects.
可以发现Factory Vs Abstract Factory à Inheritance Vs Composition
最后再来谈谈Why?
Why do we need Creator pattern?
相信很多初学者会有这么一个问题, 为什么我们需要创建者模式?
然而很多人在向别人介绍创建者模式的时候, 常常对于这个问题一带而过.(比如我的老师).
回答: 创建者模式是用来创建对象的模式. 而模式是前人经验的总结,所以创建者模式是一个好东西.
Do you need answer like this? What can we learn about Creator Pattern From this ?
首先对我来说这不是我需要的答案, 并且从中我也仅仅知道了创建者模式是用来创建对象的模式. (晕, 你读读这句话不是废话嘛)
那么我的答案是什么?
用代码说明问题. (源代码有时胜过千言万语)
首先创建了一辆奔驰.
Car car=new Benze();
突然我们的车变了, 变成宝马了. Ok 我修改一下.
Car car=new BMW();
设想一下在我们的代码中散布了无数这样的代码.不止一处(这点很重要)
那么当你以后需要换车的时候, 是不是需要一一修改我们的创建代码把Benze改成BMW.
然后我们再用工厂来实现一下:
Car car=benzeFactory().Factory();
呵呵 这算什么? 没事找事做. 如果要换车,你不是还要修改原来的代码改成下面这样.
Car car=bmwFactory().Factory();
是吗?
如果创建代码只有这里一处可能是这样, 但是如果很多地方都要创建的话就不是了.
CarFactory carFac=new BenzeFactory();
Car car=carFac.Factory();
当你需要换Car的时候你只需修改一处代码就是CarFactory carFac=new XXXFactory();
其他创建车的地方,永远不变,还是Car car=carFac.Factory();
Ok? 你明白了吗?
我们很难避免修改, 但是我们要尽量做到只修改一处.
不知道 这样的解释你是否满意.
使用创建者模式是为了提高代码的可维护性(即应对未来修改的能力).
原文出片:http://www.cnblogs.com/idior/archive/2005/04/14/137913.aspx
考虑一个日志记录工具。目前需要提供一个方便的日志API,使得客户可以轻松地完成日志的记录。该日志要求被记录到指定的文本文件中,记录的内容属于字符串类型,其值由客户提供。我们可以非常容易地定义一个日志对象:
出处:博客园
作者:wayfare