Чтение ServletOutputStream в строку

Я пытаюсь прочитать результат рендеринга FreemarkerView:

View view = viewResolver.resolveViewName(viewName, locale);
view.render(model, request, mockResponse);

Чтобы прочитать результат, я создал mockResponse, который инкапсулирует HttpServletResponse:

public class HttpServletResponseEx extends HttpServletResponseWrapper {

    ServletOutputStream outputStream;

    public HttpServletResponseEx(HttpServletResponse response) throws IOException {
        super(response);
        outputStream = new ServletOutputStreamEx();
    }

    @Override
    public ServletOutputStream getOutputStream() {
        return outputStream;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"));
    }
}

А также мой ServletOutputStream, который строит String с помощью StringBuilder:

public class ServletOutputStreamEx extends ServletOutputStream {

    StringBuilder stringBuilder;

    public ServletOutputStreamEx() {
        this.stringBuilder = new StringBuilder();
    }

    @Override
    public void write(int b) throws IOException {
    } 

    @Override
    public void write(byte b[], int off, int len) throws IOException {
        stringBuilder.append(new String(b, "UTF-8"));
    }

    @Override
    public String toString() {
        return stringBuilder.toString();
    }
}

С ними я могу легко прочитать ответ с помощью метода ServletOutputStreamEx.toString.

Моя проблема в том, что метод записи вызывается не в правильном порядке, и в конце конечная строка оказывается перемешанной и не в правильном порядке. Вероятно, это вызвано параллелизмом в Freemarker, но я понятия не имею, как это исправить.


person Vojtěch    schedule 11.03.2012    source источник
comment
Я уверен, что методы записи вызываются Freemarker в правильном порядке на этапе рендеринга, иначе это приведет к искажению результата. Более того, Freemarker не запускается одновременно. Я думаю, проблема в вашей реализации ServletOutputStreamEx. Вы не переопределили все методы записи и суперметоды. Я не вижу, чтобы вы вызывали super.write(int b), он просто ничего не сделает, если этот метод будет вызван.   -  person gigadot    schedule 11.03.2012
comment
Я не уверен, в чем ваша проблема, но если вы получите два String в результате ServletOutputStreamEx.write() вызовов, смешанных вместе. использование StringBuffer вместо StringBuilder исправит это, поскольку StringBuffer синхронизировано-   -  person Ahmad Y. Saleh    schedule 11.03.2012
comment
Я не могу согласиться, StringBuffer — это то, что должно быть объявлено устаревшим (как описано в «Эффективной Java, 2-е издание»), и использование StringBuilder — это хорошо, потому что большую часть времени один запрос (и, следовательно, один ответ) обрабатывается с помощью нить.   -  person Amir Pashazadeh    schedule 11.03.2012
comment
Что вы подразумеваете под параллелизмом в Freemarker? Когда FreeMarker вызывается из нескольких потоков, каждый поток будет иметь свой собственный объект Environment. Объект Writer, куда FreeMarker записывает выходные данные, является частью объекта Environment и предоставляется вызывающей стороной FreeMarker. Таким образом, в принципе можно передать один и тот же объект Writer FreeMarker в несколько потоков, но я не могу себе представить, зачем это делать.   -  person ddekany    schedule 12.03.2012


Ответы (1)


Спасибо за ответы: write(int b) не был реализован, потому что он никогда не вызывается. В конце концов, проблема заключается в массиве байтов, который также содержит предыдущую строку. Поэтому String нужно создать как String(b, off, len, "UTF-8").

person Vojtěch    schedule 11.03.2012