From 1c422c7c8da2b8d29d870b288214e07f84a24a70 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 9 Sep 2020 12:15:33 -0700 Subject: [PATCH] DATAGEODE-306 - Add overloaded safeDoOperation(:VoidReturningThrowableOperation, backupOperation:Runnable) method. Redefine safeDoOperation(:VoidReturningThrowableOperation) in terms of the overloaded method. Edit Javadoc. --- .../data/gemfire/util/SpringUtils.java | 213 ++++++++++++++++-- .../gemfire/util/SpringUtilsUnitTests.java | 120 +++++++++- 2 files changed, 308 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java b/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java index 6a99ce80..59014a5a 100644 --- a/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java +++ b/src/main/java/org/springframework/data/gemfire/util/SpringUtils.java @@ -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 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 {@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 Stream getOrderedStreamOfBeansByType(@NonNull BeanFactory beanFactory, + @NonNull Class 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 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 safeGetValue(Supplier valueSupplier) { - return safeGetValue(valueSupplier, (T) null); + public static Class nullSafeType(Object target) { + return nullSafeType(target, null); } - public static T safeGetValue(Supplier valueSupplier, T defaultValue) { - return safeGetValue(valueSupplier, (Supplier) () -> defaultValue); + public static Class nullSafeType(Object target, Class defaultType) { + return target != null ? target.getClass() : defaultType; } - public static T safeGetValue(Supplier valueSupplier, Supplier defaultValueSupplier) { - return safeGetValue(valueSupplier, (Function) exception -> defaultValueSupplier.get()); + public static boolean safeDoOperation(VoidReturningThrowableOperation operation) { + return safeDoOperation(operation, () -> {}); } - public static T safeGetValue(Supplier valueSupplier, Function 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 safeGetValue(ValueReturningThrowableOperation operation) { + return safeGetValue(operation, (T) null); + } + + public static T safeGetValue(ValueReturningThrowableOperation operation, T defaultValue) { + return safeGetValue(operation, (Supplier) () -> defaultValue); + } + + public static T safeGetValue(ValueReturningThrowableOperation operation, Supplier defaultValueSupplier) { + return safeGetValue(operation, (Function) exception -> defaultValueSupplier.get()); + } + + public static T safeGetValue(ValueReturningThrowableOperation operation, + Function 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 exceptionConverter) { + + try { + operation.run(); + } + catch (Throwable cause) { + throw exceptionConverter.apply(cause); + } + } + + private static class DefaultOrderedBeanWrapper implements OrderedBeanWrapper { + + private static OrderedBeanWrapper from(String beanName, T bean) { + return from(beanName, bean, Ordered.LOWEST_PRECEDENCE); + } + + private static OrderedBeanWrapper 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 extends Ordered { + + T getBean(); + + String getBeanName(); + + } + + @FunctionalInterface + public interface ValueReturningThrowableOperation { + T get() throws Throwable; + } + + /** + * @deprecated use {@link VoidReturningThrowableOperation}. + */ + @Deprecated + public interface VoidReturningExceptionThrowingOperation extends VoidReturningThrowableOperation { } + + @FunctionalInterface + public interface VoidReturningThrowableOperation { + void run() throws Throwable; + } } diff --git a/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java b/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java index 6a2214b8..ba915aec 100644 --- a/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java +++ b/src/test/java/org/springframework/data/gemfire/util/SpringUtilsUnitTests.java @@ -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 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 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 exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + ValueReturningThrowableOperation exceptionThrowingOperation = + () -> { throw newRuntimeException("error"); }; + Supplier defaultValueSupplier = () -> "test"; - assertThat(SpringUtils.safeGetValue(exceptionThrowingSupplier, defaultValueSupplier)).isEqualTo("test"); + assertThat(SpringUtils.safeGetValue(exceptionThrowingOperation, defaultValueSupplier)).isEqualTo("test"); } @Test public void safeGetValueHandlesExceptionReturnsValue() { - Supplier exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + ValueReturningThrowableOperation exceptionThrowingOperation = + () -> { throw newRuntimeException("error"); }; Function 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 exceptionThrowingSupplier = () -> { throw newRuntimeException("error"); }; + ValueReturningThrowableOperation exceptionThrowingOperation = + () -> { throw newRuntimeException("error"); }; Function exceptionHandler = exception -> { @@ -391,7 +491,7 @@ public class SpringUtilsUnitTests { }; try { - SpringUtils.safeGetValue(exceptionThrowingSupplier, exceptionHandler); + SpringUtils.safeGetValue(exceptionThrowingOperation, exceptionHandler); } catch (IllegalStateException expected) {