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.

IDE二三事: 性能差是一种美德, Bug是一种Feature

性能差是一种美德ReSharper已经成为.Net开发必备工具. 其贴心的功能和快捷键设计让coding过程可以行云流水般随心而动. 可最近在客户这边写代码时却总是磕磕绊绊, 只因ReSharper变...
  • chelsea
  • chelsea
  • 2012年03月04日 22:03
  • 2055

Deep Learning的基本思想以及训练过程

一 、DL的基本思想(通俗易懂) 假设我们有一个系统S,它有n层(S1,…Sn),它的输入是I,输出是O,形象地表示为: I =>S1=>S2=>…..=>Sn => O,如果输出O等于输入I,即输...
  • u011437229
  • u011437229
  • 2016年01月22日 13:48
  • 286

deep learning(深度学习)介绍

最近接触deep learning,看了一些论文,理论细节比如RBM等,还需要研究,先通过比较通俗的语言组织下。 deep learning 并非一种具体的机器学习model,而是一个框架,或者思...
  • hxxiaopei
  • hxxiaopei
  • 2014年01月13日 17:17
  • 8575

一个bug的成本

我想说的当然不是一个bug价值多少钱,因为软件行业因行业不同,公司不同,业务不同,你的软件价值也不同;其实bug用价值来形容当然不合适,更应该用损失或者公司的支出来形容了。         写出本文...
  • xingyu_qie
  • xingyu_qie
  • 2016年03月15日 10:44
  • 1114

遇到BUG时你应该怎么做

下面是拜读Think Python 中关于代码错误的调试方法的一些记录,特意摘出来以便告诫自己 当你的程序不工作时, 1.首先你需要问自己这些问题: ˆ 有没有什么程序应该做却没有发生?...
  • baidu_29609961
  • baidu_29609961
  • 2017年08月26日 17:05
  • 213

uva_658_It's not a Bug, it's a Feature!(最短路)

题意:补丁在修补bug时,有时候会引入新的bug。假定有n(n
  • jhgkjhg_ugtdk77
  • jhgkjhg_ugtdk77
  • 2015年08月16日 17:04
  • 1041

BUG记录:

com.sun.xml.bind.v2.util.XmlFactory createParserFactory 严重: null org.xml.sax.SAXNotRecognizedExcep...
  • J__zz
  • J__zz
  • 2016年05月16日 21:20
  • 1483

文本输入框等字数统计【文字可包含中文/英文/符号/标点等】

//考虑用户输入一段文字中既有中文又有英文还能有符号标点等总字数统计的问题 //度娘好久没找见比较满意的实现,就自个动手写个小demo分享一下,希望能帮助到其他遇到此问题的童鞋们~ //------...
  • HizyOrg
  • HizyOrg
  • 2016年06月06日 20:10
  • 584

定位Bug技巧总结

解决Bug是编程人员的天职(创造Bug算是一种天赋吧),甚至有人这么认为:开发人员的能力可以依据他能决解Bug的复杂程度来评定。简单的Bug大多数程序员是靠臆断来解决的,但是当Bug隐藏在代码的最深处...
  • sinat_25141403
  • sinat_25141403
  • 2016年05月03日 20:11
  • 3079

如何写出一个让人很难发现的bug?

点击上方“程序人生”,选择“置顶公众号” 第一时间关注程序猿(媛)身边的故事 程序员的日常三件事:写bug、改bug、背锅。连程序员都自我调侃道,为什么每天都在加班?因为我...
  • csdnsevenn
  • csdnsevenn
  • 2018年01月29日 00:00
  • 2688
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:JAXB+Socket的一个Bug(或者算一个Feature吧)
举报原因:
原因补充:

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