Skip to content

Commit ef6cca8

Browse files
authored
Merge branch '8.0.x' into grails8-groovy5-sb4
2 parents 649d7bb + 963189e commit ef6cca8

12 files changed

Lines changed: 1019 additions & 17 deletions

File tree

SECURITY.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ For information about the currently supported versions of the Grails framework,
2525
## Reporting Vulnerabilities
2626

2727
Please see the page of the [ASF Security Team](https://www.apache.org/security/) for further information and contact information.
28+
29+
## Threat Model
30+
31+
The framework's threat model - what is in scope, what is out, what security properties are claimed, what is explicitly disclaimed, and how inbound reports and findings are triaged - is documented in [THREAT_MODEL.md](./THREAT_MODEL.md). Reporters and triagers should consult that document alongside this policy.

THREAT_MODEL.md

Lines changed: 516 additions & 0 deletions
Large diffs are not rendered by default.

grails-async/core/src/main/groovy/grails/async/factory/AbstractPromiseFactory.groovy

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ abstract class AbstractPromiseFactory implements PromiseFactory {
8484
* @see PromiseFactory#createPromise(java.util.List, java.util.List)
8585
*/
8686
<T> Promise<List<T>> createPromise(List<Closure<T>> closures, List<PromiseDecorator> decorators) {
87+
if (closures == null) {
88+
return new BoundPromise<List<T>>(Collections.<T> emptyList())
89+
}
8790
List<Closure<T>> decoratedClosures = new ArrayList<Closure<T>>(closures.size())
8891
for (Closure<T> closure : closures) {
8992
decoratedClosures.add(applyDecorators(closure, decorators))

grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public class DelegateAsyncTransformation implements ASTTransformation, Transform
7272
public static final ClassNode GROOVY_OBJECT_CLASS_NODE = new ClassNode(GroovyObjectSupport.class);
7373
public static final ClassNode OBJECT_CLASS_NODE = new ClassNode(Object.class);
7474

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

217-
private static Parameter[] copyParameters(Parameter[] parameterTypes) {
218+
private static Parameter[] copyParameters(Parameter... parameterTypes) {
218219
Parameter[] newParameterTypes = new Parameter[parameterTypes.length];
219220
for (int i = 0; i < parameterTypes.length; i++) {
220221
Parameter parameterType = parameterTypes[i];
@@ -236,6 +237,7 @@ public int priority() {
236237
}
237238

238239
private static class NoopDelegateAsyncTransactionalMethodTransformer implements DelegateAsyncTransactionalMethodTransformer {
240+
@Override
239241
public void transformTransactionalMethod(ClassNode classNode, ClassNode delegateClassNode, MethodNode methodNode, ListExpression promiseDecoratorLookupArguments) {
240242
// noop
241243
}

grails-async/core/src/test/groovy/grails/async/SynchronousPromiseFactorySpec.groovy

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,27 @@ class SynchronousPromiseFactorySpec extends Specification {
164164
then: 'the closure is executed twice'
165165
2 * callable.call()
166166
}
167-
}
167+
168+
void 'createPromise with a null closures list returns a completed empty-list promise'() {
169+
170+
given: 'a factory and a decorator list'
171+
def factory = Promises.promiseFactory
172+
def decorators = [{ Closure c -> c } as PromiseDecorator]
173+
174+
when: 'createPromise is called with a null closures list'
175+
def promise = factory.createPromise((List<Closure<Object>>) null, decorators)
176+
177+
then: 'the returned promise is already done and resolves to an empty list'
178+
promise.isDone()
179+
promise.get() == []
180+
181+
when: 'onComplete is registered on the resulting promise'
182+
def callbackResult = null
183+
def callbackInvoked = false
184+
promise.onComplete { value -> callbackInvoked = true; callbackResult = value }
185+
186+
then: 'the callback is invoked synchronously with an empty list rather than busy-waiting'
187+
callbackInvoked
188+
callbackResult == []
189+
}
190+
}

grails-async/gpars/src/main/groovy/org/grails/async/factory/gpars/LoggingPoolFactory.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class LoggingPoolFactory implements PoolFactory {
4848
private static final long KEEP_ALIVE_TIME = 10L
4949
public static final Logger LOG = LoggerFactory.getLogger(LoggingPoolFactory)
5050

51-
public static Method createThreadNameMethod
51+
public static final Method createThreadNameMethod
5252

5353
static {
5454
createThreadNameMethod = DefaultPool.getDeclaredMethod('createThreadName')

grails-async/plugin/src/main/groovy/grails/async/services/PersistenceContextPromiseDecorator.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ package grails.async.services
2020

2121
import groovy.transform.CompileStatic
2222

23-
import grails.persistence.support.PersistenceContextInterceptorExecutor
2423
import grails.async.decorator.PromiseDecorator
24+
import grails.persistence.support.PersistenceContextInterceptorExecutor
2525

2626
/**
2727
* A {@link PromiseDecorator} that wraps a promise execution in a persistence context (example Hibernate session)

grails-async/plugin/src/main/groovy/grails/async/web/AsyncGrailsWebRequest.groovy

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,25 @@
1919

2020
package grails.async.web
2121

22-
import groovy.transform.CompileStatic
23-
import org.grails.web.util.GrailsApplicationAttributes
24-
import org.grails.web.servlet.mvc.GrailsWebRequest
25-
import org.springframework.context.ApplicationContext
26-
import org.springframework.util.Assert
27-
import org.springframework.web.context.request.async.AsyncWebRequest
28-
2922
import java.util.concurrent.atomic.AtomicBoolean
3023
import java.util.function.Consumer
3124

25+
import groovy.transform.CompileStatic
26+
3227
import jakarta.servlet.AsyncContext
3328
import jakarta.servlet.AsyncEvent
3429
import jakarta.servlet.AsyncListener
3530
import jakarta.servlet.ServletContext
3631
import jakarta.servlet.http.HttpServletRequest
3732
import jakarta.servlet.http.HttpServletResponse
3833

34+
import org.springframework.context.ApplicationContext
35+
import org.springframework.util.Assert
36+
import org.springframework.web.context.request.async.AsyncWebRequest
37+
38+
import org.grails.web.servlet.mvc.GrailsWebRequest
39+
import org.grails.web.util.GrailsApplicationAttributes
40+
3941
/**
4042
* Implementation of Spring 4.0 {@link AsyncWebRequest} interface for Grails
4143
*

grails-async/plugin/src/main/groovy/org/grails/async/transform/internal/DefaultDelegateAsyncTransactionalMethodTransformer.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ import org.codehaus.groovy.syntax.Types
4444
import org.springframework.transaction.PlatformTransactionManager
4545
import org.springframework.transaction.annotation.Transactional
4646

47+
import grails.transaction.TransactionManagerAware
4748
import org.grails.compiler.injection.GrailsASTUtils
4849
import org.grails.compiler.web.async.TransactionalAsyncTransformUtils
49-
import grails.transaction.TransactionManagerAware
5050

5151
/**
5252
* Modifies the @DelegateAsync transform to take into account transactional services. New instance should be created per class transform, as state is held.
@@ -61,7 +61,9 @@ class DefaultDelegateAsyncTransactionalMethodTransformer implements DelegateAsyn
6161
private static final ClassNode TRANSACTIONAL_CLASS_NODE = new ClassNode(Transactional)
6262
private static final ClassNode INTERFACE_TRANSACTION_MANAGER = new ClassNode(PlatformTransactionManager).getPlainNodeReference()
6363
private static final ClassNode INTERFACE_TRANSACTION_MANAGER_AWARE = new ClassNode(TransactionManagerAware).getPlainNodeReference()
64-
private static final Parameter[] SET_TRANSACTION_MANAGER_METHOD_PARAMETERS = [new Parameter(INTERFACE_TRANSACTION_MANAGER, 'transactionManager')] as Parameter[]
64+
private static final Parameter[] SET_TRANSACTION_MANAGER_METHOD_PARAMETERS = [
65+
new Parameter(INTERFACE_TRANSACTION_MANAGER, 'transactionManager')
66+
] as Parameter[]
6567
private static final String FIELD_NAME_TRANSACTION_MANAGER = '$transactionManager'
6668
private static final String FIELD_NAME_PROMISE_DECORATORS = '$promiseDecorators'
6769
private static final ClassNode CLASS_NODE_MAP = new ClassNode(Map).getPlainNodeReference()

grails-async/plugin/src/main/groovy/org/grails/plugins/web/async/AsyncWebRequestPromiseDecorator.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ import org.springframework.web.context.request.RequestContextHolder
3232
import org.springframework.web.context.request.async.WebAsyncManager
3333
import org.springframework.web.context.request.async.WebAsyncUtils
3434

35+
import grails.async.decorator.PromiseDecorator
3536
import grails.async.web.AsyncGrailsWebRequest
3637
import org.grails.web.servlet.mvc.GrailsWebRequest
3738
import org.grails.web.util.WebUtils
38-
import grails.async.decorator.PromiseDecorator
3939

4040
/**
4141
* A promise decorated lookup strategy that binds a WebRequest to the promise thread

0 commit comments

Comments
 (0)