Merge branch 'gh-11584'
This commit is contained in:
@@ -26,12 +26,10 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.boot.actuate.endpoint.EndpointFilter;
|
||||
@@ -41,6 +39,7 @@ import org.springframework.boot.actuate.endpoint.Operation;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvoker;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor;
|
||||
import org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper;
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
@@ -66,8 +65,6 @@ import org.springframework.util.StringUtils;
|
||||
public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O extends Operation>
|
||||
implements EndpointsSupplier<E> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EndpointDiscoverer.class);
|
||||
|
||||
private final ApplicationContext applicationContext;
|
||||
|
||||
private final Collection<EndpointFilter<E>> filters;
|
||||
@@ -313,23 +310,15 @@ public abstract class EndpointDiscoverer<E extends ExposableEndpoint<O>, O exten
|
||||
return isFilterMatch(filter, getFilterEndpoint(endpointBean));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isFilterMatch(EndpointFilter<E> filter, E endpoint) {
|
||||
try {
|
||||
return filter.match(endpoint);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
String msg = ex.getMessage();
|
||||
if (msg == null || msg.startsWith(endpoint.getClass().getName())) {
|
||||
// Possibly a lambda-defined EndpointFilter which we could not resolve the
|
||||
// generic EndpointInfo type for
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Non-matching Endpoint for EndpointFilter: " + filter,
|
||||
ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
return LambdaSafe.callback(EndpointFilter.class, filter, endpoint)
|
||||
.withLogger(EndpointDiscoverer.class).invokeAnd((f) -> f.match(endpoint))
|
||||
.get();
|
||||
}
|
||||
|
||||
public <A, B> void doIt(Function<A, B> x) {
|
||||
|
||||
}
|
||||
|
||||
private E getFilterEndpoint(EndpointBean endpointBean) {
|
||||
|
||||
@@ -17,16 +17,15 @@
|
||||
package org.springframework.boot.actuate.metrics.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.Tag;
|
||||
import io.micrometer.core.instrument.Tags;
|
||||
import io.micrometer.core.instrument.binder.MeterBinder;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* Register supported {@link Cache} to a {@link MeterRegistry}.
|
||||
@@ -36,8 +35,6 @@ import org.springframework.core.ResolvableType;
|
||||
*/
|
||||
public class CacheMetricsRegistrar {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CacheMetricsRegistrar.class);
|
||||
|
||||
private final MeterRegistry registry;
|
||||
|
||||
private final String metricName;
|
||||
@@ -74,41 +71,15 @@ public class CacheMetricsRegistrar {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
private MeterBinder getMeterBinder(Cache cache, Tags tags) {
|
||||
tags = tags.and(getAdditionalTags(cache));
|
||||
for (CacheMeterBinderProvider<?> binderProvider : this.binderProviders) {
|
||||
Class<?> cacheType = ResolvableType
|
||||
.forClass(CacheMeterBinderProvider.class, binderProvider.getClass())
|
||||
.resolveGeneric();
|
||||
if (cacheType.isInstance(cache)) {
|
||||
try {
|
||||
MeterBinder meterBinder = ((CacheMeterBinderProvider) binderProvider)
|
||||
.getMeterBinder(cache, this.metricName, tags);
|
||||
if (meterBinder != null) {
|
||||
return meterBinder;
|
||||
}
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
String msg = ex.getMessage();
|
||||
if (msg == null || msg.startsWith(cache.getClass().getName())) {
|
||||
// Possibly a lambda-defined CacheMeterBinderProvider which we
|
||||
// could not resolve the generic Cache type for
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Non-matching Cache type for CacheMeterBinderProvider: "
|
||||
+ binderProvider,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
Tags cacheTags = tags.and(getAdditionalTags(cache));
|
||||
return LambdaSafe
|
||||
.callbacks(CacheMeterBinderProvider.class, this.binderProviders, cache)
|
||||
.withLogger(CacheMetricsRegistrar.class)
|
||||
.invokeAnd((binderProvider) -> binderProvider.getMeterBinder(cache,
|
||||
this.metricName, cacheTags))
|
||||
.filter(Objects::nonNull).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,11 +20,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* Invokes the available {@link CacheManagerCustomizer} instances in the context for a
|
||||
@@ -35,8 +32,6 @@ import org.springframework.core.ResolvableType;
|
||||
*/
|
||||
public class CacheManagerCustomizers {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CacheManagerCustomizers.class);
|
||||
|
||||
private final List<CacheManagerCustomizer<?>> customizers;
|
||||
|
||||
public CacheManagerCustomizers(
|
||||
@@ -53,32 +48,12 @@ public class CacheManagerCustomizers {
|
||||
* @param cacheManager the cache manager to customize
|
||||
* @return the cache manager
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends CacheManager> T customize(T cacheManager) {
|
||||
for (CacheManagerCustomizer<?> customizer : this.customizers) {
|
||||
Class<?> generic = ResolvableType
|
||||
.forClass(CacheManagerCustomizer.class, customizer.getClass())
|
||||
.resolveGeneric();
|
||||
if (generic.isInstance(cacheManager)) {
|
||||
customize(cacheManager, customizer);
|
||||
}
|
||||
}
|
||||
LambdaSafe.callbacks(CacheManagerCustomizer.class, this.customizers, cacheManager)
|
||||
.withLogger(CacheManagerCustomizers.class)
|
||||
.invoke((customizer) -> customizer.customize(cacheManager));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void customize(CacheManager cacheManager, CacheManagerCustomizer customizer) {
|
||||
try {
|
||||
customizer.customize(cacheManager);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
// Possibly a lambda-defined customizer which we could not resolve the generic
|
||||
// cache manager type for
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Non-matching cache manager type for customizer: " + customizer,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,10 @@ package org.springframework.boot.autoconfigure.transaction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
@@ -34,44 +32,21 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
*/
|
||||
public class TransactionManagerCustomizers {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(TransactionManagerCustomizers.class);
|
||||
|
||||
private final List<PlatformTransactionManagerCustomizer<?>> customizers;
|
||||
|
||||
public TransactionManagerCustomizers(
|
||||
Collection<? extends PlatformTransactionManagerCustomizer<?>> customizers) {
|
||||
this.customizers = (customizers == null ? null : new ArrayList<>(customizers));
|
||||
this.customizers = (customizers == null ? Collections.emptyList()
|
||||
: new ArrayList<>(customizers));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void customize(PlatformTransactionManager transactionManager) {
|
||||
if (this.customizers != null) {
|
||||
for (PlatformTransactionManagerCustomizer<?> customizer : this.customizers) {
|
||||
Class<?> generic = ResolvableType
|
||||
.forClass(PlatformTransactionManagerCustomizer.class,
|
||||
customizer.getClass())
|
||||
.resolveGeneric();
|
||||
if (generic.isInstance(transactionManager)) {
|
||||
customize(transactionManager, customizer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void customize(PlatformTransactionManager transactionManager,
|
||||
PlatformTransactionManagerCustomizer customizer) {
|
||||
try {
|
||||
customizer.customize(transactionManager);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
// Possibly a lambda-defined customizer which we could not resolve the generic
|
||||
// transaction manager type for
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Non-matching transaction manager type for customizer: "
|
||||
+ customizer, ex);
|
||||
}
|
||||
}
|
||||
LambdaSafe
|
||||
.callbacks(PlatformTransactionManagerCustomizer.class, this.customizers,
|
||||
transactionManager)
|
||||
.withLogger(TransactionManagerCustomizers.class)
|
||||
.invoke((customizer) -> customizer.customize(transactionManager));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Utility that can be used to invoke lambdas in a safe way. Primarily designed to help
|
||||
* support generically typed callbacks where {@link ClassCastException class cast
|
||||
* exceptions} need to be dealt with due to class erasure.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class LambdaSafe {
|
||||
|
||||
private LambdaSafe() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a call to a single callback instance, dealing with common generic type
|
||||
* concerns and exceptions.
|
||||
* @param callbackType the callback type (a {@link FunctionalInterface functional
|
||||
* interface})
|
||||
* @param callbackInstance the callback instance (may be a lambda)
|
||||
* @param argument the primary argument passed to the callback
|
||||
* @param additionalArguments any additional argument passed to the callback
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
* @return a {@link Callback} instance that can be invoked.
|
||||
*/
|
||||
public static <C, A> Callback<C, A> callback(Class<C> callbackType,
|
||||
C callbackInstance, A argument, Object... additionalArguments) {
|
||||
Assert.notNull(callbackType, "CallbackType must not be null");
|
||||
Assert.notNull(callbackInstance, "CallbackInstance must not be null");
|
||||
return new Callback<>(callbackType, callbackInstance, argument,
|
||||
additionalArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a call to a single callback instance, dealing with common generic type
|
||||
* concerns and exceptions.
|
||||
* @param callbackType the callback type (a {@link FunctionalInterface functional
|
||||
* interface})
|
||||
* @param callbackInstances the callback instances (elements may be lambdas)
|
||||
* @param argument the primary argument passed to the callbacks
|
||||
* @param additionalArguments any additional argument passed to the callbacks
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
* @return a {@link Callbacks} instance that can be invoked.
|
||||
*/
|
||||
public static <C, A> Callbacks<C, A> callbacks(Class<C> callbackType,
|
||||
Collection<? extends C> callbackInstances, A argument,
|
||||
Object... additionalArguments) {
|
||||
Assert.notNull(callbackType, "CallbackType must not be null");
|
||||
Assert.notNull(callbackInstances, "CallbackInstances must not be null");
|
||||
return new Callbacks<C, A>(callbackType, callbackInstances, argument,
|
||||
additionalArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class for lambda safe callbacks.
|
||||
*/
|
||||
private static abstract class LambdaSafeCallback<C, A, SELF extends LambdaSafeCallback<C, A, SELF>> {
|
||||
|
||||
private final Class<C> callbackType;
|
||||
|
||||
private final A argument;
|
||||
|
||||
private final Object[] additionalArguments;
|
||||
|
||||
private Log logger;
|
||||
|
||||
private Filter<C, A> filter = new GenericTypeFilter<C, A>();
|
||||
|
||||
protected LambdaSafeCallback(Class<C> callbackType, A argument,
|
||||
Object[] 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 not
|
||||
* 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
|
||||
*/
|
||||
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<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;
|
||||
}
|
||||
logNonMachingType(callbackInstance, ex);
|
||||
}
|
||||
}
|
||||
return InvocationResult.noResult();
|
||||
}
|
||||
|
||||
private boolean isLambdaGenericProblem(ClassCastException ex) {
|
||||
return (ex.getMessage() == null
|
||||
|| startsWithArgumentClassName(ex.getMessage()));
|
||||
}
|
||||
|
||||
private boolean startsWithArgumentClassName(String message) {
|
||||
Predicate<Object> startsWith = (argument) -> argument != null
|
||||
&& message.startsWith(argument.getClass().getName());
|
||||
return startsWith.test(this.argument)
|
||||
|| Stream.of(this.additionalArguments).anyMatch(startsWith);
|
||||
}
|
||||
|
||||
private void logNonMachingType(C callback, ClassCastException ex) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
Class<?> expectedType = ResolvableType.forClass(this.callbackType)
|
||||
.resolveGeneric();
|
||||
String message = "Non-matching "
|
||||
+ (expectedType == null ? "type"
|
||||
: ClassUtils.getShortName(expectedType) + " type")
|
||||
+ " for callback " + ClassUtils.getShortName(this.callbackType)
|
||||
+ ": " + callback;
|
||||
this.logger.debug(message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private SELF self() {
|
||||
return (SELF) this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single callback that can be invoked in a lambda safe way.
|
||||
*
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
*/
|
||||
public static final class Callback<C, A>
|
||||
extends LambdaSafeCallback<C, A, Callback<C, A>> {
|
||||
|
||||
private C callbackInstance;
|
||||
|
||||
private Callback(Class<C> callbackType, C callbackInstance, A argument,
|
||||
Object[] additionalArguments) {
|
||||
super(callbackType, argument, additionalArguments);
|
||||
this.callbackInstance = callbackInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback instance where the callback method returns void.
|
||||
* @param invoker the invoker used to invoke the callback
|
||||
*/
|
||||
public void invoke(Consumer<C> invoker) {
|
||||
invoke(this.callbackInstance, () -> {
|
||||
invoker.accept(this.callbackInstance);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback instance where the callback method returns a result.
|
||||
* @param invoker the invoker used to invoke the callback
|
||||
* @param <R> the result type
|
||||
* @return the result of the invocation (may be {@link InvocationResult#noResult}
|
||||
* if the callback was not invoked)
|
||||
*/
|
||||
public <R> InvocationResult<R> invokeAnd(Function<C, R> invoker) {
|
||||
return invoke(this.callbackInstance,
|
||||
() -> invoker.apply(this.callbackInstance));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a collection of callbacks that can be invoked in a lambda safe way.
|
||||
*
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
*/
|
||||
public static final class Callbacks<C, A>
|
||||
extends LambdaSafeCallback<C, A, Callbacks<C, A>> {
|
||||
|
||||
private Collection<? extends C> callbackInstances;
|
||||
|
||||
private Callbacks(Class<C> callbackType,
|
||||
Collection<? extends C> callbackInstances, A argument,
|
||||
Object[] additionalArguments) {
|
||||
super(callbackType, argument, additionalArguments);
|
||||
this.callbackInstances = callbackInstances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback instances where the callback method returns void.
|
||||
* @param invoker the invoker used to invoke the callback
|
||||
*/
|
||||
public void invoke(Consumer<C> invoker) {
|
||||
Function<C, InvocationResult<Void>> mapper = (callbackInstance) -> invoke(
|
||||
callbackInstance, () -> {
|
||||
invoker.accept(callbackInstance);
|
||||
return null;
|
||||
});
|
||||
this.callbackInstances.stream().map(mapper).forEach((result) -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback instances where the callback method returns a result.
|
||||
* @param invoker the invoker used to invoke the callback
|
||||
* @param <R> the result type
|
||||
* @return the results of the invocation (may be an empty stream if not callbacks
|
||||
* could be called)
|
||||
*/
|
||||
public <R> Stream<R> invokeAnd(Function<C, R> invoker) {
|
||||
Function<C, InvocationResult<R>> mapper = (callbackInstance) -> invoke(
|
||||
callbackInstance, () -> invoker.apply(callbackInstance));
|
||||
return this.callbackInstances.stream().map(mapper)
|
||||
.filter(InvocationResult::hasResult).map(InvocationResult::get);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter that can be used to restrict when a callback is used.
|
||||
*
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
*/
|
||||
@FunctionalInterface
|
||||
interface Filter<C, A> {
|
||||
|
||||
/**
|
||||
* Determine if the given callback matches and should be invoked.
|
||||
* @param callbackType the callback type (the functional interface)
|
||||
* @param callbackInstance the callback instance (the implementation)
|
||||
* @param argument the primary argument
|
||||
* @param additionalArguments any additional arguments
|
||||
* @return if the callback matches and should be invoked
|
||||
*/
|
||||
boolean match(Class<C> callbackType, C callbackInstance, A argument,
|
||||
Object[] additionalArguments);
|
||||
|
||||
/**
|
||||
* Return a {@link Filter} that allows all callbacks to be invoked.
|
||||
* @param <C> the callback type
|
||||
* @param <A> the primary argument type
|
||||
* @return an "allow all" filter
|
||||
*/
|
||||
static <C, A> Filter<C, A> allowAll() {
|
||||
return (callbackType, callbackInstance, argument,
|
||||
additionalArguments) -> true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Filter} that matches when the callback has a single generic and primary
|
||||
* argument is an instance of it.
|
||||
*/
|
||||
private static class GenericTypeFilter<C, A> implements Filter<C, A> {
|
||||
|
||||
@Override
|
||||
public boolean match(Class<C> callbackType, C callbackInstance, A argument,
|
||||
Object[] additionalArguments) {
|
||||
ResolvableType type = ResolvableType.forClass(callbackType,
|
||||
callbackInstance.getClass());
|
||||
if (type.getGenerics().length == 1 && type.resolveGeneric() != null) {
|
||||
return type.resolveGeneric().isInstance(argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of a callback which may be a value, {@code null} or absent entirely if
|
||||
* the callback wasn't suitable. Similar in design to {@link Optional} but allows for
|
||||
* {@code null} as a valid value.
|
||||
* @param <R> The result type
|
||||
*/
|
||||
public final static class InvocationResult<R> {
|
||||
|
||||
private static final InvocationResult<?> NONE = new InvocationResult<Object>(
|
||||
null);
|
||||
|
||||
private final R value;
|
||||
|
||||
private InvocationResult(R value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if a result in present.
|
||||
* @return if a result is present
|
||||
*/
|
||||
public boolean hasResult() {
|
||||
return this != NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result of the invocation or {@code null} if the callback wasn't
|
||||
* suitable.
|
||||
* @return the result of the invocation or {@code null}
|
||||
*/
|
||||
public R get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result of the invocation or the given fallback if the callback
|
||||
* wasn't suitable.
|
||||
* @param fallback the fallback to use when there is no result
|
||||
* @return the result of the invocation or the fallback
|
||||
*/
|
||||
public R get(R fallback) {
|
||||
return (this == NONE ? fallback : this.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link InvocationResult} instance with the specified value.
|
||||
* @param value the value (may be {@code null})
|
||||
* @param <R> the result type
|
||||
* @return an {@link InvocationResult}
|
||||
*/
|
||||
public static <R> InvocationResult<R> of(R value) {
|
||||
return new InvocationResult<R>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@link InvocationResult} instance representing no result.
|
||||
* @param <R> the result type
|
||||
* @return an {@link InvocationResult}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <R> InvocationResult<R> noResult() {
|
||||
return (InvocationResult<R>) NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains miscellaneous utility classes.
|
||||
*/
|
||||
package org.springframework.boot.util;
|
||||
@@ -21,15 +21,12 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.boot.util.LambdaSafe;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -45,9 +42,6 @@ import org.springframework.util.Assert;
|
||||
public class WebServerFactoryCustomizerBeanPostProcessor
|
||||
implements BeanPostProcessor, BeanFactoryAware {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(WebServerFactoryCustomizerBeanPostProcessor.class);
|
||||
|
||||
private ListableBeanFactory beanFactory;
|
||||
|
||||
private List<WebServerFactoryCustomizer<?>> customizers;
|
||||
@@ -75,45 +69,13 @@ public class WebServerFactoryCustomizerBeanPostProcessor
|
||||
return bean;
|
||||
}
|
||||
|
||||
private void postProcessBeforeInitialization(WebServerFactory bean) {
|
||||
for (WebServerFactoryCustomizer<?> customizer : getCustomizers()) {
|
||||
Class<?> type = ResolvableType
|
||||
.forClass(WebServerFactoryCustomizer.class, customizer.getClass())
|
||||
.getGeneric().resolve(WebServerFactory.class);
|
||||
if (type.isInstance(bean)) {
|
||||
invokeCustomizer(customizer, bean);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private void invokeCustomizer(WebServerFactoryCustomizer customizer,
|
||||
WebServerFactory webServerFactory) {
|
||||
try {
|
||||
customizer.customize(webServerFactory);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
String msg = ex.getMessage();
|
||||
if (msg == null || msg.startsWith(webServerFactory.getClass().getName())) {
|
||||
// Possibly a lambda-defined WebServerFactoryCustomizer which we could not
|
||||
// resolve the
|
||||
// generic WebServerFactory type for
|
||||
logLambdaDebug(customizer, ex);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void logLambdaDebug(WebServerFactoryCustomizer<?> customizer,
|
||||
ClassCastException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Non-matching WebServerFactory type for WebServerFactoryCustomizer: "
|
||||
+ customizer,
|
||||
ex);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
|
||||
LambdaSafe
|
||||
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
|
||||
webServerFactory)
|
||||
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
|
||||
.invoke((customizer) -> customizer.customize(webServerFactory));
|
||||
}
|
||||
|
||||
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
* Copyright 2012-2018 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.boot.util.LambdaSafe.Filter;
|
||||
import org.springframework.boot.util.LambdaSafe.InvocationResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link LambdaSafe}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class LambdaSafeTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void callbackWhenCallbackTypeIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("CallbackType must not be null");
|
||||
LambdaSafe.callback(null, new Object(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbackWhenCallbackInstanceIsNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("CallbackInstance must not be null");
|
||||
LambdaSafe.callback(Object.class, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbackInvokeWhenNoGenericShouldInvokeCallback() {
|
||||
NonGenericCallback callbackInstance = mock(NonGenericCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(NonGenericCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeWhenHasGenericShouldInvokeCallback() {
|
||||
StringCallback callbackInstance = mock(StringCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeWhenHasResolvableGenericMatchShouldInvokeCallback() {
|
||||
StringBuilderCallback callbackInstance = mock(StringBuilderCallback.class);
|
||||
StringBuilder argument = new StringBuilder("foo");
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeWhenHasResolvableGenericNonMatchShouldNotInvokeCallback() {
|
||||
GenericCallback<?> callbackInstance = mock(StringBuilderCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verifyZeroInteractions(callbackInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeWhenLambdaMismatchShouldSwallowException() {
|
||||
GenericCallback<StringBuilder> callbackInstance = (s) -> {
|
||||
fail("Should not get here");
|
||||
};
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeWhenLambdaMismatchOnDifferentArgumentShouldSwallowException() {
|
||||
GenericMultiArgCallback<StringBuilder> callbackInstance = (n, s, b) -> {
|
||||
fail("Should not get here");
|
||||
};
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericMultiArgCallback.class, callbackInstance, argument)
|
||||
.invoke((c) -> c.handle(1, argument, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbackInvokeAndWhenNoGenericShouldReturnResult() {
|
||||
NonGenericFactory callbackInstance = mock(NonGenericFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(123);
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(NonGenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result.hasResult()).isTrue();
|
||||
assertThat(result.get()).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenHasGenericShouldReturnResult() {
|
||||
StringFactory callbackInstance = mock(StringFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(123);
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result.hasResult()).isTrue();
|
||||
assertThat(result.get()).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenReturnNullShouldReturnResult() {
|
||||
StringFactory callbackInstance = mock(StringFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(null);
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result.hasResult()).isTrue();
|
||||
assertThat(result.get()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenHasResolvableGenericMatchShouldReturnResult() {
|
||||
StringBuilderFactory callbackInstance = mock(StringBuilderFactory.class);
|
||||
StringBuilder argument = new StringBuilder("foo");
|
||||
given(callbackInstance.handle(any(StringBuilder.class))).willReturn(123);
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
assertThat(result.hasResult()).isTrue();
|
||||
assertThat(result.get()).isEqualTo(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenHasResolvableGenericNonMatchShouldReturnNoResult() {
|
||||
GenericFactory<?> callbackInstance = mock(StringBuilderFactory.class);
|
||||
String argument = "foo";
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result.hasResult()).isFalse();
|
||||
verifyZeroInteractions(callbackInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenLambdaMismatchShouldSwallowException() {
|
||||
GenericFactory<StringBuilder> callbackInstance = (s) -> {
|
||||
fail("Should not get here");
|
||||
return 123;
|
||||
};
|
||||
String argument = "foo";
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result.hasResult()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackInvokeAndWhenLambdaMismatchOnDifferentArgumentShouldSwallowException() {
|
||||
GenericMultiArgFactory<StringBuilder> callbackInstance = (n, s, b) -> {
|
||||
fail("Should not get here");
|
||||
return 123;
|
||||
};
|
||||
String argument = "foo";
|
||||
InvocationResult<Integer> result = LambdaSafe
|
||||
.callback(GenericMultiArgFactory.class, callbackInstance, argument)
|
||||
.invokeAnd((c) -> c.handle(1, argument, false));
|
||||
assertThat(result.hasResult()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbacksInvokeWhenNoGenericShouldInvokeCallbacks() {
|
||||
NonGenericCallback callbackInstance = mock(NonGenericCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe
|
||||
.callbacks(NonGenericCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenHasGenericShouldInvokeCallback() {
|
||||
StringCallback callbackInstance = mock(StringCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callbacks(GenericCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenHasResolvableGenericMatchShouldInvokeCallback() {
|
||||
StringBuilderCallback callbackInstance = mock(StringBuilderCallback.class);
|
||||
StringBuilder argument = new StringBuilder("foo");
|
||||
LambdaSafe.callbacks(GenericCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
verify(callbackInstance).handle(argument);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenHasResolvableGenericNonMatchShouldNotInvokeCallback() {
|
||||
GenericCallback<?> callbackInstance = mock(StringBuilderCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callbacks(GenericCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(null));
|
||||
verifyZeroInteractions(callbackInstance);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenLambdaMismatchShouldSwallowException() {
|
||||
GenericCallback<StringBuilder> callbackInstance = (s) -> {
|
||||
fail("Should not get here");
|
||||
};
|
||||
String argument = "foo";
|
||||
LambdaSafe.callbacks(GenericCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(argument));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenLambdaMismatchOnDifferentArgumentShouldSwallowException() {
|
||||
GenericMultiArgCallback<StringBuilder> callbackInstance = (n, s, b) -> {
|
||||
fail("Should not get here");
|
||||
};
|
||||
String argument = "foo";
|
||||
LambdaSafe
|
||||
.callbacks(GenericMultiArgCallback.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invoke((c) -> c.handle(1, argument, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callbacksInvokeAndWhenNoGenericShouldReturnResult() {
|
||||
NonGenericFactory callbackInstance = mock(NonGenericFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(123);
|
||||
Stream<Integer> result = LambdaSafe
|
||||
.callbacks(NonGenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).containsExactly(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenHasGenericShouldReturnResult() {
|
||||
StringFactory callbackInstance = mock(StringFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(123);
|
||||
Stream<Integer> result = LambdaSafe.callbacks(GenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).containsExactly(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenReturnNullShouldReturnResult() {
|
||||
StringFactory callbackInstance = mock(StringFactory.class);
|
||||
String argument = "foo";
|
||||
given(callbackInstance.handle("foo")).willReturn(null);
|
||||
Stream<Integer> result = LambdaSafe.callbacks(GenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).containsExactly((Integer) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenHasResolvableGenericMatchShouldReturnResult() {
|
||||
StringBuilderFactory callbackInstance = mock(StringBuilderFactory.class);
|
||||
StringBuilder argument = new StringBuilder("foo");
|
||||
given(callbackInstance.handle(any(StringBuilder.class))).willReturn(123);
|
||||
Stream<Integer> result = LambdaSafe.callbacks(GenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).containsExactly(123);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenHasResolvableGenericNonMatchShouldReturnNoResult() {
|
||||
GenericFactory<?> callbackInstance = mock(StringBuilderFactory.class);
|
||||
String argument = "foo";
|
||||
Stream<Integer> result = LambdaSafe.callbacks(GenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenLambdaMismatchShouldSwallowException() {
|
||||
GenericFactory<StringBuilder> callbackInstance = (s) -> {
|
||||
fail("Should not get here");
|
||||
return 123;
|
||||
};
|
||||
String argument = "foo";
|
||||
Stream<Integer> result = LambdaSafe.callbacks(GenericFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> (c).handle(argument));
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeAndWhenLambdaMismatchOnDifferentArgumentShouldSwallowException() {
|
||||
GenericMultiArgFactory<StringBuilder> callbackInstance = (n, s, b) -> {
|
||||
fail("Should not get here");
|
||||
return 123;
|
||||
};
|
||||
String argument = "foo";
|
||||
Stream<Integer> result = LambdaSafe
|
||||
.callbacks(GenericMultiArgFactory.class,
|
||||
Collections.singleton(callbackInstance), argument)
|
||||
.invokeAnd((c) -> c.handle(1, argument, false));
|
||||
assertThat(result).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbacksInvokeWhenMultipleShouldInvokeSuitable() {
|
||||
List<GenericFactory<?>> callbackInstances = new ArrayList<>();
|
||||
GenericFactory<String> callback1 = (s) -> 1;
|
||||
GenericFactory<CharSequence> callback2 = (s) -> 2;
|
||||
GenericFactory<StringBuilder> callback3 = (s) -> 3;
|
||||
StringFactory callback4 = mock(StringFactory.class);
|
||||
given(callback4.handle(any(String.class))).willReturn(4);
|
||||
StringBuilderFactory callback5 = mock(StringBuilderFactory.class);
|
||||
given(callback5.handle(any(StringBuilder.class))).willReturn(5);
|
||||
callbackInstances.add(callback1);
|
||||
callbackInstances.add(callback2);
|
||||
callbackInstances.add(callback3);
|
||||
callbackInstances.add(callback4);
|
||||
callbackInstances.add(callback5);
|
||||
String argument = "foo";
|
||||
Stream<Integer> result = LambdaSafe
|
||||
.callbacks(GenericFactory.class, callbackInstances, argument)
|
||||
.invokeAnd((c) -> c.handle(argument));
|
||||
assertThat(result).containsExactly(1, 2, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackWithFilterShouldUseFilter() {
|
||||
GenericCallback<?> callbackInstance = mock(StringBuilderCallback.class);
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.withFilter(Filter.allowAll()).invoke((c) -> c.handle(null));
|
||||
verify(callbackInstance).handle(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void callbackWithLoggerShouldUseLogger() {
|
||||
Log logger = mock(Log.class);
|
||||
given(logger.isDebugEnabled()).willReturn(true);
|
||||
GenericCallback<StringBuilder> callbackInstance = (s) -> {
|
||||
fail("Should not get here");
|
||||
};
|
||||
String argument = "foo";
|
||||
LambdaSafe.callback(GenericCallback.class, callbackInstance, argument)
|
||||
.withLogger(logger).invoke((c) -> c.handle(argument));
|
||||
verify(logger).debug(contains("Non-matching CharSequence type for callback "
|
||||
+ "LambdaSafeTests.GenericCallback"), any(Throwable.class));
|
||||
}
|
||||
|
||||
interface NonGenericCallback {
|
||||
|
||||
void handle(String argument);
|
||||
|
||||
}
|
||||
|
||||
interface GenericCallback<T extends CharSequence> {
|
||||
|
||||
void handle(T argument);
|
||||
|
||||
}
|
||||
|
||||
interface StringCallback extends GenericCallback<String> {
|
||||
|
||||
}
|
||||
|
||||
interface StringBuilderCallback extends GenericCallback<StringBuilder> {
|
||||
|
||||
}
|
||||
|
||||
interface GenericMultiArgCallback<T extends CharSequence> {
|
||||
|
||||
void handle(Integer numner, T argument, Boolean bool);
|
||||
|
||||
}
|
||||
|
||||
interface NonGenericFactory {
|
||||
|
||||
Integer handle(String argument);
|
||||
|
||||
}
|
||||
|
||||
interface GenericFactory<T extends CharSequence> {
|
||||
|
||||
Integer handle(T argument);
|
||||
|
||||
}
|
||||
|
||||
interface StringFactory extends GenericFactory<String> {
|
||||
|
||||
}
|
||||
|
||||
interface StringBuilderFactory extends GenericFactory<StringBuilder> {
|
||||
|
||||
}
|
||||
|
||||
interface GenericMultiArgFactory<T extends CharSequence> {
|
||||
|
||||
Integer handle(Integer numner, T argument, Boolean bool);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user