2010年7月27日星期二

一个奇怪的BUG

   今天在工作中碰到一个奇怪的BUG,客户端通过RMI远程调用一个服务时抛异常,日志如下:
org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service at [ http://xxx/xxxService]; nested exception is java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: java.util.RandomAccessSubList
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.convertHttpInvokerAccessException(HttpInvokerClientInterceptor.java:211)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:144)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
   ......
看日志应该是有一个类未实现序列化接口导致的,浏览了代码,也没发现有什么异常的。然后就开始跟踪这个类:RandomAccessSubList, 终于发现ArrayList实例在调用subList方式是,返回的就是这个类,代码如下(AbstractList.java):
    public List<E> subList(int fromIndex, int toIndex) {
        return (this instanceof RandomAccess ?
                new RandomAccessSubList<E>(this, fromIndex, toIndex) :
                new SubList<E>(this, fromIndex, toIndex));
    }
显然就是类RandomAccessSubList未实现序列化接口,由此得到一个写RMI服务需要注意的地方:返回对象不能包含通过调用subList方法得到的结果,否则会抛出该异常。为了避免这个缺陷,有两种方法:
1. 通过hessian等远程调用方式绕开RMI
2. 不直接调用subList,实现一个自己的类似方法即可。