JAXB+Socket的一个Bug(或者算一个Feature吧)

原创 2007年09月29日 15:41:00

把Socket和JAXB结合起来用的时候会有问题: 当我们让JAXB 去 unmarshal  InputStream或者marshal outputStream的时候,它会自动close掉InputStream,这样Socket就会处于半关闭状态,而Socket处于这种状态会自动关闭,这样用Socket只能单向通讯一次就结束了。一般的原则是,谁打开了stream,谁就应该关闭它,但是JAXB底层使用的是JAXP特别是SAX,而SAX让parser负责关闭流(原因请参见后附的在Kohsuke Kawaguchi's Blog摘抄下来的英文说明),所以使用完JAXB的操作后,socket就会莫名的关闭了。按照Kohsuke Kawaguchi所说的我重新改了代码,写了NoCloseInputStream和NoCloseOutputStream,他们复写了父类中的close():

 

package edu.jlu.fuliang.net;

import java.io.FilterInputStream;
import java.io.InputStream;

public class NoCloseInputStream extends FilterInputStream ...{
    
public NoCloseInputStream(InputStream in) ...{
        
super(in);
    }

    
    
public void close() ...{} // ignore close
}


package edu.jlu.fuliang.net;

import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class NoCloseOutputStream extends FilterOutputStream ...{
    
public NoCloseOutputStream(OutputStream out) ...{
        
super(out);
    }

    
    
public void close() ...{} // ignore close
}

这样就不会关闭Stream了,从而也不会关闭Socket了:

Server端:
//Write
CatalogTypeUtils.buildXml(catalog,new NoCloseOutputStream(socket.getOutputStream()));
socket.shutdownOutput();
//Read
catalog = CatalogTypeUtils.buildObject(new NoCloseInputStream(socket.getInputStream()));
System.out.println(
"Server: " + catalog.getPublisher());
Client端:
/**//*Read*/
CatalogType catalog 
= CatalogTypeUtils.buildObject(new NoCloseInputStream(socket.getInputStream()));
System.out.println(
"Client:" + catalog.getPublisher());
/**//*Write*/
CatalogTypeUtils.buildXml(catalog, 
new NoCloseOutputStream(socket.getOutputStream()));
socket.shutdownOutput();


他们最后通过关闭Socket来关闭流:
socket.close();

package edu.jlu.fuliang.test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import edu.jlu.fuliang.net.NoCloseInputStream;



public class CatalogTypeUtils ...{
    
private static JAXBContext jaxbContext = null;
    
private static Marshaller marshaller = null;
    
private static ObjectFactory factory = null;

    
public static void buildXml(CatalogType catalogType, OutputStream output) ...{

        
try ...{
            jaxbContext 
= JAXBContext.newInstance("edu.jlu.fuliang.test");
            marshaller 
= jaxbContext.createMarshaller();
            ObjectFactory factory 
= new ObjectFactory();
            JAXBElement
<CatalogType> catalogElement = factory.createCatalog(catalogType);
            marshaller.marshal(catalogElement,output);
        }
 catch (JAXBException e) ...{
            e.printStackTrace();
        }

    }


    
public static CatalogType buildObject(InputStream input) ...{
        JAXBElement
<CatalogType> catalogElement = null;
        
try ...{
            JAXBContext jaxbContext 
= JAXBContext.newInstance("edu.jlu.fuliang.test");
            Unmarshaller unMarshaller 
= jaxbContext.createUnmarshaller();
            catalogElement 
= (JAXBElement<CatalogType>) unMarshaller.unmarshal(input);
        }
 catch (Exception e) ...{
            e.printStackTrace();
        }

        
return catalogElement.getValue();
    }

}

Kohsuke Kawaguchi's Blog:

How did I write "Socket + XML = pitfall"?

Posted by kohsuke on July 22, 2005 at 11:40 AM | Permalink | Comments (1)

Well, I kind of know the XML code inside out ;-), so I knew beforehand that a SAX parser closes the stream it reads. So I just mostly wrote a simple program to confirm the socket behavior. Also, I think I know about TCP probably more than average developers. That probably have helped, too.

Mostly I just verified that calling socket.getInputStream().close() brings down the whole connection. This you can quickly check by attempting to socket.getOutputStream().write(0); afterward. While no authoritative document was found on this issue, there's overwhelming side evidence to back up this hypothesis; for example, JDK added the partial close support only in 1.3. That must mean that any socket method that pre-date JDK 1.3 cannot possibly do partial close (or else why they needed to add it later?)

Ethereal could have been used. When I was studying TCP I used it a lot. But in this case, since I was just interested in if the connection was still alive, netstat would have been just fine.

I think most of the times I do tricky trouble-shooting by tracing into JDK and seeing the code in action by myself using a debugger. One thing I hate is that JDK core class libraries are compiled without the debug info. This is also done for a reason (to reduce the size of JRE), but one of those days I'll recompile everything in src.zip to create my custom rt.jar with full debug info!

Socket + XML = pitfall

Posted by kohsuke on July 15, 2005 at 12:04 PM | Permalink | Comments (10)

Yesterday, one of the JAXB users sent me an e-mail, asking for how to solve the problem he faced.

The scenario was like this; you have a client and a server, and you want a client to send an XML document to a server (through a good ol' TCP socket), then a server sends back an XML document. A very simple use case that should just work.

The problem he had is that unless the client sends the "EOS" (end of stream) signal to the server, the server keeps blocked. When he modified his code to send EOS by partial-closing the TCP socket (Socket.shutdownOutput), the server somehow won't be able to send back the response saying the socket is closed.

What's Happening?

So, what's happening and who's fault is this?

When you tell JAXB to unmarshal from InputStream, it uses JAXP behind the scene, in particular SAX, for parsing the document. Normally in Java, the code who opened the stream is responsible for closing it, but SAX says a parser is responsible for closing a stream.

Call it a bug or a feature, but this is done for a reason. People often assume that a parser only reads a stream until it hits the end tag for the root element, but the XML spec actually requires a parser to read the stream until it hits EOS. This is because a parser needs to report an error if anything other than comment, PI, or whitespace shows up. Given that, I'd imagine SAX developers thought "well, if a parser needs to read until EOS, why not have a parser close it? after all, the only thing you can do with a fully read stream is to close it!" In a sense, it makes sense. In any case, it's too late to change now.

So, the net effect is that when you pass in an InputStream from Socket.getInputStream to a JAXB unmarshaller, the underlying SAX parser will call the InputStream.close automatically.

Now, what happens when a socket InputStream is closed? The JDK javadoc really doesn't seem to say definitively, but a little exeriment reveals that it actually fully closes the TCP session. That explains why our guy couldn't write back the response --- by the time he read an input, his socket was already closed!

This seems like a surprising behavior. It would have been better if closing a stream only closes a socket partially in that direction, and you would need to close both InputStream and OutputStream to fully shutdown a socket. It would have made a lot of sense. I guess the reason why it's not done this way is because of the backward compatibility. The Socket class was there since the very first JDK 1.0, but the notion of partial close is only added in JDK 1.3. JDK 1.3 of course couldn't change the behavior of earlier JDKs, no matter how undesirable it is.

By putting those two behaviors, we now know what has happened. At server, a SAX parser who was reading a request is terminating a connection too prematurely.

写了一个bug,最后却变成了feature,要不要修呢?

事情是这样子的,前不久接到一个需求,为一个游戏开发礼包码功能   通常一款游戏运营期间会搞各种各样的活动吸引玩家,其中最常见的就是发放礼包,  玩家可以通过礼包码兑换礼包。   用礼包码兑...

前端动画的bug:hover一个li,左滑进背景图,li上文字消失太快(或者说出现闪现消失)

要求实现的动效:左边每一条li鼠标经过的时候,蓝色框从左边滑进,右边图片从右边滑进 出现的bug:hover上一个li,文字的变白速度比滑出条速度快,出现一瞬间的闪现效果(或者说完全消失) 解决,...

VC 2008 一个 可能的BUG ,或者本人的无知,请有大侠解读一下

/*源码*/ #include void ttt(size_t bsize) { size_t _size = bsize > 1 ? bsize : 9; printf("re...

erlang关闭一个socket进入死循环的bug修复过程

1、现象 游戏服务器与多个客户端建立连接,socket设置参数如下[binary, {packet, 4}, {active, 0}, {reuseaddr, true}, {nodelay, fa...

今天发现一个hibernate的bug,或者说一个应该注意的地方比较合适

今天把myblog用sql server部署了一下,可是发现分页查询的时候出现错误,看控制台报错说语句有错,由来发现分页的时候先查询总记录数目的那条语句出错了 select count(*) as ...

用JAXB生成一个XML文档

一个xml 模式(Schema)用XML语法表达了一个XML文档的结构。J2EE的开发者也许会需要一个符合XML模式的XML文档。javaXML绑定架构(JAXB)提供了一个绑定编译器,xjc,来从一...

介绍一个成功的 Git 分支模型(master - hotfix - develop - feature - release)

● master和develop并行。 ● master上始终是最稳定的代码,develop是正在开发的代码。 ● feature则是某个开发为了自己的功能拉的分支。 不一般情况: ● develop...
  • hherima
  • hherima
  • 2015年12月23日 12:17
  • 4985

一个用于制作C# 或者VB.NET编辑器

  • 2008年06月10日 18:38
  • 7.42MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JAXB+Socket的一个Bug(或者算一个Feature吧)
举报原因:
原因补充:

(最多只允许输入30个字)