• 关于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。
    


  • 相关阅读:
    JQ轮播
    JS中正则匹配的三个方法match exec test的用法
    JavaScript 表单验证
    JS 控制CSS样式表
    AJAX 的简单用法:
    shell之运用sed将其值存到变量
    shell之创建文件及内容
    修复vbox的共享文件夹的符号链接错误
    字符转码
    php魔术方法
  • 原文地址:https://www.cnblogs.com/secbook/p/2655176.html
Copyright © 2020-2023  润新知