Do not build exception instances in advance (#3983)

* Do not build exception instances in advance

When no `cause` and no clean context of the exception thrown,
the code place where an exception has been created is confusing.

* Fix `SimpleMessageStore` to create an `out of capacity` exception in the
`addMessagesToGroup()` exactly at the point where it is thrown
* Fix `FunctionExpression` and `SupplierExpression` do not pre-create `readOnlyException`:
it is unlikely these kind of expressions are going to be used in the SpEL `write` context,
so we save some time and memory not creating extra object and the plain call `throw new`
gives us a clean context what method call has ended up with such an exception

* * Extract `outOfCapacityException` to the method in the `SimpleMessageStore`
to avoid duplication.
Even if we got in the end extra line in th stack trace, it is still clear from
where it is thrown:

```
org.springframework.messaging.MessagingException: SimpleMessageStore was out of capacity (1) for group 'foo', try constructing it with a larger number.

	at org.springframework.integration.store.SimpleMessageStore.outOfCapacityException(SimpleMessageStore.java:324)
	at org.springframework.integration.store.SimpleMessageStore.addMessagesToGroup(SimpleMessageStore.java:302)
	at org.springframework.integration.store.AbstractMessageGroupStore.addMessageToGroup(AbstractMessageGroupStore.java:189)
	at org.springframework.integration.store.SimpleMessageStoreTests.shouldNotHoldMoreThanGroupCapacity(SimpleMessageStoreTests.java:128)
```
This commit is contained in:
Artem Bilan
2023-01-05 11:35:24 -05:00
committed by GitHub
parent cf5f218449
commit 308f3fadeb
4 changed files with 93 additions and 101 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -57,13 +57,9 @@ public class FunctionExpression<S> implements Expression {
private final EvaluationContext defaultContext = new StandardEvaluationContext();
private final EvaluationException readOnlyException;
public FunctionExpression(Function<S, ?> function) {
Assert.notNull(function, "'function' must not be null.");
this.function = function;
this.readOnlyException = new EvaluationException(getExpressionString(),
"FunctionExpression is a 'read only' Expression implementation");
}
@Override
@@ -123,60 +119,65 @@ public class FunctionExpression<S> implements Expression {
@Override
public Class<?> getValueType() throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(@Nullable Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(EvaluationContext context) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(@Nullable Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, @Nullable Object rootObject)
throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(EvaluationContext context, @Nullable Object value) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(@Nullable Object rootObject, @Nullable Object value) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(EvaluationContext context, @Nullable Object rootObject, @Nullable Object value)
throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
private EvaluationException readOnlyException() {
return new EvaluationException(getExpressionString(),
"FunctionExpression is a 'read only' Expression implementation");
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ import org.springframework.util.Assert;
*
* @author Artem Bilan
* @author Gary Russell
*
* @since 5.0
*/
public class SupplierExpression<T> implements Expression {
@@ -55,13 +56,9 @@ public class SupplierExpression<T> implements Expression {
private final EvaluationContext defaultContext = new StandardEvaluationContext();
private final EvaluationException readOnlyException;
public SupplierExpression(Supplier<T> supplier) {
Assert.notNull(supplier, "'function' must not be null.");
this.supplier = supplier;
this.readOnlyException = new EvaluationException(getExpressionString(),
"SupplierExpression is a 'read only' Expression implementation");
}
@Override
@@ -108,58 +105,63 @@ public class SupplierExpression<T> implements Expression {
@Override
public Class<?> getValueType() throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(EvaluationContext context) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public Class<?> getValueType(EvaluationContext context, Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor() throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(Object rootObject) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public TypeDescriptor getValueTypeDescriptor(EvaluationContext context, Object rootObject)
throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(EvaluationContext context, Object value) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(Object rootObject, Object value) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
@Override
public void setValue(EvaluationContext context, Object rootObject, Object value) throws EvaluationException {
throw this.readOnlyException;
throw readOnlyException();
}
private EvaluationException readOnlyException() {
return new EvaluationException(getExpressionString(),
"SupplierExpression is a 'read only' Expression implementation");
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -127,6 +127,7 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
*/
public SimpleMessageStore(int individualCapacity, int groupCapacity, long upperBoundTimeout,
LockRegistry lockRegistry) {
super(false);
Assert.notNull(lockRegistry, "The LockRegistry cannot be null");
this.individualUpperBound = new UpperBound(individualCapacity);
@@ -278,13 +279,9 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
try {
UpperBound upperBound;
MessageGroup group = this.groupIdToMessageGroup.get(groupId);
MessagingException outOfCapacityException =
new MessagingException(getClass().getSimpleName() +
" was out of capacity (" + this.groupCapacity + ") for group '" + groupId +
"', try constructing it with a larger capacity.");
if (group == null) {
if (this.groupCapacity > 0 && messages.length > this.groupCapacity) {
throw outOfCapacityException;
throw outOfCapacityException(groupId);
}
group = getMessageGroupFactory().create(groupId);
this.groupIdToMessageGroup.put(groupId, group);
@@ -302,7 +299,7 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
lock.unlock();
if (!upperBound.tryAcquire(this.upperBoundTimeout)) {
unlocked = true;
throw outOfCapacityException;
throw outOfCapacityException(groupId);
}
lock.lockInterruptibly();
group.add(message);
@@ -323,6 +320,12 @@ public class SimpleMessageStore extends AbstractMessageGroupStore
}
}
private MessagingException outOfCapacityException(Object groupId) {
return new MessagingException(getClass().getSimpleName() +
" was out of capacity (" + this.groupCapacity + ") for group '" + groupId +
"', try constructing it with a larger number.");
}
@Override
public void removeMessageGroup(Object groupId) {
Lock lock = this.lockRegistry.obtain(groupId);