Question

The background thread is here

Just to make objective clear - the user will upload a large file and must be redirected immediately to another page for proceeding different operations. But the file being large, will take time to be read from the controller's InputStream. So I unwillingly decided to fork a new Thread to handle this I/O. The code is as follows :

The controller servlet

/**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub

        System.out.println("In Controller.doPost(...)");

        TempModel tempModel = new TempModel();
        tempModel.uploadSegYFile(request, response);

        System.out.println("Forwarding to Accepted.jsp");

        /*try {
            Thread.sleep(1000 * 60);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }*/

        request.getRequestDispatcher("/jsp/Accepted.jsp").forward(request,
                response);
    }

The model class

package com.model;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.utils.ProcessUtils;

public class TempModel {

    public void uploadSegYFile(HttpServletRequest request,
            HttpServletResponse response) {
        // TODO Auto-generated method stub

        System.out.println("In TempModel.uploadSegYFile(...)");

        /*
         * Trigger the upload/processing code in a thread, return immediately
         * and notify when the thread completes
         */
        try {
            FileUploaderRunnable fileUploadRunnable = new FileUploaderRunnable(
                    request.getInputStream());

            /*
             * Future<FileUploaderRunnable> future = ProcessUtils.submitTask(
             * fileUploadRunnable, fileUploadRunnable);
             * 
             * FileUploaderRunnable processed = future.get();
             * 
             * System.out.println("Is file uploaded : " +
             * processed.isFileUploaded());
             */

            Thread uploadThread = new Thread(fileUploadRunnable);
            uploadThread.start();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } /*
         * catch (InterruptedException e) { // TODO Auto-generated catch block
         * e.printStackTrace(); } catch (ExecutionException e) { // TODO
         * Auto-generated catch block e.printStackTrace(); }
         */

        System.out.println("Returning from TempModel.uploadSegYFile(...)");
    }

}

The Runnable

package com.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;

public class FileUploaderRunnable implements Runnable {

    private boolean isFileUploaded = false;
    private InputStream inputStream = null;

    public FileUploaderRunnable(InputStream inputStream) {
        // TODO Auto-generated constructor stub
        this.inputStream = inputStream;
    }

    public void run() {
        // TODO Auto-generated method stub

        /* Read from InputStream. If success, set isFileUploaded = true */
        System.out.println("Starting upload in a thread");

        File outputFile = new File("D:/06c01_output.seg");/*
                                                         * This will be changed
                                                         * later
                                                         */
        FileOutputStream fos;
        ReadableByteChannel readable = Channels.newChannel(inputStream);
        ByteBuffer buffer = ByteBuffer.allocate(1000000);

        try {

            fos = new FileOutputStream(outputFile);

            while (readable.read(buffer) != -1) {
                fos.write(buffer.array());
                buffer.clear();
            }

            fos.flush();
            fos.close();

            readable.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("File upload thread completed");
    }

    public boolean isFileUploaded() {
        return isFileUploaded;
    }

}

My queries/doubts :

  1. Spawning threads manually from the Servlet makes sense to me logically but scares me coding wise - the container isn't aware of these threads after all(I think so!)
  2. The current code is giving an Exception which is quite obvious - the stream is inaccessible as the doPost(...) method returns before the run() method completes :

    In Controller.doPost(...)
    In TempModel.uploadSegYFile(...)
    Returning from TempModel.uploadSegYFile(...)
    Forwarding to Accepted.jsp
    Starting upload in a thread
    Exception in thread "Thread-4" java.lang.NullPointerException
        at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:512)
        at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:497)
        at org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:559)
        at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:324)
        at org.apache.coyote.Request.doRead(Request.java:422)
        at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:287)
        at org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:407)
        at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:310)
        at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:202)
        at java.nio.channels.Channels$ReadableByteChannelImpl.read(Unknown Source)
        at com.model.FileUploaderRunnable.run(FileUploaderRunnable.java:39)
        at java.lang.Thread.run(Unknown Source)
    
  3. Keeping in mind the point 1., does the use of Executor framework help me in anyway ?

    package com.utils;
    
    import java.util.concurrent.Future;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    
    public final class ProcessUtils {
    
        /* Ensure that no more than 2 uploads,processing req. are allowed */
        private static final ScheduledThreadPoolExecutor threadPoolExec = new ScheduledThreadPoolExecutor(
                2);
    
        public static <T> Future<T> submitTask(Runnable task, T result) {
    
            return threadPoolExec.submit(task, result);
        }
    }
    

So how should I ensure that the user doesn't block and the stream remains accessible so that the (uploaded)file can be read from it?

Was it helpful?

Solution

Actually it doesn't.You're trying to spawn thread and read content of POST request, and you're also trying to forward user to another page with same request object. This confuses servlet container.

You may

  • use separate frame for upload form and controller
  • use pop-up window with upload form and separate controller
  • use AJAX to load next page in-place, while still upload the content in current page (let browser handle that for you)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top