null

Liferay, HttpServletResponse и переносы строк

В процессе работы над портлетом столкнулся с небольшой проблемой, решение которой в итоге потребовало определённого количества шаманства. Мой портлет, в ответ на заполнение пользователем текстового поля, должен был возвращать ему некоторый динамически сгенерированный файл. Для того, чтобы это сделать, портлетный ActionResponse не подходит, нужно использовать HttpServletResponse. В итоге код, который возвращает пользователю файл, должен выглядеть примерно так:

HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse(response);
servletResponse.setContentType("text/plain");
servletResponse.setHeader("Content-disposition", "attachment; filename=Lab2.java");
PrintWriter out = servletResponse.getWriter();

generateFile(out); // Пишем файл в поток вывода
		
out.flush();
out.close();

После того, как портлет с такой реализацией генерации файла был развёрнут на сервере, выяснилась одна странность - в возвращаемом файле были аккуратно "вырезаны" все переносы строк (остальной контент остался в целости и сохранности):

Все переносы строк куда-то пропали

Стандартные операции с установкой кодировки ответа, добавлением "\n\r" в конец каждой строки файла и т.д. ни к чему не привели - переносы строк в возвращаемом файле так и не появились. При этом, если таким же PrintWriter'ом попытаться записать данные в файл, то переносы строк волшебным образом возвращаются на свои места:

При записи в файл переносы на месте

Гугление никаких конкретных результатов не дало, но, неожиданно, положительный результат был получен при простой замене вызова метода getWriter() респонса на getOutputStream():

HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse(response);
servletResponse.flushBuffer(); // Предотвращаем IllegalStateException
servletResponse.setContentType("text/plain");
servletResponse.setHeader("Content-disposition", "attachment; filename=Lab2.java");
PrintWriter out = new PrintWriter(servletResponse.getOutputStream());

generateFile(out);

out.flush();
out.close();

Обратите внимание на строку 2 листинга - если заранее не выгрузить буфер, то при обращении к потоку портлет упадёт на IllegalStateException (проблема описана здесь).

В итоге от замены кода на, казалось бы, эквивалентный, переносы строк в файл мистическим образом вернулись:

Переносы строк вернулись

Причины этой мистики сокрыты в глубинах исходников Liferay и мне пока что непонятны.

Коротко о себе:

Работаю ведущим программистом в компании Tune IT и ассистентом кафедры Вычислительной техники в Университете ИТМО .

Занимаюсь проектами, связанными с разработкой разного рода веб-приложений (порталы, CRM-системы, системы электронного документооборота), а также, в рамках научной работы на кафедре, изучаю возможности применения семантического анализа в задачах САПР.