Java EE Concurrency API

    Hello!

    And here we indulge in buns, launching the second stream of the Java Enterprise Developer course . The permanent creator and teacher of the course, Vitaly Ivanov , wrote an article about it even which we hope will seem useful to you :)

    So let 's go :)

    This article is dedicated to studying the API to the JavaEE Concurrency specification ( JSR 236), which defines the standard for performing parallel tasks in a JavaEE container using the concept of managed resources. The release of the seventh version of JavaEE made it possible to run parallel tasks in Enterprise containers, providing the developer with convenient tools and utilities for working with multitasking. Until that very moment, all multitasking was given to the specific implementation of the application server used, independently deciding on the optimization of task execution. Violation of this principle was regarded as a bad practice of building an enterprise application architecture. As a result, the developer was not recommended to create new threads, and sometimes similar behavior was prohibited at the container level.


    Manage threads. It is a priority or a name. Enterprise enterprise bean

    (free translation of the author: EJBs should not try to control threads, namely, try to start, stop, suspend, or restore their execution, or change the priority or change the name of a stream. Also, EJBs should not try to manage groups of threads).

    In fact, prohibiting the creation of your own threads in JavaEE containers is problematic, but with this approach the “background” container services cannot guarantee the correctness of their work. For example, closing a transaction upon the completion of an EJB method could potentially work incorrectly in the case of running tasks in a new thread using Threads heirs (or Runnable implementations) from JavaSE. Also, the use of basic interface types from the Executor API delivery, such as ExecutorService and ScheduledExecutorService, when created using Executors class static methods, would lead to potential errors and disrupting the order of execution of container services.

    From the JavaEE specification-recommended tools for asynchronous tasks, the developer had at his disposal the use of asynchronous Stateless / Statefull EJB and / or Message Driven bins, the capabilities of which are sufficient for a specific set of tasks and most importantly that the management of which is entirely and entirely controlled by the application server, namely the EJB container.

    However, as noted earlier, thanks to JSR 236 , container-managed resources have emerged that implement support for multithreading and asynchronous task execution, extending the capabilities of a package java.util.concurrentfrom JavaSE. For the JavaEE stack, the classes of managed resources are located in a package javax.enterprise.concurrent, while access to the objects of these classes is carried out through the resource injection using the annotation @Resourceor through a JNDI context (in particular, InitialContext). At the same time, the possibility of using Future / ScheduledFuture / CompletableFuture objects familiar to the multithreaded environment inside JavaEE applications was added.

    So, quite lyric and let's take a closer look at each of the managed resources provided by the specification from a practical point of view, namely in terms of use in the application code of the application, as well as in terms of configuring resources on the example of the Glassfish application server 5.

    Well, first on The review queue selected the ManagedExecutorService class, which (already understanding the name) extends the capabilities of the usual JavaSE ExecutorService designed for asynchronous execution of tasks in the JavaEE environment.

    To configure not only this type of ExecutorService within the Glassfish application server, refer to the domain.xml configuration file, whose location is defined by the $ {GLASSFISH_HOME} / domains / <domain_name> / config directory. A fragment of this file is presented below:

    <domain application-root="${com.sun.aas.instanceRoot}/applications" version="25" log-root="${com.sun.aas.instanceRoot}/logs">
        <resources>
            <context-service object-type="system-all" jndi-name="concurrent/__defaultContextService" />
            <managed-executor-service object-type="system-all" jndi-name="concurrent/__defaultManagedExecutorService" />
            <managed-scheduled-executor-service object-type="system-all" jndi-name="concurrent/__defaultManagedScheduledExecutorService" />
            <managed-thread-factory object-type="system-all" jndi-name="concurrent/__defaultManagedThreadFactory" />
        </resources>
        <servers>
            <server config-ref="server-config" name="server">
                <resource-ref ref="concurrent/__defaultContextService" />
                <resource-ref ref="concurrent/__defaultManagedExecutorService" />
                <resource-ref ref="concurrent/__defaultManagedScheduledExecutorService" />
                <resource-ref ref="concurrent/__defaultManagedThreadFactory" />
            </server>
        </servers>
    </domain>

    Going into the Glassfish 5 admin panel interface, configuring the

    ManagedExecutorService looks like this:



    In this section, you can create new resources of the same type, manage existing resources, delete, and also lock and unlock.

    For fans of console administration, Glassfish provides a powerful asadmin utility, using which create-managed-executor-serviceyou can create new resources ManagedExecutorService



    in the application : In the application code to get a reference to the object created by ManagedExecutorService it is more convenient to use resource injection, but you can also use JNDI tools as shown below:

    @Resource(lookup = "concurrent/OtusExecutorService")
    ManagedExecutorService executor; 
    InitialContext context = new InitialContext();
    ManagedExecutorService managedExecutorServiceWithContext = 
            (ManagedExecutorService) context.lookup(
                    "concurrent/OtusExecutorService"); 
    

    I would like to draw the reader’s attention to the fact that for the annotation @Resourcethe lookup parameter is optional and if it is not defined by the developer in the application code, then the container injects default resources with the prefix in the name __default. In this case, for the developer, the code becomes even more concise:

    @Resource
    ManagedExecutorService executor; 

    After obtaining a reference to this object, using the methods execute()and submit(), you can run tasks inside the container that implement the Runnable or Callable interface.

    Turning to the example, I would like to note that among all the variety of possible cases, the tasks of particular interest are those performed in the distributed JavaEE environment and in which it is important to ensure the support of transactionality in a multi-threaded environment. As you know, JavaEE has developed the JTA (Java Transaction API) specification, which allows you to define the boundaries of a transaction, explicitly starting with its method begin()and completing with methods commit(), committing changes, or rollback()rolling back the actions taken.

    Consider an example of a task that returns a message from a certain list of one hundred elements, by index in a user transaction:

    publicclassTransactionSupportCallableTaskimplementsCallable<String> {
        privateint messageIndex;
        publicTransactionSupportCallableTask(int messageId){
            this. messageIndex = messageId;
        }
        public String call(){
            UserTransaction tx = lookupUserTransaction();
            String message = null;
            try {
                tx.begin();
                message = getMessage(messageIndex);
                tx.commit();
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    tx.rollback();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
            return message;
        }
        privatevoidgetMessage(int index){ … }
        private UserTransaction lookupUserTransaction(){ … }
    }

    The code of the servlet that displays the message on the list at a randomly selected index:

    @WebServlet("/task")
    publicclassManagedExecutorServiceServletextendsHttpServlet{
        @Resource(lookup = "concurrent/OtusExecutorService")
        ManagedExecutorService executor;
        protectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            Future<String> futureResult = executor.submit(new TransactionSupportCallableTask(Random.nextInt(100)));
            while (!futureResult.isDone()) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                response.getWriter().write("Callable task has received message with following content '" + futureResult.get() + "'");
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    The next resource to be selected is the ManagedScheduledExecutorService resource, the main purpose of which is to schedule tasks that are repeated with a certain periodicity or require deferred execution.



    From the point of view of configuring this resource through the GlassFish administration console, no special changes were found in comparison with the previous type:



    To quickly create a resource with the ManagedScheduledExecutorService type, the utility asadminhas a command. create-managed-scheduled-executor-service



    In the application code, we still use resource injection:

    @Resource(lookup = "concurrent/OtusScheduledExecutorService")
    ManagedScheduledExecutorService scheduledExecutor;

    The main methods for executing tasks for this type of ExecutorService are schedule(), accepting tasks like Runnable or Callable as input, and scheduleAtFixedRate()additionally defining the initial delay in performing the task and specifying the interval of repetitions in TimeUnit-s (seconds, minutes, etc.).

    The previous case can be rewritten as follows:

    @WebServlet("/scheduledTask")
    publicclassManagedScheduledExecutorServiceServletextendsHttpServlet{
        @Resource(lookup = "concurrent/OtusScheduledExecutorService")
        ManagedScheduledExecutorService scheduledExecutor;
        protectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            ScheduledFuture<String> futureResult = scheduledExecutor.schedule(
                    new TransactionSupportCallableTask(Random.nextInt(100)), 5, TimeUnit.SECONDS);
            while (!futureResult.isDone()) {
                try {
                    Thread.sleep(50); // Wait
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                response.getWriter().write("Callable task received message with following content '" + futureResult.get() + "'");
            } catch ( Exception e) {
                e.printStackTrace();
            }
        }
    }

    Also, the Concurrency API for the Enterpise environment provides the ability to create managed threads. For these tasks, you should use the capabilities of a managed thread factory that implements its functionality through the ManagedThreadFactory class of the same name and which is also accessed via the JNDI service:

    @Resource
    ManagedThreadFactory factory;

    The administration window of the Glassfish console looks “old-fashioned”:



    Using a managed thread factory, it is possible not only to provide the container with thread control mechanisms, but also to initialize the properties of the generated threads: set names and define priorities, which can seriously simplify the search for problems when parsing threads , easily finding out the order of execution of previously named threads.

    In our case, we define the class of the stream that displays information about a friend with which the task is inextricably linked to the console:

    publicclassSimpleThreadTaskimplementsRunnable{
        private String friend;
        publicSimpleThreadTask(String friend){
            this.friend = friend;
        }
        @Overridepublicvoidrun(){
            System.out.println("Hello, " + friend);
        }
    }

    Let the servlet start a thread and report it at the output:

    @WebServlet("/thread")
    publicclassManagedThreadFactoryServletextendsHttpServlet{
        @Resource
        ManagedThreadFactory factory;
        protectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            Thread thread = factory.newThread(new SimpleThreadTask("Otus"));
            thread.setName("ManagedThreadFromPool");
            thread.setPriority(7);
            thread.start();
            response.getWriter().write("Custom thread has been running.");
        }
    }
    

    Turning to the final JavaEE multithreading capabilities of Context Services, it should be noted that these services create dynamic context proxies. We all know perfectly well the capabilities of dynamic proxies from JavaSE ( java.lang.reflect.Proxy), which allow to generate dynamic implementations of the required interfaces, whose capabilities are actively used for the tasks of creating connections to the database and managing transactions, are used for all sorts of AOP interceptors and so on. Moreover, for proxies created via context-specific JavaEE services, it is supposed to work within the framework of a common JNDI context, security context, and container class wizard.

    To connect the service it is enough to use the code:

    @Resource
    ContextService service;

    From the point of view of administration and configuration of this resource, everything is extremely familiar and similar to the types already considered:



    Below is an example of a thread that runs a proxy task in the context of a container:

    publicclassSampleProxyTaskimplementsRunnable{
        @Overridepublicvoidrun(){
    	//контекст контейнера
            Subject subject = Subject.getSubject(AccessController.getContext());
            logInfo(subject.getPrincipals()); //логируем информацию о принципалах
            calculateSmth();
        }
        privatevoidcalculateSmth(){ … }
        privatevoidlogInfo(Set<Principal> subject){ … }
    }

    Stateless EJB Bin for creating context proxies:

    @StatelesspublicclassContextServiceBean{
        @Resource
        ContextService service;
        @Resource
        ManagedExecutorService executor;
        publicvoidperform(Runnable task){
            Runnable proxy = service.createContextualProxy(task, Runnable.class);
            executor.submit(proxy);
        }
    }

    Finally, the code of the servlet performing the task:

    @WebServlet("/context")
    publicclassContextServiceServletextendsHttpServlet{
        @Inject
        ContextServiceBean contextServiceBean;
        protectedvoiddoGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            contextServiceBean.perform(new SampleProxyTask());
        }
    }

    This actually ends the JavaEE developer’s ability to work in a multi-threaded environment. Thanks to them, all processes and services occurring in the container will be under the strict control of the server, coordinating their work and not disturbing the usual order of execution. For the target tasks of an Enterprise developer, these opportunities are often enough and in the eighth version this API has not changed.

    THE END

    As always, we are waiting for questions and comments and be sure to take a look at Vitaly for an open lesson , there you can also ask questions and listen to \ participate in the topic “CDI in action @

    Also popular now: