DATAGEODE-306 - Add overloaded safeDoOperation(:VoidReturningThrowableOperation, backupOperation:Runnable) method.

Redefine safeDoOperation(:VoidReturningThrowableOperation) in terms of the overloaded method.

Edit Javadoc.
This commit is contained in:
John Blum
2020-09-09 12:15:33 -07:00
parent b75b732e81
commit 24f2231674
2 changed files with 352 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 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.
@@ -21,17 +21,32 @@ import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.core.annotation.OrderUtils;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -41,9 +56,15 @@ import org.springframework.util.StringUtils;
* @author John Blum
* @see java.lang.Class
* @see java.lang.Object
* @see java.util.function.Function
* @see java.util.stream.Stream
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.FactoryBean
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.config.RuntimeBeanReference
* @see org.springframework.core.Ordered
* @see org.springframework.core.annotation.AnnotationAwareOrderComparator
* @see org.springframework.core.annotation.Order
* @since 1.8.0
*/
@SuppressWarnings("unused")
@@ -57,26 +78,178 @@ public abstract class SpringUtils {
* @param beanName {@link String name} of the bean.
* @param beanType {@link Class type} of the bean.
* @return a boolean value indicating whether the {@link BeanFactory Spring container} contains a bean
* matching by both {@link String name} as well as {@link Class type}.
* @see BeanFactory
* @see Class
* @see String
* matching by both {@link String name} and {@link Class type}.
* @see org.springframework.beans.factory.BeanFactory
* @see java.lang.Class
* @see java.lang.String
*/
public static boolean isMatchingBean(BeanFactory beanFactory, String beanName, Class<?> beanType) {
public static boolean isMatchingBean(@NonNull BeanFactory beanFactory, String beanName, Class<?> beanType) {
return beanFactory.containsBean(beanName) && beanFactory.isTypeMatch(beanName, beanType);
}
public static BeanDefinition addDependsOn(BeanDefinition beanDefinition, String... beanNames) {
/**
* Adds an array of bean dependencies (by name) to the given {@link BeanDefinition}.
*
* @param beanDefinition {@link BeanDefinition} to add the bean dependencies to.
* @param beanNames {@link String} array containing names of beans to which the {@link BeanDefinition}
* has a dependency.
* @return the given {@link BeanDefinition}.
* @see org.springframework.beans.factory.config.BeanDefinition
*/
@NonNull
public static BeanDefinition addDependsOn(@NonNull BeanDefinition beanDefinition, @Nullable String... beanNames) {
List<String> dependsOnList = new ArrayList<>();
Collections.addAll(dependsOnList, nullSafeArray(beanDefinition.getDependsOn(), String.class));
Collections.addAll(dependsOnList, ArrayUtils.nullSafeArray(beanDefinition.getDependsOn(), String.class));
dependsOnList.addAll(Arrays.asList(nullSafeArray(beanNames, String.class)));
beanDefinition.setDependsOn(dependsOnList.toArray(new String[0]));
return beanDefinition;
}
/**
* Returns a {@link List} of beans by the given {@link Class type} in order.
*
* @param <T> {@link Class type} of the bean.
* @param beanFactory {@link ConfigurableListableBeanFactory Spring container} used to acquire the ordered beans.
* @param beanType {@link Class type} of beans to acquire.
* @return a {@link List} of beans of the given {@link Class type} in order.
* @see #getBeansOfTypeOrdered(ConfigurableListableBeanFactory, Class, boolean, boolean)
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see java.lang.Class
* @see java.util.List
*/
@NonNull
public static <T> List<T> getBeansOfTypeOrdered(@NonNull ConfigurableListableBeanFactory beanFactory,
@NonNull Class<T> beanType) {
return getBeansOfTypeOrdered(beanFactory, beanType, true, true);
}
/**
* Returns a {@link List} of beans by the given {@link Class type} in order.
*
* @param <T> {@link Class type} of the bean.
* @param beanFactory {@link ConfigurableListableBeanFactory Spring container} used to acquire the ordered beans.
* @param beanType {@link Class type} of beans to acquire.
* @param includeNonSingletons boolean indicating whether to include non-Singleton beans from the Spring container.
* @param allowEagerInit boolean indicating whether to eagerly initialize {@link FactoryBean FactoryBeans}.
* @return a {@link List} of beans of the given {@link Class type} in order.
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see java.lang.Class
* @see java.util.List
*/
@NonNull
public static <T> List<T> getBeansOfTypeOrdered(@NonNull ConfigurableListableBeanFactory beanFactory,
@NonNull Class<T> beanType, boolean includeNonSingletons, boolean allowEagerInit) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
Assert.notNull(beanType, "Bean type must not be null");
Map<String, T> beansOfType =
CollectionUtils.nullSafeMap(beanFactory.getBeansOfType(beanType, includeNonSingletons, allowEagerInit));
Set<String> beanNamesOfType = new HashSet<>(beansOfType.keySet());
// Handles @Order annotated beans and beans implementing the Ordered interface
List<OrderedBeanWrapper<T>> orderedBeansOfType = beansOfType.entrySet().stream()
.map(SpringUtils::toOrderedBeanWrapper)
.filter(Objects::nonNull)
.collect(Collectors.toList());
Set<String> orderedBeanNamesOfType = orderedBeansOfType.stream()
.map(OrderedBeanWrapper::getBeanName)
.collect(Collectors.toSet());
Set<String> unorderedBeanNamesOfType = new HashSet<>(beanNamesOfType);
// Set Difference
unorderedBeanNamesOfType.removeAll(orderedBeanNamesOfType);
orderedBeansOfType.addAll(orderUnorderedBeans(beanFactory, beansOfType, unorderedBeanNamesOfType));
orderedBeansOfType.sort(AnnotationAwareOrderComparator.INSTANCE);
return orderedBeansOfType.stream()
.map(OrderedBeanWrapper::getBean)
.collect(Collectors.toList());
}
@NonNull
private static <T> List<OrderedBeanWrapper<T>> orderUnorderedBeans(@NonNull ConfigurableListableBeanFactory beanFactory,
@NonNull Map<String, T> beansOfType, @NonNull Set<String> unorderedBeanNames) {
List<OrderedBeanWrapper<T>> orderedBeanWrappers = new ArrayList<>(unorderedBeanNames.size());
for (String beanName : unorderedBeanNames) {
Integer order = Optional.ofNullable(beanName)
.filter(StringUtils::hasText)
.map(beanFactory::getBeanDefinition)
.filter(AnnotatedBeanDefinition.class::isInstance)
.map(AnnotatedBeanDefinition.class::cast)
.map(AnnotatedBeanDefinition::getFactoryMethodMetadata)
.filter(methodMetadata -> methodMetadata.isAnnotated(Order.class.getName()))
.map(methodMetadata -> methodMetadata.getAnnotationAttributes(Order.class.getName()))
.map(annotationAttributes -> annotationAttributes.getOrDefault("value", Ordered.LOWEST_PRECEDENCE))
.map(Integer.class::cast)
.orElse(Ordered.LOWEST_PRECEDENCE);
orderedBeanWrappers.add(DefaultOrderedBeanWrapper.from(beanName, beansOfType.get(beanName), order));
}
return orderedBeanWrappers;
}
@Nullable
private static <T> OrderedBeanWrapper<T> toOrderedBeanWrapper(@NonNull Map.Entry<String, T> beanEntry) {
T bean = beanEntry.getValue();
Integer order = getOrder(bean);
if (order == null) {
order = bean != null ? OrderUtils.getOrder(bean.getClass()) : null;
}
return order != null
? DefaultOrderedBeanWrapper.from(beanEntry.getKey(), bean, order)
: null;
}
/**
* Null-safe operation to return the {@link Integer order} of the given {@link Object} if it is {@link Ordered}
* or {@literal null} if the given {@link Object} is not {@link Ordered}.
*
* @param target {@link Object} to evaluate; may be {@literal null}.
* @return the {@link Integer order} of the given {@link Object} if {@link Ordered},
* otherwise return {@literal null}.
* @see org.springframework.core.Ordered
*/
public static @Nullable Integer getOrder(@Nullable Object target) {
return target instanceof Ordered ? ((Ordered) target).getOrder() : null;
}
/**
* Returns bean of the given {@link Class type} in an ordered {@link Stream}.
*
* @param <T> {@link Class type} of the beans.
* @param beanFactory {@link BeanFactory} from which to acquire the beans.
* @param beanType {@link Class type} of the beans.
* @return an ordered {@link Stream} of beans from the {@link BeanFactory} of the given {@link Class type}.
* @see org.springframework.beans.factory.BeanFactory
* @see java.util.stream.Stream
* @see java.lang.Class
*/
public static <T> Stream<T> getOrderedStreamOfBeansByType(@NonNull BeanFactory beanFactory,
@NonNull Class<T> beanType) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
Assert.notNull(beanType,"Bean type must not be null");
return beanFactory.getBeanProvider(beanType).orderedStream();
}
public static Optional<Object> getPropertyValue(BeanDefinition beanDefinition, String propertyName) {
return Optional.ofNullable(beanDefinition)
@@ -141,33 +314,47 @@ public abstract class SpringUtils {
return type != null ? type.getSimpleName() : null;
}
public static Class<?> nullSafeType(Object target) {
return nullSafeType(target, null);
}
public static Class<?> nullSafeType(Object target, Class<?> defaultType) {
return target != null ? target.getClass() : defaultType;
}
public static boolean safeDoOperation(VoidReturningThrowableOperation operation) {
return safeDoOperation(operation, () -> {});
}
public static boolean safeDoOperation(VoidReturningThrowableOperation operation, Runnable backupOperation) {
try {
operation.run();
return true;
}
catch (Throwable cause) {
backupOperation.run();
return false;
}
}
public static <T> T safeGetValue(Supplier<T> valueSupplier) {
return safeGetValue(valueSupplier, (T) null);
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation) {
return safeGetValue(operation, (T) null);
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, T defaultValue) {
return safeGetValue(valueSupplier, (Supplier<T>) () -> defaultValue);
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation, T defaultValue) {
return safeGetValue(operation, (Supplier<T>) () -> defaultValue);
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, Supplier<T> defaultValueSupplier) {
return safeGetValue(valueSupplier, (Function<Throwable, T>) exception -> defaultValueSupplier.get());
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation, Supplier<T> defaultValueSupplier) {
return safeGetValue(operation, (Function<Throwable, T>) exception -> defaultValueSupplier.get());
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, Function<Throwable, T> exceptionHandler) {
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation,
Function<Throwable, T> exceptionHandler) {
try {
return valueSupplier.get();
return operation.get();
}
catch (Throwable cause) {
return exceptionHandler.apply(cause);
@@ -189,6 +376,61 @@ public abstract class SpringUtils {
}
}
private static class DefaultOrderedBeanWrapper<T> implements OrderedBeanWrapper<T> {
private static <T> OrderedBeanWrapper<T> from(String beanName, T bean) {
return from(beanName, bean, Ordered.LOWEST_PRECEDENCE);
}
private static <T> OrderedBeanWrapper<T> from(String beanName, T bean, int order) {
return new DefaultOrderedBeanWrapper<>(beanName, bean, order);
}
private final int order;
private final T bean;
private final String beanName;
private DefaultOrderedBeanWrapper(String beanName, T bean, int order) {
Assert.notNull(bean, "Bean must not be null");
Assert.hasText(beanName, "Bean name is required");
this.bean = bean;
this.beanName = beanName;
this.order = order;
}
@Override
public T getBean() {
return this.bean;
}
@Override
public String getBeanName() {
return this.beanName;
}
@Override
public int getOrder() {
return this.order;
}
}
public interface OrderedBeanWrapper<T> extends Ordered {
T getBean();
String getBeanName();
}
@FunctionalInterface
public interface ValueReturningThrowableOperation<T> {
T get() throws Throwable;
}
/**
* @deprecated use {@link VoidReturningThrowableOperation}.
*/

View File

@@ -26,11 +26,14 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.util.ArrayUtils.asArray;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
import java.sql.Time;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -50,16 +53,20 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.gemfire.test.model.Person;
import org.springframework.data.gemfire.util.SpringUtils.ValueReturningThrowableOperation;
/**
* Unit Tests for {@link SpringUtils}.
*
* @author John Blum
* @see java.util.function.Function
* @see org.junit.Test
* @see org.junit.runner.RunWith
* @see org.mockito.Mock
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.data.gemfire.util.SpringUtils
* @since 1.9.0
*/
@@ -213,7 +220,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void setBeanDefinitionPropertyReference() {
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
@@ -233,7 +239,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void setBeanDefinitionPropertyValue() {
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
@@ -333,7 +338,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void equalsIgnoreNullIsFalse() {
assertThat(SpringUtils.equalsIgnoreNull(null, "null")).isFalse();
@@ -355,7 +359,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void nullOrEqualsWithNullIsTrue() {
assertThat(SpringUtils.nullOrEquals(null, "test")).isTrue();
}
@@ -371,7 +374,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void nullSafeEqualsWithNullObjectsIsFalse() {
assertThat(SpringUtils.nullSafeEquals(null, "test")).isFalse();
assertThat(SpringUtils.nullSafeEquals("test", null)).isFalse();
@@ -382,6 +384,58 @@ public class SpringUtilsUnitTests {
assertThat(SpringUtils.nullSafeEquals("test", "mock")).isFalse();
}
@Test
public void nullSafeNameWithType() {
assertThat(SpringUtils.nullSafeName(Boolean.class)).isEqualTo(Boolean.class.getName());
assertThat(SpringUtils.nullSafeName(Integer.class)).isEqualTo(Integer.class.getName());
assertThat(SpringUtils.nullSafeName(Double.class)).isEqualTo(Double.class.getName());
assertThat(SpringUtils.nullSafeName(String.class)).isEqualTo(String.class.getName());
assertThat(SpringUtils.nullSafeName(Time.class)).isEqualTo(Time.class.getName());
assertThat(SpringUtils.nullSafeName(Person.class)).isEqualTo(Person.class.getName());
}
@Test
public void nullSafeNameWithNull() {
assertThat(SpringUtils.nullSafeName(null)).isNull();
}
@Test
public void nullSafeSimpleNameWithType() {
assertThat(SpringUtils.nullSafeSimpleName(Boolean.class)).isEqualTo(Boolean.class.getSimpleName());
assertThat(SpringUtils.nullSafeSimpleName(Integer.class)).isEqualTo(Integer.class.getSimpleName());
assertThat(SpringUtils.nullSafeSimpleName(Double.class)).isEqualTo(Double.class.getSimpleName());
assertThat(SpringUtils.nullSafeSimpleName(String.class)).isEqualTo(String.class.getSimpleName());
assertThat(SpringUtils.nullSafeSimpleName(Time.class)).isEqualTo(Time.class.getSimpleName());
assertThat(SpringUtils.nullSafeSimpleName(Person.class)).isEqualTo(Person.class.getSimpleName());
}
@Test
public void nullSafeSimpleNameWithNull() {
assertThat(SpringUtils.nullSafeSimpleName(null)).isNull();
}
@Test
public void nullSafeTypeWithObject() {
assertThat(SpringUtils.nullSafeType(new Object())).isEqualTo(Object.class);
}
@Test
public void nullSafeTypeWithObjectAndDefaultType() {
assertThat(SpringUtils.nullSafeType("test", Person.class)).isEqualTo(String.class);
}
@Test
public void nullSafeTypeWithNull() {
assertThat(SpringUtils.nullSafeType(null)).isNull();
}
@Test
public void nullSafeTypeWithNullAndDefaultType() {
assertThat(SpringUtils.nullSafeType(null, Person.class)).isEqualTo(Person.class);
}
@Test
public void safeDoOperationWithNonThrowingOperation() {
@@ -396,6 +450,30 @@ public class SpringUtilsUnitTests {
assertThat(SpringUtils.safeDoOperation(() -> { throw new RuntimeException("TEST"); })).isFalse();
}
@Test
public void safeDoOperationWithNonThrowingOperationAndBackupOperation() {
AtomicReference<Object> operationValue = new AtomicReference<>();
Runnable mockRunnable = mock(Runnable.class);
assertThat(SpringUtils.safeDoOperation(() -> operationValue.set("MOCK"), mockRunnable)).isTrue();
assertThat(operationValue.get()).isEqualTo("MOCK");
verifyNoInteractions(mockRunnable);
}
@Test
public void safeDoOperationWithThrowingOperationAndBackupOperation() {
Runnable mockRunnable = mock(Runnable.class);
assertThat(SpringUtils.safeDoOperation(() -> { throw new RuntimeException("TEST"); }, mockRunnable)).isFalse();
verify(mockRunnable, times(1)).run();
verifyNoMoreInteractions(mockRunnable);
}
@Test
public void safeGetValueReturnsSuppliedValue() {
assertThat(SpringUtils.safeGetValue(() -> "test")).isEqualTo("test");
@@ -415,16 +493,19 @@ public class SpringUtilsUnitTests {
@Test
public void safeGetValueReturnsSuppliedDefaultValue() {
Supplier<String> exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); };
ValueReturningThrowableOperation<String> exceptionThrowingOperation =
() -> { throw newRuntimeException("error"); };
Supplier<String> defaultValueSupplier = () -> "test";
assertThat(SpringUtils.safeGetValue(exceptionThrowingSupplier, defaultValueSupplier)).isEqualTo("test");
assertThat(SpringUtils.safeGetValue(exceptionThrowingOperation, defaultValueSupplier)).isEqualTo("test");
}
@Test
public void safeGetValueHandlesExceptionReturnsValue() {
Supplier<String> exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); };
ValueReturningThrowableOperation<String> exceptionThrowingOperation =
() -> { throw newRuntimeException("error"); };
Function<Throwable, String> exceptionHandler = exception -> {
@@ -435,13 +516,14 @@ public class SpringUtilsUnitTests {
return "test";
};
assertThat(SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler)).isEqualTo("test");
assertThat(SpringUtils.safeGetValue(exceptionThrowingOperation, exceptionHandler)).isEqualTo("test");
}
@Test(expected = IllegalStateException.class)
public void safeGetValueHandlesExceptionAndCanThrowException() {
Supplier<String> exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); };
ValueReturningThrowableOperation<String> exceptionThrowingOperation =
() -> { throw newRuntimeException("error"); };
Function<Throwable, String> exceptionHandler = exception -> {
@@ -453,7 +535,7 @@ public class SpringUtilsUnitTests {
};
try {
SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler);
SpringUtils.safeGetValue(exceptionThrowingOperation, exceptionHandler);
}
catch (IllegalStateException expected) {