2006年12月18日星期一

MDB应用实例

Message Drive Bean据说是EJB 2.0规范里面最有好的一个特性,可惜一般应用和项目中鲜有EJB的使用,更不谈MDB了。
应用中需要将一些消息发送email到主机,为此配置服务器jms,建立消息的queue和factory(使用工具如WSAD可快速的配置,可以参考developerworks的这篇文章这篇)。创建一SLSB AlertsService,该bean作为jms的工具类bean,提供接口将消息发送到队列。该EJB有对queue和factory的resource-ref。
在EJB中建立jms监听端口(在queue, factory上),创建一MDB OutboundAlerts监听该端口,在onMessage()中处理消息:send emails to host。MDB的目标类型为Queue(只有一个消息接受者)。
这里用jms的原因是:发送email可能延误时间,必须为异步的。相对用多线程的实现异步方法,MDB提供了持久性(只有Topic才有这个功能),更稳定。
这里MDB的onMessage的事务类型为:NotSupported,一般消息的发送者和接受者分别在各自的事务中,不可能在一个事务中。

2006年12月16日星期六

jdk6 has been released!

Tonight I occasionally browsed java.sun.com, It was very surprising that J2SE 6 had been available. COOL! Well Done! It was so quickly that sun pushed the newer edition of jdk.
After going through this page, I found there are not too much exciting features in this release except script.
More detail documents can be found in DevX: Mustang Must-Haves: What's Cool in Java SE 6 and Java SE 6's New Scripting and Compiling Goodies .
You can see another cool stuff is compile API. Even there is a project gosling using compile API as a replacement for ANT. It used java as build language other than xml. Also it used some advanced feature such as annotation, compile API.
The questions for gosling are :
1) the build java file need not be compiled?
2) Is it more quickly than old ANT?
3) Why not use script as a language? Though the java maybe be a better choice than XML, it also has a more complex grammar than XML. With XSL XML can be validated easily. If the java don't be compiled in eclipse, how can I make sure my java is correct? till the run-time? How about script? maybe it is more appropriate.

2006年10月26日星期四

JSP,Velocity,Jelly and Ruby

项目中要动态产生文档,开始用JSP做了个简单原型,后来觉得代码比较混乱,换上了Velocity:一种模板语言。 虽然大同小异,但有些地方还是很方便。比如原来jsp中:
<%=person.getPostion()%>
现在只要$person.position就可以了,少了很多讨厌的百分号。还可以定义function,以及其他一些你能想到的特性。很有点脚本语言的味 道,有如prototype.js的$(),更符合编程思路,大家都喜欢,觉得这样才自然(prototype这里大概从ruby中汲取了灵感)。
还有另外一个类是的库jellyXML-based scripting and processing engine。使用了一种XML和${}的混和语法。比如:

<j:forEach items="${IMPORTS}" var="i" trim="false">
import ${i};</j:forEach>

而velocity则较为一致:

#macro( getBox $thisboxvalue $boxvalue )
#if ( $thisboxvalue == $boxvalue ) a #else b #end
#end

单从语法上讲,jelly类似jsp + jstl。While Jelly has found some success in projects such as Maven, the general consensus is that XML is a poor choice for a programming language even the creator of Jelly apologized at http://radio.weblogs.com/0112098/2004/03/26.html#a472.
其实ANT用XML作语言也有同样的问题。TheServerSide上有篇文章Allen Holub: Just Say No to XML 说明了这一点。 那为什么jsp不做成这样子呢?连rails也是这种讨厌的风格?(还好ruby的语法很方便)个人认为一则效率的考虑:velocity大概有个预先装入、解析、预编译的过程(待考证),还会将结果放在cache中。二则(更主要)多了一种语法,造成复杂和重复。
velocity还有些设计上的亮点:使用中遇到一种情况,从bean中取出属性后要作个代码页转换然后在页面上显示。由于原来bean的getXXX方 法都已经产生好,手工改费力不讨好。这时参考velocity文档时发现有一个get()的方法:当对于属性A,bean不存在getA()方法时,调用 bean的该方法。nice! 让人想到了ruby也有类似设计:Object有个method_missing的方法.也是很好的idea,方法简单但是效果很好。(参考我的blog:Ruby的method的动态特性
从脚本语言看,Groovy之类从语法上更为强大,也更复杂。如果Java有成熟的、广泛接受的脚本语言技术,velocity大概也无立足之地了。
另: Roller Weblogger的模板也是用了Velocity技术。

2006年10月20日星期五

小试Rails RJS

    RJS是Rails1.1的一个ajax的功能点。这里是一个简单明了的turturail。
    这个framework引入了一个rjs类型的文件,当control的function执行后,同名的rjs文件会被执行来动态产生出(或者说是被翻译为)javascript文件。
    rjs可以为如下的形式:
a = "fkp"
page.insert_html :bottom, 'list',
                 content_tag("li", "Fox2")
page.visual_effect :highlight, 'list', :duration => 3
page.replace_html 'header',
                  a + "b"
利用工具(for example:charles)可以得到其产生的response为:
try {
new Insertion.Bottom("list", "<li>Fox2</li>");
new Effect.Highlight("list",{duration:3});
Element.update("header2", "fkpb");
} catch (e) ......
    让人惊奇!我原来以为返回的是xml形式的(ajax必须返回xml吗?)。
    这样的做法产生一个问题就是js的代码量可能过大,当然这里是因为直接调用而显得很简短(application.js就可以放自定义的js function的地方)。这些js基本上是没有变化的,那么能不能直接传参数呢?这样产生出来的js可以cache(google web tools好像是这样做的)。而rjs的做法感觉是对于page.methodxx的返回值每次来个字符串的相加?
    rjs不仅提供了ruby语法的api来简化js的开发,更重要的是将ajax从view曾剥离出来,而且可以访问server端同名function的导出变量(@开头的?这湮没了xml在B/S间的传递)。
    跟其它的ajax框架不一样(大多可以用js直接访问后台的java function),这种方式去掉了访问的接口的定义(method的返回值没有定义),灵活性更强,因为view可能改变频繁。
    用这种模式后,契约存在两个地方:1)rjs和html。rjs用id来对html element控制,一般的ajax模式是把js和html混合写,由于id粒度过低,分开加大了耦合度;2)ruby和rjs之间的context。显然,google web tools解决了这些问题,但是需要讨厌的compile的过程。
    如果用java来实现,rjs模板可以用java写,也可以为脚本语言(jdk6有更好的支持),而主要不同可能是java代码和模板之间的contex的共享:java可能没有ruby实现的优雅。
    模板产生出来的javascriptbroswer端用如下的形式引用:
<script src="/javascripts/prototype.js?1159875410" type="text/javascript"></script>
<script src="/javascripts/effects.js?1159875410" type="text/javascript"></script>
还不知这些number是做何用处。

2006年10月19日星期四

XUL中的UI重用

XUL是Mozilla的界面描述语言,用来解决跨平台的问题。
XUL的Overlays特性令人感兴趣,其描述为:
Overlays are XUL files used to describe extra content for the UI. They are a general mechanism for adding UI for additional components, overriding small pieces of a XUL file without having to resupply the whole UI, and reusing particular pieces of the UI.
有点类似OO中的继承和方法重写。
一般来说,页面级的重用是比较困难的。JSF(组件级的重用)或者jsp tag只是重用了页面的一部分,开发起来也是费周折。

2006年9月21日星期四

XP中的人生哲学

我过去对极限编程一直不是很热忱,抱有很多的偏见。首先就这个名称从字面上而言,极限?好像是噱头,或是旁门左道的东西。
其实里面很多都是朴素的道理。
比如:增量开发。“对于一个婴儿来说,他的力量就是成长的力量。”,“真正的光明决不是永没有黑暗的时间,只是永不被黑暗所掩蔽罢了。真正的英雄决不是永没有卑下的情操,只是永不被卑下的情操所屈服罢了。所以在你要战胜外来的敌人之前,先得战胜你内在的敌人;你不必害怕沉沦堕落,只消你能不断的自拔与更新。”
比如:测试驱动。告诉我们的道理是做事前要先确定好目标,有的放矢。或者“没有反省的生活是没有意义的。”

2006年9月9日星期六

Hibernate乐观离线锁(Appfuse Practise)


一个在Hibernate中的实践操作描述的很好的文档在这里。原理上的描述在Addison Wesley : Patterns of Enterprise Application Architecture Chapter 16里面有详尽的描写,这本Martin Fowler撰写的书称乐观锁定为Optimistic Offline Lock,Hibernate的实现基本上跟这本书里面的一样。里面有副时序图,画的相当的明白。(copy from the book. copyleft.)

另外一本书Hibernate in Action将乐观锁划分为Application Transacation,而悲观锁则划分为Database Transaction(Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!见:Hibernate的reference document)。
Appfuse实践
Appfuse可以generate一个model的CRUD所有jsf页面,这可以作为实践上的起步。然后,version字段应该在页面上不能修改,而是作为一个hidden的字段,这样在显示record详细信息界面上就需要有两个hidden字段:
<h:inputHidden value="#{personForm.person.id}" id="id"/>
<h:inputHidden value="#{personForm.person.version}" id="version"/>
Patterns of Enterprise Application Architecture里面的做法是将version作为session变量,都是可以的。只要这个值是读取对象时取出来的就可以。
这样做后,如果两个不同的用户同时修改一条记录时,hibernate就会报错Row was updated or deleted by another transaction。

2006年8月30日星期三

appfuse(1.9.3)中的几个小问题

1.如果创建了一个新的应用,ApplicationResources_zh_CN.properties在创建过程中被转换为乱码。简单的做法就是用appfuse原来的文件直接覆盖。
2.displaytag_zh_CN.properties没被转换。修改build.xml的copy-resources task:
<!-- Copy any resource or configuration files -->
<target name="copy-resources" depends="prepare"
description="Copy .properties and .xml files from source directory">
<copy todir="${build.dir}/web/classes" includeEmptyDirs="no">
<fileset dir="web/WEB-INF/classes">
<exclude name="ApplicationResources_zh*.properties"/>
<exclude name="displaytag_zh_CN.properties"/>
<include name="*.properties"/>
<include name="*.xml"/>
<include name="*.vm"/>
</fileset>
<filterset refid="variables.to.replace"/>
</copy>
<native2ascii src="web/WEB-INF/classes" dest="${build.dir}/web/classes"
includes="*_zh*.properties" encoding="UTF-8"/>
<generate-database-properties/>
<copy todir="${build.dir}/web/classes" file="database.properties"/>
</target>
3.appgen的ant install的做法
ant install的所做的工作包括了ant[default]的工作,好像文档写的不是很准确。而且这个appgen一股脑的将ibtias and hibernate的dao都产生出来,没有一个设置目标DAO的地方。
忘了在哪里看到的说法:j2ee开发中deploy是一个非常关键的过程,确实真知灼见。
发布过程要有几个特征:
1.方便(自动化的发布);
2.灵活(定制);
3.有升级的功能(持续的升级);
4.速度要快。这样才能减少测试-开发-测试需要的round时间。

4.中文编码的问题
刚开始遇到中文问题的时候,觉得小case:统一将编码改为gb2312,原来就是经常这么干的。后来网上查了会儿,原来大家现在都用UTF-8了,就像google一样,cool!
appfuse本身就是utf-8的配置,问题在于数据库的编码,需要在my.ini中加入:
[mysqld]
default-character-set=utf8
或者mysqld命令行带上参数。
但是ant setup-db后,依然乱码。后来发现新建表的character set为latin1,column也为latin1,god! 都改为utf8后问题解决。而如何让ant setup-db自动创建utf-8的table呢?
所以appfuse的中文问题就是所有地方都要用utf-8编码!

2006年8月16日星期三

continuation in Spring Web Flow

   平心而论,SWF是现在java中对continuation支持最好的和最现实的,虽然这种支持是模拟的。其他的一些框架都有点技术上的理想而造成的不成熟。
    SWF默认使用的控制是SimpleFlowExecutionRepository,这是不支持continuation的,比如浏览器回退、新开浏览器等。如果要支持,要使用ContinuationFlowExecutionRepository。SWF这样的设置是很灵活的,因为一般的应用可以使用SimpleFlowExecutionRepository来避免continuation引起的不必要的开销。
    以swf-sample中的guessnumber为例(guess number是一个经典的例子),修改/web-inf/dispatcher-servlet.xml相关内容如下:
     <bean name="/play.htm" class="org.springframework.webflow.executor.mvc.FlowController">
            <property name="flowExecutor" ref="flowExecutor"/>
     </bean>
    <bean id="flowExecutor" class="org.springframework.webflow.executor.FlowExecutorImpl">
        <constructor-arg ref="repositoryFactory"/>
    </bean>
    <bean id="repositoryFactory" class="org.springframework.webflow.execution.repository.continuation.ContinuationFlowExecutionRepositoryFactory">
        <constructor-arg ref="flowRegistry"/>
    </bean>   
   
如此后,应用就支持浏览器后退键了。这里的后退键是跟后台逻辑关联在一起的:按下后退键,相当于刚才的猜测不算,再猜一次,而计数也“后退”(或者说“恢复”)到前面到状态。如果是购物篮应用,后退就表示“退回”刚才购买的商品。(这种方式也可能造成用户感觉上的混乱)
    这种功能用一般的会话变量(比如将计数存在session中、将购物篮存在session中)是很难处理的。这里的会话变量比如计数、购物篮是一个会话活动中多个步骤的结果的记录,用一个变量是无法记录这些步骤的先后顺序的。而一般的会话变量只是一个步骤中标志或者状态的记录,一个变量就足够使用。
    SWF的解决方法就是在每个步骤中添加一个标志,当这个步骤提交时,后台根据提交上来的标志恢复(在回退的情况下)当时这个步骤的上下文,这样就可以在work flow中继续运行下去。
    例如当步骤1->2->3->4,当浏览器在步骤4时回退时,浏览器会把步骤2提交的数据再次提交。
    用户每次提交,后台都要形成一个上下文的continuation(每次提交的数据都可能不一样),这可能会占用大量的资源。

Spring中Singletons的线程安全

Thread-safe在Java Concurrency in Practice这本书里面的定义是:

A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.

Spring本人不是很熟悉,网上Google了下相关方面的话题,得到的有用的结果很少。可以参考的是:Thread safety, singletons and Spring。还有一篇是:Thread-safe webapps using Spring
基本上,Spring的thread-safe是其API自身的thread-safe。比如一个常见的场景(from appfuse):
public class UserManagerImpl extends BaseManager implements UserManager {
private UserDao dao;
......
public class UserDaoHibernate extends BaseDaoHibernate implements UserDao, UserDetailsService {
......
public class BaseDaoHibernate extends HibernateDaoSupport implements Dao {
这些bean都是Singletons的。
一个类如果没有成员变量,那这个类肯定是thread-safe的,所以UserDaoHibernate的thread-safe取决于其父类。而 UserManagerImpl 的安全性又取决于UserDaoHibernate,最后是HibernateTemplate。可以看出,这里的bean都小心翼翼的维护其成员变量,或者基本没有成员变量,而将thread-safe转嫁给Spring的API。如果开发者按照约定的或者用自动产生的工具(appgen不错)来编写数据访问层,是没有线程安全性的问题的。Spring本身不提供这方面的保证。
或者bean的定义为Singletons="false",也可以参考前面的一篇文章Thread safety, singletons and Spring,用lookup-method。<pro spring> charpter 5介绍的更详细:
Lookup Method Injection was added to Spring to overcome the problems encountered when a bean depends on another bean with a different lifecycle—specifically, when a singleton depends on a non-singleton. In this situation, both setter and constructor injection result in the singleton maintaining a single instance of what should be a non-singleton bean. In some cases, you will want to have the singleton bean obtain a new instance of the non-singleton every time it requires the bean in question.
显然,如果A(Singletons) depends B(Propotype),使用这种方式可以避免A对B的访问并发和争用的问题。
<pro spring>这本书(强于spring in action,后者感觉是本reference book)也对Singletons=“true/false"的选择做了个小结:
使用Singletons的情况有:
1.Shared objects with no state;
2.Shared object with read-only state;
3.Shared object with shared state;
4.High throughput objects with writable state. (synchronizing is need)
使用propotype的情况有:
1.Objects with writable stat;
2.Objects with private state.
与Spring的高度灵活不同,EJB的规范将同步作为一个服务(one of primary services),开发者开编写bean时不必考虑(也不能)线程相关的问题。session bean其分为两类,也有同步上的考虑。
虽然thread-safe的问题总是存在,EJB也没有从本质上解决这个问题,但是其提出了这个问题,并给出了规范。