2006年4月9日

论剑归来(一)

应朋友邀请,周六早从上海出发往杭州参加阿里巴巴网侠大会。同行四人,有锐道的macro chen、杨光(还是我师弟)、移动的王伟旭(特长是linux和网络安全,也是中国linux推广的先驱)。一路上,言谈甚欢。老庄给我们订的票,他一早肠胃有恙,仍然坚持把票送到火车站,之后去吊盐水,下午又出现在会场。确实精神可嘉,建议阿里巴巴颁发“最佳精神奖”。

到杭州已是中午,错过了上午大会。下午Robbin进行Java技术展望和RoR实现REST的演讲,既然是朋友,肯定是要捧场的。Robbin旁征博引,以其深厚的技术功底和对新技术的敏锐洞察赢得了听众。

晚上一堆人去聚会,各路豪杰纷至:有阿里巴巴的,有自己创业的,有技术大牛,还有媒体(Infoq),出版社(博文的周总领3员大将赴会)。大家互换名片,认识的不免寒暄几句,不认识的也很快就熟捻了,还不时有“原来你就是×××”的惊呼,原来网上就“互通心曲”,只是一直没机会认识罢了。

席间觥筹交错,具体内容暂且不表,只说一件令我感受颇深之事。一个阿里巴巴的员工表现出对公司的无比忠诚,讲起公司的奖惩制度,说是一个员工的绩效不仅跟所在项目相关,还与部门、其它部门甚至整个公司的业绩相关。所以只要是对公司有利的事情,即使与自己现在的工作无关,他们也会去做。按常理来说,这有点不公平,我只能努力做好自己的事情,而如果别人不努力,我就是白做。但如果大家都努力,又变成了共赢。

这里让我讲一个简单的博弈问题,就是“囚徒困境”。AB两个同犯被抓,因为没有其它任何证据和证人,只能让2人分别交供。如果AB都矢口否认,那么两人无罪释放。如果A承认,B不承认;A是坦白从宽,判1年;B抗拒从严,判5年,反之亦然。如果2人都承认,ok证据确凿,各判2年。如果2人都是理性人,且没有互通消息,按照博弈,每个人的最优解就是承认,也就是各判2年。其实对2人真正有利的就是打死不承认然后都无罪释放,而这种状态在理性人的假设下是很难实现的--除非有一个教父,一直灌输他们不要出卖同伙。

马云就是这个“教父”!

卡内基有篇文章,我总结成一句话就是:用崇高的理想打动别人。据说马云一直是以个人魅力及“创造中国电子商务的明天”类似的理想,激励员工的。有了统一的企业文化,员工都不计较个人得失,努力奋进,最终企业和所有员工取得共赢,这绝对是摆脱“囚徒困境”的典型案例。

话说回来,阿里巴巴能让你感受到团队的力量,一群精英在一块做很有价值的事情,对每个人也是很好的锻炼。个人认为,如果有吃苦耐劳的打算,眼光放长远点,又没有其它方面的束缚,阿里巴巴的确是不错的选择。(得向阿里巴巴收代言费,呵呵!)

第二天听了多场论道,主要是SAAS,搜索,分词方面。结合阿里巴巴的战略,我把几点融合起来讲一下。这个下篇再细细道来。

 

 

posted @ 2007-05-21 20:35 pesome 阅读(1116) | 评论 (1)编辑 收藏

sitemesh的问题及解决

再说 sitemesh 的问题,同样是做 openfans 网摘功能出现的。做好了 IE 插件,有 fans 反馈说弹出页面太大,这也难怪,用了 sitemesh head footer 都在页面上,想缩小页面,肯定得把 head footer 都去掉,做个干净点的。既然用了 sitemesh 肯定是希望最方便的搞定这个,而不用动任何逻辑(包括页面逻辑)。这个好办,文档里有说,通过在 sitemesh.xml 中增加

< mapper

           
class = " com.opensymphony.module.sitemesh.mapper.ParameterDecoratorMapper " >

           
< param name = " decorator.parameter "  value = " decorator "   />

           
< param name = " parameter.name "  value = " confirm "   />

           
< param name = " parameter.value "  value = " true "   />

       
</ mapper >

就能搞定,这样只需要使用参数如 saveSnippet.html?decorator=simple&confirm=true ,就能让它使用指定的 decorator 。然后做一个去掉 head footer 只保留必要的 js css 引用的 simple.jsp 页面,同时在 decorator.xml 中配

< decorator name = " simple "  page = " simple.jsp " ></ decorator >

就应该可以搞定。

但怎么测,就是不使用这个 simple decorator 。没办法,只好跟进 sitemesh 源码中去调试,进入 ParameterDecoratorMapper 类,它继承了 AbstractDecoratorMapper 类,然后有一个 init 方法一个 getDecorator 方法。各打一个断点, init 方法在 tomcat 启动就会执行,也正常初始化了, getDecorator 方法却始终不调用,一时想不出办法。看到篇文章说不自己写 sitemesh.xml 而使用自带的 com/opensymphony/module/sitemesh/factory/sitemesh-default.xml 也可以,于是备份并删除我写的 sitemesh.xml ,居然成功了。为什么我写的就不行呢?差别一是我的简化了很多,只有

< mapper

    
class ="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper" >

           
< param  name ="config"  value ="${decorators-file}"   />

       
</ mapper >

和刚加的

ParameterDecoratorMapper ,二就是顺序,我的 ParameterDecoratorMapper 放在了 ConfigDecoratorMapper 后面,会不会是顺序原因呢,调换一下,果然成功了。问题是解决了,原理呢?再花点时间跟一下代码了解下原理,原来使用了 Factory ,它会调用各个配置的 Mapper init 方法,并放入 map 中,然后到时选择一个 Mapper ,如果符合条件就使用,否则继续往下找。这里就有了顺序的问题,因为我在 decorator.xml 中使用了
<decorator name="default" page="default.jsp">

        
<pattern>/*.html</pattern>

    
</decorator>

,这样如果 ConfigDecoratorMapper 放在前面,他就先找这个 Mapper ,一看 saveSnippet.html?decorator=simple&confirm=true ,符合 pattern 啊,就使用它了,另一个 ParameterDecoratorMapper 根本得不到机会,所以也进不了它的 getDecorator 方法。换了顺序就是它先进去,所以执行,其它的 url 也是先找它,但不匹配,就又交给 ConfigDecoratorMapper 了。呵呵,问题也解决了,对 sitemesh 的认识又进了一步。

posted @ 2007-03-11 22:54 pesome 阅读(2978) | 评论 (0)编辑 收藏

中文问题的解决

这次给 openfans 做网摘功能,主体程序倒是很快就写完了,另外要做个 IE 插件,却碰到了不少问题。 IE 插件其实很简单,就是用 js 获得页面的标题、 url 和选择的内容,然后通过弹出窗口,将其送到服务器。这里就有中文的问题了,开始使用 escape ,如 escape(title) 形式, request.getParameter 碰到中文就为 null ,网上搜了一通,说是可以通过 java 编码搞定,但拿到就为 null 了,还怎么换编码?忙活了好几个小时,又是 alert ,又是 document.write ,看上去也没什么问题。不 escape ,直接在浏览器中输入带中文的 url ,拿到的不为 null 了,拿到后,通过 new String(str.getBytes("ISO-8859-1"), "UTF-8"); 还真显示正常了。但用 window.open 又出乱码了。看到文章说还有 encodeURIComponent 方法可用,就试了下,把 escape 换成 encodeURIComponent 居然搞定了,服务端还是得用 new String(str.getBytes("ISO-8859-1"), "UTF-8") 进行处理。注意这里用的 tomcat ,它的默认编码就是 "ISO-8859-1" ,如果改了编码程序也得做相应的改动了。

posted @ 2007-03-11 22:51 pesome 阅读(1028) | 评论 (0)编辑 收藏

hibernate native sql的小技巧

为了性能考虑,使用了 native sql 。因为需要分页,需要 2 sql ,一个获取 list 一个取得总数。获取 list 很好写:

 

 1 private  List getListByNativeSQL( final  Class cls,  final  String sql)  {
 2
 3         return  (List) getHibernateTemplate().execute( new  HibernateCallback()  {
 4
 5             public  Object doInHibernate(Session session)
 6
 7                    throws  HibernateException  {
 8
 9                return  session.createSQLQuery(sql).addEntity(cls).list();
10
11            }

12
13        }
);
14
15     }

16

 

获取总数查了下 hibernate reference, 试了几次才明白用法 :

 1 private  BigInteger getCountByNativeSQL( final  String sql)  {
 2
 3         return  (BigInteger) getHibernateTemplate().execute(
 4
 5                new  HibernateCallback()  {
 6
 7                    public  Object doInHibernate(Session session)
 8
 9                           throws  HibernateException  {
10
11                       return  (BigInteger) (session.createSQLQuery(sql).uniqueResult());
12
13                   }

14
15               }
);
16
17     }

18

这里的

sql 是“ select count(*) 开头的”。这里大家可能要问,为什么要使用 BigInteger ,因为如果用 uniqueResult() 默认就返回 BigInteger ,而 BigInteger cast Integer 会出错。那么如果我就是要返回 Integer 呢,可以通过下面的办法实现:

 1 private  Integer getCountByNativeSQL( final  String sql)  {
 2
 3         return  (Integer) getHibernateTemplate().execute(
 4
 5                new  HibernateCallback()  {
 6
 7                    public  Object doInHibernate(Session session)
 8
 9                           throws  HibernateException  {
10
11                       return  (Integer) (session.createSQLQuery(sql).addScalar( " count " , Hibernate.INTEGER).uniqueResult());
12
13                   }

14
15               }
);
16
17     }

18

大家注意粗体的部分,这里是给一个

alias 赋予类型,那么 sql 就需要变成以 ”select count(*) as count ” 开头了。

posted @ 2007-03-02 10:52 pesome 阅读(5240) | 评论 (2)编辑 收藏

php5+mysql5+apache2需要注意的几点

今天为了在本机装个wordpress玩玩,搞了搞php5+mysql5+apache2。网上搜了一篇文档,很快就让php与apache跑起来了,但连mysql始终不行。报错:Call to undefined function mysql_connect()。查了一下半天,就是php关于mysql的ext没配好,但我改了php.ini啊,也把"extension=php_mysql.dll"放出来了。查了好久,看到一篇说php5需要加上"extension=php_mysqli.dll",试了下果然好了。
     然后需要以index.php作为默认的welcomefile(不知道怎么叫,web.xml里是这个),需要在"DirectoryIndex index.html index.html.var"后加上 index.php就行。
然后飞快的装了phpmyadmin、dvbbs的php版。发现php应用的安装的确很是方便,解压,拷贝到htdocs下,马上就能运行了,比java应用简单的多,更别提复杂的要死的企业应用了。这点上java要好好向php学习啊。

posted @ 2007-01-14 17:34 pesome 阅读(1279) | 评论 (0)编辑 收藏

开始研究电子支付

项目需要,开始研究电子支付。国外的电子支付提供商,得好好研究它的文档和api。全是e文,只能慢慢看了。

posted @ 2007-01-12 15:41 pesome 阅读(1278) | 评论 (1)编辑 收藏

准备给openfans添加spring2.0支持

  学习了下spring2.0。对openfans而言,有2个比较重要的改进。首先是aspectj的支持,可以方便的使用aspectj语法定义aspect和pointcut了,openfans准备在domain object的自动注入上和权限等方面使用aop。另外就是spring form标签库的引入,现在springmvc也有自己的标签库,以前自己给checkbox和radio写的request.getParameter可以改写了。

posted @ 2006-12-28 15:15 pesome 阅读(1127) | 评论 (1)编辑 收藏

一个定时更新cache框架

     摘要: 应项目需要做了一个定时更新的 cache 框架,采用 spring+quartz 很方便的实现,可以适用任何需要定时才更新的地方,比如静态网页 cache 等。代码很简单: ---------------------------------QuartzCacheHandler-------------------...  阅读全文

posted @ 2006-12-13 14:46 pesome 阅读(4119) | 评论 (7)编辑 收藏

这次做portal的一些总结(二)

接着前面的写。上文主要写了 ajax portal 中的使用,这篇写集群方面的体会。现在比较流行的架构就是前端 F5 做负载均衡,后面 2 websphere server 做成集群,各自都有 HttpServer ,每个 HttpServer 都向 2 was 做转发。这样每台都能独立完成从 HttpServer was 的流程。一台出现故障, F5 首先进行切换,只向正常 server HttpServer 发起请求,这台 HttpServer 再进行切换只向同一台 server 上的 was 做转发。这次 portal 就是采用的这种架构,不妨称为架构 A

另一种简单点的架构就是只做 F5 负载均衡,不做 was 集群,每台 websphere server 上的 HttpServer 接受 F5 转发的请求,只向本 server was 转发。这样每台 websphere server 保持独立,相互间没有数据交换和转发。不妨称为架构 B

架构 A B 各有优劣,适合不同的需要,下面进行些比较:

Ø         从应用部署上看:

A 使用了 websphere 集群,由一个 DeployManager 进行分发,部署应用,只需部署一次,由 DM 分发到几个节点上。而 B 每个 server 都是独立的,部署应用只能一台台部署,如果 server 较少差别还不明显,如果达到 10 台以上,一台台部署将是一个比较痛苦的事情。

Ø         session 上看:

A 使用了 websphere 集群,可以使用集群提供的 session 复制,对于一些关键应用(某台服务器宕机, session 也必须保持的应用)很有必要。而对于一些能够允许 session 丢失的应用,才可以使用 B 。当然 A 也可以关闭 session 复制,因为 session 复制不管是使用数据库方式还是内存方式,总会消耗一定的性能。具体消耗多少性能,就要看不同的 application server session 复制方案了,想深入了解,可以看集群方面的文档,我也只记得一个比较简单的 round robbin 了。

Ø         从架构复杂性看:

B 更为简单,因为没有 DM 的概念,每台 server 都保持独立。而使用了 DM 有时也会出现莫名奇妙的问题,这当然是由于不了解 DM 的机制所致,但总归也增加了复杂度,这点在后面的教训中进行说明。

Ø         从水平扩展性上看:

B 肯定更胜一筹。只要 F5 能支持,多少台 server 都没关系。而 A 多台 server 做集群,要看 websphere 支持的节点数量,应该不会太大。这个如果哪位同学知道,敬请告知。

当然 A B 在服务器较多的情况下是可以共存的,可以考虑几台机器做集群,然后集群间做负载均衡,这样既可以减少部署的复杂度,又可以带来较好的水平扩展。由于没做过更大型的项目,这个也只是我的假象,请做过的同学斧正。

 

说一说集群中碰到的问题。

Ø         首先是对各节点的同步:

有时为了方便测试,我们只对其中一个节点进行更改,测试通过再放到其它节点。而如果测试周期较长,有时就会造成节点的不同步,出现各种各样莫名其妙的问题。一个经验就是:无论如何,在每天下班前要保证各节点的同步,不同步的现象不要过夜。

Ø         然后是对 DM 的理解:

我现在还只是实践阶段,没有看过相关文档。从意义上看,它控制了相关的配置文件,如果进行节点同步,就会由它把配置文件同步到它管理的节点上。这对配置文件的修改提出了要求。我们开始只修改节点的配置文件而没有修改 DM 的,结果进行节点同步就会覆盖修改的配置文件,带来很多不必要的工作。经验就是:或者修改 DM 的配置文件,然后进行节点同步,或者直接同时修改所有节点和 DM 的。

Ø         还有关于 cache 的:

Cache 是性能优化的一个有效手段。在单机环境下,最简单的就是内存 cache ,使用 static Map 就行。而在集群环境中, cache 就变的比较复杂了。首先还是从应用需求入手,是否要保持每台机器的 cache 同步。如果只是信息展示等要求不高的 cache ,不需保证 cache 的同步,问题也比较简单,自己写内存 cache ,或者使用开源的 cache 组件如 ehcache,oscache 等就可以很好的解决问题。而如果需要 cache 在几个节点保持同步,就需要特殊的机制了, ehcache 等号称支持分布式 cache ,但好像需要 jgroup ,配置比较麻烦,我没有用过,有用过的同学请指教。我本来想使用 session 保存,然后进行 session 同步,后来 IBM 建议使用数据库 cache ,即自己写代码, cache 在数据库中。这样不需要 session 同步,对象不大,性能也能得到保证,现在用下来效果还可以。

 

posted @ 2006-12-13 13:39 pesome 阅读(3776) | 评论 (9)编辑 收藏

这次做portal的一些总结(一)

这次做 ibm portal ,算是临危受命。做了几个月的 SA 离职,留下一个功能和性能都有很多问题的项目,临时让我顶上。经过一个多月的紧张工作(经常加班,上班上不了网,也没时间上网),总算功能和性能上都能达到客户要求了。而我也由一个不懂 portal 的人,经过项目中实战,不说成为高手,一般的概念、开发、配置、优化等也都有了很多体会。

这次技术上值得推荐的就是合理的使用 ajax ,既加快了首页的 load 速度,又带来了很好的用户体验。开始首页上所有 portlet 都是串行加载,有的 portlet 比如新邮件,依赖于 mail 系统提供的接口。开始这个接口在较大压力下就出现性能瓶颈,后在我们的要求下替换了协议,性能也在 1s-2s 之间。如果采用常规的办法,加上 wps 验证、运算,显示主题、皮肤,加载所有 portlet ,响应时间肯定在 10s 以上。

我在 openfans 中使用了 ajax ,有些经验,所以决定采用异步加载:首页 load 时一些 portlet 直接显示正在 loading 的字样,在 body onload 时再使用 ajax 填充内容;使用 iframe portlet ,也是 src 先指向一个静态的正在 loading 页面, body onload 时再替换 src 到实际地址(这是 ajax 模式的一种)。这样首页登录实际上只经过 wps 内部的验证和显示,所有业务逻辑都是加载成功后再并行进行。实际表现效果就是:头上的主题很快出来,一块块区域显示正在 loading 字样,性能快的 portlet 很快出来,需要几秒的 portlet 随后出来,而不是让用户傻等 10 s 再一下全部显示。

使用 ajax 同时也能解决页面刷新问题和获取返回值的问题。比如前面显示新邮件的 portlet ,用户点击了一封邮件,新邮件数应该减 1 ,刚点击的邮件也应该上页面上消失。原始的做法就是刷新整个页面,既加大服务器压力,又带来很差的用户体验。使用 ajax ,在点击后 1s (或者更长,这取决于邮件系统对点击操作的响应快慢)刷新 div 的内容,用户甚至感觉不到内容已经更新。其它 portlet 也不需要重新载入,大大减轻服务器的压力。有的操作需要提交给其它系统,而且可能成功可能失败,这就需要获得返回值。如果使用普通的 form 提交,需要更新整个页面。而使用 ajax 提交,可以方便的获得其返回值,进而显示不同的提示。

另一个架构上的特点就是 portal 服务器职责单一 。开始所有的业务逻辑都是写在 portlet 里,加重了 portlet 服务器的压力。我进来后做的一个大的规划就是,把业务逻辑抽离到其它 server 上,然后通过 ajax 加载到 portlet 中。这样既可以充分利用服务器资源(新的 server 使用单独的内存空间和线程池),又使得 portal 服务器职责更单一:仅进行验证、权限控制、主题、皮肤和 portlet 的展示。

先写这么多。因为使用了 2 server 做集群,在分布式环境下,开发也有了更多的要求(比如 cache ),后一篇文章再细细道来。

posted @ 2006-11-28 14:49 pesome 阅读(3613) | 评论 (9)编辑 收藏

程序之外

难得有空,写篇程序之外的文章,关于压力的,也是自己近来的亲身体会。

众所周知软件这行压力是很大的。各种各样的问题层出不穷,每天上班工作内容都是排的满满的,遇到突发问题就得加班。如果不及时进行疏解,积累到一定的程度,就可能产生一定的负面问题,比如上班精神状态差、注意力不能集中、遇事喜欢逃避等等。我就亲身经历了这样的状况,明知自己工作积极性差、效率很低,但也很难一下子找回自我。

一次偶然的出游让我从中很快走了出来。一个亲戚考上厦大的博士,我请了 2 天的假,利用周末时间顺便去厦门旅游。厦门依山傍海,的确是旅游的好去处。晚上到海边,凉风习习,光脚沿着沙滩走过,任起落的潮水在腿上脚上留下层层薄沙。内心也变得平静,能够感受到海的呼吸。天地间仿佛只剩下我和大海,在进行心灵的交流,俗世烦扰皆抛诸脑后,只剩下对海的依恋。白天去爬南普陀山,并不太高,慢慢爬到山顶,整个思明区尽收眼底,远处一艘快艇在海面掠过,留下一条美丽的浪花。然后顺山而下到植物园,途径无数奇花异草、层天老树,走得累了,找个湖边石凳休息一下,人也觉得轻松愉快。

经过大自然的洗礼,回到单位,人的精神面貌焕然一新,抱着积极的心态处理事情,很多问题迎刃而解。压力测试做的很累,经常要熬夜,但通过一轮轮的测试,逐步定位到性能问题所在,自己也学了不少相关知识,想想也就没那么烦了。

做事的方式,也有了长进。我现在信奉人一时只做一件事效率最高的原则。事情再多,也是一件件做,每天安排好近日的工作,并排个优先级,什么是要亲自处理的,什么是让别人处理的,什么是需要预先通知他人的,需要什么资源,每件事情的预计时间如何,需要如何 check 等等。做好一件事就打个勾,做到心中有数。如果事情有延误,分析是什么原因,该如何补救,而不要有太大的心理负担,自己尽力了就好,是自己的责任就要勇敢扛下,死不了人的。这其实是很简单的原则,谁都能够学会,但的确很管用。

总结:压力是无处不在的,关键在于如何应对和排解。用积极的心态和恰当的方法面对,压力也就没那么大了。感觉压力积累到一定程度,在还未影响正常工作之前就先想办法排解,出去旅游、运动等都是缓解压力的好办法。

posted @ 2006-11-06 22:27 pesome 阅读(1152) | 评论 (2)编辑 收藏

关于代码的一些比喻

最近对项目组的一些较差的代码进行了些重构,同时灵光一闪,对代码有些比较形象的比喻。

坏的代码就象揉面团,管什么接口什么实现全揉成一团,一个方法几百行,注释写再多也是面团(夹了些小纸条而已)。然后需要重用了,就是从中抓起一把面团,然后放到其它的面团里继续揉。这样重复代码一堆,什么易读性、扩展性、可维护性都是无从谈起。

好的代码就象堆积木,接口实现定义清清楚楚,每个接口只做一件事情,重复代码都是通过更细的接口来消除。重用就是把积木块往该放的地方堆,这样的代码,几个大块几个小块一目了然,只要方法命名规范,连注释都可以省去。这样耦合性低,易读性、扩展性、可维护性都可以得到保证。

把面团变成积木并不复杂,定义好模具,面团一团团往里面填充,待稳定下来,就成了一块块积木。这里关键就是模具的制作,推荐制作宝典: martin fowler 的那本重构。还得有模具的丈量工具,就非 junit 莫属了。

posted @ 2006-10-30 17:42 pesome 阅读(1464) | 评论 (0)编辑 收藏

数据库同步trigger的记录

    项目需要写了几个数据库同步用的 trigger ,就是记录用户的操作到一个 temp 表,然后每天通过 webservice 同步到其它系统,同步成功清空该 temp 表。自认为写的还行,做个记录。是 db2 的。

 

-- 用户组新增触发器

--DROP TRIGGER TG_USERG;

CREATE TRIGGER LIBING.TG_USERG AFTER INSERT ON LIBING.TM_USERG

  REFERENCING NEW AS NROW

  FOR EACH ROW

  MODE DB2SQL  

  BEGIN ATOMIC

 

  declare @groupId integer;

  declare @name varchar(30);

  declare @descn varchar(100);

  declare @syntype varchar(4);

  declare @ddlsql varchar(1024);

  declare @isprimary char(1);

  declare @updateTime timestamp;

  declare @createTime timestamp;

  declare @createBy integer;

  declare @updateBy integer;

  declare @groupType integer;

  declare @adminType integer;

  declare @appId integer;

 

  declare @oldGroupId integer;

 

  set @groupId=NROW.GROUP_ID;

  set @name=NROW.name;

  set @descn=NROW.descn;

  set @syntype=NROW.syn_type;

  set @ddlsql=NROW.ddlsql;

  set @isprimary=NROW.isprimary;

  set @updateTime=NROW.update_time;

  set @createTime=NROW.create_time;

  set @createBy=NROW.create_by;

  set @updateBy=NROW.update_by;

  set @groupType=NROW.group_type;

  set @adminType=NROW.admin_type;

  set @appId=NROW.app_id;

 

  INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'INSERT');

  END;

 

  -- 更新用户组数据的触发器

 -- DROP TRIGGER TG_USERG_UPDATE;

  CREATE TRIGGER TG_USERG_UPDATE AFTER UPDATE ON TM_USERG

              REFERENCING NEW AS NROW

               FOR EACH ROW

               MODE DB2SQL

               BEGIN ATOMIC

               

               declare @groupId integer;

              declare @name varchar(30);

              declare @descn varchar(100);

              declare @syntype varchar(4);

              declare @ddlsql varchar(1024);

              declare @isprimary char(1);

              declare @updateTime timestamp;

              declare @createTime timestamp;

              declare @createBy integer;

              declare @updateBy integer;

              declare @groupType integer;

              declare @adminType integer;

              declare @appId integer;

               

               set @groupId=NROW.GROUP_ID;

              set @name=NROW.name;

              set @descn=NROW.descn;

              set @syntype=NROW.syn_type;

              set @ddlsql=NROW.ddlsql;

              set @isprimary=NROW.isprimary;

              set @updateTime=NROW.update_time;

              set @createTime=NROW.create_time;

              set @createBy=NROW.create_by;

              set @updateBy=NROW.update_by;

              set @groupType=NROW.group_type;

              set @adminType=NROW.admin_type;

              set @appId=NROW.app_id;

               

               -- 如果已经有 update 则只记录最后一条 update

               IF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='UPDATE') THEN

                     UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,

                                   NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,

                                   ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,

                                   CREATE_TIME=@createTime,CREATE_BY=@createBy,

                                   UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,

                                   ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='UPDATE'

                                   where GROUP_ID=@groupId AND ACTION='UPDATE';

               -- 如果有 insert 则把后面的 update 当作 insert

              ELSEIF  EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId AND ACTION='INSERT') THEN

                     UPDATE TM_USERG_TEMP SET GROUP_ID=@groupId,

                                   NAME=@name,DESCN=@descn,DDLSQL=@ddlsql,

                                   ISPRIMARY=@isprimary,UPDATE_TIME=@updateTime,

                                   CREATE_TIME=@createTime,CREATE_BY=@createBy,

                                   UPDATE_BY=@updateBy,GROUP_TYPE=@groupType,

                                   ADMIN_TYPE=@adminType,APP_ID=@appId,ACTION='INSERT'

                                   where GROUP_ID=@groupId AND ACTION='INSERT';

              ELSE      INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

                              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

                               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'UPDATE');

              end if;

               END;          

 

 

-- 删除用户组触发器

--DROP TRIGGER TG_USERG_DELETE;

CREATE TRIGGER TG_USERG_DELETE AFTER DELETE ON TM_USERG

         REFERENCING OLD AS OROW

         FOR EACH ROW

         MODE DB2SQL

         BEGIN ATOMIC

        

         declare @groupId integer;

        declare @name varchar(30);

        declare @descn varchar(100);

        declare @syntype varchar(4);

        declare @ddlsql varchar(1024);

        declare @isprimary char(1);

        declare @updateTime timestamp;

        declare @createTime timestamp;

        declare @createBy integer;

        declare @updateBy integer;

        declare @groupType integer;

        declare @adminType integer;

        declare @appId integer;

               

         set @groupId=OROW.GROUP_ID;

        set @name=OROW.name;

        set @descn=OROW.descn;

        set @syntype=OROW.syn_type;

        set @ddlsql=OROW.ddlsql;

        set @isprimary=OROW.isprimary;

        set @updateTime=OROW.update_time;

        set @createTime=OROW.create_time;

        set @createBy=OROW.create_by;

        set @updateBy=OROW.update_by;

        set @groupType=OROW.group_type;

        set @adminType=OROW.admin_type;

        set @appId=OROW.app_id;

        

          -- 如果没有操作记录,则插入 delete 记录

          IF NOT EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId) THEN

              INSERT INTO TM_USERG_TEMP(GROUP_ID,NAME,DESCN,DDLSQL,ISPRIMARY,UPDATE_TIME,CREATE_TIME,

              CREATE_BY,UPDATE_BY,GROUP_TYPE,ADMIN_TYPE,APP_ID,ACTION) VALUES (@groupId,@name,@descn,

               @ddlsql,@isprimary,@updateTime,@createTime,@createBy,@updateBy,@groupType,@adminType,@appId,'DELETE');

               

               -- 如果有 insert 记录,则整体结果相当于没有进行任何操作

               ELSEIF EXISTS(SELECT GROUP_ID FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT') THEN

                            DELETE FROM TM_USERG_TEMP WHERE GROUP_ID=@groupId and ACTION='INSERT';

               -- 如果没有 insert 记录,则只需记录最后的 delete 操作

               ELSE

                       UPDATE TM_USERG_TEMP set ACTION='DELETE' where GROUP_ID=@groupId;

          END IF;

         

          END;

posted @ 2006-10-30 17:06 pesome 阅读(1407) | 评论 (0)编辑 收藏

关于重用

在项目中碰到一些重用上的问题,有些想法,就先写一点。

重用应该是高层的复用,逻辑的复用,接口的复用,而不是具体实现的复用。 我们项目开始讲复用,就是大家把别人的代码拿过来,可用的地方就用,不同的地方改改,结果问题一堆。说到底就是接口没有定义清楚的,很多该复用的逻辑隐藏在了具体的实现中。这样导致无法进行接口的复用,转而使用具体的实现复用。从程序员的角度看,他们总会使用成本最小的方法完成任务。所以我们要时刻思考如何能让最正确的方法在他们看来同时也是成本最小。

这里有一个较为简单的办法,就是尽量使用方法封装实现,使接口的粒度最小。如果一个实现需要几百行,且其中包含多个逻辑,就最好抽取出多个方法,然后在主体接口内进行调用。这样的代码逻辑清晰易读,可重用性也高。看看大师们对代码的不断重构,很大程度上就是重构出粒度最细,复用性最高的接口。

如何达到最大程度的复用,其实是非常复杂的问题,还需要在今后的项目中不断体会。

posted @ 2006-10-15 23:13 pesome 阅读(474) | 评论 (0)编辑 收藏

最近做压力测试的总结

最近做 portal 的压力测试,一个字“累”。其中犯了不少错误,白白加了几天班,也有一些体会,就记录下来,希望对大家有所帮助。

首先讲压力测试环境。这个很是关键,我们就是在这个上面吃了苦头。我们用的 loadrunner ,原理也很简单,一台主控机,控制多台客户机,模拟并发用户访问应用。然后需要能实时监控各相关应用服务器, ldap 服务器等的性能。这里每台客户机最好能使用同样的配置,使用足够带宽的网络,给予同样的负载(模拟同样数量的用户)。同时要注意监控客户机的 cpu 和网络状况,时刻保证 cpu 和网络利用率低于 100% 。我们犯的很大错误就是使用各自的笔记本,而且都使用的是一个 10M hub 牵出的网线,这样导致实际的网络阻塞,既没有给予服务器足够的负载,又导致报告的响应时间比实际更长,从而带来了后续很多的无用测试。

然后讲测试方法。用的较多的还是持续压力测试,就是持续给予服务器一段时间的并发量(一般为 5 10 分钟),然后看平均响应时间是否在可以接受的范围内。这个“可接受”要视应用类型和实际的并发用户而定,如何估计并发就要靠经验了。对于 portal 而言,由于要与众多的应用接口,如进行 SSO ,获取数据等,有很大程度也依赖于其它应用的性能,其性能要求不会太高。我们测试首页的性能,在放上全部的 portlet 的情况下, 100 个并发的平均响应时间就在 17s 左右,这肯定是不能接受的( 10s 只能算勉强可以)。接下来就是发现性能瓶颈,并尝试进行优化了。

初步的发现瓶颈的方法也很简单,通过对 portlet 的增减,发现最影响性能的 portlet ,然后不断优化,直至达到可以接受范围。发现瓶颈所在了,就得进一步确定是什么原因:是我们本身程序的问题,还是其它应用接口性能不佳等等。这里光靠猜是不行的,要讲数据讲事实,记录时间日志就是简单有效的办法,我们对各个时间点打印了日志,比如 doview 方法的全部执行时间, jsp 的载入时间,具体接口的执行时间等。有些接口可能在压力较小的情况下性能不错,而在大压力情况下出现性能隐患,所以一定要在进行压力测试后查看日志。我们就是这样发现了性能隐患,同时更进一步对各方面进行优化,直至达到客户可以接受为止。

由于不是专门的测试人员,很多地方都是实际项目中的体会,也没什么理论基础。有什么不对的地方,大家多交流。

posted @ 2006-10-15 23:13 pesome 阅读(2493) | 评论 (1)编辑 收藏

项目应该提倡的一些做法

接着昨天的写。今天写我认为的一个 javaEE 项目中应该提倡的做法。

1.       开发流程尽量简化,采用迭代增量的模式,做适合项目需要的文档。很多时候千言不如一图,原型开发我认为也非常重要。

2.       采用成熟的框架, ssh 组合或更多 full-stack 的框架如 seam 等都是不错的选择。如果一定要用公司的框架,至少 SA 要非常熟悉这个框架,在出现问题时要能快速的解决。

3.       对业务的分析做到越细越好,如果有条件让更多的开发人员参与业务的分析,同时形成项目通用的业务语言(实在不行,精简的 user story 也可以)。对于每个达成共识的业务都要能记录下来,并能方便的进行查阅。业务模型和业务规则要始终与当前需求、代码和数据库保持一致。

4.       在团队的建设上,需要更多的投入。不要为了节约成本,让很多程序员老后面才加入团队。一个稳定、团结、有冲劲的团队能比松散而人数更多的团队,完成的更快更好。然后要加强沟通,比如每天开个小的茶话会,大家交流下各自的工作情况,有什么困惑和疑难,提出来大家一起解决,避免大家各自做相同的逻辑(很多东西经过抽象可能就是一个)。在工作之余大家一块吃吃饭,打打游戏等都是增进感情的好方法,大家彼此熟悉了,工作上也能更好的协作。

5.       对程序员要有更高的要求, SA 有责任让程序员了解更多的东西,如面向对象的 5 大原则、一些模式、 junit 、重构等,这些其实并不是什么高深的东西,仅仅是掌握一些方面也能对代码质量和开发中的愉悦度产生很大促进。要激发他们对技术的热爱和对代码质量的追求,因为最终受益的还是他们。 XP 所提倡的结对编程也是快速进行知识传递的好办法。

6.       采用 wiki 进行项目进度跟踪和一些文档的展示。这次用 excel+cvs 的方式感觉很是麻烦,在 spring 翻译中我们采用 wiki 的方式就感觉很好。

暂时先想到这么多,有更多体会,再来补充!

posted @ 2006-09-21 15:59 pesome 阅读(1019) | 评论 (2)编辑 收藏

用Maven做项目管理

     摘要: 在程序员发表的一篇maven文章,跟大家共享。用 Maven 做项目管理 在 Java世界中我们很多的开发人员选择用 Ant来构建项目,一个 build.xml能够完成编译、测试、打包、部署等很多任务,但我们...  阅读全文

posted @ 2006-09-21 11:37 pesome 阅读(18664) | 评论 (10)编辑 收藏

从错误中学到更多

开发进行到尾声了,但 bug 仍然层出不穷。总的来说,算是一个比较失败的项目,原因很多,有外在因素也有我作为一个 SA 不可推卸的责任。正好借加班的时间写点总结,也算是在失败总吸取教训,从错误中感受更多吧。

首先是开发流程。我是 xp 的坚定支持者,但在项目中由于外界原因还是采用了传统的开发流程,没有迭代,就是需求 -> 设计 -> 程序员进场开发 -> bug 。由于程序员进场时间较晚,一上来就开始开发,没有时间进行培训和团队的融合。然后开发中缺少沟通,就是一个人负责一块,开发完了再做其它。结果开发到现在,还有人不清楚我们项目的全貌,到底是为了解决什么业务。

然后是开发框架。使用了公司的框架,而我们作为 SA (我们是双 SA ),都是第一次接触,程序员也就一个人用过。我们最早是达成共识采用 SSH 的组合(我至少还算是了解吧,其它人也都用过),但由于上层因素没有实施(这也导致我好长一段时间进入不了状态)。开发前期大家都在探索这个框架(的确很难用,出错机制较差,配置文件很多,耦合较强 ... ),在一堆莫名奇妙的问题中摸索前行,花费大量的精力。而比较搞笑的是,在大家开始学习这个框架之时,我作为 SA ,因为要写一堆只为应付客户的设计文档(后面就没人看过),错过了和大家共同进步的机会,后面总是感觉“低人一等”。

在业务方面也存在很多问题。很多业务逻辑并没有以很好的载体保存下来,在需求文档中很多逻辑并没有体现。我维护了一套 pd 的业务模型,从概念模型 -> 物理模型 -> 数据库,这解决了后面的一些沟通问题,但由于更多体现的是静态的实体及关联,对于一些动态的业务流程没法体现。我们 SA 之间有时在一些问题上的理解还存在分歧(讨论过也达成过共识,但没有记录下来,后面可能就忘了),程序员就更是无所适从。谈到这,我更感受到 DDD 这本书的价值,他所提倡的开发人员参加模型的讨论,形成项目的模型语言,并不断随着业务进行演化。。。好多理念都是项目经验的结晶啊。

在开发管理上我也是无所作为。 Junit 都没有推广下去,更别说 TDD 了,这也与框架相关,它就没提供写 test case 的地方,等我搞明白一堆配置文件,做出脱离 web 容器的 test 框架,都开发一大半了,说起 test 的好处,大家也表示不理解(或者表示理解但没时间 = 没理解),就让他们慢慢 debug 吧!代码的质量也没有保证,程序员不明白代码的味道,更别说理解重构的意义以及进行恰当的重构了。一个函数写上 100 多行,什么逻辑都混在一块,但由于时间较紧,我也只好睁一只眼闭一只眼“功能完成就行吧!也不是我一个人在管”,到现在很多代码混成一团,展现层直接调用 dao (又是框架惹得祸),相同的逻辑 copy n 处,我也是后悔莫及。

今天先写失误,明天写从中学到的东西,从错误中学到的也许更多!

posted @ 2006-09-20 21:24 pesome 阅读(882) | 评论 (1)编辑 收藏

对领域模型的认识

最近看了看领域模型驱动这本书,只看了前面几章,但也深切的感受到了模型的重要性。通过与代码同步的模型,能够维护一个很好的知识共享的空间,包括设计者与程序员之间,客户与设计者之间 …… 而且模型应该尽可能简单,让不同背景的人都能够很快学会,并都能对模型有所增益。

那么这个模型应该是什么样的?书我没有细看,只说说自己的体会。关于设计,很早就有数据驱动和对象驱动的提法。在 Without EJB 里, Rod 也有讲:数据驱动或者说面向数据库设计更成熟,工具更多;而对象驱动更符合面向对象程序的特性,但由于掌握的人较少,风险较大。而通过模型驱动,我认为很大程度填补了 2 种方式的鸿沟,核心是模型,具体是对象模型还是数据模型并不重要,重要的是这个模型能够与需求、代码、数据库保持一致。

说到这里,顺便谈一谈我对文档的理解。我一直是 XP 的坚定支持者,甚至有点偏执。而由于文档不易阅读和沟通,且经常会出现与设计和代码的脱节,导致其可读性更差,所以我一向对文档不大感冒,更倾向于使用代码说话。但在目前的公司项目中,由于更多采用传统的软件过程,我也写了很多的文档,包括需求规格说明书、概要设计文档、详细设计文档等等。从对项目的帮助来看,文档作用并不太大,或者说是付出收益比太低,更多的是给客户写的,而不是给程序员写的。从程序员的需要来看,他关心的是每个实体的属性和关联,核心的接口、输入和输出,页面间的跳转和数据流,然后有一个统一的框架和编程模式。我的体会是:如果以文档为核心,很难描述清楚这些东西,且难以应对变化。

而通过以模型为核心(项目现在采用的 power designer 的概念模型为基础),辅以适当的描述,既能够加快大家对项目的认识(程序员是后面才加入),又能够节省一些写文档的时间,更早投入开发。

说到 power designer ,我也比较惭愧。用了好久,一直只是把它当成看数据库的工具。项目一开始就是从物理模型入手,结果举步维艰。后面从概念模型入手,就感受到了它的好处。使用概念模型,不用考虑太多关联表、外键什么的,而是从实体出发,然后确定相互间的关联,是一对一、一对多还是多对多。然后自动转成物理模型,并直接与相应的数据库挂钩。从这点上看与从对象设计出发真的非常相似。其实这也是合情合理的,正体现了这个世界的统一性吧(物理学界不也在搞什么统一场理论的证明吗)。 Power designer 也做了 conceptual model, physical model, object-oriented model xml model 的自动转换,我现在还没全部摸熟。

openfans 则是从对象入手,并通过 hibernate 建立与数据库的联系,也体现了一定的方便灵活性。但比较糟糕的是,只有代码和配置文件,没有清晰的便于交流的模型,谁要想参与只能先去慢慢看代码。所以我先通过 together reverse 出来一个类图,然后适当加以文字进行说明。类图已经做好,但比较乱,还需要更多的图例加以说明。文字说明就是下一篇 blog 的工作了。也算是预告吧!

posted @ 2006-08-13 17:41 pesome 阅读(3630) | 评论 (3)编辑 收藏

项目感悟

近来在一个项目做 SA ,也是第一次做比较大的项目的设计,感觉比较吃力。同时又要参与 spring 文档的翻译,一直没时间写 blog 。今天终于有点时间,就写一下最近的感悟。

首先是不适应。要参与需求阶段,因为需求初期并不确定,客户都不清楚他们需要什么东西,只是有一个很模糊的概念。我们得不断调研、讨论、出方案、出原型 …… 而这都是我比较不擅长的。还好有个职务较高的老大带着我们,才能逐渐把需求理顺。我也从他身上学到不少,准备写一篇“如何做需求”,但毕竟是第一次做较大的需求,理解还不很深刻,怕贻笑大方,所以只拿 MindManager 列了个提纲。

其次还是不适应。项目开始好几个月,没写过一行代码。项目没有采用 XP 的方式,而是普通的瀑布。需求就做了几个月,然后做概设、详设。我是 XP 的支持者,所以对这种方式持反对态度,但老大不同意,没办法!写文档,我也是很不情愿,但转念一想: Rod Without EJB ,但他 ejb 的理解比谁都深,什么方式都实践下可能更好。由于同时在看 Joel on software ,他对需求规格说明书却很是强调,我也就听听大师的话,好好写需求,顺便把他的一招用上了 ------- 写的有趣点,就当写故事吧。

最后还是不适应。以前做程序员,可以好好研究很多东西,现在不行了。有个 xml bean 转换的技术要解决,我能研究不?不行,我得写文档,这种比较 detail 的事情得给程序员做。看着程序员兴高采烈的比较各种开源工具,最后选定 JIBX openfans 发挥了一定的作用),然后跟我讲这个如何如何好,我只有附和的份。

讲到这里,让我想到一则小故事:有一个学钢琴的拜一个牛人为师。牛人交给他一个曲谱,说:“回去练好,一个月再过来。”他好歹把这个曲练熟了,还想展示一下,牛人又交给他一个更难的曲谱,又是同样的话。他只好回去继续苦练,每次都感觉不适应。这样往返多次,他忍不住了,问牛人:“你是不是故意整我,每次都给我更难的,还不给我表现的机会”。牛人让他把上次的曲弹弹,他感觉不错,让弹再上次的,更是轻松,最后弹第一次,他弹的是出神入化。他明白了!

大家都明白没:只有不断的感到不适应,才能进步。如果一切感觉良好,没什么挑战,就该考虑。。。。。。(此处省略 2 字)了。

posted @ 2006-08-11 18:34 pesome 阅读(1064) | 评论 (3)编辑 收藏

结合openfans谈算法的重要性

不经意看到了程序员的一期算法专题,细细研读多位高手(包括李开复)的文字之后,对算法的重要性重新进行了反思。我研究生毕业 2 年,一直从事 J2EE 开发,由于项目的原因,很少需要自己去设计算法,甚至 stack tree 这些数据结构都很少使用。还好自己也不甘于平淡,如 Effective Java Practical Java Refactory Design Pattern 等等这些流行书还是抽空学习,这些书的确很是经典,对我的编码风格,模式的理解,设计能力都起到了很好的促进。也快速的由一个程序员成长为架构师(只是公司的,离真正的架构师还差得远)。

因为项目需要,去年下半年开始全面接触开源软件,使用了 spring maven hibernate ibatis 等众多开源软件,也对开源软件产生了浓厚的兴趣,于是拿这些开源软件做了 openfans ,一方面是推进开源软件在中国的使用的交流,一方面也为自己在实践中更多使用这些软件(因为没有项目和利益因素,可以做想做的事,用想用的软件)。使用这些开源软件倒很是顺利,很多软件拿来就能用,都有 sample ,简单使用还是不难的。

但一些关键的问题一直悬而未决!比如 tag 的设计:我现在简单的使用平铺的模型, tag 没有层次之分, tag 间产生双向关联。但这样是最符合 tag 特性的模型吗?如何对这些 tag 进行分类,如何定义 tag 的多级关联(如 spring hibernate 有关联, hibernate 又与持久层关联, spring 是否与持久层有间接关联,依次类推)。。。。。。而做出一个好的 tag 模型,可能就需要图论方面的知识。再比如用户相似度设计(号称是豆瓣的核心,难以复制):每个用户拥有了一些 tag ,如何根据这些 tag 定义用户的相似度,一个用户有 spring hibernate 2 tag ,一个用户有 spring ibatis 2 tag ,他们相似度为多少,如果每个人 tag 都很多,再加上权重的概念,问题又复杂的多。简单的做法就是每个用户 tag 一个个匹配,匹配的越多相似度越大,但这样设计一是不准确,二是时间复杂度很大,最坏情况为 n*n*m*m n 为用户数, m 为每个用户的 tag 数。

这些都需要扎实的算法基础。而我的基础就很薄弱:本科学的比文科还文科的专业,研究生又学的比较上层的东西( UML RUP PM 等,也都一知半解),选修了一门算法导论,又被 1000 多页的经典英文教材吓趴下了,上了几次课就直接放弃,没敢参加最后考试。现在想临时抱佛脚,谈何容易。

所以算法也并非没有用处,关键要看你在做什么,想做什么。想去 google 、百度不用会 spring ,算法基础扎实,只会 c 语言都行;一些行业如电信、金融也很是需要算法高手。而国内更多的企业做企业应用,一般是连连数据库,写写页面,最多引入些开源框架和软件,如 spring hibernate struts 等。这方面的需求较大,会了 spring ,省了公司的培训成本,自然还是给找工作加了一些砝码。

所以有时听到某些人对某项技术不以为然,说“这些东西有什么是我在几个星期学不会的”的时候,一方面是对其狂妄进行些鄙视,一方面也真要问问自己,我的核心价值到底在哪。这个问题很重要,涉及面很广,选择也很多,而我也只是有些模糊的答案,等以后再仔细写写。

不管如何,我是要开始研究算法了,得解决问题阿!先在 openfans 开个算法的 tag ,一边学一边积累,对算法有兴趣的同学也可以跟我一块进步。

PS :做个广告, blogjava 很多好的 bloger ,能否到 www.openfans.net 导入下 blog ,跟大家分享下你的感悟,谢谢!

posted @ 2006-07-12 15:01 pesome 阅读(2587) | 评论 (6)编辑 收藏

openfans新功能,欢迎大家使用

很不好意思,不是原创技术。做个广告,有不妥,欢迎管理员从首页拿掉。
    你是开源软件的爱好者,平时学习和使用这些软件,也不时写写
blog ,记下些心得。

你是开源软件的传播者,你希望更多的人了解和使用开源软件,希望你的文章被更多的人阅读,并展开更深刻的讨论。

你是开源软件的参与者,平时参与参与国外的开源项目,也希望中国能有更多的开源团体,大家一起做国人自己的开源软件。。。。。。

只要你对开源软件保持着一份热爱,欢迎来到 openfans(www.openfans.net)

非常方便的注册后,你就可以点击“提交 feed ”,只要输入你的 rss 地址(由于时间原因,还没做直接从 web 地址发现 feed ),就可以将你的 feed 加入,同时我们对一些网站提供了简单的匹配(如 blogjava ,只需输入你在 blogjava 的用户名,系统会自动匹配成你在 blogjava feed )。完成后,点击“立即导入”,就可以将你的文章入库,点击“最新日志”可以查看。以后系统会每日定期读取你的 feed ,自动将新的文章加入。由于你提供的是 rss ,内容应该是文章的简短描述(视你的 blog 提供商而定),而且我们会为每篇文章提供原文链接,直接指向你的 blog 原文。

导入的日志一般是没有进行分类的,不方便大家的查找。在每篇日志上都有个“我要推荐”链接,点击并输入你认为适合的标签(如 spring hibernate cms )等,就可以把这篇日志形成文章,放在相应的标签下,永久保存。需要学习 spring hibernate cms 的后来者,可以方便的查找到标签和软件,找到你的文章,进而进入你的 blog

同时你也可以发表文章,推荐软件,创建和加入小组,进行评论。。。。。。我们会不断完善功能,给大家提供更方便的功能和更好的用户体验。

由于现在人员较少,开发进度较慢。但先做个广告,下一步会做 digg ,提供对软件、文章、用户等的 digg 。做对一些标签的 rss ,如 springframework 网站的 rss ,自动获取 spring 的版本更新信息。还要完善小组功能好友功能,给大家提供一个方便交流的平台。

网站拿 java 的一堆开源软件做成,同时本身也是开源软件,希望参与的同学可以 email pesome@gmail.com ,大家一块为推动开源软件在中国的发展做出自己的贡献。

posted @ 2006-06-09 12:42 pesome 阅读(1333) | 评论 (6)编辑 收藏

maven换到jdk1.5的问题解决

  前面openfans用的JDK1.4,今天下决心换成1.5了。运行倒是好好的,在jetty下也没有什么问题。一不小心点了下eclipse里我做的mvn eclipse:eclipseExternal Tools,就开始maven了。停也没用了,等着吧。结果报错:D:\javaproject\openfans\main\src\org\openfans\domain\Group.java:[29,19] -source 1.3 中不支持泛型(请尝试使用 -source 1.5 以启用泛型)。看了看mavenbat,会自动使用环境变量配置的jdk,应该没问题啊。还好我网上认识人多,想起alin用的jdk1.5,就问他怎么回事。发过来这个:

< plugin >

       < artifactId > maven-compiler-plugin </ artifactId >

              < configuration >

                  < source > 1.5 </ source >

                  < target > 1.5 </ target >

              </ configuration >

</ plugin >

我一看就明白了, mvn 时是用 1.3 给我编译的,得告诉它用 1.5 。拷到 pom 文件中,再 mvn eclipse:eclipse 搞定。问题是很快解决了,同时却留下了很多思考:

1. 技术没有止境,做人一定要谦虚。 Maven2 我用的也算比较早,还曾经被白衣说是对maven2的推广做了贡献的,自己也颇以为然。而现在这个简单的问题却不知道了,还得google或问人解决。还好我一直比较谦虚(本身也没啥可骄傲的资本),否则要狂被鄙视了。

2. 技术的推广要不遗余力,好的东西要让大家都知道。 Maven2 我也只是使用,了解并不深入(项目中碰到了的知道,没碰到的就不懂了),但我是到处推荐,碰到个人就说这个好。这下很多朋友都知道了,也引入项目实践了。一方面他们用的舒服,提高了效率,有点问题还可以向我这个所谓的maven2高手请教,我自是“知无不言,言无不尽”;另一方面,他们也许就碰到其它问题了,然后知道如何解决,在我碰到类似问题时,就可以向他们请教了。你看,多好的良性循环,想想都美滋滋的。

3. 多进行知识共享,大家的智慧比个人强。 这是从更高的角度看了,通过知识的共享,能迅速集合大家的经验和智慧,让个体更快的进行学习,少走弯路。你共享自己知识的同时,也能获得别人的成果。如果你知道谁spring比较强,谁hibernate比较强,谁在用maven,而且碰到问题能看他们的文章或直接向他们请教,做起项目来是不是都安心的多。可能有人说有google,但google信息量太大,而且很多文章是处处转载千篇一律,经常半天找不到东西。我是深有体会,所以想到做openfans,做一个知识共享的平台,并做到去糟取精。现在还远远达不到要求,但我会努力的。

posted @ 2006-05-29 17:11 pesome 阅读(3986) | 评论 (4)编辑 收藏

openfans领域模型驱动的尝试

领域模型驱动( Domain Driven Design ),很热的名词。 Openfans ,不太热的网站。今天俺就借着很热的 ddd ,给不太热的 openfans 再造点势。 Openfans 就不多介绍了,网站用 spring+hibernate 为核心的一堆开源软件构建。有了 spring IOC hibernate ORM ,打着 ddd 的旗号也就名正言顺了很多。先声明其实俺对 ddd 的理解也多是道听途说,没有什么系统的学习过,倒是和 Joe 阿牛讨论过几次,颇有受益,他对这个理解还是很深刻的。

言归正传,就讲 openfans 现在经 ddd 思想改造过的模型。整体上看还是普通的三层架构体系:展现层、业务层、持久层。展现层用 spring mvc ,力图做到只是展示相关,避免出现业务逻辑。再具体细分,就是 jsp 页面只有展示逻辑,主要使用 jstl 完成显示功能。 Controller 负责从页面获得参数、把数据传回页面、控制页面流传和调用业务层的接口。持久层使用 hibernate ,在设计上我不是按 dao 方式为每个对象建立相应的 dao ,也不是 ddd 推荐的每个 domain 一个 repository ,而是分成了 Persistence Fetcher2 个接口。 Persistence 处理持久相关如 save remove 方法, Fetcher 则处理 get 相关。这样分的原因也很简单, persistence 是很稳固的,对象都可以共用一个接口如 save Object ),而 fetcher 就千变万化,需要分页、排序等接口。

这样设计是与业务层架构相关的。我采用的是 domain 对象(简称 DO + 一层薄薄 façade 的方式。 DO 处理自身的逻辑,包括持久功能。本身 DO 是没有持久能力的,需要依靠注入的 persistence 接口,这里就体现按 Persistence Fetcher 分开的一个好处, persistence 所有 DO 可以共用一个,给编程带来了方便。 Openfans 中采用的是 DO 继承一个抽象 PersistentObject 类的方式,这样 DO 方便的获得了注入的能力和持久的能力。这样做有何优缺点还需要做些讨论,为了方便我也就先这么用。 PersistentObject 代码如下:

public abstract class PersistentObject implements NeedPersist {

       private Persistence persistence;

 

       public void save() {

              persistence.save(this);

 

       }

 

       public void remove() {

              persistence.remove(this);

       }

 

       public void setPersistence(Persistence persistence) {

              this.persistence = persistence;

       }

 

       public Persistence getPersistence() {

              return persistence;

       }

}

这样 DO 只需要注入 persistence 就获得了持久的能力,而且可以把这种能力往下传递。 DO 获得了持久能力,就有点接近富血模型的想法了,他能够处理一些业务,做持久然后调用引用对象的业务和持久方法 ( 从另外的角度看持久与业务其实是分不开的 ) 。这样把业务封装在了领域本身,更适于用领域对象出发的方式去思考问题。领域层的 façade 主要是为了 Transaction 管理和隐藏 DO 接口。这样 DO 的业务方法都可以设置成 friendly ,仅相互间可见。 Façade 就放在 domain 包中,它负责给 DO 注入 persistence bean ,调用 DO 的接口,提供给 controller 一个 use case 级别的接口,同时它也代理 fetcher 的接口。

有了这个架构,实现起来也不复杂,要配置的 bean 很少(现在还没有使用 spring 2.0 DO 配置在容器中)。设计就从 DO 出发,明确它的属性和方法,让 hibernate 自己生成数据库表。

       这样设计也算是一次尝试吧,其中肯定有很多考虑不周的地方,需要不断的讨论和改进。

posted @ 2006-05-22 18:28 pesome 阅读(1376) | 评论 (4)编辑 收藏

jetty与maven集成

     摘要: 自己做个小项目,使用了 maven2 ,一直使用 tomcat ,但很不方便。采用直接改 server.xml 指向项目路径的方法,但这样要求把编译路径改向 WEB-INF/classes ,而且要求把需要的 jar 放到 WEB-I...  阅读全文

posted @ 2006-05-15 11:19 pesome 阅读(2041) | 评论 (0)编辑 收藏

bea usergroup归来

今天下午去参加了上海的 bea usergroup 大会,主题是深入极限编程,感觉还是受益良多的。这次大会有 yanger 主持,少了很多的商业气息,只是在中间休息阶段问了一些有关 bea 产品的问题。关于 bea 我也不大熟,平时用的都是 ibm 的一套,也就不多评论了。

一共 3 speaker ,第一个是 thoughtworks 的资深顾问,讲了些 xp 要注意的地方,并配了些照片和图表,讲如何实施 xp ,还是有些收获的。第二个是阿里巴巴的性能测试专家,讲了如何进行性能测试,如模拟用户,确定负载等等。人挺牛的,但想想阿里巴巴每天 1 个亿的 pv(page view) ,他都搞得定,咱也没话说。

第三个出场的就是这篇文章的重点了,讲的的确很好,而且很有新意。这位是红工厂的老板,在中国工作几年,然后去加拿大开始了他的 xper 之路,回国后做出了自己的 JDO 实现。他的主题就是结合自己的经历,讲授一个 xper 的成长过程。因为是亲身所感,他讲出来很是实在,加上时不时幽它一默,如:上班让打游戏感觉很爽,跟印度美媚 pair programmer 却感觉很累。。。。。。让人在会意的微笑中体味他当时的感受和成长,同时加深对 xp 的一些实践的实际认识。

他并没有讲 xp 所涉及的所有要素,只是强调了 pp 和沟通的重要性以及测试优先和重构是程序员的基本素质这几个方面。他很是关注沟通的精神甚至一个人的性格要素,他认为好的 programmer 应该乐于去沟通,勇于承认不足,并能主动去向同伴或团队寻求解答(而不是首先去 google )。同时他对 xp 的精神提出了自己的看法,也是以人为本,但是另一种意义上的以人为本。他半开玩笑的说,以人为本并不是给你更多的工资,让你上班打游戏,让你 happy 的工作,而是把以前对流程的关注更多转到开发人员上面,让开发人员具备一个 xper 应有的素质。他通过亲身体验说明了一个好的 xper 即使不在 xp 的团队中,也能创造更快的开发效率,更高的代码质量和更少的加班。关于这点我也很是认同并有一些亲身的实践:通过测试先行,不断重构和努力消除重复代码,是能大大优化代码结构,提高代码质量,减少 bug 率的,而相应的反而会提高开发效率。

也许目前国内还较少有真正能实施 xp 的团队,我们也很难真的在项目中进行 xp 的完整实践。但只要我们接受 xp 的思想,在平时的实践中就采用一些 xp 推荐的方式,如测试优先、重构、持续集成、乐于沟通等等,先把自己变成一个合格的 xper ,那么在真的有机会实施 xp 的时候,我们就能更快的融入团队,更好的用 xp 的精髓指引项目走向成功。

posted @ 2006-04-15 23:30 pesome 阅读(1569) | 评论 (3)编辑 收藏

校庆一日(继续)

接着昨天的写。

出了大楼,师弟带我们到新建好的南门去看看。这不是凯旋门的模仿吗?厚重的大理石砌出中间一大两边一小的空透长门,再加上一些简单的雕饰,中间的下面却立了很大石碑,刻上学校的名字,再围上一些花,阻止人的通行,顿时减少了很多宏伟的气势。但这样却便于照相,好多同学三五成群往石碑前一站,然后很好取景:人面鲜花相映,加上“凯旋门”的气势和石碑的明喻,很有纪念意义。“凯旋门”两旁是弧形的长廊,仿欧洲古典主义建筑风格,一排排石柱过去,凭添了不少文化气息。

从“凯旋门”往校园里走,眼前一片开阔,是一大片草坪,有几个足球场大。草坪上有不少学生,或站成一圈玩飞盘,或几个人围坐打扑克,或几个人乱卧其上感受草的气息。我们走进看看,一块块醒目的小牌上都有一句颇有意味的话语“小草在休息,请勿打扰”。本意大家都明白,但看这么多人在上面,也就暂且从众一把。我们也打扰了一下小草,我还忍不住在上面打了个滚。同时在想:这么大的一块草坪,花费不菲,如果只是看看,未免可惜了点,好像国外的草坪也并不禁止人踩踏的,只要不在上面做剧烈运动,如踢足球之类,应该也不会带来很大的影响。

沿着路往东走,右边都是新建的教学区和学院大楼,风格很是统一。经过一座小桥,往下看去,河水缓缓流过,旁边一个小的岸边广场,河岸边有沿河走道,一直往远方延续。在草坪的绿色基调下又多出一些点缀,真是风景这边独好。我又在心底感叹:真是适合谈情说爱的好地方,可惜我们那时没有,只有西边一个小的人工湖,晚上拥挤的很。再往东就看到一个红漆的仿古大门,是把老校区的大门 copy 过来了。照样门前一对石狮,也是适合摄影留恋的地方。

大家到这也就慢慢散去了,大部分打的走了。我的爱车还在学院门口停着呢,于是又跟几个同学往里走。一边聊聊工作的事情,一边大家好像有默契的往以前的宿舍楼所在走去。走了好远,过了一个地道,来到以前的宿舍楼下。又翻新了,涂上浅黄色涂料,加上四角的红砖墙装饰。可惜原本就整体感觉不佳,就好像一个丑媳妇,涂上再多胭脂水粉也变不成美女一样。忍不住从下往上张量以前我住的宿舍,阳台挂了件运动衫,里面还是有位运动健将的。

又得睡觉了,还没写完,明天继续。

posted @ 2006-04-09 22:53 pesome 阅读(835) | 评论 (6)编辑 收藏

<2006年4月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

导航

统计

公告

主要记录作者在学习java中的每一步足迹。除非特别说明,所有文章均为本blog作者原创,如需转载请注明出处和原作者,如用于商业目的,需跟作者本人联系。
欢迎大家访问:

常用链接

留言簿(16)

随笔分类

随笔档案

文章分类

文章档案

相册

收藏夹

java技术

人间百态

朋友们的blog

搜索

最新评论

阅读排行榜

评论排行榜