您的位置:首页 > 理论基础 > 计算机网络

关于jetty和webx对于HttpServletResponse getWriter和getOutputStream的处理

2012-05-31 13:51 567 查看
这个异常经过在jetty的一个简单程序的测试验证,确定问题及分析如下:



这个程序在使用response输出结果时,先调用response的getWriter获得PrintWrite对象后输出内容,然后再调用getOutputStream方法获得outputStream对象后输出二进制内容,然后就跑出上面那个异常了。

这两个方法在jetty容易中是这么处理:
org.eclipse.jetty.server.Response继承自j2ee里面的HttpServletResponse.java类
org.eclipse.jetty.server.Response.java类里面

public ServletOutputStream getOutputStream() throws IOException
{
if (_outputState!=NONE && _outputState!=STREAM)    如果状态为WRITER状态,则抛出异常
throw new IllegalStateException("WRITER");

_outputState=STREAM;         把response状态改为STREAM流状态
return _connection.getOutputStream();
}

public PrintWriter getWriter() throws IOException
{
if (_outputState!=NONE && _outputState!=WRITER)  如果状态为STREAM,则抛出异常
throw new IllegalStateException("STREAM");

/* if there is no writer yet */
if (_writer==null)
{
/* get encoding from Content-Type header */
String encoding = _characterEncoding;

if (encoding==null)
{
/* implementation of educated defaults */
if(_mimeType!=null)
encoding = null; // TODO getHttpContext().getEncodingByMimeType(_mimeType);

if (encoding==null)
encoding = StringUtil.__ISO_8859_1;

setCharacterEncoding(encoding);
}

/* construct Writer using correct encoding */
_writer = _connection.getPrintWriter(encoding);
}
_outputState=WRITER;             把response状态改为WRITER状态,
return _writer;
}

也就是说在j2ee,web应用里面不能同时打开PrintWriter和OutputStream,否则就是抛出上面那个异常。

jetty的response里面有三种状态:
public static final int
NONE=0,  未调用getPrintWriter和getOutputStream之前的默认状态
STREAM=1,  二进制流状态  调用getOutputStream之后的状态
WRITER=2;   字符流状态

解决方法:
1.在应用中只使用一个,要么都用getPrintWriter,要么都用getOutputStream。
2.在webx 中的com.alibaba.citrus.service.requestcontext.buffered.impl.BufferedResponseImpl.java类中有下面这么解决方案:

/**
* 取得输出流。
*
* @return response的输出流
* @throws IOException 输入输出失败
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
if (stream != null) {
return stream;
}

if (writer != null) {
// 如果getWriter方法已经被调用,则将writer转换成OutputStream
// 这样做会增加少量额外的内存开销,但标准的servlet engine不会遇到这种情形,
// 只有少数servlet engine需要这种做法(resin)。
if (writerAdapter != null) {
return writerAdapter;
} else {
log.debug("Attampt to getOutputStream after calling getWriter.  This may cause unnecessary system cost.");
writerAdapter = new WriterOutputStream(writer, getCharacterEncoding());
return writerAdapter;
}
}

if (buffering) {
// 注意,servletStream一旦创建,就不改变,
// 如果需要改变,只需要改变其下面的bytes流即可。
if (bytesStack == null) {
bytesStack = new Stack<ByteArrayOutputStream>();
}

ByteArrayOutputStream bytes = new ByteArrayOutputStream();

bytesStack.push(bytes);
stream = new BufferedServletOutputStream(bytes);

log.debug("Created new byte buffer");
} else {
stream = super.getOutputStream();
}

return stream;
}

/**
* 取得输出字符流。
*
* @return response的输出字符流
* @throws IOException 输入输出失败
*/
@Override
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}

if (stream != null) {
// 如果getOutputStream方法已经被调用,则将stream转换成PrintWriter。
// 这样做会增加少量额外的内存开销,但标准的servlet engine不会遇到这种情形,
// 只有少数servlet engine需要这种做法(resin)。
if (streamAdapter != null) {
return streamAdapter;
} else {
log.debug("Attampt to getWriter after calling getOutputStream.  This may cause unnecessary system cost.");
streamAdapter = new PrintWriter(new OutputStreamWriter(stream, getCharacterEncoding()), true);
return streamAdapter;
}
}

if (buffering) {
// 注意,servletWriter一旦创建,就不改变,
// 如果需要改变,只需要改变其下面的chars流即可。
if (charsStack == null) {
charsStack = new Stack<StringWriter>();
}

StringWriter chars = new StringWriter();

charsStack.push(chars);
writer = new BufferedServletWriter(chars);

log.debug("Created new character buffer");
} else {
writer = super.getWriter();
}

return writer;
}

所以在我们自己的应用中就不要再调用完j2ee的原生response的getPrintWriter之后再调用原生的getOutputStream(),或者调用原生的response的getOutputStream之后再调用getPrintWriter。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐