View Javadoc
1   /*
2    * Copyright (c) 2012-2024, jcabi.com
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met: 1) Redistributions of source code must retain the above
8    * copyright notice, this list of conditions and the following
9    * disclaimer. 2) Redistributions in binary form must reproduce the above
10   * copyright notice, this list of conditions and the following
11   * disclaimer in the documentation and/or other materials provided
12   * with the distribution. 3) Neither the name of the jcabi.com nor
13   * the names of its contributors may be used to endorse or promote
14   * products derived from this software without specific prior written
15   * permission.
16   *
17   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
19   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21   * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28   * OF THE POSSIBILITY OF SUCH DAMAGE.
29   */
30  package com.jcabi.aspects.aj;
31  
32  import java.util.concurrent.ExecutorService;
33  import java.util.concurrent.Executors;
34  import java.util.concurrent.Future;
35  import org.aspectj.lang.ProceedingJoinPoint;
36  import org.aspectj.lang.annotation.Around;
37  import org.aspectj.lang.annotation.Aspect;
38  import org.aspectj.lang.reflect.MethodSignature;
39  
40  /**
41   * Execute method asynchronously.
42   *
43   * <p>It is an AspectJ aspect and you are not supposed to use it directly. It
44   * is instantiated by AspectJ runtime framework when your code is annotated
45   * with {@link com.jcabi.aspects.Async} annotation.
46   *
47   * @since 0.16
48   */
49  @Aspect
50  public final class MethodAsyncRunner {
51  
52      /**
53       * Thread pool for asynchronous execution.
54       */
55      private final transient ExecutorService executor =
56          Executors.newFixedThreadPool(
57              Runtime.getRuntime().availableProcessors(),
58              new NamedThreads(
59                  "async",
60                  "Asynchronous method execution"
61              )
62          );
63  
64      /**
65       * Execute method asynchronously.
66       *
67       * <p>This aspect should be used only on {@code void} or
68       * {@link Future} returning methods.
69       *
70       * <p>Try NOT to change the signature of this method, in order to keep
71       * it backward compatible.
72       *
73       * @param point Joint point
74       * @return The result of call
75       */
76      @Around("execution(@com.jcabi.aspects.Async * * (..))")
77      @SuppressWarnings("PMD.AvoidCatchingThrowable")
78      public Object wrap(final ProceedingJoinPoint point) {
79          final Class<?> returned = ((MethodSignature) point.getSignature()).getMethod()
80              .getReturnType();
81          if (!Future.class.isAssignableFrom(returned)
82              && !returned.equals(Void.TYPE)) {
83              // @checkstyle LineLength (3 lines)
84              throw new IllegalStateException(
85                  String.format(
86                      "%s: Return type is %s, not void or Future, cannot use @Async",
87                      Mnemos.toText(point, true, true),
88                      returned.getCanonicalName()
89                  )
90              );
91          }
92          final Future<?> result = this.executor.submit(
93              // @checkstyle AnonInnerLength (23 lines)
94              () -> {
95                  Object ret = null;
96                  try {
97                      final Object res = point.proceed();
98                      if (res instanceof Future) {
99                          ret = ((Future<?>) res).get();
100                     }
101                 // @checkstyle IllegalCatch (1 line)
102                 } catch (final Throwable ex) {
103                     throw new IllegalStateException(
104                         String.format(
105                             "%s: Exception thrown",
106                             Mnemos.toText(point, true, true)
107                         ),
108                         ex
109                     );
110                 }
111                 return ret;
112             }
113         );
114         Object res = null;
115         if (Future.class.isAssignableFrom(returned)) {
116             res = result;
117         }
118         return res;
119     }
120 
121 }