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 506f913ee4
commit 1c422c7c8d
2 changed files with 308 additions and 25 deletions

View File

@@ -14,7 +14,6 @@
* limitations under the License.
*
*/
package org.springframework.data.gemfire.util;
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
@@ -26,38 +25,116 @@ import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.core.Ordered;
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;
/**
* SpringUtils is a utility class encapsulating common functionality on objects and other class types.
*
* @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")
public abstract class SpringUtils {
public static BeanDefinition addDependsOn(BeanDefinition bean, String... beanNames) {
/**
* Determines whether a given bean registered in the {@link BeanFactory Spring container} matches by
* both {@link String name} and {@link Class type}.
*
* @param beanFactory {@link BeanFactory Spring container} in which to resolve the bean.
* @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} and {@link Class type}.
* @see org.springframework.beans.factory.BeanFactory
* @see java.lang.Class
* @see java.lang.String
*/
public static boolean isMatchingBean(@NonNull BeanFactory beanFactory, String beanName, Class<?> beanType) {
return beanFactory.containsBean(beanName) && beanFactory.isTypeMatch(beanName, beanType);
}
/**
* 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(bean.getDependsOn(), String.class));
Collections.addAll(dependsOnList, ArrayUtils.nullSafeArray(beanDefinition.getDependsOn(), String.class));
dependsOnList.addAll(Arrays.asList(nullSafeArray(beanNames, String.class)));
bean.setDependsOn(dependsOnList.toArray(new String[dependsOnList.size()]));
beanDefinition.setDependsOn(dependsOnList.toArray(new String[0]));
return bean;
return beanDefinition;
}
/**
* 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)
.map(it -> it.getPropertyValues())
.map(BeanDefinition::getPropertyValues)
.map(propertyValues -> propertyValues.getPropertyValue(propertyName))
.map(propertyValue -> propertyValue.getValue());
.map(PropertyValue::getValue);
}
public static BeanDefinition setPropertyReference(BeanDefinition beanDefinition,
@@ -116,25 +193,131 @@ public abstract class SpringUtils {
return type != null ? type.getSimpleName() : null;
}
public static <T> T safeGetValue(Supplier<T> valueSupplier) {
return safeGetValue(valueSupplier, (T) null);
public static Class<?> nullSafeType(Object target) {
return nullSafeType(target, null);
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, T defaultValue) {
return safeGetValue(valueSupplier, (Supplier<T>) () -> defaultValue);
public static Class<?> nullSafeType(Object target, Class<?> defaultType) {
return target != null ? target.getClass() : defaultType;
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, Supplier<T> defaultValueSupplier) {
return safeGetValue(valueSupplier, (Function<Throwable, T>) exception -> defaultValueSupplier.get());
public static boolean safeDoOperation(VoidReturningThrowableOperation operation) {
return safeDoOperation(operation, () -> {});
}
public static <T> T safeGetValue(Supplier<T> valueSupplier, Function<Throwable, T> exceptionHandler) {
public static boolean safeDoOperation(VoidReturningThrowableOperation operation, Runnable backupOperation) {
try {
return valueSupplier.get();
operation.run();
return true;
}
catch (Throwable cause) {
backupOperation.run();
return false;
}
}
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation) {
return safeGetValue(operation, (T) null);
}
public static <T> T safeGetValue(ValueReturningThrowableOperation<T> operation, T defaultValue) {
return safeGetValue(operation, (Supplier<T>) () -> defaultValue);
}
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(ValueReturningThrowableOperation<T> operation,
Function<Throwable, T> exceptionHandler) {
try {
return operation.get();
}
catch (Throwable cause) {
return exceptionHandler.apply(cause);
}
}
public static void safeRunOperation(VoidReturningThrowableOperation operation) {
safeRunOperation(operation, cause -> new InvalidDataAccessApiUsageException("Failed to run operation", cause));
}
public static void safeRunOperation(VoidReturningThrowableOperation operation,
Function<Throwable, RuntimeException> exceptionConverter) {
try {
operation.run();
}
catch (Throwable cause) {
throw exceptionConverter.apply(cause);
}
}
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}.
*/
@Deprecated
public interface VoidReturningExceptionThrowingOperation extends VoidReturningThrowableOperation { }
@FunctionalInterface
public interface VoidReturningThrowableOperation {
void run() throws Throwable;
}
}

View File

@@ -14,7 +14,6 @@
* limitations under the License.
*
*/
package org.springframework.data.gemfire.util;
import static org.assertj.core.api.Assertions.assertThat;
@@ -26,12 +25,16 @@ 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.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -39,20 +42,25 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
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
*/
@@ -164,7 +172,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void setBeanDefinitionPropertyReference() {
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
@@ -186,7 +193,6 @@ public class SpringUtilsUnitTests {
}
@Test
@SuppressWarnings("all")
public void setBeanDefinitionPropertyValue() {
BeanDefinition mockBeanDefinition = mock(BeanDefinition.class);
@@ -334,6 +340,96 @@ 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() {
AtomicReference<Object> operationValue = new AtomicReference<>();
assertThat(SpringUtils.safeDoOperation(() -> operationValue.set("TEST"))).isTrue();
assertThat(operationValue.get()).isEqualTo("TEST");
}
@Test
public void safeDoOperationWithThrowingOperation() {
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");
@@ -353,16 +449,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 -> {
@@ -373,13 +472,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 -> {
@@ -391,7 +491,7 @@ public class SpringUtilsUnitTests {
};
try {
SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler);
SpringUtils.safeGetValue(exceptionThrowingOperation, exceptionHandler);
}
catch (IllegalStateException expected) {