2015-09-21 2 views
1

У меня есть старое приложение, в котором клиент загружает большие файлы по очень медленному соединению (GPRS). В настоящее время мы используем Spring MVC и более старый стандарт сервлета 2.0 и получаем запрос inputStream напрямую, что, очевидно, приводит к длительному запуску заблокированных потоков. Мне было поручено обновить приложение до сервлета 3.1, чтобы воспользоваться новым асинхронным прослушивателем чтения (который считался прямым!), Однако я попал в тупик.InputStream закрыт при использовании DeferredResult

Выполнение прямого апгрейда приложения на сервлет 3.1 прошло отлично, игнорируя тот факт, что я сразу возвращал ответ, поскольку меня больше интересовало тестирование факта, что сам файл был загружен правильно, а onDataAvailable() был которые вызываются правильно несколько раз. Проблема возникает, когда я пошел использовать объект DeferredResult (который, похоже, был более или менее изобретен для этой цели), чтобы вернуть имя представления контроллеру, поскольку мне нужно явно сообщить клиенту, была ли загрузка успешной или нет, и Я не вижу другого способа сделать это в этой асинхронной среде.

Когда я возвращаю объект DeferredResult, он некорректно возвращается сразу клиенту, а при первом вызове onDataAvailable() входной поток не может быть прочитан, поскольку я получаю java.io.IOException: исключение Stream закрыто. Вызов getDispatcherType() здесь говорит мне, что теперь тип переключился на DispatcherType.ERROR, тогда как когда я запускаю код без объекта DeferredResult (и сразу отвечаю независимо), тип все равно DispatcherType.REQUEST. Я подумал, что где-то в стеке inputStream читается рано (не мной) и вызывает это, но это, похоже, не так, и странно, что inputStream.isReady() возвращает true и inputStream.isFinished() возвращает false и Я могу видеть текст с файла в буфере на первом onDataAvailable(), но я просто не могу прочитать из inputStream напрямую, не получая java.io.IOException: исключение Stream закрыто.

Никакое количество тестов с крошечными файлами или действительно большими файлами не изменяет его поведение. Кто-нибудь успешно использовал DeferredResult в Spring MVC и сервлет 3.1? Я был в тупике в течение нескольких дней и отлаживал больше кода, чем я хотел бы сделать снова!

Heres пришел мой код (некоторый бизнес-материал обрежется в случае, если вы думаете, это выглядит немного вялый)

@Controller 
public class ContentController { 
    @RequestMapping(value = “/upload/“, method = POST) 
    public DeferredResult<String> upload(ServletRequest request) throws IOException, InterruptedException { 
     DeferredResult<String> deferredResult = new DeferredResult<String>(); 
     final AsyncContext asyncContext = httpServletRequest.startAsync(); 
     ServletInputStream servletInputStream = httpServletRequest.getInputStream(); 
     NioReadListener readListener = new NioReadListener(servletInputStream, asyncContext, deferredResult, size); 
     servletInputStream.setReadListener(readListener); 
     return deferredResult 
} 

public class NioReadListener implements ReadListener { 
    private final ServletInputStream _input; 
    private final AsyncContext _context; 
    private final DeferredResult<String> deferredResult; 

    public NioReadListener(ServletInputStream servletInputStream, AsyncContext asyncContext, DeferredResult<String> deferredResult, Long size) throws IOException { 
     this._input = servletInputStream; 
     this._context = asyncContext; 
     this.deferredResult = deferredResult; 
    } 

    @Override 
    public void onDataAvailable() throws IOException { 
     try { 
      int bytesRead; 
      byte b[] = new byte[_size.intValue()]; 
      while (_input.isReady() && (bytesRead = _input.read(b)) != -1) { 
       _totalBytesRead += bytesRead; 
      } 
     } catch (IOException e) { 
      _log.error(e); 
     } 

    } 

    @Override 
    public void onAllDataRead() throws IOException { 
     this._context.complete(); 
     deferredResult.setResult("VIEW_NAME"); 
    } 
} 
+0

Вы настроили сервлет как асинхронный? i.e установите ' true' в web.xml – jny

+0

Привет, я сделал это. И я знаю, что это сработало, потому что, если я сразу верну ответ, асинхронный ввод продолжает поступать по входному потоку. Только при добавлении функции DeferredResult эта проблема возникает. Проблема в том, что мне нужна эта идея DeferredResult, я не могу вернуть что-либо пользователю, пока не узнаю, что я успешно поддержал их загрузку! –

+0

Я еще не совсем решил это, но я нашел (я думаю), почему отложенный запрос немедленно возвращается.'DispatcherServlet' весной имеет чек' наконец { \t \t \t \t \t если (asyncManager.isConcurrentHandlingStarted()) { \t \t \t \t \t \t возвращение; \t \t \t \t \t} \t \t \t \t} ', которая не возвращает истину, как мой' asyncWebRequest' часть '' asyncManager' является null' даже думал, остальные атрибуты на 'asyncManager' установлены. Теперь просто чтобы понять, что часть ... –

ответ

0

(Это своего рода ответ на мою исходную задачу, но только не ответ на всю проблему приложения). После многих часов отладки и расчесывания головы я нашел причину, по которой мой deferredResult не был правильно настроен. Мы включили стороннюю библиотеку для некоторых REST utils, которая заставляла приложение использовать устаревший AnnotationMethodHandlerAdapter от Spring, а не RequestMappingHandlerAdapter, который он должен использовать. Как только я переопределил этот внешний bean-компонент, обработанный deferredResult обрабатывался правильно.

Только дело в том, что асинхронный характер этого потока вызовов по-прежнему не работает, что восходит к одному из моих первоначальных вопросов о том, есть ли у кого-нибудь весна MVC/DeferredResults и Servlet 3.1, работающие вместе. Я видел несколько вопросов stackoverflow, задающих одно и то же, но без ответа.