Bilberryfm
Bilberryfm

Reputation: 537

Thread in spring mvc controller not working

I'm trying to create new thread in controller that will perform long-running query to service (listFromService = getListFromService();). For now, while I don't have connection to Service. I simulate this process by doing Thread.sleep(100000) and populating the listFromService with dummy data.

After that, I want to put this list in CSV file and stream to HttpServletResponse response directly.

This code is working perfectly without thread. But its blocking main thread and user cannot do anything.

@RequestMapping(value = "/download")
	public void downloadCSV1(@RequestParam() final Map<String, String> params,
			HttpServletResponse response) throws IOException 
	{
	
		new Thread()
		{
		    public void run() {
		        System.out.println("Before Running thread");
		        List<Objects> listFromService;
		        try {
					Thread.sleep(10000);
					listFromService = getListFromService();
					response.setHeader("Content-disposition", "attachment;filename="+ "metrics.csv");
					ServletOutputStream outputStream;
					try {
						outputStream = response.getOutputStream();
						listFromService.stream().forEach(item -> {
							try {
								processMetricsDownloadListItem(item, outputStream);
							} catch (IOException e) {

								e.printStackTrace();
							}
						});
						outputStream.flush();
					} catch (IOException e1) {
						e1.printStackTrace();
					}	
					
					
				} catch (InterruptedException e) {
        e.printStackTrace();
				}    
		  } // end or run()
		}.start();
	
		

		
	}

The error I'm getting is following:

 Exception in thread "Thread-5" java.lang.NullPointerException
[tomcat:launchProperties] 	at org.apache.coyote.http11.InternalNioOutputBuffer.flushBuffer(InternalNioOutputBuffer.java:235)
[tomcat:launchProperties] 	at org.apache.coyote.http11.InternalNioOutputBuffer.addToBB(InternalNioOutputBuffer.java:190)
[tomcat:launchProperties] 	at org.apache.coyote.http11.InternalNioOutputBuffer.commit(InternalNioOutputBuffer.java:178)
[tomcat:launchProperties] 	at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:750)
[tomcat:launchProperties] 	at org.apache.coyote.Response.action(Response.java:177)
[tomcat:launchProperties] 	at org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:249)
.........

Upvotes: 0

Views: 1276

Answers (1)

Mikita Harbacheuski
Mikita Harbacheuski

Reputation: 2253

When http method call is performed, servlet container(Tomcat for example) creates a dedicated thread to serve given request. This thread performs several actions and one of them is invoking the logic inside controller method. It your case it just starts another thread. After this point containers thread continues it's lifecycle which send response back to client and closes HttpServletResponse in the end. This is performed simultaneously with your application thread. So when your application tread wakes up and tries to flush OutputStream which has been already closed by container thread it receives an error. If you add t.join() after start() the code will work the same as without the separate thread.

Unfortunately it's not possible to release client connection and send the result of computation later without blocking using only application server, because http connection can't be reused after client receives response from server. You should use async tool on client side (AJAX for example) to make async call to the server and when result is ready take an action. Call to the server is always blocking but in this case async client thread is blocked and your main application thread continues to process user's actions.

Upvotes: 2

Related Questions