2008年07月07日

直接看代码,比较容易理解
<div  style="position: relative; width: 600px; height: 300px; border: 1px solid gray;" >
                        <div style="position: absolute; left: 0; top: 0; border: 1px dashed blue">
                                左上角
                        </div>
                        <div style="position: absolute; left: 0; bottom: 0; border: 1px dashed blue">
                                左下角
                        </div>
                        <div style="position: absolute; bottom: 50%; right: 48%; border: 1px dashed blue">
                                中间
                        </div>
                        <div style="position: absolute; top: 0; right: 0; border: 1px dashed blue">
                                右上角
                        </div>
                        <div style="position: absolute; bottom: 0; right: 0; border: 1px dashed blue">
                                右下角
                        </div>
                </div>

2008年05月15日
看来现在搜索现在变成一种潮流了,程序的开发者也越来越考虑到用户的使用体验了,不错。呵呵,看看自己的 谷歌金山词霸合作版,QQ2008,都能看到搜索的影子
不过有了搜索,效率的确快不少,现在QQ找人挺方便的,你的程序也搜索的影子吗?
2008年04月16日
 发现 Building J2EE Applications with the Rational Unified Process  <<基于RUP构建J2EE应该>>一书中,  有一处翻译很容易让人误解的地方,也是比较重要的地方,该书的第9章的标题将 "Implementation" 翻译成 “实施”  感觉很不妥当,,因为实施在中文里面有实施部署的意思,这里实际要表达的意思是实现。
2008年04月05日

CXF2结合Spring用起来实在太爽了,而且跨语言支持也不错(支持BP,还真不是盖的)
java写的服务端,java,.net,delphi的ws客户调用一切正常;
但是,在定义接口时,有个比较头痛的问题就是,如何处理接口方法的异常声明了
本来是考虑用java 的 check exception来定义,但是由于一些方法中可能发生的异常
情况很难马上稳定下来,可能随着业务的变化,会不断的有新的异常情况
出现特别是,一个接口方法搞了一堆又臭又长的异常声明真是太粗陋了.
并且生成的wsdl文件中,会对每个异常类都进行结构定义,感觉实在不爽。

interface {

   youMehtod()throws ExceptionA,ExceptionB……………..
}

如果将这些声异常定义得比较宽泛,确实可以大大减少接口方法中的异常声明数量
以及,大大减少wsdl文件的size
但这样一来,对ws客户端就有影响了, 客户端可能需要对不同的异常,做出不同的处理
  例如:
   if(is ExceptionA) then processA
   if(is ExceptionB) then processB
  
   ……………….
  
有没有办法,既可以马上稳定接口的异常声明、又能照顾ws客户端非常细的的异常判断呢
当然,最好也能摆脱又臭又长 checked exception 声明

最后,还是决定采用比较原始的方式处理了,异常code+ msg, 感觉基本满足以上需求
格式如下:
  000:异常信息

接口中的所有方法全部不用进行异常声明,整个ws服务端,只需要一个扩展RuntimeExcepiton的WebServiceException
将 code+msg作为该异常信息的message
需要抛异常的地方统一 throw new WebServiceException(异常编码:异常消息);

客户端异常判断
  从异常信息中取出,异常码(前3位)
  if(异常码 == xxxx) then
   …….

这样,接口的声明一下子就被稳定住了,如果业务变化了,只需添加一下异常码就好了,而且接口的声明也环保多了

只是,需要告诉ws客户端的东西又多了一项,当然是 异常定义文档了。

2008年04月03日

为了节省本本的资源,决定装个Oracle Database 10g Express Edition ,没想到惹到一大堆麻烦

 解决思路,先找出双方的服务端字符集,比较差异
 
  通过查看视图 select * from V$NLS_PARAMETERS 
   parameter value
  NLS_LANGUAGE AMERICAN
  NLS_TERRITORY AMERICA
 NLS_CHARACTERSET AL32UTF8
 NLS_NCHAR_CHARACTERSET AL16UTF16
 
  以这次解决问题为例:
 得出9i 服务端 编码为:  NLS_CHARACTERSET = ZHS16GBK
 10g 服务端 编码为: NLS_CHARACTERSET = AL32UTF8
 
  也可以用 DBA身份登录,通过  select * from  props$表查看服务端字符集。
 
  先在9i客户端 exp 出 dmp文件
 
  然后通过 以下命令更改 10g方的数据库 字符集 为 9i的字符集
 
   connect system/oracle10g as sysdba;
 shutdown immediate;
 startup mount;
 alter system enable restricted session ;
 alter system set JOB_QUEUE_PROCESSES=0;
 alter system set AQ_TM_PROCESSES=0;
 alter database open;
 — 改字符集为 9i的服务端字符集 ZHS16GBK
 alter database character set internal_use ZHS16GBK;
 shutdown immediate;
 startup;
   
   然后在 10g 方导入 imp dmp文件
  
   可以通过查看注册表来查oracle 客户端的 字符集配置 ,
   查看项为NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
   一般客户端字符集与服务器端字符集保持一致 ZHS16GBK = ZHS16GBK

参考连接:
 RACLE 10G 修改字符集  http://hi.baidu.com/ssms/blog/item/b93a951363bb12035aaf53d9.html
 Oracle数据库字符集问题解决方案大全  http://blog.csdn.net/adijava/articles/56665.aspx

 SqlRowSet rs = (SqlRowSet)jdbcTemplate.query(sql,params,new SqlRowSetResultSetExtractor() {
 protected CachedRowSet newCachedRowSet() throws SQLException {
  return new OracleWebRowSet();
 }
 });

 

2007年12月08日
   因目前项目需要对项目中100多个网页填报的表单,提供excel导入填报功能,
    考虑到,项目中这么多格式不一的表单全部需要excel导入,如果针对每个表单写一个单独的
    excel解析和导入功能,真是一场恶梦。
    在大概浏览了一些要填报的表单格式后,就开始思考能否搞个项目级的通用excel导入功能,
    来解决以上问题, 如是就有了下文.
    
   因excel导入功能是在项目快完成时提出的需求,项目中需要导入excel数据的表单,
   全部已经在java代码中有对应的pojo对象(getXXX,setXXX …)。
   现在要做的是,通过上传一个包含表单需要内容的excel文件(大部分excel文件格式基本已经确定,但可能会发生变化),
   程序通过解析上传的excel文件,将内容填充到对应的pojo实例中,比如,person.xsl对应的 Person.java类。
  
   在仔细分析解析excel到pojo的过程后,
   发现,实际要解决的问题就是可以简单的描述为:
      将excel中的某一行某一列中的值,赋值给java pojo中的 某一个对应的属性.
  
   问题域搞清楚后,就好办了,现在只要能把这部分逻辑在程序外通过配置描述清楚,就可以让程序自动去做excel to pojo的赋值了
  
     我的解决方法如下:
    
     通过xml文件来描述以上的excel to pojo的赋值逻辑,实例如下:
    
     <?xml version="1.0" encoding="UTF-8"?>
     <!–
 public static int  TYPE_STRING = 1;
 public static int TYPE_NUMERIC = 2;
 public static int  TYPE_DATE = 3;
 required attrs[name,rowNum,cellNum]
 DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
 DEFAULT_NUMERIC_FORMAT = "#,#00.0#";
 –>
     <excel2pojo pojo="com.epeai.yx.pojo.YourPojoClass">
 <properties>
  <!– name对应pojo的属性名, rowNum 和 cellNum 用来表示 属性在excel中的值位置–>
  <property name="ecode" rowNum="5" cellNum="0" />
  <property name="untilCurMonthReceEleFee" type="2" rowNum="5" cellNum="1" />
  <property name="untilCurMonthReceEleFee" type="2" rowNum="5" cellNum="2"/>
  <property name="untilCurMonthRealEleFee" type="2" rowNum="5" cellNum="3"/>
  <property name="totalOweFee" type="2" rowNum="5" cellNum="4"/>
  <property name="curMonthReceTimeEleFee" type="2" rowNum="5" cellNum="5"/>
  <property name="totalCallbackRate" type="2" rowNum="5" cellNum="6"/>
  <property name="untilCurYearOweEleFee" type="2" rowNum="5" cellNum="7"/>
  <property name="totalReceCallbackEleFee" type="2" rowNum="5" cellNum="8"/>
  <property name="oweEleFeeCallbackRate" type="2" rowNum="5" cellNum="9"/>
  <property name="oweThreeYearEleFee" type="2" rowNum="5" cellNum="10"/>
  <property name="balance" type="2" rowNum="5" cellNum="11"/>
  <property name="dealProvinceEleFee" type="2" rowNum="5" cellNum="12"/>
  <property name="totalPaidProvinceEleFee" type="2" rowNum="5" cellNum="13"/>
  <property name="oweProvinceEleFee" type="2" rowNum="5" cellNum="14"/>
  <property name="remark" rowNum="5" cellNum="15"/>
  <property name="month" type="3" format="yyyy-MM" rowNum="1" cellNum="1"/>
 </properties>
  </excel2pojo>

 这里是用来描述以上xml配置的java模型:
      代码如下:
    
      public class Excel2PojoModel implements java.io.Serializable {

 /**
  * pojo的完整名称 com.xxx.pojo
  */
 private String pojoName;

 /**
  * 描述完整的pojo属性集(以及属性在excel 单元格(Sheet)中对应的位置(rowNum,cellNum))
  */
 private List<PojoPropertyModel> properties = new ArrayList<PojoPropertyModel>();

 public String getPojoName() {
  return pojoName;
 }

 public List<PojoPropertyModel> getProperties() {
  return properties;
 }

 public void setPojoName(String pojoName) {
  this.pojoName = pojoName;
 }

 public void setProperties(List<PojoPropertyModel> properties) {
  this.properties = properties;
 }
 
 public void addProperties(String name, String format, int type, int rowNum, int cellNum) {
  properties.add(new PojoPropertyModel(name,format,type,rowNum,cellNum));
 }
 
 public Iterator propertieIterator() {
  
  return properties.iterator();
 }
}

——————————————–
 
/**
 *
 *    用于描述pojo属性的详细信息,
      以及属性值在excel 单元格(Sheet)中对应的位置(rowNum,cellNum)
 *    要查找pojo属性对应于excel中的位置,请使用
 *   (com.epeai.common.util.input.poi.ExcelPositionGen 工具)
 *
 */
public class PojoPropertyModel implements Serializable {

 private static final long serialVersionUID = 1L;

 public final static int TYPE_STRING = 1;

 public final static int TYPE_NUMERIC = 2;

 public final static int TYPE_DATE = 3;

 public final static String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";

 public final static String DEFAULT_NUMERIC_FORMAT = "#,#00.0#";

 /**
  * 属性名称
  */
 private String name;

 /**
  * 属性值格式 (如 日期格式、数值型格式,需要指定格式)
  */
 private String format;

 /**
  * 属性类型 (默认为 string)
  */
 private int type = TYPE_STRING;

 /**
  * 在excel中的 行号 (从 0 开头 )
  */
 private int rowNum = -1;

 /**
  * 在excel中的 列号 (从 0 开头 )
  */
 private int cellNum = -1;

 public PojoPropertyModel(String name, String format, int type, int rowNum,
   int cellNum) {

  this.name = name;
  this.format = format;
  this.type = type;
  this.rowNum = rowNum;
  this.cellNum = cellNum;
 }

 public PojoPropertyModel(String name, int type, int rowNum, int cellNum) {

  this.name = name;
  this.type = type;
  this.rowNum = rowNum;
  this.cellNum = cellNum;
 }

 public PojoPropertyModel(String name, int rowNum, int cellNum) {

  this.name = name;
  this.rowNum = rowNum;
  this.cellNum = cellNum;
 }

 public String getFormat() {
  return format;
 }

 public String getName() {
  return name;
 }

 public int getType() {
  return type;
 }

 public void setFormat(String format) {
  this.format = format;
 }

 public void setName(String name) {
  this.name = name;
 }

 public void setType(int type) {
  this.type = type;
 }

 public int getCellNum() {
  return cellNum;
 }

 public int getRowNum() {
  return rowNum;
 }

 public void setCellNum(int cellNum) {
  this.cellNum = cellNum;
 }

 public void setRowNum(int rowNum) {
  this.rowNum = rowNum;
 }

}

好了,excel to pojo 模型建好了,现在就剩下具体的解析环节了:

我这里使用了3个开源的工具包,如下:

1,poi:用于解析excel的

2,ognl:(动态给pojo值,而且可以包含层次关系

3,dom4j:用来解析xml配置文件

 具体实现类清单:

 1, E2P:(excel to pojo),最终暴露给调用方的类,通过一个E2PdefinitionParser的实现来实例化
  (代码太长,需要可以给我留email)
     
 
 2, E2PdefinitionParser
   
 /* 
 *
 * E2P 定义解析接口,用于解析e2p 配置文件
 * xml定义解析器接口,考虑到有些excel格式相当复杂,通用的解决不了时,可以考虑实现自己的解析器
 */
public interface E2PdefinitionParser {
    /**
     *
     * @param definitionXml 定义文件流
     * @return Excel2PojoModel e2p模型
     */
 public Excel2PojoModel parser(InputStream definitionXml);

}
 
 3,DefaultE2PdefinitionParser
     E2PdefinitionParser的默认实现,对于大部分简单的excel格式,可以直接使用
 
 
 
 4, ExcelPositionGen:用来产生excel cell位置的工具类
 

最终实现,效果如果如下:
    //创建e2p实例
    E2P e2p = E2P.getInstance(new DefaultE2PdefinitionParser());
    //通过e2p定义xml文件,和指定的excel文件,构造一个填充好内容的pojo实例
    YourPojo pojo = (YourPojo)e2p.execute(new FileInputStream(e2PdefinitionXmlDefPath), new FileInputStream(excelPath));

当需要新加入一个表单的excel导入功能,只需根据excel格式与对应的pojo,写一个xml配置文件描述pojo属性值在excel中对应的位置就可以了,如果那天,excel的格式发生变化了,也只需简单的改下xml配置文件中的cell位置就好了.

对应很复杂的excel格式导入,可能需要实现一个自己的E2PdefinitionParser就好了.

很奇怪,blog不知道怎么上传文件,对本文感兴趣的朋友,如果需要完整代码,请在回复中留email

 

       用了将近4年的struts 框架,丢下还真不舍得,加上现在struts和webwork搞联姻了,搞出个struts2,
   看来现实的技术世界逼着不让人从一而终了,呵呵…
  
      原来很早时候听说google都用webwork了,那时也忍不住到webwork家把她down下来下折腾了一翻,
   但终究没有用在项目开发上,过段就没关注了…
  
   这回项目开发web框架使用的是webwork,刚好有机会深入接触一翻…
  
   从struts转到webwork可以说是非常的easy,几乎不用怎么需要学习了, 就稍微看下demo程序,
   手头备用一份随时翻阅的官方文档,就直接可以开发了。
  
   经过1个月的使用体验,整体感觉webwork使用起来还是比struts1.x要方便、优雅的多,特别感慨的是,一直比较厌烦,
   但不得不用的struts1.x FormBean在webwork中给优雅的解决掉了,而且,webwork中的Action比struts1.x的Action要轻多了,
   不用传一堆固定的参数。另外,现在想想,struts1.x的ActionForward真是没有什么必要,
   看看webwork一个简单的string给漂亮解决了,爽!
  
   当然,webwork还有很多好的特性 (免费提供的一大堆 interceptors,ognl的使用等等)。
   唯一不太喜欢的是webwork的标签库,可能是自己用jstl久了,看很多做重复功能的标签库就反感,特别是有些又不如jstl直白.

    正式投入struts2/webwork的怀抱!

 

2007年12月06日

写于(2007-6-10)  

好久没有写blog了 ,也许今天又是一个开始   

      经过之前2个月的摸索,自认为完全掌握appfuse的开发思路和相关技术,了解其每一行代码(包括模板代码、构建ant代码)、每一个配置后,才真正开始了我的appfuse项目开发征途;先是小小搞了些小demo,然后不断的增加一些特性,改了一些bug,和改进了一些组件支持,最后,才是实际项目中应用,俺是很保守的…

appfuse包含的相关开源技术可以说保罗万象,先在这里列一下我的项目中主要用到的一些技术清单:
  spring 2.x
  struts1.x (呵呵,现在已经发展到2.0,大变样了)
  hibernate 3.x
  sitemesh (网站布局)
  acegi-security
  struts menu(小tool解决大问题)
  extremecomponents (appfuse自带的 displayTag不适应现实项目的需要,只好更改代码生产模板)
  urlWriter (url美化/友好)
  FCKeditor (见过的最好的在线编辑器)
  dwr (界面交互改进)
  xdoclet (hibernate/action注释,用于生产hibernate的映射文件和struts的映射和动态生产Form)
  jstl(用起来最爽的一套标签,而且是sun的正规军)
  jfreechar/cewolf tag (动态生成图形)
  prototype.js/script.aculo.us (让人爱不失手的两个js tool)
  lightbox (实现让人不敢想象的效果)
  ant (自动化的心脏)
 
 到目前为止,已成功跟据 appfuse框架开发了3个完整的项目开发(80%代码生成)
 
 如下:
 
 keysingCMS
 cferp                   http://www.cn-fungi.com/cferp
 食用菌交易网     http://www.cn-fungus.com

    这期间也碰到了无数的问题,一个个的解决后(包括修复appfuse的bug,局限功能的扩展),
    特别是第一个项目时;但到第二个项目,才真正体会到appfuse开发带来的爽快和效率。

主要的开发步骤完全按照appfuse的思路走,具体如下:
(这里假设,已完成前期需求分析,对要开发的项目需求已经清楚)
1,在rose里画 领域模型(model),不断改进和完善model的相关属性(这步也可以省略,直接手工写java bean)
2,使用rose的生成代码功能,生产model的java代码
3,在model上根据需要,加上xdoclet标签,主要做的是hibernate的OR map,和 struts FormBean的属性指定和验证规则
4,使用appgen(一句简单的命令行),对指定的model生产所以代码,包括(action,service,dao,jsp,all test,测试数据,strtus menu菜单配置)
5,构建发布,如果model不包含复杂关系的话,重新refresh后,就可以直接启动tomcat,在浏览器里对这个model做增删改查了,而且提示信息和友好交互一应俱全
6,根据业务需要,修改生产的代码,直到满意,这部分的工作通常也比较简单,大多都是在各层增加些方法,一般对jsp的改动比较大些

看出来了没有,开发就是这么简单,如果熟练的话,给系统增加个常见的增、删、改、查模块,还不要5分钟
我的3个项目都是这么干的,特别是,在第二个项目,效率非常高;

现在再来看下,如果model发生变化后,修改的量如何
Case,给已有的model(模型),增加个属性
1,打开model,添加属性,根据需要加上 xdoclet
2,setup一下,自动更新hibernate配置
3,jsp页面form增加个属性,搞定。。。

说到这里,你可能会问,怎么开发时,没有看到数据库设计呢?
是的,开发时,我确实把数据库给忽略了,因为,这时我根本不用关心数据库
因为,数据库结构都是通过model生成的,而且,一些细节,在写xdoclet时,已经搞定
appfuse框架,会自动为我打理数据库事宜。
呵呵,那我什么时候会想到数据库呢,等你要做些统计分析(hibernate不擅长)功能时,出与性能考虑和方便
可能要做些手工统计分析的 sql语句,这时就用到数据库了.

另外,对于单元测试代码都会自动生成到test目录,可以调用ant test命令,对他们进行测试,生成测试报告
,如果要增加测试case,也只是简单的在已有的Test类上加个方法就好了

一些想法:
  通过通读appfuse的代码和实践开发,发现我们项目开发不管是技术还是流程上,跟老外的差距非常大的差距,里面有太多的东西值得学习了,一些我们刚想到的和看到的,还有没有想到的,他们已经在项目开发中运用起来了;比如对测试和集成的自动化(测试代码、测试数据的自动生成,包括action,dao,service,jsp等等的测试代码和数据),在任何时候,一个简单的命令,就可以发布一个可以运行的系统,等等,这些都是我们目前没有办法做到的(我自己开发时,也是偷懒,对生成的test代码,都没有怎么ant test ,还没养成这习惯,汗!)。
  appfuse除了给我们一个好的快速开发的平台外,我觉得一个更大的贡献就是,引导我们走上正确的项目开发
路线上来,让我们把项目开发的重心放到,将现实世界的业务模型 转到 让计算机能够理解的 模型上来,也就是
所谓的领域建模,这才是应用的核心;一旦将重心放到领域建模上来后,就迫使自己不得不抛弃面向数据库设计的
恶习,回到面向对象分析和设计的正途上来。
  领域建模是一件很复杂的学问,需要有很强的抽象分析能力,需要不断的学习和经验积累才能慢慢掌握,
目前自己发现需要恶补这方面的知识,但目前市场上关于这方面的知识也是比较贫乏。

后记:
  刚掌握和应用appfuse 1,9x开发比较爽,没想到 appfuse 2.0就来了个翻天腹地的变化
以下是appFuse2 新的特征清单
    * Maven 2 Integration
    * Upgraded WebWork to Struts 2
    * JDK 5, Annotations, JSP 2.0, Servlet 2.4
    * JPA Support
    * Generic CRUD backend
    * Full Eclipse, IDEA and NetBeans support
    * Fast startup and no deploy with Maven Jetty Plugin
    * Testable on multiple appservers with Cargo and profiles

俺需要跟着升级的技术包括 ,呜呜…
  Ant      to  Maven
  JDK1.42   to JDK5(泛化支持,注释元数据支持)
  struts1.2  to struts2.0(比较大的变化)
  CSS Framework (appFuse 之前的版本也有,这次特别)

 

 

 

 

 

 

 

 

 

 

2006年12月09日

   最近在看<<基于RUP构建J2EE应用>>,先是下载了一个英文版的chm文件,看着很对头,开发项目时,我们就参考着上面的路线来实践,慢慢感觉这个咚咚确实是个宝藏,赶紧去买了一本纸质的版本,准备做为近期睡前的重点关注对象了。

    通过最近的项目实践,才越发感觉项目前期的分析的重要性了,我们之前的领域分析还是太马虎了,在几个核心的域模型还没有完全分析透彻的情况下,就开始开发了,这导致了后面的重构代价很大,还有一个问题是得尽快想办法来做用例变更的管理了,因为发现,我们经常花了大量宝贵的时间讨论,来达成一个一至用例的设计方案(而且这个反感随着时间的推移,和项目的逐步细化,还在发生变化),目前,对这个过程除了我们的大脑中保留的记忆外和代码中的逻辑外,并没有通过其他方式来纪录,真担心,时间一长,大家记忆淡化时,又得花时间来讨论了,这就太不值得了。

     那么问题出来了,如何有效的跟踪和管理平时讨论的开发话题(用例)?

       暂时想到两种,issue traker system、和 用word来管理固定格式的use case,还是两种结合,use case用固定格式来记录最后一个版本的用例方案,issue traker system 来跟踪和纪录,用例方案发生的变化过程?

这个下周要拿出来讨论一下了..

顺便找到了几个不错的 编写用例和领域建模的 资源站点,备忘一下:

http://www.zhangxun.com/
http://sunshineormer.blogdriver.com/sunshineormer/index.html