Java EE 7: Using Concurrency Utilities in Asynchronous Servlets

Overview

    Purpose

    This tutorial shows you how to create an application that uses the concurrency utilities for Java Platform, Enterprise Edition 7 (Java EE 7) to generate an asynchronous random number generator and a distributed search utility.

    Time to Complete

    Approximately 1 hour

    Introduction

    In computer science and computer systems, concurrency refers to the act in which several tasks are executed simultaneously (or asynchronously). The concurrency utilities for Java EE adds asynchronous capabilities to Java EE. They also provide a standard way for Java EE applications to submit asynchronous tasks and obtain managed threads from the Java EE application servers.

    In this tutorial, you learn how to use Java EE's package for concurrency utilities to create concurrent applications.

    Scenario

    In this tutorial, you create two applications. First, you create an asynchronous random number generator by using the ManagedScheduledExecutor class. The numbers are generated periodically and can be visualized on all clients connected to the server. Next, you create a minimalistic distributed search utility. This utility searches words asynchronously among different files in your computer.

    Software Requirements

    The following software requirements are needed for this tutorial:

    Prerequisites

    Before starting this tutorial, you should have:

    • Knowledge of the Java programming language
    • Knowledge of Java EE, especially servlets and Enterprise JavaBeans (EJB)
    • Basic knowledge of HTML/HTML 5

Introduction to Concurrency Utilities in Java EE 7

    The concurrency utilities for the Java EE (JSR-236) package provide a powerful, extensible framework of high-performance threading utilities. Concurrency utilities provide a standard way of accessing low-level asynchronous processing capabilities.

    The following are the primary components of the concurrency utilities:

    • ManagedExecutorService, is used by applications to execute submitted tasks asynchronously.
    • ManagedScheduledExecutorService, is used by applications to execute submitted tasks asynchronously at specific times.
    • ManagedThreadFactory, is used by application to create managed threads.
    • ContextFactory, is used to create dynamic proxy objects. The objects capture the context of a container and enable applications to run within that context later or to be submitted to a ManagedExecutorService object.

Generating a Random Number

    Creating a Java EE 7 Project

      Open the NetBeans IDE.

      Select File > New Project.

      Select Java Web from Categories and Web Application from Projects and click Next.

      Note: NetBeans may prompt you to activate the Java Web and EE feature. This is a normal step for first-time NetBeans users. Follow the wizard to activate that feature and then proceed to the next step in this tutorial.

      Enter ConcurrencyUtils as the project name and click Next.

      Select GlassFish Server 4.0 from the server list.

      Select Java EE 7 Web as the Java EE version and click Finish.

      The ConcurrencyUtils project is created.

      Right-click the ConcurrencyUtils project and select Run to test your application.

      A browser window displays a TODO write content message. You successfully created a Java EE 7 web application by using NetBeans.

    Creating a Java Servlet

      Select File > New File.

      Select Java from Categories and Java Class from File Types and click Next.

      Enter RandomNumberServlet as the class name.

      Enter org.example.concurrency as the package and click Finish.

      The RandomNumberServlet.java file is created and is added to the project.

      Add the @WebServlet annotation to specify the class as a Java servlet.

      package org.example.concurrency;
      
      @WebServlet
      public class RandomNumberServlet {
        
      }
      

      Import the necessary dependencies.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet
      public class RandomNumberServlet {
        
      }
      

      Configure the servlet path.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet {
        
      }
      

      Extend the RandomNumberServlet class to use the HttpServlet class.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
        
      }
      

      Override the doGet method.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
          @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        }
      }
      

      Specify a servlet response.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();   
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: </h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Import the missing dependencies.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: </h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Select File > Save to save the file.

      Open the index.html file.

      Add the following code to create a link to the RandomNumberServlet page.

      <!DOCTYPE html>
      <html>
          <head>
              <title></title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          </head>
          <body>
              <h1>Menu</h1>
          <ul>
              <li><a href="RandomNumber">Random Number</a></li>
          </ul>
          </body>
      </html>
      

      Select File > Save to save the file.

    Generating an Atomic Random Number Asynchronously

      Open the RandomNumberServlet.java file.

      Declare an AtomicInteger number.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private final AtomicInteger number;
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: </h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Create the RandomNumberServlet constructor method.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: </h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Add the AtomicInteger number to the servlet response.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Declare a new ManagedScheduledExecutorService object.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
          
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
      }
      

      Create the executor service's init() method.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
        
        @Override
        public void init() throws ServletException {
          
        }
      }
      

      Configure the executorService object to run periodically.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
        
        @Override
        public void init() throws ServletException {
          executorService.scheduleAtFixedRate(new Runnable() {
            
      }, 0, 3, TimeUnit.SECONDS);
        }
      }
      

      The ExecutorService object is set to run every three seconds.

      Create the run() method that is executed by the executorService object.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
        
        @Override
        public void init() throws ServletException {
          executorService.scheduleAtFixedRate(new Runnable() {
              @Override
      public void run() {
         
      }
          }, 0, 3, TimeUnit.SECONDS);
        }
      }
      

      Generate a new random number inside the run() method.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
        
        @Override
        public void init() throws ServletException {
          executorService.scheduleAtFixedRate(new Runnable() {
              @Override
              public void run() {
                 number.set((int) (Math.random() * 100));
              }
          }, 0, 3, TimeUnit.SECONDS);
        }
      }
      

      Save the file.

    Configuring the ManagedExecutorService Object

      In the Projects pane, right-click the WEB-INF folder and select New > Other.

      Select XML from Categories and XML Document from File Types and click Next.

      Enter web as the file name and click Next.

      Select Well-formatted Document and click Finish.

      The web.xml file is added to the project.

      Declare the ManagedScheduledExecutorService object name and specify its type by replacing the existing code with the following.

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="3.1"
               xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
                  
          <resource-env-ref>
              <resource-env-ref-name>concurrent/scheduledExecutor</resource-env-ref-name>
              <resource-env-ref-type>javax.enterprise.concurrent.ManagedScheduledExecutorService</resource-env-ref-type>
          </resource-env-ref>
      </web-app> 
      

      Note: Please ensure to remove the <root> and </root> tags if they are present.

      Save the file.

      Open the RandomNumberServlet.java file.

      Annotate ManagedScheduledExecutorService as a resource.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          @Resource(name = "concurrent/scheduledExecutor")
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
          }
        
           @Override
           public void init() throws ServletException {
             executorService.scheduleAtFixedRate(new Runnable() {
                 @Override
                 public void run() {
                     number.set((int) (Math.random() * 100));
                 }
             }, 0, 3, TimeUnit.SECONDS);
           }
      }
      

      Import the missing packages.

      package org.example.concurrency;
      
      import javax.servlet.annotation.WebServlet;
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.atomic.AtomicInteger;
      import javax.enterprise.concurrent.ManagedScheduledExecutorService;
      import javax.annotation.Resource;
      
      @WebServlet("/RandomNumber")
      public class RandomNumberServlet extends HttpServlet {
      
          @Resource(name = "concurrent/scheduledExecutor")
          private ManagedScheduledExecutorService executorService;
          private final AtomicInteger number;
      
          public RandomNumberServlet() {
             number = new AtomicInteger();   
          }
      	
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
             response.setContentType("text/html");
             PrintWriter out = response.getWriter();
             out.write("<html>");
             out.write("<head>");
             out.write("</head>");
             out.write("<body>");
             out.write("<h1>Number: ");
             out.write(number.toString());      
             out.write("</h1>");
             out.write("</body>");
             out.write("</html>");
        }
        
        @Override
        public void init() throws ServletException {
          executorService.scheduleAtFixedRate(new Runnable() {
              @Override
              public void run() {
                 number.set((int) (Math.random() * 100));
              }
          }, 0, 3, TimeUnit.SECONDS);
        }
      }
      

      Save the file.

    Testing the RandomNumberGenerator Servlet

      Open the RandomNumberServlet.java file.

      Save the file.

      Right-click the ConcurrencyUtils project and select Run.

      A new web browser window displays the menu.

      Click the Random Number link.

      A number is displayed.

      Wait three seconds and refresh the browser window. The browser window displays another random number.

      Open a second browser window and place it next to the first one.

      Refresh both browser windows.

      Both windows display the same random number.

Creating a Distributed Search by Using Concurrency Utilities

    Preparing the Project

      Open the index.html file.

      Add a search field.

      <!DOCTYPE html>
      <html>
          <head>
              <title></title>
              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
          </head>
          <body>
              <h1>Menu</h1>
              <ul>
                  <li><a href="RandomNumber">Random Number</a></li>
                  <li>
          <form action="MultiSearchServlet" method="GET">
              <input type="text" name="search">
              <input type="submit" value="Search">
          </form>
      </li>
              </ul>
          </body>
      </html>
      

      Note: The HTML form is configured to be processed in the MultiSearchServlet class. You create that class in the next section.

      Save the file.

    Creating the MultiSearchServlet Class

      Right-click the org.example.concurrency package and select New > Java Class.

      Enter MultiSearchServlet as the class name and click Finish.

      The MultiSearchServlet.java file is added to the project.

      Annotate the MultiSearchServlet class as a WebServlet and configure it.

      package org.example.concurrency;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet {
       
      }
      

      Extend the HttpServlet class and override the doGet method.

      package org.example.concurrency;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet extends HttpServlet {
      
          @Override
      protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
      }
      }
      

      Generate a servlet response.

      package org.example.concurrency;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet extends HttpServlet {
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
              response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<!DOCTYPE html>");
      out.println("<html>");
      out.println("<head>");
      out.println("<title>Servlet MultiSearchServlet</title>");
      out.println("</head>");
      out.println("<body>");
      out.println("<h1>Search Results:</h1>");
      out.println("<ul>");
      out.println("</ul>");
      out.println("</body>");
      out.println("</html>");
          }
      }
      

      Save the file.

    Creating the SearchResult Class

      Right-click the org.example.concurrency package and select New > Java Class.

      Enter SearchResult as the class name and click Finish.

      The SearchResult.java class is added to the project.

      Declare the search result variables.

      package org.example.concurrency;
      
      public class SearchResult {
      
         private final String lineContent;
      private final String filename;
      private final int line;
      
      }
      

      Create the constructor, getter, and setter methods for the class.

      package org.example.concurrency;
      
      public class SearchResult {
      
        private final String lineContent;
        private final String filename;
        private final int line;
      
      public SearchResult(String lineContent, String filename, int line) {
          this.lineContent = lineContent;
          this.filename = filename;
          this.line = line;
        }
      
        public String getLineContent() {
          return lineContent;
        }
      
        public String getFilename() {
          return filename;
        }
      
        public int getLine() {
          return line;
        }
      
        @Override
        public String toString() {
          return "SearchResult: {" + "file=" + filename + ", line=" + line + ",content=" + lineContent + '}';
        }
      
      }
      
      

      Save the file.

    Creating the MultiSearchEjb Class

      Right-click the org.example.concurrency package and select New > Java Class.

      Enter MultiSearchEjb as the class name and click Finish.

      The MultiSearchEjb.java is created and is added to the project.

      Create a search method that receives a search string as the input.

      package org.example.concurrency;
      
      public class MultiSearchEjb {
      
         public List<SearchResult> search(final String search) {
      
      }
      }
      

      Add the @Stateless annotation to specify the MultiSearchEjb class as a stateless session bean.

      package org.example.concurrency;
      
      @Stateless
      public class MultiSearchEjb {
      
         public List<SearchResult> search(final String search) {
      
         }
      }
      

      Import the missing dependencies.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      
      @Stateless
      public class MultiSearchEjb {
      
         public List<SearchResult> search(final String search) {
      
         }
      }
      

      Save the file.

      Open the MultiSearchServlet.java file.

      Inject the MultiSearchEjb class.

      package org.example.concurrency;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet extends HttpServlet {
      
          @Inject
      private MultiSearchEjb ejb;
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println("<!DOCTYPE html>");
              out.println("<html>");
              out.println("<head>");
              out.println("<title>Servlet MultiSearchServlet</title>");
              out.println("</head>");
              out.println("<body>");
              out.println("<h1>Search Results:</h1>");
              out.println("<ul>");
              out.println("</ul>");
              out.println("</body>");
              out.println("</html>");
          }
      }
      

      Iterate over the MultiSearchEjb class results and print them.

      package org.example.concurrency;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet extends HttpServlet {
      
          @Inject
          private MultiSearchEjb ejb;
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println("<!DOCTYPE html>");
              out.println("<html>");
              out.println("<head>");
              out.println("<title>Servlet MultiSearchServlet</title>");
              out.println("</head>");
              out.println("<body>");
              out.println("<h1>Search Results:</h1>");
              out.println("<ul>");
              for (SearchResult result : ejb.search(request.getParameter("search"))) {
         out.println("<li>");
         out.println(result);
         out.println("</li>");
      }
              out.println("</ul>");
              out.println("</body>");
              out.println("</html>");
          }
      }
      

      Import the missing packages.

      package org.example.concurrency;
      
      import java.io.IOException;
      import java.io.PrintWriter;
      import javax.inject.Inject;
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      
      @WebServlet("/MultiSearchServlet")
      public class MultiSearchServlet extends HttpServlet {
      
          @Inject
          private MultiSearchEjb ejb;
      
          @Override
          protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
              response.setContentType("text/html");
              PrintWriter out = response.getWriter();
              out.println("<!DOCTYPE html>");
              out.println("<html>");
              out.println("<head>");
              out.println("<title>Servlet MultiSearchServlet</title>");
              out.println("</head>");
              out.println("<body>");
              out.println("<h1>Search Results:</h1>");
              out.println("<ul>");
              for (SearchResult result : ejb.search(request.getParameter("search"))) {
                  out.println("<li>");
                  out.println(result);
                  out.println("</li>");
              }
              out.println("</ul>");
              out.println("</body>");
              out.println("</html>");
          }
      }
      

      Save the file.

    Generating the Search Results

      Open the MultiSearchEjb.java file.

      Declare a ManagedExecutorService object.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      
      @Stateless
      public class MultiSearchEjb {
      
         private ManagedExecutorService executorService;
      
         public List<SearchResult> search(final String search) {
      
         }
      }
      

      Open the web.xml file and declare the ManagedExecutor service.

      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="3.1"
               xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
                  
          <resource-env-ref>
              <resource-env-ref-name>concurrent/scheduledExecutor</resource-env-ref-name>
              <resource-env-ref-type>javax.enterprise.concurrent.ManagedScheduledExecutorService</resource-env-ref-type>
          </resource-env-ref>
          <resource-env-ref>
          <resource-env-ref-name>concurrent/executor</resource-env-ref-name>
          <resource-env-ref-type>javax.enterprise.concurrent.ManagedExecutorService</resource-env-ref-type>
      </resource-env-ref>
      </web-app> 
      

      Save the file.

      Open the MultiSearchEjb.java file.

      The ManagedExecutorService resource is configured to be accessed by the concurrent/executor name.

      Add the @Resource annotation to inject the ManagedExecutorService resource.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
         public List<SearchResult> search(final String search) {
      
         }
      }
      

      Import the missing classes.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
         public List<SearchResult> search(final String search) {
      
         }
      }
      

      Save the file.

    Implementing the Search Class

      Create a synchronizedList to store the search results.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
         public List<SearchResult> search(final String search) {
            List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
      return results;
         }
      }
      

      Declare a Semaphore object and initialize it to zero permits.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
         public List<SearchResult> search(final String search) {
            List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
            Semaphore semaphore = new Semaphore(0);
            return results;
         }
      }
      

      Declare a list of files to search.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
      files.add(new File("/C:/temp/Words1.txt"));
      files.add(new File("/C:/temp/Words2.txt"));
      files.add(new File("/C:/temp/Words3.txt"));
      files.add(new File("/C:/temp/Words4.txt"));
             return results;
          }
      }
      

      Note: In this example, the files are located in C:/temp/Words1.txt, C:/temp/Words2.txt, C:/temp/Words3.txt, and C:/temp/Words4.txt. Please ensure that these files exist are in your machine.

      Run the tryAcquire method to conduct a search within a 10-second window.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             try {
         semaphore.tryAcquire(4, 10, TimeUnit.SECONDS);
      } catch (InterruptedException ex) {
         Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
      }
             return results;
          }
      }
      

      Modify the tryAcquire method to wait for the search of the files.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;	   
          }
      }
      

      Iterate over the files on which the search is conducted and execute the SearchTask class for each file.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
         executorService.execute(new SearchTask(file, search, semaphore, results));
      }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      }
      

      Note: You create the SearchTask class in the next section.

      Save the file.

    Assigning Asynchronous Permissions by Using Semaphores

      Create the SearchTask inner class that implements Runnable.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
      }
      }
      
      
      

      Create the constructor method and declare its fields.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
             private final File file;
      private final String searchString;
      private final Semaphore semaphore;
      private final List<SearchResult> allResults;
      
      private SearchTask(File file, String searchString, Semaphore semaphore, List<SearchResult> allResults) {
         this.file = file;
         this.searchString = searchString;
         this.semaphore = semaphore;
         this.allResults = allResults;
      }
          }
      }
      

      Create and override the run() method with the @Override annotation.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
             private final File file;
             private final String searchString;
             private final Semaphore semaphore;
             private final List<SearchResult> allResults;
      
             private SearchTask(File file, String searchString, Semaphore semaphore, List<SearchResult> allResults) {
               this.file = file;
               this.searchString = searchString;
               this.semaphore = semaphore;
               this.allResults = allResults;
             }
      	   
             @Override
      public void run() {
      }
          }         
      
      }
      

      Conduct a search over the received file.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
             private final File file;
             private final String searchString;
             private final Semaphore semaphore;
             private final List<SearchResult> allResults;
      
             private SearchTask(File file, String searchString, Semaphore semaphore, List<SearchResult> allResults) {
               this.file = file;
               this.searchString = searchString;
               this.semaphore = semaphore;
               this.allResults = allResults;
             }
      	   
             @Override
             public void run() {
               try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                  int count = 0;
                  String filename = file.getAbsolutePath();
                  String line;
                  int lineNumber = 1;
                  while ((line = reader.readLine()) != null) {
                     if (count <= 3 && line.contains(searchString)) {
                      allResults.add(new SearchResult(line, filename, lineNumber));
                      count++;
                    }
                   lineNumber++;
                 }
               } catch (IOException ex) {
                  Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
               }   
            }
         }         
      }
      

      When the search is completed, release the semaphore's permit.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
             private final File file;
             private final String searchString;
             private final Semaphore semaphore;
             private final List<SearchResult> allResults;
      
             private SearchTask(File file, String searchString, Semaphore semaphore, List<SearchResult> allResults) {
               this.file = file;
               this.searchString = searchString;
               this.semaphore = semaphore;
               this.allResults = allResults;
             }
      	   
             @Override
             public void run() {
               try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                  int count = 0;
                  String filename = file.getAbsolutePath();
                  String line;
                  int lineNumber = 1;
                  while ((line = reader.readLine()) != null) {
                     if (count <= 3 && line.contains(searchString)) {
                      allResults.add(new SearchResult(line, filename, lineNumber));
                      count++;
                    }
                   lineNumber++;
                 }
               } catch (IOException ex) {
                  Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
               }   
               semaphore.release();
            }
         }         
      }
      

      Import the missing dependencies.

      package org.example.concurrency;
      
      import java.util.List;
      import javax.ejb.Stateless;
      import javax.enterprise.concurrent.ManagedExecutorService;
      import java.io.BufferedReader;
      import java.io.File;
      import java.io.FileReader;
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.concurrent.Semaphore;
      import java.util.concurrent.TimeUnit;
      import java.util.logging.Level;
      import java.util.logging.Logger;
      import javax.annotation.Resource;
      
      @Stateless
      public class MultiSearchEjb {
      
         @Resource(name = "concurrent/executor")
         private ManagedExecutorService executorService;
      
          public List<SearchResult> search(final String search) {
             List<SearchResult> results = Collections.synchronizedList(new ArrayList<SearchResult>());
             Semaphore semaphore = new Semaphore(0);
             List<File> files = new ArrayList<>();
             files.add(new File("/C:/temp/Words1.txt"));
             files.add(new File("/C:/temp/Words2.txt"));
             files.add(new File("/C:/temp/Words3.txt"));
             files.add(new File("/C:/temp/Words4.txt"));
             for (File file : files) {
                executorService.execute(new SearchTask(file, search, semaphore, results));
             }
             try {
                semaphore.tryAcquire(files.size(), 10, TimeUnit.SECONDS);
             } catch (InterruptedException ex) {
                Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
             }
             return results;
          }
      	
          class SearchTask implements Runnable {
             private final File file;
             private final String searchString;
             private final Semaphore semaphore;
             private final List<SearchResult> allResults;
      
             private SearchTask(File file, String searchString, Semaphore semaphore, List<SearchResult> allResults) {
               this.file = file;
               this.searchString = searchString;
               this.semaphore = semaphore;
               this.allResults = allResults;
             }
      	   
             @Override
             public void run() {
               try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
                  int count = 0;
                  String filename = file.getAbsolutePath();
                  String line;
                  int lineNumber = 1;
                  while ((line = reader.readLine()) != null) {
                     if (count <= 3 && line.contains(searchString)) {
                      allResults.add(new SearchResult(line, filename, lineNumber));
                      count++;
                    }
                   lineNumber++;
                 }
               } catch (IOException ex) {
                  Logger.getLogger(MultiSearchEjb.class.getName()).log(Level.SEVERE, null, ex);
               }   
               semaphore.release();
            }
         }         
      }
      

      Save the file.

    Testing the Asynchronous Search

      Create the Words1.txt, Words2.txt, Words3.txt, and Words4.txt files in the C:/temp directory.

      Populate each of the Words files with any text of your choice (such as an article).

      Right-click the ConcurrencyUtils project and click Run.

      A new web browser window opens.

      Enter a search term and click Search.

      The search results are displayed in the browser.

      Because the search space is limited, it is difficult to notice its concurrency. However, running the search multiple times can yields different results. Alternatively, you can conduct the search over more and larger files (larger than 1 MB) to better notice the results of the concurrent search.

Summary

    In this tutorial, you learned how to use the ManagedExecutorService and the ManagedScheduledExecutorService classes of the concurrency utilities for Java EE.

    You also learned how to create an asynchronous random number generator that periodically updates its value and how to create a distributed search utility that handles concurrency.

    Resources

    For more information about the topics in this tutorial, see:

    Credits

    • Lead Curriculum Developer: Miguel Salazar
    • Other Contributors: Eduardo Moranchel

To help navigate this Oracle by Example, note the following:

Hiding Header Buttons:
Click the Title to hide the buttons in the header. To show the buttons again, simply click the Title again.
Topic List Button:
A list of all the topics. Click one of the topics to navigate to that section.
Expand/Collapse All Topics:
To show/hide all the detail for all the sections. By default, all topics are collapsed
Show/Hide All Images:
To show/hide all the screenshots. By default, all images are displayed.
Print:
To print the content. The content currently displayed or hidden will be printed.

To navigate to a particular section in this tutorial, select the topic from the list.