Archive for July, 2004

你还敢看这些励志书吗?

Thursday, July 29th, 2004

如果你和我一样,在之前看到这样的宣传语:

《给管理者的忠告》(费尔森著)上:“这些年读过许多管理方面的书,但像这本从细微处给人以提醒和倡导的书还是第一次看到。———管理大师汤姆·彼得斯”

《拒绝借口》(奥里森著)上:“我把这本书推荐给每一个人。———《华盛顿邮报》”

《狼性》(劳伦兹编著)上:“一本所有总裁的必读书!———路易斯安那州医院联合会主席鲍勃·默克尔”


从而一时冲动购买下他们,作为激励自己的读物,那么,你可能受骗了:

http://www.blogchina.com/new/display/38135.html

这里揭露了整个骗局。

感谢上帝,我没有买到这篇新闻中的任何一本。

开源项目OpenBUGZ启动,采用Tapestry作为前端展现框架

Monday, July 26th, 2004

我几乎是和gigix一起学习Tapestry的。那时候学习的人并不多。不过当时gigix了解的东西比我要多很多(现在也是),比如Blog,比如Hibernate。去年吧,写了篇Tapestry入门级的文章后,便转向了数据分析、商业智能方面的研究。以后对Tapestry就没有接触了。

半个月前决定开启一个新的开源项目,目的首先是做成一个成型能用的产品,至少供自己部门使用;二是验证我对AOP, IoC的思考结果,三是表现J2EE的各种最佳实践,并争取能够形成某种开发模板,为以后的开发提供铺垫。刚开始本想采用appfuse的方式,Spring+Struts+Tiles+JSTL…看到这一连串的加号,我有点头晕。开发目的是为了简单,这么一个复杂的模型肯定很难说服其他人使用。Spring的使用是毫无疑问的,前台表现我想到了Tapestry,并最终选择了它。

上周末之前我已经设计出了大部分的用例和模型类。在DomainObject和POJO+DAO的方式选择上,我采用了后者。原因是我对DomainObject还有些理解不清。DAO也被我简化了,直接就是Service层。这要得益于Spring提供的HibernateTemplate。就是这样Service层还是简单得要命,而且异常健壮。针对Service层的Hibernate实现,我做了大量的单元测试,结果令我满意。

集成Tapestry的过程一波三折。按照Spring Reference中的步骤,死活无法初始化ApplicationContext。后来才恍然大悟的把ContextLoaderListener加入到web.xml才成功;对于如何在应用程序中取得ApplicationContext, 我尝试了两种方法:一是直接编写了新的BaseEngine和新的BasePage,在其中我添加了ApplicationContext的初始化和引用。这样我能从任何一个Page中取得context。这种方式是目前我采用的方式;二是将各种Page类受Spring托管,并将各种Service注入到Page中。考虑到复杂性(主要是麻烦),我没有这么做。后来阅读了Tapestry的User Manual, 便采用了更为灵活的方式,例子如下:

public abstract SomePage extends MyBasePage {
    public abstract UserManager getUserManager();
   …
}

SomePage.page:

<property-specification name=“userManager“ type=“openbugz.service.UserManager“>
          global.appContext.getBean(“userManager“)
</property-specification>

呵呵,真是自由而方便的实现!

目前OpenBUGZ已经开始编码,中间过程可能会有不少问题,一个月内应该能够完成第一个版本。届时我会将源代码发布到cosoft,有兴趣参与项目并且对上述技术不太陌生的朋友,可以与我联系。

AOP实现分离权限关注 – 补遗

Friday, July 23rd, 2004

昨天的Blog中我描述了使用AOP分离权限关注的基本做法。回家仔细想了想,把思考的结果补充一下。

我们知道,在基本的RBAC模型中有以下基本(接口)对象:Domain, Group, User, Role, Privilege, Operation, Resource以及对外的SecurityManager。Privilege通过Operate Resource而产生,Role则对应若干个Privilege。不同的Domain, Group, User拥有不同数量的Role,其中User的Role可以继承自Group,也可以继承自Domain;Group的权限可以继承自Domain。对外表现时,可以简单地通过SecurityMananger.checkPermission(User,  Privilege)的返回值true/false进行判断。上面每个对象都是接口,SecurityManager可以通过IoC反向注入,从而使得这种权限模型的使用比较灵活。

在实际情况中,这种权限系统中Resource的定义方法与粒度最难以控制。不同的业务系统中可能有不同的定义方式。而且这种方式的交流非常不方便。以一个普通的“科研项目管理”项目为例:系统中需要进行权限控制的东西可能有:用户信息(增删改查),项目信息(增删改查),日志信息(查看,统计)等等。这种权限的控制如果按照垂直编程的方式来写,需要进行以下工作:

研究:需要控制的资源到底是什么?用户信息?项目信息?这些东西如何表现?这个问题在这种模型中很难考虑清楚。最终的结果往往是,使用没有Resource接口的权限模型,形成一份新的文档:权限代码以及对应说明表。每个编程人员在编写相应的业务逻辑时,都需要参考这个表,并将程序中加上一序列的if — else来判断权限。这种做法无疑是危险的。因为一旦权限代码发生变动, 这个业务方法马上得重写。稍微考虑不难知道,这种方法根本不可取:连基于URI的解决方案都不如。

可以看到,像用户信息、项目信息等等,都是业务对象,这些业务对象对于人是可理解的,但将这些业务对象作为RBAC中的资源是不合理的。因此我提出一种新的资源概念:将具体业务类、业务方法作为RBAC模型中的资源。这样才能够真正脱离权限与业务逻辑。具体做法是:将所有的业务方法名称以及对应的角色名称存入到XML文件或者数据库中,形成一个Resource库:

Method                   Role
——————————————
UserManager.*            UserManager
UserManager.update       UserManager
UserManager.update       User
LogInfo.view             *
LogInfo.statistics       LogManager
…                      …
——————————————

对于权限的判断,可以在RBAC的具体实现中进行。这里假定有一个SecurityManager.checkPermission(User, Privilege)(这里的Privilege实际上就是能否执行特定业务方法)的方法。根据这个方法,制作一个Aspect:

public class PermissionCheckAdvice implements MethodBeforeAdvice {
    public void before(Method arg0, Object[] arg1, Object arg2)  throws Throwable {
          if (!SecurityManager.checkPermission(User, Privilege)) {
                throws new PermissionDeniedException(User, Privilege);
          }
    }
}

然后使用AOP将这个Advice应用到所有的业务方法(类或者方法可能在具有普遍意义的一个package中,也可能具有一定正则意义),并截获所有的PermissionDeniedException:

public class PermissionThrowsAdvice implements ThrowsAdvice {
    public void afterThrowing(Method method, Object[] args, Object target,
            Throwable subclass) {
        //对Permission Denied Exception进行自己的处理。
    }

}

对应的Spring Beans配置请参考上一片Blog。

AOP分离权限关注的文字到此截至,有些比较重要但是来不及完成的如SecurityManager如何引入,上下文环境如何取得等等,可能需要各位自行完成了。这里提出的是一种将Method作为资源的概念,希望对做权限系统的同仁有所帮助。

使用AOP来分离权限关注

Thursday, July 22nd, 2004

对于权限管理的做法,在WEB实现上,无非有以下几种:

1 利用Filter,对所有进入的URI进行解析,并取得当时Session中的User信息,然后通过RBAC的机制,将此链接需要的权限与用户拥有的权限进行比较,然后进行相应的处理。这种做法有很多好处:简单,容易实现,并且对系统侵入性也不强。这里URL就是RBAC中的资源了。这样做的缺点是所有对数据的操作必须通过URL来体现,这一点在现代的程序中不太好实现。如果采用Struts, XWork或者Tapestry,采用同一个URL(浏览器看来)进行处理多项任务已不是什么稀奇的事。

2 利用一个BaseServlet(Servlet+Jsp经典模式)或者BaseAction(Struts模式)或者BasePage(Tapestry模式)或者BaseController(SpringMVC模式),对所有的请求先进行过滤进行权限操作,然后再处理。稍微看一下就知道这种模式跟Filter并无本质不同。优缺点同上。

那么,如果要实现更为细致的权限操作,精确到某个方法的权限,该如何做呢?典型的做法如下:

public someFunciton() {
  //权限判断
  User user = context.getUser();
  if (user.canExecuteThisFunction()) {
      // do the business method
      // …
  } else {
     throw new PermissionDeniedException();
  }

}

这种做法能够将权限的粒度控制到具体的业务方法,因此它的控制能力应该是强大的。可以看到,权限判断部分对于每个方法几乎是独立的,同时也是无趣,易出错的。有了AOP,这一部分就可以忽略了,新的业务方法可以这样写:

public someFunciton() {
      // do the business method
      // …
}

没有了额外的权限操作,这个业务方法看起来那么清晰自然。

将对权限的操作作为一个Advice,并将Advisor关注到所有的业务方法(可能有某一个特定package),然后,剩下的事情就由你的RBAC以及AOP来完成了。通过这样的分离,纵向的一个业务方法被分割为一个更为自然的业务方法和一个关注点。这个关注点写法可能如下:

public class PermissionCheckAdvice implements MethodBeforeAdvice {
    public void before(Method arg0, Object[] arg1, Object arg2)  throws Throwable {
           //权限判断
          if (!this.getContext().getUser().canExcute(this, arg0)) {
                throws new PermissionDeniedException();
          }
    }
}

可能有个问题:如何取得context或者当时上下文环境的User呢?答案是使用IoC(或称Dependency Injection),将上下文环境或者User作为参数反向传入到逻辑方法中。当然,在传入之前,这些变量是需要初始化的。这个初始化工作可以在SuperServlet中进行,并且以Session单例的形式保存在应用程序中。下面是Spring配置文件的例子:

<beans>
   <!– Bean configuration –>
   <bean id=”businesslogicbean”
   class=”org.springframework.aop.framework.ProxyFactoryBean”>
      <property name=”proxyInterfaces”>
         <value>IBusinessLogic</value>
      </property>
      <property name=”target”>
         <ref local=”beanTarget”/>
      </property>
      <property name=”interceptorNames”>
         <list>
            <value>thePermissionCheckBeforeAdvisor</value>
         </list>
         </property>
   </bean>

   <!– Bean Classes –>
   <bean id=”beanTarget” class=”BusinessLogic”>
       <property name=”user”><<YOUR USER OBJECT>></property>
   </bean>

   <!– Advisor pointcut definition for before advice –>
   <bean id=”thePermissionCheckBeforeAdvisor”
      class=”org.springframework.aop.support.RegexpMethodPointcutAdvisor”>
      <property name=”advice”>
         <ref local=”thePermissionCheckBeforeAdvice”/>
      </property>
      <property name=”pattern”>
         <value>.*</value>
      </property>
   </bean>
   
   <!– Advice classes –>
   <bean id=”thePermissionCheckBeforeAdvice” class=”PermissionCheckBeforeAdvice”/>
 
</beans>

———————————————————–

注:API名称还需要深刻考虑,这里仅用作解释使用。Spring的配置文件也仅作参考,可能有错误,实际中还需需要更多考虑。

今天看了看链接统计

Thursday, July 15th, 2004

今天看了看Blog中的链接统计,发现一个很有趣的现象:受访问最多的Blog居然是“大话西游台词回顾”,几乎每天都有十几次点击,多的达三十多次。来源无一例外是Baidu或者Google等搜索引擎。而与此同时反馈最多的也是这个Blog。从此可以看出,大部分人对《大话西游》依然情有独钟,即便多年过后,有些台词现在读来依然令人回味无穷,特定的场合想起特定的台词,令人不禁心有戚戚。比如,“在一个月黑风高阴森恐怖的晚上我是至尊宝你是白晶晶,奇妙的爱情就从桥头上这一点火开始的< ?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />”,想起电影中菩提和至尊宝的场景重现,令人捧腹,还有……算了,不说了,再次读这些台词,感觉真的不错。

原来,机缘是这样离我而去

Wednesday, July 14th, 2004

昨日梦佳人, 欢声笑语,耳厮鬓摩,醒来惆怅未了, 却在QQ收到梦中人的消息:

我昨天离开北京了,想想还是没跟你联系。

很难分析当时的心情。惆怅?遗憾?失落?人说“七年之痒”,七年过后,依然如故。谁说少年的轻狂只能激情一时?

记起了97年我写的一首诗:

三月水涨,那条河淹不过我的思念
双凤亭里,你永在亭阁旁

7月了,曼河涨水时间已过,学生的暑假开始。至于双凤亭,更估计是无人前往了。当初的佳人,已经天各一方;那个在河堤上因为思念而久久不愿离去的少年,那个以河堤杜撰悲剧爱情小说的少年,除了纯真,似乎什么也没失去

我们在惧怕什么?

Tuesday, July 13th, 2004

无知无畏,这句话在技术领域似乎行不通。一旦某种技术奠定下来,这种技术形式,不,更要命的是技术思路,牢牢地限制住了开发人员特别是项目领导人员的思维。新的技术观点来的时候,要么充耳不闻,要么极端排斥。从某种意义上说,软件公司的软件管理水平反而不如这些公司的客户。我们在给客户做解决方案的时候,想办法说服客户:这套软件会给管理带来多大观念的转变从而使得工作效率大幅提升——我们成功地说服了客户,做成了一个又一个的项目。但是,当新的技术,新的观念来的时候,我们却比客户还顽固。

对一个新技术的论证过程一般是从对它的批判开始的。我觉得这一点非常怪异。论证的基本论调往往首先是这个新的东西必须适应现有的哪怕是不正确的开发模式和开发技术。如果说这是对现有技术的尊重和继承也无可厚非,但是这一点绝不应当是评估一个新技术(新模式)的首要考虑因素。时代在在发展,技术在进步,抱残守缺只能导致生产效率、生产质量的降低,从而导致整体竞争力的下降,两相比较,孰重孰轻?

重新发明轮子, 并不是每次都那么愚蠢

Monday, July 12th, 2004

Don’t reinvent the wheel – 这句话在Java领域以至讳莫如深。他的大意是好的,不要做重复的工作,在已经有解决方案的情况下,采用成熟的东西比重复的创造要好得多。然而,并非所有情况下都满足的。我要一只自行车轮,但在我面前的只有火车轮和汽车轮。两者功能相近,构造原理却大相径庭。为了不重新发明轮子,那么我有两种选择:1) 将火车轮或者汽车轮进行修改,让他适合我需要的自行车轮项目;2) 更改我的基于自行车轮的解决方案,让他成为一个基于火车轮的项目。一看便知,这两种解决办法都是如此笨拙与牵强,那么,这个时候,除了Reinvent the wheel, 你有更好的办法吗?

本来想在项目中应用Struts, 但是不知原因他与公司的现有平台不兼容。花了两天时间,请产品部的同事来帮忙,都没有解决这个问题,谁知道会在这个问题上纠缠多久?于是干脆放弃。参考了SprintMVC和Struts的MVC实现后,花了两天时间,整理了一下想法,自己做了一个MVC实现。整个实现核心部分只用了4个POJO,一个Servlet和一些跟具体项目相关的配置文件。它最大的特点是简单。具备Java知识的人可以在1小时内熟练掌握它;支持多模块同时开发,将整合工作量降到最低;更具优越性的是它对公司现有的开发模式影响很小,学习难度以及思维转换的坡度很小。

看来,我发明的这个轮子还挺适用。:-)

Joshua Bloch leaves Sun and joins Google

Thursday, July 8th, 2004

一大早就在TSS看到这个消息,这下的震惊与失落不亚于当初Rich Gree离开Sun.

Josh是Effective Java的作者,他的书一直陪伴着我,为我编写高质量的Java代码提供指引。他设计的Collections框架也是迄今最受好评的类库框架之一。同时他还是JSR175(A Metadata Facility for the JavaTM Programming Language)的Expert Lead. 它对Java领域的贡献远远不止这些。

又要走了,无论Josh在新的工作地点从事什么工作,有一点可以确认,他的从事立场再也不会是SUN了。这对Sun是巨大的损失。

感叹:有多少东西被你丢弃

Tuesday, July 6th, 2004

我犯了一个巨大的错误,那就是打算不用任何O/R映射的工具来实现新项目中所有的底层数据操作。当我完成两个实体类的CRUD操作时,我面对的是两个巨大的、充满SQL语句、看起来乏味至极毫无创意的一堆INSERT, UPDATE, DELETE。我不得不承认我的错误:面对这样一个可能有三十多个表、每个表最多有二十多个字段的O/R映射操作,正常的人都不会将这些无味的工作进行到底的。我不禁想起了EJB中的BMP:在一个稍具规模的系统中,究竟有谁会最终去手写这么多重复毫无创造力的纯粹体力劳动的该死的SQL来维护对象持久性?也许有人,肯定不是我。

因为项目以及技术熟练度的原因,我们不能采用Hibernate、JDO或者任何一种开源的O/R映射框架。无奈之下我重新看起了公司自有产品平台的关于数据映射部分的东西。在早先的时候,这个笨重庞大缺乏文档的平台被我讥讽了无数次:没有体现任何设计模式,所有的对象都可以New出来(也只能通过new的方式产生实例)。他的DAO跟我理解的DAO完全不是一回事,平台的DAO等于数据操作+数据原型。毫无疑问,追求优美的我当然不会放过这个毫不OO的缺点,于是他被我打入冷宫,直到现在。然而,现在重新看这个DAO的设计,却能够很好的将我从困境中解脱出来——他提供了一个代码生成器!通过一步一步地Next,一个二十个字段的表映射的DAO(我很忌讳这个词,读音同’岛’)和它对应的ValueObject,在几秒之内产生出来。令我不禁震惊。

于是我开始重新思考这个平台的价值:简单,易用,容易上手。这是我对它最直接的评价。无论如何,它在现有条件下为我节省了大量的时间精力,并且提高了项目的成功率。这个饱经我嘲笑冷遇的平台,第一次在我眼里可爱起来。

我不禁开始感叹,有多少东西被我们在追求技术的过程中轻松的丢弃?由于第一眼的看不上,以后便一直抱有偏见,比如我现在一直耿耿于心的WebService, EJB, 以及抱有观望态度的AOP。自以为对软件之美已经有了自己的见解,但在这个项目中救我于水火的却是我看不上的东西。

我又记起来很土的一句话,生活中不是缺少美,而是缺少发现。这句话用在对技术的学习,倒也合适。我想我以后不应该再随便下结论了,无论对一个小得不能再小的方法,还是一个宏大的框架蓝图。