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 }