0

I have a servlet running on Tomcat, and I'm trying to figure out why my output goes like this:

One thread in!
Stops right before API call
successful! Time: 25
AppAsyncListener onComplete

One thread in!
Stops right before API call
AppAsyncListener onTimeout
successful! Time: 204

This should not happen. It blocked my doPost method for 25 seconds before the next thread went in. I read that servlets create a new thread for every doPost, but for some reason here it's being blocked like it's synchronized. Here is my code:

'@WebServlet(name = "League", urlPatterns = { "/League/*" }, asyncSupported = true)
public class BetaServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    ClientConnector connection;
    APICaller caller;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public BetaServlet() {
        super();
        initializeConnectionPool();
        caller = new APICaller();
    }

    private void initializeConnectionPool() {
        connection = new ClientConnector("na");// default is North America
    }
    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
        String reqURL = request.getPathInfo();
        if (reqURL != null) {// this means there is an API call using AJAX
            System.out.println(reqURL);
            switch (reqURL) {
            case "/postMatches":
                AsyncContext asyncCtx = request.startAsync();
                asyncCtx.addListener(new AppAsyncListener());
                ThreadPoolExecutor executor = (ThreadPoolExecutor) request.getServletContext().getAttribute("executor");
                executor.execute(new AsyncRequestProcessor(asyncCtx, caller, connection));
                break;
            default:// someone tried to access API that does not exist
                response.sendRedirect("/");
                break;
            }
        }
    }

I tried to fix it by making it async, but then I realized that the async only works inside of the doPost, which is the bottleneck of the entire operation.

    @WebListener
public class AppContextListener implements ServletContextListener {

    public void contextInitialized(ServletContextEvent servletContextEvent) {
        // create the thread pool
        ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
        servletContextEvent.getServletContext().setAttribute("executor",
                executor);
    }
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
            ThreadPoolExecutor executor = (ThreadPoolExecutor) servletContextEvent
                    .getServletContext().getAttribute("executor");
            executor.shutdown();
        }

    }

If any other code is needed, please let me know. I'm really confused as to why my doPost is getting jammed when really it was implemented to be multithreaded. Thank you!

EDIT: Code to show where the System.out.println's are.

public class AsyncRequestProcessor implements Runnable {

    private AsyncContext asyncContext;
    private APICaller caller;
    private ClientConnector connection;
    private static final boolean KILL_UPON_DUPLICATE = true;

    public AsyncRequestProcessor() {
    }

    public AsyncRequestProcessor(AsyncContext asyncCtx, APICaller caller, ClientConnector connection) {
        this.asyncContext = asyncCtx;
        this.caller = caller;
        this.connection = connection;
    }

    @Override
    public void run() {

        System.out.println("Async Supported? "
                + asyncContext.getRequest().isAsyncSupported());
        postMatches(asyncContext.getRequest(), asyncContext.getResponse());
        //complete the processing
        asyncContext.complete();
    }

    private JSONObject readRequest(ServletRequest request) {
        StringBuffer jb = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null)
                jb.append(line);
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject json = null;
        try {
            json = new JSONObject(jb.toString());
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return json;
    }

    private String generateQueryStringArray(String prefix, String arrayName, JSONObject root) {
        try {
            JSONArray array = root.getJSONArray(arrayName);
            String[] list = new String[array.length()];
            for (int i = 0; i < list.length; i++)
                list[i] = array.getString(i);
            prefix += String.join(",", list);
        } catch (Exception e) {
            return "";
        }
        return prefix;
    }

    private void postMatches(ServletRequest req, ServletResponse res) {
        System.out.println("One thread in!");
        ... other stuff happens ...
    }

I am making API calls to RiotGames using Jersey's client framework.

OneRaynyDay
  • 3,658
  • 2
  • 23
  • 56
  • Where does ths output come from? I don't see any line in the posted code that would generate that output. You're also not telling how you're sending requests to the web server. – JB Nizet Aug 25 '15 at 06:30
  • @JBNizet I will update the code, but everything is inside the thread class that implements Runnable. Edited! – OneRaynyDay Aug 25 '15 at 06:31
  • Are you using one or two clients to test it? – BalusC Aug 25 '15 at 06:35
  • @BalusC Two. I open up two tabs on google chrome and send my html form together as fast as I can with no time in between(I have about 25 seconds before the next one triggers in this example). – OneRaynyDay Aug 25 '15 at 06:36
  • 3
    A tab is not a client. Chrome itself is the client. Servlet uses one thread per HTTP connection when HTTP 1.1 keep alive is on. Retry with two physically different clients, e.g. Chrome and Firefox, or with Chrome on two physically different client environments, e.g. a virtual machine or a second machine. – BalusC Aug 25 '15 at 06:36
  • @BalusC Sure, I'll report back with the results. I'll use Safari and Chrome. – OneRaynyDay Aug 25 '15 at 06:40

0 Answers0