• 近期总结 - [自写]

    2008-05-11

    有好些日子没有写Blog了,奥运越来越近,工作是越来越忙了,呵呵。

    最近主要是在搞公司的工作, 抽空把Scala研究了一下。

    用Scala写了一些程序,因为还不熟悉,所以经常要查文档,看资料。

    着重研究了一下Scala的Actor用法,在此推荐两篇论文: 

    Event-Based Programming without Inversion of Control 和  Actors that Unify Threads and Events,

    这两个论文都是在Actor 的API中引用的。

    Sala的Actor即有Erlang中轻量级Process的优势,又可以直接当作本地线程使用。

    我总结了一下,对于Actor的使用,基本上可以根据任务的特点来决定使用哪种了:

    1.cpu相关的

    对于这种任务,无论是Erlang的Process,还是Scala的Actor都可以支持得很好,Scala的实现是用一个线程池来执行Actor,它可以根据cpu的个数决定线程池中工作线程的个数,同时根据任务的个数,适时的调整这工作线程的个数。

    写法在Scala的例子中有很多,类似如下: 

    loop {      react {  ........           }  } 



    2.io相关的

    这种任务主要的工作不在cpu,而在IO操作上,比如读写Socke等,类似这种操作的时候,最好使用Actor的线程特性,也就是在ract的时候,根据参数新创建一个Actor来执行和IO相关的任务,下面的“主Actor”的任务是接收消息,每收到一个消息就启动一个Actor去请求页面的内容,然后再做相关的处理,这也是Actors that Unify Threads and Events 里所主要表达的内容。

    loop {  
        react {  
           case url:String =>
              actor{
                 //由个新建的actor执行IO操作,避免阻塞后续的其它的请求
                  val response = HttpUtil.get(url) 
             }          
     }  
    } 

    用Scala写并发方面的程序要比Java来得容易,Actor的使用要小心一些,否则不小心就搞出一个“串行”来,呵呵。

    说说Struts2吧, 

     很久没有用过Struts2之类的Web框架了,最近在用Struts2,开始是同事基本上把环境都搭好了,我只需要下载更新后,按步就班地开发就好了。昨天需要我从开始弄一个应用,那就一个背。照猫画虎地把应用弄起来,写action,写jsp,还算顺利。这时发现了一个问题,因为我用的了所谓的"模型驱动"这种参数注入方式,OGNL以前也用过,它可以根据表达来给Action中的对象属性设置由页面中传入的参数。

    private Sample sample;

    public void setSample(Sample sample){

    this.sample = sample;

    }

    有了上面这段代码,OGNL就能自动地把提交的参数设置到sample这个对象的属性中,比如sample.user.

    这就让我产生了一个疑问,如果用户知道我们的Action中的其它setter方法,那会不会受到”攻击“呢。

    我试了一下,  

    private OwnSample ownSample;

    public void setOwnSample(OwnSample sample){

    this.OwnSample = sample;

    }

     假设用户知道有这么一个属性可以用,那种他在发送请求的时候会加上一个ownSample.id=XX之类的参数,就会造成OGNL首先会生成一个OwnSample对象,然后设置属性,然后调用 setOwnSample设置给Action中。如果这个参数是使用其它的途径注入,如用Spring,那用户设置的参数会被覆盖掉(在这种情况下也是有害的,因为无谓地多生成了一次对象,还要看创建对象的代价大小,如果是那种比较费时的创建那就惨了),反之用户的参数就会生效了。

    虽然是内部使用的程序,我觉得还是能避免这种问题最好,找了一下API,发现可以定义一个acceptableParameterName的方法来过滤一下参数就保险了。

    类似的问题还有Struts2在配置文件中定义Action的时候,支持通配符,例如:

    <action name="user_*" class="com.UserAction" method="{1}">

    这种配置我觉得也是有风险的,如果UserAction里有一个一般用户都不知道的方法,那么如果被猜出来,或者被“离职的职员”恶意访问,问题就大了,除非对这种方法有额外的权限检查。

    所以如果有这种问题,还是勤快点儿,尽量少用通配符的好,呵呵。

    这两个问题是我正经用了两天Struts2之后感觉不太对的地方,写出来,也可能有更好的解决办法,供大家留意一下。

    多说一句,现在的各种框架的Demo 或者样例一般是怎么简单怎么来,让人一看就觉得这太简单了,太好用了,省得我老得request.getParameter一个一个的把参数凑整齐再用,这么用多方便。我们还是小心点好,Demo中的例子,不一定在生产环境中靠谱,还需要我们自己推敲一下。:)

     

  • Tapestry4之前在Page类中访问HttpSession或HttpRequest是很方便的,这样子就可以了IRequestCycle.getRequestContext().getSession(),昨天看了一下它的源代码,竟然有这样子的注释“ 

      /**
         * Returns the {@link RequestContext}. This is provided to ease the upgrade from Tapestry 3.0.
         *
         * @deprecated To be removed in 4.1.
         */

    在4.1的时候会被删除,虽然我现在用得还是4.0,考虑到以后的升级还是决定不这样做了。

    那只好这样子用了

    /**注入Request*/

    @InjectObject("infrastructure:request")
     public abstract WebRequest getRequest();

    public void login(IRequestCycle cycle) {
        this.getRequest().getSession(true).setAttribute("user", getName());
    }

    上面的方法能够在session中保存对象,可想删除怎么办呢?    this.getRequest().getSession(true)这样子得到的对象是Tapestry包装的WebSession,当中竟然没有removeAttribute这个方法,郁闷。看看Api中是怎么说的,attribute  the new value for the attribute, or null to delete the attribute entirely.

    找到答案了,把属性设为null就从session中删除这个对象了。

     this.getRequest().getSession(true).setAttribute("user", null);

    用了一段时间Tapestry觉得在开发的时候最好看看它的源代码,有些问题文档上没有,看源代码就很明白了。

    最近在做新的项目,竟然把以前一想就头大的javascript用了不少,第一次发现原来javascript可以这样写,在写之前看了些Prototype的东西,在写我们自己的项目的一些页面功能时,参照着Prototype的方法来做,竟然也有点面向对象编程的感觉。

    刚过完五一,工作起来状态恢复得慢,今天才差不多有些感觉了,继续努力工作,好好学习:)

  • 最近在工作中遇到一个问题,有一个jsp很大,其中包括了有300多个tag,反应是无法编译(在有的服务器上可以编译,但无法加载),打开生成的java文件,发现在jspService中有超过5000行的代码,其生成的class的code属性的长度超过了65535这个限制,jvm加载的时候认为是无效的class文件,抛出异常。必须给它减肥了,再就是把文件的内容分成两个文件来存放,分别编译,解决问题。

    今天突然又想起这个问题了,想看看Tapestry是如何应对这种情况的呢,另外今天开始学习asp.net,也想看看它的反应如何,由于写了一个变态的测试程序,生成所需要的测试文件。

    下面是python脚本:


    #生成taspstry的测试文件, 生成20000个tagestry 标记
    f=open("t.html","w")
    s0="""<span jwcid="$content$">"""
    s1="""<div><span jwcid="@Insert" value="ognl:msg" raw="true"/></div>"""
    s2="</span>"
    f.write(s0)
    i = 0
    while i<20000:
     f.write(s1+"\n")
     i=i+1
    f.write(s2)
    f.close()

    #生成jsp的测试文件, 生成2000个jsp tag标记
    f=open("t1.jsp","w")
    s0="""<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>"""
    s1="""<div><c:out value="test"/></div>"""
    f.write(s0)
    i = 0
    while i<2000:
     f.write(s1+"\n")
     i=i+1
    f.close()

    #生成asp.net的测试文件, 生成20000个Label组件
    s0="""<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <title>Untitled Page</title>
    </head>
    <body>"""

    s1 = """<asp:Label ID="Label%d" runat="server" Height="32px" Width="72px"/> """
    s2="""</body>
    </html>"""

    f=open("t1.aspx","w")
    f.write(s0)
    i = 0
    while i<20000:
     f.write(s1%(i)+"\n")
     i=i+1
    f.write(s2)
    f.close()
     

  • 在websphere上做数据库的开发,一定要注意数据库获取连接的问题。因为websphere会自动的有一个本地事务处理的规则,如果在应用程序日志中发现有

    J2CA0086W

    这样的信息,那就一定要检查程序中管理数据库连接的方法地。典型的情况下,这种问题是由于下面的调用方法导致的:

    get connection1

    use connection1

    get connection2 

    use connection2

    close connection 2

    close connection 1

    就是在已经取得一个连接的情况下,在没有关闭它之前又取得了一个连接,由于websphere的本事务规则会把它们放到shared pool中,会引起连接不够用。导致系统中大量的线程等待连接.

  •  前段时间,websphere总是当机,其实也不是当,就是响应特别的慢,几乎没有响应了。实在没有办法只好生成javacore,看一看线程的情况。他们是每隔10分左右生成一个javacore,在websphere中,生成的内容全部在native_stdout.log中,开始看的时候发现有很多线程的调用栈中都有

    - locked <0xc2527c58> (a oracle.jdbc.ttc7.TTC7Protocol)

    我怀疑可能是这里的问题,用眼看那些日志可真够受的了,也得不到什么有用的信息。就用python写了一个分析程序,把这些locked全找出来,于是就写了下面这个小程序:

    # -*- coding: cp936 -*-
    import sys
    import re
    pattern = '[\s*]- locked <0x(.*)>.*
    \\(a oracle.jdbc.*\\)'
    prog = re.compile(pattern)
    lockMap = {}
    lineMap = {}
    if __name__ == "__main__":
        if(len(sys.argv)<2):
            print '缺少参数'
        print sys.argv[1]
        print '行数','地址','内容'
        dump = open(sys.argv[1],'rb')
        line=dump.readline()
        ln = 1
        lock = 0
        crash = 0
        while line!='':
            #print line       
            result = prog.match(line)
            #print result
            if(result != None):
                lock = lock +1
                objectref = result.group(1)
               
                if lockMap.has_key(objectref):
                    crash = crash + 1
                    lockMap[objectref].append(ln)
                else:
                    lockMap[objectref]=[ln]

                lineMap[ln] = result.group(0)
                print str(ln),objectref,result.group(0)
            line = dump.readline()
            ln = ln+1
        print '共有'+str(lock)+'个锁对象出现'

        lockkeys = lockMap.keys()
        print '\n'+str(crash)+'个锁被两个或两个以上的线程取得\n'
        print '锁对象\t在log中出现的行数'
        for key in lockkeys:
            print str(key)
            for lc in lockMap[key]:
                print '\t'+str(lc)+str(lineMap[lc])
        #print lockMap
        sys.exit(0)

    通过对native_stdout.log运行这个程序,得出的结果就是:

    ......

    共有288个锁对象出现

    144个锁被两个或两个以上的线程取得

    锁对象 在log中出现的行数
    c0e6aef0
     2562 - locked <0xc0e6aef0> (a oracle.jdbc.ttc7.TTC7Protocol)
     5995 - locked <0xc0e6aef0> (a oracle.jdbc.ttc7.TTC7Protocol)
    c04e1ae8
     2990 - locked <0xc04e1ae8> (a oracle.jdbc.driver.OracleConnection)
     6423 - locked <0xc04e1ae8> (a oracle.jdbc.driver.OracleConnection)
    c1da32c8
     2019 - locked <0xc1da32c8> (a oracle.jdbc.driver.OraclePreparedStatement)
     5452 - locked <0xc1da32c8> (a oracle.jdbc.driver.OraclePreparedStatement)

    ..............

    由这个结果可以知道,144个锁在线程中被引用,通过结果中显示的行数,locked后面的对象锁始终是被同一个线程持有,比如:

    2019 - locked <0xc1da32c8> (a oracle.jdbc.driver.OraclePreparedStatement)
     5452 - locked <0xc1da32c8> (a oracle.jdbc.driver.OraclePreparedStatement)

    这里有一个对象<0xc1da32c8>在同步块里,而在2019和5452这两行上,找到它们的线程号,是同一个,其余的也是一样的结果。这就排除了死锁的可能,也初步排除了程序造成死锁的可能。

    而在每个这样的输出中,都有下面的内容:

    at java.net.SocketInputStream.socketRead0(Native Method)
     at java.net.SocketInputStream.read(SocketInputStream.java:129)
     at oracle.net.ns.Packet.receive(Unknown Source)

    就是由于在读数据的时候线程被阻塞了,导致很多的线程被挂起,在这里是77个线程被挂起了。

    我怀疑是oracle的jdbc驱动的问题,于是在网上找资料,果然有相似的问题。于是就按照oracle metalink上所说的,下了一个10g的驱动来用,(忘记说了,我们用的是9.2.0.5那一个驱动),在本地测试了一下,问题没有再现,把服务器上的驱动换掉,现在已经是第3天了,一直没有当机,而且连接也很正常。:)

    9.2.0.5的驱动有严重的bug,它可能会导致server当机:(

    另外,要避免这个问题发生,还可以通过设置查询超时来避免,不至于把server弄当了。

  • 一个好工具

    2005-03-18

    最近在配置一个websphere,可以刚配置好后就起不来了,检查启动日志,发现ssl的443端口已经被占用了,可一时又找不出是哪个过程占用的端口,同事推荐了一个叫做Fport的程序,拿来一运行,不错,马上找到了占用443的是IIS,关掉IIS,没事了:)
  • 今天下载了一个,装上后不能使用,找了半天不知道什么毛病,卸载其他的J2EE开发插件再试,不错,成功了,哈哈.....慢慢研究吧......
  • 实践maven

    2004-07-12

    上周6下了Maven,准备自己实践一下。真是万事开头难。安装比较顺利,设置完环境变量,在命令窗口下敲maven -v,显示了下面的图案,正常。

     __  __
    |  \/  |__ _Apache__ ___
    | |\/| / _` \ V / -_) ' \  ~ intelligent projects ~
    |_|  |_\__,_|\_/\___|_||_|  v. 1.0-rc4

    之后想编译一个项目试试,把一个现有的项目拖过来,照着例子修改project.xml,运行maven jar,一大堆的问号,不知道在说什么,不过在过程中它不断地从http://www.ibiblio.org/maven(这个网站我刚才上还是上不去)上下载jar包,不过总是连接超时,让我百思不得其解,这样来回折腾了10几次,还是一要的问题,就在想它是不是在自动地下载一些需要的jar包呢?后问网友,确认。并给了我几个有效的网站,在build.properties加入:

    maven.repo.remote=
    http://www.ganet.org/maven/,
    http://dist.codehaus.org/,
    http://public.planetmirror.com/pub/maven/,
    http://mirrors.sunsite.dk/maven/

    (这里其实是在一行,因为显示不好看,我就把它们分开了)

    再试,ok,虽然还有问号,但是已经可以下载jar包了,好不容易把需要的jar包下载完毕,开始编译,正常,生成jar包。

    接着试maven site:generate,开始出现更多的问题了,现在几乎所有要用到包都已经下载了,但还是有异常,也不知道是什么异常。查看maven的帮助,有个选项-X,会输出deubg信息,加上一试 maven -X site:generate ,输出了好多的信息,前面的现在用不到,到最后有异常栈出现时,暂停输出,发现是ClassNotFound异常,我想要用到的jar差不多都已经下载了,还会有什么问题呢,我尝试打开其中一个有问题的jar,报告文件错误,看来是下载时文件被破坏了。从%USER_HOME%\.maven\repository中把这个包的所在的目录做掉,这下好了,它开始重新下载文件了,这样反复几次总算生成完毕了。到targe\docs下看,很漂亮,功能很强,就是时间长了点儿:)。

    最后尝试maven site:deploy,这次是没办法了,因为它要用ssh来上传文件,而我没有ssh服务器,问朋友,说windows下没有ssh服务器,放弃了:(等有时间在我家的Red Hat上试试吧。

    这个过程差不多花了我一天的时间,总体上感觉Maven真是棒极了,除了中文的输出有些问题,我要尝试一下看自己能不能解决掉:)

    让我郁闷的是,我把它介绍过一同事,那小子竟然说Maven和Ant差不多,没办法,自己研究吧:)

     

  • Ejb3

    2004-05-08

    早上一打开电脑,启动了NewzCrawler,在Gavin的blog上看到了有关EJB3的介绍,http://blog.hibernate.org/cgi-bin/blosxom.cgi/2004/05/07#ejb3,大概的看了一下,和以前的ejb规范相差很大,总之是更易使用了,感觉很像一些JAVA开源社区的产品的集成,像Hibernate,Spring等。
  • Eclipse3M6+Lomboz+Jboss3.2.1开发EJB。
    在Lomboz的server配置中选择好jboss的安装目录,配置好jboss。

    在Window窗口的show view 中打开Lomboz的J2ee View窗口方便调试。

    使用Lomboz的wizard建立j2ee工程,会在目录下生成EJB相关的配置文件,使用wizard生成session bean,直接对业务方法编程。选择此session bean点右键选择"create ejbs",系统自动生成EJBHome,,EJBRemote等类,真是方便。