LambdaSafeCallback Class — spring-boot Architecture
Architecture documentation for the LambdaSafeCallback class in LambdaSafe.java from the spring-boot codebase.
Entity Profile
Relationship Graph
Source Code
core/spring-boot/src/main/java/org/springframework/boot/util/LambdaSafe.java lines 105–233
protected abstract static class LambdaSafeCallback<C, A, SELF extends LambdaSafeCallback<C, A, SELF>> {
private final Class<C> callbackType;
private final A argument;
private final @Nullable Object @Nullable [] additionalArguments;
private Log logger;
private Filter<C, A> filter = new GenericTypeFilter<>();
LambdaSafeCallback(Class<C> callbackType, A argument, @Nullable Object @Nullable [] additionalArguments) {
this.callbackType = callbackType;
this.argument = argument;
this.additionalArguments = additionalArguments;
this.logger = LogFactory.getLog(callbackType);
}
/**
* Use the specified logger source to report any lambda failures.
* @param loggerSource the logger source to use
* @return this instance
*/
public SELF withLogger(Class<?> loggerSource) {
return withLogger(LogFactory.getLog(loggerSource));
}
/**
* Use the specified logger to report any lambda failures.
* @param logger the logger to use
* @return this instance
*/
public SELF withLogger(Log logger) {
Assert.notNull(logger, "'logger' must not be null");
this.logger = logger;
return self();
}
/**
* Use a specific filter to determine when a callback should apply. If no explicit
* filter is set filter will be attempted using the generic type on the callback
* type.
* @param filter the filter to use
* @return this instance
* @since 3.4.8
*/
public SELF withFilter(Filter<C, A> filter) {
Assert.notNull(filter, "'filter' must not be null");
this.filter = filter;
return self();
}
protected final <R> InvocationResult<R> invoke(C callbackInstance, Supplier<@Nullable R> supplier) {
if (this.filter.match(this.callbackType, callbackInstance, this.argument, this.additionalArguments)) {
try {
return InvocationResult.of(supplier.get());
}
catch (ClassCastException ex) {
if (!isLambdaGenericProblem(ex)) {
throw ex;
}
logNonMatchingType(callbackInstance, ex);
}
}
return InvocationResult.noResult();
}
private boolean isLambdaGenericProblem(ClassCastException ex) {
return (ex.getMessage() == null || startsWithArgumentClassName(ex.getMessage()));
}
private boolean startsWithArgumentClassName(String message) {
Predicate<@Nullable Object> startsWith = (argument) -> startsWithArgumentClassName(message, argument);
return startsWith.test(this.argument) || additionalArgumentsStartsWith(startsWith);
}
private boolean additionalArgumentsStartsWith(Predicate<@Nullable Object> startsWith) {
if (this.additionalArguments == null) {
return false;
}
return Stream.of(this.additionalArguments).anyMatch(startsWith);
}
private boolean startsWithArgumentClassName(String message, @Nullable Object argument) {
if (argument == null) {
return false;
}
Class<?> argumentType = argument.getClass();
// On Java 8, the message starts with the class name: "java.lang.String cannot
// be cast..."
if (message.startsWith(argumentType.getName())) {
return true;
}
// On Java 11, the message starts with "class ..." a.k.a. Class.toString()
if (message.startsWith(argumentType.toString())) {
return true;
}
// On Java 9, the message used to contain the module name:
// "java.base/java.lang.String cannot be cast..."
int moduleSeparatorIndex = message.indexOf('/');
if (moduleSeparatorIndex != -1 && message.startsWith(argumentType.getName(), moduleSeparatorIndex + 1)) {
return true;
}
if (CLASS_GET_MODULE != null && MODULE_GET_NAME != null) {
Object module = ReflectionUtils.invokeMethod(CLASS_GET_MODULE, argumentType);
Object moduleName = ReflectionUtils.invokeMethod(MODULE_GET_NAME, module);
return message.startsWith(moduleName + "/" + argumentType.getName());
}
return false;
}
private void logNonMatchingType(C callback, ClassCastException ex) {
if (this.logger.isDebugEnabled()) {
Class<?> expectedType = ResolvableType.forClass(this.callbackType).resolveGeneric();
String expectedTypeName = (expectedType != null) ? ClassUtils.getShortName(expectedType) + " type"
: "type";
String message = "Non-matching " + expectedTypeName + " for callback "
+ ClassUtils.getShortName(this.callbackType) + ": " + callback;
this.logger.debug(message, ex);
}
}
@SuppressWarnings("unchecked")
private SELF self() {
return (SELF) this;
}
}
Domain
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free