Dev Zone
偏执狂才能生存,人生/事业的路上需要再坚持一下
但是又怎么说得清坚持的结果,道得尽坚持的含义

2005年5月16日

     发现一个问题,使用struts进行文件上传,如果有些参数没有完全定义在ActionForm中,需要从request.getParameter获取,在表单提交并且validate失败返回input页面时,这部分需要从request.getPrameter获取的参数数据都丢失了,即使再对request进行multipart解析也不能得到。

     经过分析,发现struts的ActionServlet在接收到multipart请求之后,在RequestProcessor中会对request进行封装:MultiRequestWrapper,然后在Action执行完之后,又将已经封装的request重新还原。以下是部分代码,截直RequestProcessor:

封装:
    protected HttpServletRequest processMultipart(HttpServletRequest request) {

        
if (!"POST".equalsIgnoreCase(request.getMethod())) {
            
return (request);
        }

        
        String contentType 
= request.getContentType();
        
if ((contentType != null&&
            contentType.startsWith(
"multipart/form-data")) {
            
return (new MultipartRequestWrapper(request));
        }
 else {
            
return (request);
        }


    }

还原:
   在doForward和doInclude中在forward和include之前都执行了下面的代码:
        if (request instanceof MultipartRequestWrapper) {
            request 
= ((MultipartRequestWrapper) request).getRequest();
        }


     问题就出现在这儿。在经过测试之后,发现request只能进行一次multipart解析,这或许和解析request的时候调用了request.inputStream有关,第一次调用之后再调用就不能获取其中的有效内容了。因此发现request在调用CommonsMultipartRequestHandler.handleRequest进行解析后并还原后,调用common-upload对request进行解析已经得不到任何得提交内容了,因此当Form验证失败,返回input页面时,即使再进行multpart解析,也不能通过request.getPrameter取到你想要的数据。而此时,表单中的数据却不会丢失(定义在ActionForm中的表单域),这是因为struts的html系列tag在redisplay时值都是从ActionForm获取的。

       在将RequestProcessor.doForward和doInclude中还原request的语句注释后,问题得到了解决。到目前还不清楚为什么struts要还原request,难道是因为chain的原因?

       webwork中应该不会出现这个问题,因为webwork中无论ServletDispatcher还是FilterDispatcher在对request wrap之后都没有再还原。
posted @ 2006-03-15 06:10 dev 阅读(3916) | 评论 (1)编辑 收藏
 
tomcat、jdk1.4下出现The output format must have a '{http://xml.apache.org/xalan}content-handler' property!的原因:
    这是因为系统、tomcat或者jdk使用了老版本的xalan.jar。系统如果本身没有使用,而tomcat/common/lib、tomcat/common/endorsed/lib、tomcat/shared/lib下又没有xalan.jar的话,那就是jdk使用了老版本的xalan.jar——在jre\lib\rt.jar里面。下载最新的xalan.jar然后放到tomcat/common/endorsed下即可解决这一问题。
posted @ 2006-03-03 19:46 dev 阅读(1032) | 评论 (1)编辑 收藏
 
暂时记下,没想透彻,思考所得、个人见解以后再加入。

企业发展的4个阶段:
  • 产品阶段
  • 资产阶段
  • 资本阶段
  • 品牌阶段

    企业管理的4个阶段:
  • 人治,嘴上说
  • 法治,建立制度
  • 企业文化
  • 目标管理,充分发挥员工的积极主动型,自我管理
  •  

    posted @ 2006-02-21 02:13 dev 阅读(734) | 评论 (0)编辑 收藏
     

          blog很好,blogjava暂时感觉也很好,可是为什么不能写只允许自己看的日志呢?虽说blog就是为了大家能够分享彼此的观点感想,但是总有一些东西是不便公开或者暂时不能公开的。如果允许写只允许自己看的日志,blog的应用范围就会更大,价值也会被充分挖掘了。
         也可能是我愚昧,对blog了解还不深吧,上述所说的blog也许已经出现了(自己搞一个得了,笑谈)。

    posted @ 2006-02-21 02:02 dev 阅读(235) | 评论 (0)编辑 收藏
     
  • XP
  • SCRUM
    需要并且应该持续关注、思考。

    一些共同点:
  • 快速迭代
  • 持续改进
  • 注重测试
  • 团队协作
  • posted @ 2006-02-20 02:10 dev 阅读(195) | 评论 (0)编辑 收藏
     
           很久没有来这儿了。其实我很喜欢BLOG,可以记下每天工作中的所思所想。我是一个喜欢思考的人,经常有一些想法,有这么一块地方,能够把自己的经验、想法、创造记录下来,沉积起来,对自己无疑是一种财富。

          不知道是不是有人会写项目经理日志,我觉得这是一个很好的事情。以前做项目管理的时候只是弄了一个“项目事件”,记录项目开发过程中发生的、自己觉得是重要的事情,比如某某人进入项目组、离开项目组了,某某模块版本发布了,发生了什么大的费用等等。写项目经理日志,或者开发日志、工作日志也行,把自己在工作中、项目开发过程中、项目管理过程中的零零碎碎的想法记录下来,会是一件很棒的事情。人不应该懒,再累心情再遭也应该坚持写这种日志。

          或许小笔记本、那种可以随身携带的那种是一件更好的日志记录工具,可惜容量不够,而且不能检索。掌上电脑?写起来似乎麻烦了,table-pc,好像又太大了,携带不方便,可惜,要是手写识别技术更智能一点、掌上电脑更薄一点,而且可以展开屏幕就棒了。可惜。

     
    posted @ 2006-02-20 01:19 dev 阅读(177) | 评论 (0)编辑 收藏
     

      JDK1.5出来很久了,一直没有研究。这倒不是没有时间,而是觉得J2EE服务器要支持JDK1.5还需要一段时间,而多数客户还在使用JDK1.4,因此要基于JDK1.5开发Web应用程序还不现实,因此一直没有去弄这玩意。

      今天由于研究Desktop的开发,装了1.5,发现1.5中Java程序已经可以获得本地系统的外观,我试着不断的变换了xp的theme,Java程序始终能够保持和系统的外观一致;而且以前使用SkinLF后,在web start程序中JOptionPane的窗体经常出不来的问题也解决了。这可是一个非常好的消息。

      另一个好消息是JDK的下一个版本将对Swing/AWT组件进行增强。

      为Java感到高兴!

    posted @ 2005-05-17 22:12 dev 阅读(524) | 评论 (0)编辑 收藏
     

    这里列出一部分Java Desktop的开发资源。

    1。A java.net community for JavaDeskTop

      这是sun主持的资源很全的社区,有很多关于JavaDeskTop开发的资源。

    2。JDIC(Java Desktop Integration Components)

      Sun主持的致力于java和本地应用程序集成、交互的组件集,非常棒。目前有5大组件。

      Desktop

      FileTypes

      Brower

      System Tray Icon

      Packager

    3。JDNC(Java Desktop Network Component)

    4。JExePack

      一个将Java程序转化为Exe程序的组件。

    5。JSmooth

      作用同JExePack。

    6。JavaService

      一个将Java程序转化为NT Service的组件。

    7。SKinLF

      非常棒的Java LookAndFeel实现,采用配置包的形式定义Java的LookAndFeel,可以很方便的定义自己的LookAndFeel。它的网站上提供了大量Skin下载,其中不乏精品。

    8。JGoodies

      JGoodies最出名的是它的FormLayout,它使swing和AWT应用程序的布局变得非常简单。由于它的影响,已经有专门针对它的DESIGNER出现,Eclipse也有相应的插件支持。FormLayout使Java的几个默认Layout相形见绌。

      JGoodies还有一组Swing/Awt组件,可以很方便的创建Wizard、对话框、Splash窗体、About窗体等GUI部件。

    9。Rachel(Open Source Resource Loading Toolkit for Java Web Start )

      和WebStart打过交道的人可能知道,要在webstart中读取解析zip或者jar资源是一件非常困难的事,然而有些资源又必须以zip或者jar的形式存在,比如SKinLF就是这样。Rachel使你不再为这种事情烦恼。它提供两种解决方案:采用class://的协议装载URL资源,内嵌一个小型的多线程http server。

    10。JavaHelper(JavaHelp System)

      Sun提供的制作Java Help制作系统。允许你在GUI应用程序、Applet中提供Online Help功能。

    11。Exe4j

      Java 安装程序制作工具。

    12。JGraph

      非常出名的Java 图形编辑框架。

    13。GEF(Graphic Edit Framework)

      非常好的图形编辑框架,虽然没有JGraph出名,但是我始终觉得它的结构和API都比JGraph好,扩展性非常好。

    14。yworks

      它的YGuard是一个非常棒的混淆器,免费的,功能很强。除了YGuard它还有几个非常好的组件。

    15。Create GUI with JFC/Swing

      Sun的JFC/Swing编程初学者指南。

    16。Drag and Drop

      Sun的关于在GUI中实现拖放操作的教学文章。

    17。Joshua Marinacci的Blog

      有很多介绍GUI编程的好文章。

    18。CloseAndMaxTabbedPane An enhanced JTabbedPane

      JavaWorld上一篇关于如何在JTabbedPane的Tab上添加Close按钮、Maximize 按钮和PopupMenu的文章,有源代码下载。还可以。

    19。Creating Wizard Dialogs with Java Swing

      Sun上的介绍用Swing创建类似Elipse Wizard对话框的文章。

    posted @ 2005-05-17 22:11 dev 阅读(1397) | 评论 (0)编辑 收藏
     

      SkinLF(http://www.l2fprod.com/)是一个非常漂亮的Java LookAndFee组件,它的外观可以配置,由一组小图片和一个skinlf-themepack.xml构成。SkinLF的网站上提供了很多Skin下载,这里http://www.l2fprod.com/software/skinlf/jnlp/demo.jnlp可以看到SkinLF和这些外观的演示。可惜的是除了默认的themepack.zip之外,其他外观(好像有很少几个除外)都有中文乱码的问题。其实这是这些外观使用的字体导致的。

      打开外观zip文件中的skinlf-themepack.xml,你会发现其他外观的配置文件比themepack.zip中的配置文件多了一些地方:

      <font name="Global" value="SansSerif,0,11" />
      <font name="InternalFrame.titleFont" value="Trebuchet MS,1,11" />
      <font name="TabbedPane.font" value="Tahoma,0,11" />
      <font name="MenuBar.font" value="Tahoma,0,11" />
      <font name="MenuItem.font" value="Tahoma,0,11" />
      <font name="PopupMenu.font" value="Tahoma,0,11" />
      <font name="Menu.font" value="Tahoma,0,11" />
     把这些注释掉,然后把配置文件放回到zip文件中就可以了。当然你还可以尝试采用其他的字体。
     
     采用JGoodies的LookAndFeel出现乱码,我曾经以为是UTF-8的问题,现在看来可能也是字体导致的。可惜的是,JGoodies的LookAndFeel不能配置。
    posted @ 2005-05-17 22:11 dev 阅读(852) | 评论 (0)编辑 收藏
     

      下午,试用了SkinLF(http://www.l2fprod.com),感觉非常好,程序的外观得到了很大的改善,还可以根据自己的需要随意调整得到自己想要的外观,但是要将SkinLF应用到Web Start中还有点问题。这是因为,web start使用到的资源必须包装成jar文件,以的形式定义到jnlp资源文件中,而SkinLF的外观配置文件是zip形式的。也许有人会说,把zip转化成jar文件就可以了。是的,想象中这应当是可以的,可是实际情况不是如此,你会发现使用ClassLoader.getResourceStream("themepack.jar")的时候返回了Null,SkinLookAndFeel.loadLookAndFeel()会异常Stream closed.

      调试之后发现,即使是在客户端,在Eclipse中运行的时候,如果不把themepack.jar定义到ClassPath中,也是无法加载的。

      我想这是一个普遍的问题,根据我的理解,如果其他资源,比如图片、xml文件、属性文件等等能够被加载,themepack.jar也应该被加载。事实上应该是这样,但是关键在于SkinLF需要采用ZipInputStream解析zip(jar)文件,这时情况就不一样了。具体的原因目前没有搞清楚。

      在网上找了半天,终于发现一个Open Source的组件:Rachel,使用它很轻易就可以解决上面提到的问题。

      Rachelhttp://rachel.sourceforge.net)是一个为解决Web start装载资源困难而开发的组件。它提供了两种方法解决资源装载问题。

         方法一,使用class://URL Handler。
          这个方法采用新的URL协议:class://从jar文件中获取资源。

         Step 1,注册新的URL Handler,以支持class://协议(protocal)。
         例子:java.net.URL.setURLStreamHandlerFactory( new RachelUrlFactory() );

         Step 2,采用class://协议构造URL,从jar文件中读取资源。
      语法:class:///
      例子:

    class://test.LookAndFeelTest/themepack.zip
    class://com.l2fprod.gui.plaf.skin.SkinLookAndFeel/themepack.zip


      注意:这里,是Rachel用来定义资源文件所在的jar的,后面的相对于jar中的根目录而言。上述例子中的jar的结构如下:

          test.jar:

          test
    /LookAndFeelTest.class
          com
    /l2fprod/gui/plaf/skin/SkinLookAndFeel.class
          themepack.zip
          images
    /example.png
          html
    /index.htm

      Step 3,采用java.net.URL获取资源。
          例子:

        URL url = new URL("class://test.LookAndFeelTest/themepack.zip");
        URL url 
    = new URL("class://test.LookAndFeelTest/html/index.html");
        URL url 
    = new URL("class://test.LookAndFeelTest/images/example.png");

        URL url 
    = new URL("class://com.l2fprod.gui.plaf.skin.SkinLookAndFeel/themepack.zip");
        SkinLookAndFeel.setSkin( SkinLookAndFeel.loadThemePack(url) );

      方法二,在程序中嵌入多线程的,小型的http server。

      Step 1,把你的资源放到jar中。

      Step 2,在每一个jar中增加一个anchor class,帮组server定位资源所在的jar文件,这点类似于方法一class:///的的
      例子:

       public class CrossRefAnchor
       {
         
    public CrossRefAnchor() {}
       }

      CrossRefAnchor没有任何的实际意义,只是为了帮组定义资源所在的jar。

      Step 3,为每一个包含资源的jar用ClassResourceLoader注册到WebResourceManager。
      例子:

       WebResourceManager roots = WebResourceManager.getInstance();
       roots.addResourceLoader( 
    new ClassResourceLoader( CrossRefAnchor.class ) );
       roots.addResourceLoader( 
    new ClassResourceLoader( JavaDocAnchor.class ) );

      Step 4,启动Server。
      例子:

       try
       {
         WebServer http 
    = new WebServer( 7272, roots );
         http.start();
       }
       
    catch( IOException e )
       {
         e.printStackTrace();
       }

      Step 5,采用URL从server获取资源。
      例子:

       URL crossRefUrl = new URL( "http://localhost:7272/crossref/index.html" );
       URL url 
    = new URL( "http://localhost:7272/test.LookAndFeelTest/themepack.zip" );
       URL url 
    = new URL( "http://localhost:7272/test.LookAndFeelTest/html/index.html" );
       URL url 
    = new URL( "http://localhost:7272/test.LookAndFeelTest/images/example.png" );
    posted @ 2005-05-17 22:11 dev 阅读(1077) | 评论 (4)编辑 收藏
     

    我使用的是GEF0.10(http://gef.tigris.org),在某一个Fig上点击鼠标右键弹出PopupMenu时,发现有闪烁的现象,经过追查发现,一个鼠标右击动作会分别触发Editor.mousePressed(e)、mouseReleased(e)、mouseClicked(e)三个事件,这些事件最终都要流经ModeManager,而ModeManager采取的是广播的方式分发这些事件,把这些事件依次转发给mode对列中的所有Mode。ModePopup是默认的一个Mode之一,负责提供Fig的弹出菜单功能。而ModePopup中的这三个事件都作了如下的判断: 

       public void mouseClicked(MouseEvent me) {
            boolean popUpDisplayed 
    = false;
            
    if(me.isPopupTrigger() || me.getModifiers() == InputEvent.BUTTON3_MASK) {
                popUpDisplayed 
    = showPopup(me);
                
    if (LOG.isDebugEnabled()) {
                    
    if (popUpDisplayed) LOG.debug("MousePressed detected as a popup and popup displayed and event consumed");
                    
    else  LOG.debug("MousePressed detected as a popup but no popup to display");
                }
                
    return;
         }
            LOG.debug(
    "MousePressed is not a popup trigger");
        }

     

    其中加红的这段不知道为什么要作me.getModifiers() == InputEvent.BUTTON3_MASK的判断,加上这个判断后只要是鼠标右击动作,ModePopup的mousePressed、mouseReleased和mouseClicked事件中showPopup的代码都要被执行,这样在一个鼠标右击动作中实际上弹出了三次PopupMenu,因此造成了闪烁。解决办法是extend默认的ModePopup,覆盖这三个方法,将判断改成if ( me.isPopupTrigger())即可。

    posted @ 2005-05-17 22:10 dev 阅读(611) | 评论 (0)编辑 收藏
     

    今天做了一个测试,发现Hibernate的dynamic-update只在两种条件下生效:

    1。同一session内,对已经persisit的对象进行update,这里的“已经persist”是指update之前先进行了create或者load调用。代码示例:

    Session session = openSession();
    User user 
    = (User)session.load(User.class,new Long(12));
    user.setAddress(
    null);
    session.update(user);
    session.flush();

    将hibernate配置成show_sql=true,可以看到update产生的sql语句。

    2。不同session之间,update传入的对象是另一个session中的persist对象(对该对象调用了create或者load方法)。代码示例:

    Session session1 = openSession();
    User user 
    = (User)session1.load(User.class,new Long(12));

    Session session2 
    = openSession();
    user.setAddress(
    null);
    session2.merge(user);
    session2.flush();


    如果将session2.merge(..)改成update,则会更新所有可更新的属性。

    posted @ 2005-05-17 22:10 dev 阅读(2193) | 评论 (0)编辑 收藏
     

    今天在csdn上看到了一篇介绍PDCA理论在项目管理中应用的blog,很是感慨,想想才到公司的时候老总也给我们做过相关培训,一年下来,要是没有看到这篇blog,铁定是想不起来了。PDCA(计划、实施、检察、优化)是一个很好的理论,现摘录这篇blog的内容如下,以为珍藏。

    以下内容出自:http://blog.csdn.net/wwwxuhong/archive/2004/12/20/222804.aspx 

    项目管理是个很大的课题
    所有的事物都有其规律

    项目管理的规律是什么?
    前几天看到一个理论让我眼睛一亮,陈述如下,希望对大家有点启发

    PDCA循环是由美国统计学家戴明博士提出来的,它反映了质量管理活动的规律。P(Plan)表示计划;D(Do)表示执行;C(Check)表示检查;A(Action)表示处理。PDCA循环是提高产品质量,改善企业经营管理的重要方法,是质量保证体系运转的基本方式。

    项目管理的PDCA环。
    PDCA是指以下四个阶段,这四个阶段是环环相扣的,这个周期是周而复始的
    P  Plan  计划
    D  Do    实施
    C Check 检查
    A Action 总结、再优化

    不管是多大的项目,还是多小的任务,如果都用PDCA环实施,环环相扣,就可以大大提高管理的质量,最大程度地保障项目的成功实施。

    PDCA分以下八上步骤
    计划阶段
        1、分析现状
        2、找出问题的原因
        3、分析产生问题的原因
        4、找出其中的主要原因
        5、拟订措施计划
    实施阶段
        6、执行技术组织措施计划
    检查阶段
        7、把执行结果与预定目标对比
    总结、再优化阶段
        8、巩固成绩,进行标准化

    转贴一篇相关文章
      在企业中,通过众多小小的变革可能实现对整个企业的持久改善,从而获得巨大的成效。这在日语中叫做“改善”(kaizen),每一步都很小,这儿一个小变化,那儿一个小改进,但几年后就能发展出完全不同的产品、工序或服务。
      PDCA循环是由美国统计学家戴明博士提出来的,它反映了质量管理活动的规律。P(Plan)表示计划;D(Do)表示执行;C(Check)表示检查;A(Action)表示处理。PDCA循环是提高产品质量,改善企业经营管理的重要方法,是质量保证体系运转的基本方式。

      PDCA循环的特点PDCA表明了质量管理活动的四个阶段,每个阶段又分为若干步骤。

      在计划阶段,要通过市场调查、用户访问等,摸清用户对产品质量的要求,确定质量政策、质量目标和质量计划等。它包括现状调查、原因分析、确定要因和制定计划四个步骤。

      在执行阶段,要实施上一阶段所规定的内容,如根据质量标准进行产品设计、试制、试验,其中包括计划执行前的人员培训。它只有一个步骤:执行计划。

      在检查阶段,主要是在计划执行过程之中或执行之后,检查执行情况,看是否符合计划的预期结果。该阶段也只有一个步骤:效果检查。

      在处理阶段,主要是根据检查结果,采取相应的措施。巩固成绩,把成功的经验尽可能纳入标准,进行标准化,遗留问题则转入下一个PDCA循环去解决。它包括两个步骤:巩固措施和下一步的打算。

      PDCA循环四阶段各步骤。

      1.PDCA循环一定要按顺序进行,它靠组织的力量来推动,像车轮一样向前滚进,周而复始,不断循环。

      2.企业每个科室、车间、工段、班组,直至个人的工作,均有一个PDCA循环,这样一层一层地解决问题,而且大环套小环,一环扣一环,小环保大环,推动大循环。

      这里,大环与小环的关系,主要是通过质量计划指标连接起来,上一级的管理循环是下一级管理循环的根据,下一级的管理循环又是上一级管理循环的组成部分和具体保证。通过各个小循环的不断转动,推动上一级循环,以至整个企业循环不停转动。通过各方面的循环,把企业各项工作有机地组织起来,纳入企业质量保证体系,实现总的预定质量目标。因此,PDCA循环的转动,不是哪一个人的力量,而是组织的力量、集体的力量,是整个企业全体职工推动的结果。

      3.每通过一次PDCA循环,都要进行总结,提出新目标,再进行第二次PDCA循环,使质量管理的车轮滚滚向前。PDCA每循环一次,质量水平和管理水平均提高一步。

      PDCA循环不仅是质量管理活动规律的科学总结,是开展质量管理活动的科学程序,也是一种科学管理的工作方法。它同样可以在质量管理活动以外发挥重要效用。

      PDCA管理法在营销中的运用在运用PDCA循环进行市场营销管理方面,已经有一些优秀企业走在了前头。

      海尔集团纯熟地采用PDCA管理法来实施销售任务的计划、组织和控制。每年年终,集团商流、各产品本部根据本年度的销售额完成情况,结合各产品的发展趋势及竞争对手分析等信息,制定下一年度的销售计划,然后将这一计划分解至全国11个销售事业部。销售事业部长根据各工贸上年的完成情况、市场状况分析等信息再将销售额计划分解至其下属各工贸公司。工贸公司总经理将任务分解至各区域经理,由他们将任务下达至区域代表,区域代表将自己的销售额任务分解至其所管辖的营销网络。同时,海尔还从时间纬度上进行分解:年度计划分解至月度,月度计划分解至每日。这样,处于管理层的每位管理者都可以对下属每日的工作状况进行监督,并及时实施纠偏,最终控制每一个具体网点。海尔集团在新产品开发、新品上市等所有方面都遵循PDCA管理方法。这种做法可以保证“人人都管事,事事有人管”,避免出现管理的真空。

      PDCA管理法运用于每日的事务管理,就形成了独具海尔特色的OEC日清体系。每人均处于相应的岗位上,每一岗位均有不同的职责,并分配相应的指标,员工的激励直接与指标挂钩。指标又可分为主项指标与辅项指标以及临时任务指标等。每人在当日晚上分析一天的各项任务完成情况,并找出差距原因及纠偏办法,以使今后的工作质量得到提高,由此构成了持续不断的改进过程。员工在做完当日总结后,对明日工作做出计划,然后将OEC日清表交至主管领导处,由主管领导进行审核控制并对下属的当日工作进行评价和激励。

      OEC管理法的主要理念,海尔认为是“坚持两个原则,最大限度地对待两种人”,即坚持闭环原则,坚持优化原则,最大限度地关心员工的生活,最大限度地满足用户的需求。所谓闭环原则,指凡事要善始善终,都必须遵循PDCA循环,而且是螺旋上升。所谓优化原则,指根据木桶理论,找出薄弱项,及时整改,提高全系统的水平。在一个企业的运营过程中,必然存在着许多环节,只要找出制约企业经济效益提高的某一关键环节,把首要矛盾解决了,其他矛盾就可以迎刃而解。

      张瑞敏说,海尔生产线每天要出大大小小几万台家电产品,我们不能考虑出了问题如何处理,而要追求不出任何问题。OEC管理法把质量互变规律作为基本思想,坚持日事日清,积沙成塔,使员工素养、企业素质与管理水平的提高寓于每日工作之中,通过日积月累的管理进步,使生产力诸要素的组合与运行达到合理优化的状态,不增加投入就可使现实生产力获得尽可能大的提高,从而令管理收到事半功倍的效果。

      海尔把PDCA运用到企业内部的营销队伍管理上,那么,这种管理方法对外部营销是否适用呢?

      上海通用汽车公司成功地把此方法应用于自己的经销体系中,极大地改善了经销商的服务。在其近100家经销商中,上海通用奉行的政策是,对一些业务表现不好、不能完成上海通用的要求、不能在市场上进行有效的开拓,或者在售后服务方面不能够完全按照上海通用的理念和规范去操作的经销商,会先给他们做一个PDCA改进计划。完成了这个计划性的四部曲后,经销商的整个市场营销的管理工作应该会随之步入一个良性循环的轨道。如果还是不行,经销商就会被淘汰掉。

      由上可知,PDCA管理法的核心在于通过持续不断的改进,使企业的各项事务在有效控制的状态下向预定目标发展。

    posted @ 2005-05-17 22:09 dev 阅读(1821) | 评论 (0)编辑 收藏
     

      先前的项目采用的是TyrexFactory作为事务工厂的实现,但是运行的过程中发现很不稳定,处理大对象时容易出现事务超时的错误,即使事务设置的超时时间很长也是这样,将jotm及其相关jar copy 到lib中之后,换成JotmFactory,发现TransactionFactory.getTransactionFactory竟然报NoSuchElementException。后来发现是carol.jar中的CarolConfiguration需要装载jndi.properties文件进行初始化,而tyrex.jar自身有一个同名的文件,正是因为CoralConfiguration装载了这个同名的文件才产生了异常,去掉tyrex.jar就可以了。

    posted @ 2005-05-17 22:08 dev 阅读(444) | 评论 (0)编辑 收藏
     
    Ofbiz2.1有两个bug,都涉及到线程安全性,小并发的时候不容易发现,大并发下有时候会出现,并发数越高出现的频度就比较高,尤其对于实体引擎的那个bug,在系统初始化的时候如果遭遇大并发,会有一定频度的出现。
     
    1。entity engine的ModelEntity.getField方法存在线程安全隐患,会造成 XXXX is not a field of XXX的异常,以下是原有代码片断:

        
    public ModelField getField(String fieldName) {
            
    if (fieldName == nullreturn null;
            if (fieldsMap == null) {
                fieldsMap = new HashMap(fields.size());
                for (int i = 0; i < fields.size(); i++) {
                    ModelField field = (ModelField) fields.get(i);
                    fieldsMap.put(field.name, field);
            }
            return (ModelField) fieldsMap.get(fieldName);
        }

    由于getField方法没有同步(会造成性能下降),因此红色标标注的那段代码存在线程安全问题,必须进行同步。在大并发下如果多个调用这个方法,最先调用的线程没有执行完循环的情况下,后续的线程通过最后的语句return的时候得到的就是Null(fieldsMap已经被第一个线程赋值了,后续线程不会进入红色标准的代码区域)。
    修改后的代码如下:
       public ModelField getField(String fieldName) {
            
    if (fieldName == nullreturn null;
            
    if (fieldsMap == null) {
                 createFields();
            }
            
    return (ModelField) fieldsMap.get(fieldName);
        }

        
    public synchronized void createFields()
        {
                 fieldsMap 
    = new HashMap(fields.size());
     
                 
    for (int i = 0; i < fields.size(); i++) {
                     ModelField field 
    = (ModelField) fields.get(i);
     
                     fieldsMap.put(field.name, field);
                 }
        }
     
    这个Bug在3.0中已经被修正。
     
    2。UtilCache.get方法同样存在线程安全隐患,会造成LinkedList.remove或者LinedList.addFirst的空值针异常,不注意还会以为是LinkedList的bug。以下是原代码片断:
        public Object get(Object key) {
            
    if (key == null) {
                missCount
    ++;
                
    return null;
            }
            UtilCache.CacheLine line 
    = (UtilCache.CacheLine) cacheLineTable.get(key);
            
    if (hasExpired(line)) {
                
    // note that print.info in debug.properties cannot be checked through UtilProperties here, it would cause infinite recursion
                
    // if (Debug.infoOn()) Debug.logInfo("Element has expired with key " + key);
                remove(key);
                line 
    = null;
            }
            
    if (line == null) {
                
    // if (Debug.infoOn()) Debug.logInfo("Element not found with key " + key);
                missCount++;
                
    return null;
            }
            
    // if (Debug.infoOn()) Debug.logInfo("Element found with key " + key);
            hitCount++;
            if (maxSize > 0) {
                keyLRUList.remove(key);
                keyLRUList.addFirst(key);
            }

            return line.getValue();
        }
    红色标准的部分是有问题的代码,修改后的代码如下:
        public Object get(Object key) {
            
    if (key == null) {
                missCount
    ++;
                
    return null;
            }
            UtilCache.CacheLine line 
    = (UtilCache.CacheLine) cacheLineTable.get(key);
            
    if (hasExpired(line)) {
                
    // note that print.info in debug.properties cannot be checked through UtilProperties here, it would cause infinite recursion
                
    // if (Debug.infoOn()) Debug.logInfo("Element has expired with key " + key);
                remove(key);
                line 
    = null;
            }
            
    if (line == null) {
                
    // if (Debug.infoOn()) Debug.logInfo("Element not found with key " + key);
                missCount++;
                
    return null;
            }
            
    // if (Debug.infoOn()) Debug.logInfo("Element found with key " + key);
            hitCount++;
            
    if (maxSize > 0) {
                synchronized ( 
    this)
                {
                     keyLRUList.remove(key);
                     keyLRUList.addFirst(key);
                }
            }
            
    return line.getValue();
        }

    这个BUG在3.0种也修正了。
    posted @ 2005-05-17 22:07 dev 阅读(310) | 评论 (0)编辑 收藏
     
    1。XAPool是如何wrap jdbc driver返回的PreparedStatement的。
    以下是StandardConnectionHandler中的checkPreparedCache代码片断
        synchronized PreparedStatement checkPreparedCache(
            String sql,
            
    int type,
            
    int concurrency)
            throws SQLException {
            log.debug(
                
    "StandardConnectionHandle:checkPreparedCache sql='" + sql + "'");
            PreparedStatement ret 
    = null// the return value
            
    // NOTE - We include the Connection in the lookup key. This has no
            
    // effect here but is needed by StandardXAConnection where the the physical
            
    // Connection used can vary over time depending on the global transaction.
            String lookupKey = sql + type + concurrency;
            
    // used to lookup statements
            if (preparedStatementCache != null) {
                Object obj 
    = preparedStatementCache.get(lookupKey);
                
    // see if there's a PreparedStatement already
                if (obj != null) { // if there is
                    ret = (PreparedStatement) obj; // use as return value
                    try {
                        ret.clearParameters(); 
    // make it look like new
                    } catch (SQLException e) {
                        
    // Bad statement, so we have to create a new one
                        ret = createPreparedStatement(sql, type, concurrency);
                    }

                    preparedStatementCache.remove(lookupKey);
                    
    // make sure it cannot be re-used
                    inUse.put(lookupKey, ret);
                    
    // make sure it gets reused by later delegates
                } else { // no PreparedStatement ready
                    ret = createPreparedStatement(sql, type, concurrency);
                    inUse.put(lookupKey, ret);
                    
    // will get saved in prepared statement cache
                }
            } 
    else {
                ret 
    = createPreparedStatement(sql, type, concurrency);
            }
            
    // We don't actually give the application a real PreparedStatement. Instead
            
    // they get a StandardPreparedStatement that delegates everything except
            
    // PreparedStatement.close();

            ret 
    = new StandardPreparedStatement(this, ret, lookupKey);
            
    return ret;
        }

    2。StandardPreparedStatement的Close方法代码片断
        public void close() throws SQLException {
            
    // Note no check for already closed - some servers make mistakes
            closed = true;
            
    if (con.preparedStmtCacheSize == 0) {
                
    // no cache, so we just close
                if (ps != null) {
                    ps.close();
                }
            } 
    else {
                con.returnToCache(key);
                
    // return the underlying statement to the cache
            }
        }

    3。xapool StandardPoolDataSource的getConnection 原理:
         StandardPoolDataSource.getConnection --> GenericPool.checkOut-->StandardPoolDataSource.create -->StandardPoolDataSource.getPooledConnection:返回StandardPooledConnection。
     
         StandardPooledConnection通过StandardDataSource.getConnection获取jdbc driver返回的connection(physical connection),然后通过工厂方法newConnectionHandle采用StandardConnectionHandler对该connection进行包装。StandardConnectionHandler对PreparedStatement进行重新包装和Cache,对connection.close进行了控制。
     
    4。xapool StandardXAPoolDataSource的getConnection的原理:
       StandardXAPoolDataSource.getConnection -->StandardPoolDataSource.getConnection-->StandardXAPoolDataSource.create -->XADataSource.getXAConnection -->StandardXADatasource.getXAConnection:返回StandardXAConnection。
        StandardXAConnection通过StandardDataSource.getConnection获取jdbc driver返回的connection(physical connection),然后通过工厂方法newConnectionHandle采用StandardXAConnectionHandle对该connection进行包装。StandardXAConnectionHandler继承自StandardConnectionHandler。
     
    5。xapool从StandardPoolDataSource获取的 Connection的关闭原理。
       StandardPooledConnection.close-->StandardConnectionHandler.close(设置closed=true,回收PreparedStatement)-->StandardPooledConnection.closeEvent-->StandardPoolDataSource.connectionClosed-->GenericPool.checkIn(返回连接池)
     
    6.xapool中对连接池进行管理的类是GenericPool,该类的checkOut和checkIn方法分别完成连接获取和连接回收功能。checkOut从unlocked池中获取可用的连接,如果需要进行检查或者测试,然后返回;如果发现unlocked池中没有连接,在连接数小于maxSize的时候调用PoolHelper的实现类创建连接,如果连接数已经达到或者超过maxSize,调用wait使当前进程进入等待状态(等待期限和等待间隔可以设置),如果等待过程中其他线程释放了connection返回可用的connection,否则异常:GenericPool:checkOut ERROR  impossible to obtain a new object from the pool
    posted @ 2005-05-17 22:07 dev 阅读(1806) | 评论 (0)编辑 收藏
     
    1. sleepTime:PoolKeeper检测时间间隔
    2. lifeTime:连接生命周期(上次访问时间-当前时间)
    3. deadLockMaxWait(:超过最大连接之后的调用getConnection的等待时间
    4. deadLockRetryWait:超过最大连接之后的调用getConnection等待,在等待中重试的时间间隔
    5. maxSize:连接池的容量

    deald-lock-max-wait和dead-lock-retry-wait的设置要小心,这两个参数的意义见我的另一个日志:XAPool原理简要分析。dead-lock-retry-wait最好设置得比较短,这样不至于线程等待很长时间,dead-lock-max-wait的设置不要太长,一般是设置成比最高并发数下应用处理时间稍长一点,设置过短在大并发下会造成提交实效导致应用数据的丢失,因为超过xapool在超过等待dead-lock-max-wait之后会异常:没有可用连接分配。

     

    sleepTime是对Connection idle检测线程PoolKeeper的检测时间间隔设置。PoolKeeper会定时监测是否存在超过lifeTime的connection然后释放掉这些connection。不过PoolKeeper在运行的时候会检查running属性,以下是它的run方法中的代码片断:

      while (! running && !Thread.interrupted()) {
          System.err.println(
    "!!!!"+System.currentTimeMillis());
       
    try {
        synchronized (
    this) {
         wait(
    this.sleepTime); // wait for timeout ms before attack
        }
       } 
    catch (InterruptedException e) {
                                    
    break;
       }
       
    this.pool.cleanUp(); // clean up the Pool and reallocate objects
      }
      
    // release the pool.
      this.pool = null;

    之所以把这段代码粘出来,是因为running属性默认是true,而GenericPool在启动PookKeeper的时候并没有改变这个值,因此PookKeeper永远不会运行起来。也许这是xapool的另一个bug:)

    连接池的容量设置是有讲究的,一般至少等于AppServer(或者叫WEB 容器)的最大并发数。因为xapool在达到maxSize的时候,如果还有线程需要连接,会进入等待状态(通过deadLockMaxWait设置最大等待时间,deadLockRetryWait设置等待间隔),在大并发下会造成App Server容器线程池满,Server在一段时间内(deadLockMaxWait)停止响应的现象。将连接池的容量设置成大于App Server的最大并发数,可以尽可能的避免这种情况。App Server的最大并发数=App Server的线程池线程数,Tomcat默认是75,Websphere默认是50。集群环境下,集群的最大并发数=每台集群服务器的最大并发数之和

    posted @ 2005-05-17 22:06 dev 阅读(2388) | 评论 (1)编辑 收藏
     
    现状:我们的项目中使用了ofbiz2.1,并采用JotmFactory作为TransactionFactory,使用Oracle9i数据库,在大并发测试的时候发现数据库游标暴涨并且不释放,最终导致游标溢出。
     
    原因分析:ofbiz  entityengine的很多操作都是使用PreparedStatement完成的,这无可厚非,问题是JotmFactory采用的是XAPool作为连接池,而XAPool对PreparedStatement进行了Cache,同时Oracle有一个出名的内存漏洞,PreparedStatement使用之后必须关闭,如果不关闭连续进行SQL查询会造成前面SQL的游标不能释放;此外JotmConnectionFactory没有允许对XAPool做更多的配置,按照它使用XAPool的方式,XAPool会对PreparedStatement进行Cache。Oracle漏洞+ofbiz的不周全的使用方式+xapool的机制造成了游标不释放最终溢出的异常。
     
     
    解决办法:修改JotmConnectionFactory,调用StandardXAPoolDataSource的setPreparedStmtCacheSize(int)的方法,将preparedStmtCacheSize设置为0。需要注意的是xapool在目前的版本(1.4)当preparedStmtCacheSize=0的时候存在一个bug,close PreparedStatement的时候会报NullPointerException,请参考我的另一个日志XAPool1.4的bug
     
    posted @ 2005-05-17 22:06 dev 阅读(556) | 评论 (0)编辑 收藏
     
    现状:XAPool1.4有一个bug,当设置preparedStmtCacheSize=0的时候,关闭连接会抛出NullPointerException
     
    原因:StandardConnectionHandle在Close的时候调用了preparedStatementCache.cleanupAll(),没有进行判断;而当preparedStmtCacheSize设置为0的时候,StandardConnectionHandle在setupPreparedStatementCache中把preparedStatementCache设置为null,以下setupPreparedStatementCache是方法中的源代码片断:
     
     protected void setupPreparedStatementCache() {
      log.debug(
    "StandardConnectionHandle:setupPreparedStatementCache start");
      
    if (preparedStmtCacheSize == 0) {
       log.debug(
        
    "StandardConnectionHandle:setupPreparedStatementCache return with 0");
       preparedStatementCache 
    = null;
       
    return;
      }
     
    解决办法:修改setupPreparedStatementCache,如下:
      if (preparedStmtCacheSize == 0) {
       log.debug(
        
    "StandardConnectionHandle:setupPreparedStatementCache return with 0");
    //   preparedStatementCache = null;
       preparedStatementCache = new PreparedStatementCache(0);
       
    return;
      }
    posted @ 2005-05-17 22:05 dev 阅读(248) | 评论 (0)编辑 收藏
     
    现状:在我们的项目中使用了Ofbiz2.1,TransactionFactory配置为JotmFactory,数据库是Oracle9i,lifeTime设置为120000(2分钟)在大并发测试的时候发现经常Closed Connection的异常。
     
    原因:Oracle数据库会检查physical connection的idle时间和使用次数并关闭长时间idle的physical connection,如果应用对从连接池中
    获取的connection不进行检查,或者连接池仔返回可用连接之前不进行检查,在使用过程中就会Closed Connection的异常。大多数数据库都会检查physical connection的idle时间。
     
    解决办法:调用StandardPoolDataSource或者StandardXAPoolDataSource的setCheckLevelObject(int)方法设置连接检查级别,参数取值如下:
    1. 0:不检查
    2. 1:对unlocked池中获取的连接进行Closed检查
    3. 2:对unlocked池中获取的连接进行sql测试,需要设置setJdbcTestStmt(Test SQL)
    4. 3:对所有unlocked池中的连接进行Closed检查
    5. 4:对所有unloked池中的连接进行测试,需要设置setJdbcTestStmt(Test SQL)
     
     
    posted @ 2005-05-17 22:04 dev 阅读(1131) | 评论 (0)编辑 收藏
     

    打算将我在http://jdev.blogdriver.com 的blog搬过来。

    posted @ 2005-05-16 23:56 dev 阅读(138) | 评论 (0)编辑 收藏