Feb 23

Observer pattern implementation in web application using Comet

     Observer pattern can be implemented easily in C/S based applications, but in web application area it is not easy to implement caused by the shortcoming of HTTP protocol. Currently we have some solutions : 

  1. browser plugins: we can use socket communication in flash or applet etc. by this we can implement C/S likely applications, the server side can push messages to the small application in browser side,  when the small application receives messages, it can communication with javascript engine. 
  2. comet : it also can be called "server push". it was based on long time polling, we start a request from browser side, the server side application keep the request live, by this we can keep sending messages to browser side. nowadays, some web application servers start to support comet, like : tomcat6/tomcat7 , jetty etc.. In tomcat, NIO is used to optimized the IO performance and decrease the expense of CPU. 
  3. HTML5 web socket. 

    Thinking of implementing the Observer pattern:

  1. send a request with the concerning message types, when server side received these messages, save these concerning message types in the session scope. Of course we can persistence these concerning message type in database, then we can design a customer independent notification user experience.  
  2. open a long time polling from the browser side, when server side received this request, we save the request related  response in a message send thread, at the same time we must keep this request live. Then we can send message signal to the message send thread. In the message send thread we can determine which message must write to which response.
Jan 06

Forward-Backward function in Chrome

    Nowadays we are familiar with the forward and backward function of browser, but may be little of us know the real algorithm of it. In a recent project, I have to simulate a forward-backward function for our ajax based web application. So I spend some time on the forward-backward algorithm of Chrome. Continue reading

Jan 12

Websphere Message Broker7中配置DB2 JDBCProvider

    JDBCProvider向Websphere Message Broker(WMB)中需要操作数据库的node提供数据库连接服务,JDBCProvider作为一个服务被注册到指定的broker中。

     这里需要涉及到的几条命令有:

  1. mqsideleteconfigurableservice, 用于删除一个可配置的服务对象。
    例如:mqsideleteconfigurableservice MB7BROKER -c JDBCProviders -o SIMPLERROUTEDB
    表示从MB7BROKER这个broker中删除type为JDBCProviders的服务(configurable service)中的名为SIMPLERROUTEDB可配置对象(configurable service object).
  2. mqsicreateconfigurableservice,用于创建一个可配置的服务对象(configurable service)
    例如: Continue reading
May 22

误用jakata commons-beanutils引起bug分析

    项目里面大量的使用到了jakata的commin包,包括beanutils这个包,主要是用来copy bean property,以前一直用的很好,也没有去深究它内部的实现机制,评自己猜想就只是运用反射去取得属性的值,然后设置值吧。接着就出现了现在遇到的问题,项目中一个vo属性的类型为Byte包装类型,这种属性在实例化的时候默认为null,这个特性被我用到了业务中,也就是我会通过判断这个属性是否为null来实现某些逻辑,结果发现这个属性竟然会在没有任何人动的情况下自己被赋值为0,然后就导致依赖这个属性的业务逻辑错掉,debug一步一步的跟踪发现只要一经过beanutils,copy出来的bean的这个属性自动变为0了,以为是beanutils的bug,找来源码看了看,发现BeanUtils中其实是调用了BeanUtilsBean的copyProperties方法,BeanUtilsBean中实现了一个简单的工厂模式,将的一个实例绑定在当前线程中,有点儿类似ThreadLocal,也就是当前线程中所有调用都会采用同一机制进行copy,这里说到copy的机制其实就是指Converter,对于各种类型都有一个特定的Converter,包内部提供了一些jdk内部类型的Converter,每种类型Converter都有可以提供一种默认值,而在BeanUtils中对于Byte,Integer等数值类型提供的默认值是:0 ,所以就会出现把Byte类型值为null转换成了0。其实这么做也有他的好处,可以避免出现烦人的NullPointerExceprtion。知道原因,解决办法就很容易想到了,自己重新初始化一下BeanUtilBean中的ByteConverter。

Dec 10

NSIS制作B/S架构安装程序

    开门见山,B/S架构应用程序的安装程序无非就是拷贝一些文件,检查一下环境配置,注册服务,启动服务之类的了。拷贝文件就没什么好说的了。下面着记录一下如下几点:

  1. 端口检查
  2. 读写注册表
  3. 调用外部可执行文件
  4. 注册、启动、停止服务
  5. 注册环境变量

Continue reading

Jun 11

Java HeapSpace OutOfMemory一例分析

        这两天经常听到同事说我们的系统运行一两个小时过后就会报Java HeapSpace OutOfMemory的异常,一开始我还没怎么在意这个问题,还以为是在eclipse中用debug状态运行tomcat导致的,后来又出现了几次,觉得事情有点儿不对了,检查了我的代码,基本没有什么地方会出现内存泄漏,该释放资源的地方也都释放了。一开始懒得去看同事的代码,就找了jconsole工具来分析,这不看不知道,一看才吓了一跳。查看内存占用发现大致每隔两分钟左右内存就会有一个飙升,线程数跟内存占用一样每当内存波动的时候线程数就有一个飙升,而且一上去了就不下来,查看具体的线程堆栈,发现增加的线程全部是一个snmp4j中的一个类型。这一块恰好是我那同事实现的,于是没办法只有看看他的代码了(貌似搞开发的都不愿意看别个的代码…). 在我们的系统中有一个轮询操作,在这个轮询操作中会使用到snmp4j api,查看snmp4j api发现用到的一个类中会持有socket资源并创建内部线程,这个socket会中断在那块儿,而俺那同事的实现是在每次轮询的时候都会新建一个该对象,而且在使用过后也没有close掉。查看snmp4j的源码发现该类的内部实现是有从一个内部线程池中取得一个线程,然后在该线程中用到socket,于是由于线程中的socket为关闭一直阻塞,这条新建的线程 也就没办法释放了。问题就算是找到了,下面的解决办法就很容易了,将这个地方实现为一个单例模式,最后测试的结果也很好,运行三四个小时,线程数始终保持在估计的范围内。

等有时间了,再好好写一篇jconsole的使用方法,发现这玩意儿还挺好玩儿的。

May 20

Process waitFor无法返回原因分析

    今天做一个mysql数据恢复的小程序,采用Runtime.exec的方式来执行mysql命令。主要用来恢复一些由定时程序自动备份出来的数据库文件,有时候可能会出现恢复的数据在数据库中还存在,于是会出现主键重复的错误,于是我在恢复的时候加入–force参数,手动在cmd控制台下执行可以很明显看到mysql返回了一些duplicate的error,按道理在java app中也能正常执行过去,可在执行java app的时候看控制台中打印的日志显示,程序阻塞在了waitFor这一步。我的程序片段如下:
……   
p = Runtime.getRuntime().exec(cmd);
……
    p.waitFor();
……
    百思不得其解,最后在google了下,找到一片最早分析该原因的文章是在javaworld上。原因是操作系统这一层对输入输出流有一个buffer,由于受到这个buffer的大小限制,当buffer满了而又没有线程去读,则当前执行的线程就会阻塞掉,于是修改一下程序,开辟两个线程专门用于读取输出流,一个用于读取返回消息流,一个用于读取异常消息流,于是修改程序如下:
……
p = Runtime.getRuntime().exec(cmd);
   
new PrintStream(p.getInputStream()).start();
new PrintStream(p.getErrorStream()).start();
p.waitFor();
……
此处PrintStream为一个读取inputstream的线程。

May 14

Java串口通信总结

    串口通信这词儿听上去跟java似乎有点儿沾不上边儿,因为java大多是拿来做大规模企业应用的,像串口通信这种很底层的东东一般都会用比较低级别的语言来做,但最近就遇到了这么一事儿,于是google了下,没想到还真能用java做,要用到的是javax扩展类库javacomm,它是一系列的标准,该类库在sun的官网上只提供linux版本。由于我所应用的平台是win32,所以还得去其他地儿找win32的实现,找了好久终于找到了。下面对其用法做一个简要说明。

    1.首先在下载到了javacomm包后,需要将其解压,解压后需要做的就是将java扩展库安装到本地开发环境以及运行环境中。对于开发环境需要做的就是将win32com.dll复制到%jdk install dir%/bin下,然后将comm.jar和javax.comm.properties拷贝到%jdk install dir%/lib下,对于运行环境要做的就是将这些文件拷贝到相应的jre下即可。

    2.javacomm API

    其API相当简单,下面看如下代码
   CommPortIdentifier serialPortId = CommPortIdentifier.getPortIdentifier(portName);
   log.info(“open serial port COM1 …”);
  SerialPort  port = (SerialPort) serialPortId.open(appName, timeout);
   log.info(“serial port COM1 configuration : ” + baudrate + ” , ” + dataBits + ” , ” + stopBits + ” , ” + parity);
   port.setSerialPortParams(baudrate, dataBits,stopBits, parity);
   port.enableReceiveTimeout(IOTIMEOUT);
   OutputStream out = port.getOutputStream();
   InputStream in = port.getInputStream();
   Request req = Request.getInstance();
   req.setIn(in);
   req.setOut(out);

    第一行取得一个名为portName的串口描述(看这个用法,这个地方有点儿像是单例跟工厂模式)。然后从串口描述中打开串口得到一个串口对象,注意这个地方的timeout参数是用来设置打开串口的超时时间,往后看可以看到还有一个receiveTimeout的设置,这个设置害我冥思苦想了好长时间。一开始没有注意到打开串口时可以设置这个timeout时间,使用的是一个单appName参数的方法,结果后来出现无法设置io超时时间的问题,而java API中也没有提供io超时时间的方法,最后终于一天晚上在给我的她打完电话后发现了这个苦苦期盼了好久的方法,问题迎刃而解了。之后就是设置通信的一些参数诸如波特率,数据位,停止位,校验方式等。然后就可以从串口中得到输入输出流了,这个地方要注意的是串口通信是全双工的,输入流跟输出流相互没有什么关系。

Jan 01

Java timezone设置问题

    上个月碰到一个问题,有一个客户在装了俺们的系统后出现时间错误问题,查了很长时间也没有查到问题所在。后来上google查了下,在sun的网站上查到有人遇到过类似的一个问题,不过那人描述的好像是jdk1.4中的,解决办法就是下载sun出的一个TZupdater的补丁包,它会修正一些时区的问题,可我们的系统用的都是jdk6,应当是不存在这样的问题的。服务器上的时间是正常的北京时间,查看了服务器上的时区设置也是东八区,操作系统是server 2003,按照我的理解jre在默认的情况下应当是去取操作系统的时区的(当然只是猜测),可以查俺们应用的log发现log4j打印出来的日志时间也是错的,跟我们的应用一样时间出现好几个小时的偏差,后来查了下好像我们的应用内用的是北美时区,最后在resin启动的参数中加了 -Dtimezone=asia/shanghai 使得在resin启动的时候就将运行时环境人工设置到asia/shanghai上来,重启resin,问题就解决了。今天又碰到同样的问题,可不同的是我无论怎样设置时区问题依然存在,有点儿搞不明白了,希望在元旦长假过后能顺利解决这个问题。