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

这个异常经过在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。


原文地址:https://www.cnblogs.com/secbook/p/2655176.html