Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ abstract class AbstractPromiseFactory implements PromiseFactory {
* @see PromiseFactory#createPromise(java.util.List, java.util.List)
*/
<T> Promise<List<T>> createPromise(List<Closure<T>> closures, List<PromiseDecorator> decorators) {
if (closures == null) {
return new BoundPromise<List<T>>(Collections.<T> emptyList())
}
Comment thread
jamesfredley marked this conversation as resolved.
List<Closure<T>> decoratedClosures = new ArrayList<Closure<T>>(closures.size())
for (Closure<T> closure : closures) {
decoratedClosures.add(applyDecorators(closure, decorators))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public class DelegateAsyncTransformation implements ASTTransformation, Transform
public static final ClassNode GROOVY_OBJECT_CLASS_NODE = new ClassNode(GroovyObjectSupport.class);
public static final ClassNode OBJECT_CLASS_NODE = new ClassNode(Object.class);

@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
throw new GroovyBugError("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + Arrays.asList(nodes));
Expand Down Expand Up @@ -118,7 +119,7 @@ private void applyDelegateAsyncTransform(ClassNode classNode, ClassNode targetAp
if (existingMethod == null) {
ClassNode promiseNode = ClassHelper.make(Promise.class).getPlainNodeReference();
ClassNode originalReturnType = m.getReturnType();
if (!originalReturnType.getNameWithoutPackage().equals(VOID)) {
if (!VOID.equals(originalReturnType.getNameWithoutPackage())) {
ClassNode returnType;
if (ClassHelper.isPrimitiveType(originalReturnType.redirect())) {
returnType = ClassHelper.getWrapper(originalReturnType.redirect());
Expand Down Expand Up @@ -200,7 +201,7 @@ protected DelegateAsyncTransactionalMethodTransformer lookupAsyncTransactionalMe
try {
Class<?> transformerClass = getClass().getClassLoader().loadClass("org.grails.async.transform.internal.DefaultDelegateAsyncTransactionalMethodTransformer");
return (DelegateAsyncTransactionalMethodTransformer) transformerClass.getDeclaredConstructor().newInstance();
} catch (Throwable e) {
} catch (Exception ignored) {
// ignore
}
return new NoopDelegateAsyncTransactionalMethodTransformer();
Expand All @@ -214,7 +215,7 @@ private static boolean isCandidateMethod(MethodNode declaredMethod) {
!GROOVY_OBJECT_CLASS_NODE.hasMethod(declaredMethod.getName(), declaredMethod.getParameters());
}

private static Parameter[] copyParameters(Parameter[] parameterTypes) {
private static Parameter[] copyParameters(Parameter... parameterTypes) {
Parameter[] newParameterTypes = new Parameter[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Parameter parameterType = parameterTypes[i];
Expand All @@ -236,6 +237,7 @@ public int priority() {
}

private static class NoopDelegateAsyncTransactionalMethodTransformer implements DelegateAsyncTransactionalMethodTransformer {
@Override
public void transformTransactionalMethod(ClassNode classNode, ClassNode delegateClassNode, MethodNode methodNode, ListExpression promiseDecoratorLookupArguments) {
// noop
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,27 @@ class SynchronousPromiseFactorySpec extends Specification {
then: 'the closure is executed twice'
2 * callable.call()
}
}

void 'createPromise with a null closures list returns a completed empty-list promise'() {

given: 'a factory and a decorator list'
def factory = Promises.promiseFactory
def decorators = [{ Closure c -> c } as PromiseDecorator]

when: 'createPromise is called with a null closures list'
def promise = factory.createPromise((List<Closure<Object>>) null, decorators)

then: 'the returned promise is already done and resolves to an empty list'
promise.isDone()
promise.get() == []

when: 'onComplete is registered on the resulting promise'
def callbackResult = null
def callbackInvoked = false
promise.onComplete { value -> callbackInvoked = true; callbackResult = value }

then: 'the callback is invoked synchronously with an empty list rather than busy-waiting'
callbackInvoked
callbackResult == []
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class LoggingPoolFactory implements PoolFactory {
private static final long KEEP_ALIVE_TIME = 10L
public static final Logger LOG = LoggerFactory.getLogger(LoggingPoolFactory)

public static Method createThreadNameMethod
public static final Method createThreadNameMethod

static {
createThreadNameMethod = DefaultPool.getDeclaredMethod('createThreadName')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ package grails.async.services

import groovy.transform.CompileStatic

import grails.persistence.support.PersistenceContextInterceptorExecutor
import grails.async.decorator.PromiseDecorator
import grails.persistence.support.PersistenceContextInterceptorExecutor

/**
* A {@link PromiseDecorator} that wraps a promise execution in a persistence context (example Hibernate session)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,25 @@

package grails.async.web

import groovy.transform.CompileStatic
import org.grails.web.util.GrailsApplicationAttributes
import org.grails.web.servlet.mvc.GrailsWebRequest
import org.springframework.context.ApplicationContext
import org.springframework.util.Assert
import org.springframework.web.context.request.async.AsyncWebRequest

import java.util.concurrent.atomic.AtomicBoolean
import java.util.function.Consumer

import groovy.transform.CompileStatic

import jakarta.servlet.AsyncContext
import jakarta.servlet.AsyncEvent
import jakarta.servlet.AsyncListener
import jakarta.servlet.ServletContext
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse

import org.springframework.context.ApplicationContext
import org.springframework.util.Assert
import org.springframework.web.context.request.async.AsyncWebRequest

import org.grails.web.servlet.mvc.GrailsWebRequest
import org.grails.web.util.GrailsApplicationAttributes

/**
* Implementation of Spring 4.0 {@link AsyncWebRequest} interface for Grails
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ import org.codehaus.groovy.syntax.Types
import org.springframework.transaction.PlatformTransactionManager
import org.springframework.transaction.annotation.Transactional

import grails.transaction.TransactionManagerAware
import org.grails.compiler.injection.GrailsASTUtils
import org.grails.compiler.web.async.TransactionalAsyncTransformUtils
import grails.transaction.TransactionManagerAware

/**
* Modifies the @DelegateAsync transform to take into account transactional services. New instance should be created per class transform, as state is held.
Expand All @@ -61,7 +61,9 @@ class DefaultDelegateAsyncTransactionalMethodTransformer implements DelegateAsyn
private static final ClassNode TRANSACTIONAL_CLASS_NODE = new ClassNode(Transactional)
private static final ClassNode INTERFACE_TRANSACTION_MANAGER = new ClassNode(PlatformTransactionManager).getPlainNodeReference()
private static final ClassNode INTERFACE_TRANSACTION_MANAGER_AWARE = new ClassNode(TransactionManagerAware).getPlainNodeReference()
private static final Parameter[] SET_TRANSACTION_MANAGER_METHOD_PARAMETERS = [new Parameter(INTERFACE_TRANSACTION_MANAGER, 'transactionManager')] as Parameter[]
private static final Parameter[] SET_TRANSACTION_MANAGER_METHOD_PARAMETERS = [
new Parameter(INTERFACE_TRANSACTION_MANAGER, 'transactionManager')
] as Parameter[]
private static final String FIELD_NAME_TRANSACTION_MANAGER = '$transactionManager'
private static final String FIELD_NAME_PROMISE_DECORATORS = '$promiseDecorators'
private static final ClassNode CLASS_NODE_MAP = new ClassNode(Map).getPlainNodeReference()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ import org.springframework.web.context.request.RequestContextHolder
import org.springframework.web.context.request.async.WebAsyncManager
import org.springframework.web.context.request.async.WebAsyncUtils

import grails.async.decorator.PromiseDecorator
import grails.async.web.AsyncGrailsWebRequest
import org.grails.web.servlet.mvc.GrailsWebRequest
import org.grails.web.util.WebUtils
import grails.async.decorator.PromiseDecorator

/**
* A promise decorated lookup strategy that binds a WebRequest to the promise thread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,4 @@ class PromiseFactoryBean extends PromiseFactoryBuilder implements FactoryBean<Pr
boolean isSingleton() {
return true
}

}
Loading