From b878feee8a891d4384ce348c2d35825ae5d71f46 Mon Sep 17 00:00:00 2001 From: Serhii Hryhus Date: Tue, 25 Jan 2022 09:54:28 +0200 Subject: [PATCH 1/3] GP-138 create related package --- .../bean-post-processor/pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 3-0-spring-framework/bean-post-processor/pom.xml diff --git a/3-0-spring-framework/bean-post-processor/pom.xml b/3-0-spring-framework/bean-post-processor/pom.xml new file mode 100644 index 0000000..a7d2ae5 --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/pom.xml @@ -0,0 +1,19 @@ + + + + 3-0-spring-framework + com.bobocode + 1.0-SNAPSHOT + + 4.0.0 + + bean-post-processor + + + 11 + 11 + + + \ No newline at end of file From dc4f36b4863b1bb9f2088312ea0eef98c99612a3 Mon Sep 17 00:00:00 2001 From: Serhii Hryhus Date: Tue, 25 Jan 2022 12:39:29 +0200 Subject: [PATCH 2/3] GP-138 add annotations with tests --- .../annotation/EnableStringTrimming.java | 8 ++ .../java/com/bobocode/annotation/Trimmed.java | 8 ++ .../com/bobocode/service/TextService.java | 7 ++ .../com/bobocode/BeanPostProcessorTest.java | 75 +++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/Trimmed.java create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java create mode 100644 3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java new file mode 100644 index 0000000..314988b --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java @@ -0,0 +1,8 @@ +package com.bobocode.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface EnableStringTrimming { +} diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/Trimmed.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/Trimmed.java new file mode 100644 index 0000000..ff1c423 --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/Trimmed.java @@ -0,0 +1,8 @@ +package com.bobocode.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Trimmed { +} diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java new file mode 100644 index 0000000..9b9fffd --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java @@ -0,0 +1,7 @@ +package com.bobocode.service; + +import org.springframework.stereotype.Component; + +@Component +public class TextService { +} diff --git a/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java new file mode 100644 index 0000000..6f83862 --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java @@ -0,0 +1,75 @@ +package com.bobocode; + +import lombok.SneakyThrows; +import org.junit.jupiter.api.*; +import org.reflections.Reflections; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Optional; +import java.util.Set; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BeanPostProcessorTest { + + private Class trimmedAnnotationClass; + private Class enableStringTrimmingAnnotationClass; + + public static final String ANNOTATIONS_PACKAGE = "com.bobocode.annotation"; + public static final String TRIMMED = "Trimmed"; + public static final String ENABLE_STRING_TRIMMING = "EnableStringTrimming"; + + + private Reflections annotationReflections; + + @BeforeEach + @SneakyThrows + public void init(){ + annotationReflections = new Reflections(ANNOTATIONS_PACKAGE); + trimmedAnnotationClass = Class.forName(ANNOTATIONS_PACKAGE + "." + TRIMMED); + enableStringTrimmingAnnotationClass = Class.forName(ANNOTATIONS_PACKAGE + "." + ENABLE_STRING_TRIMMING); + } + + @Test + @Order(1) + @DisplayName("@Trimmed annotation exists") + void trimmedAnnotationExists() throws ClassNotFoundException { + Class.forName(ANNOTATIONS_PACKAGE + "." + TRIMMED); + } + + @Test + @Order(2) + @DisplayName("Trimmed annotation class is marked by @Retention with \"Runtime\" policy") + void trimmedAnnotationClassIsMarkedByRetentionRuntimePolicy(){ + Optional> annotationClass = getAnnotationClassMarketByRetentionRuntimePolicy(TRIMMED); + + assertThat(annotationClass).isPresent(); + } + + @Test + @Order(3) + @DisplayName("@EnableStringTrimming annotation exists") + void enableStringTrimmingAnnotationExists() throws ClassNotFoundException { + Class.forName(ANNOTATIONS_PACKAGE + "." + ENABLE_STRING_TRIMMING); + } + + @Test + @Order(4) + @DisplayName("EnableStringTrimming annotation class is marked by @Retention with \"Runtime\" policy") + void enableStringTrimmingAnnotationClassIsMarkedByRetentionRuntimePolicy(){ + Optional> annotationClass = getAnnotationClassMarketByRetentionRuntimePolicy(ENABLE_STRING_TRIMMING); + + assertThat(annotationClass).isPresent(); + } + + private Optional> getAnnotationClassMarketByRetentionRuntimePolicy(String className) { + Set> annotations = + annotationReflections.getTypesAnnotatedWith(Retention.class); + return annotations.stream() + .filter(c -> c.getSimpleName().equals(className)) + .filter(c -> c.getAnnotation(Retention.class).value().equals(RetentionPolicy.RUNTIME)) + .findAny(); + } +} From 2a9fa3386998d1ad3f607183d42f751958e94c91 Mon Sep 17 00:00:00 2001 From: Serhii Hryhus Date: Tue, 25 Jan 2022 09:54:28 +0200 Subject: [PATCH 3/3] GP-138 demo exercise version --- .../bean-post-processor/README.MD | 38 +++ .../bean-post-processor/pom.xml | 8 + .../annotation/EnableStringTrimming.java | 6 + .../bobocode/config/ApplicationConfig.java | 16 ++ .../com/bobocode/service/TextService.java | 20 +- .../util/StringTrimmingConfiguration.java | 14 ++ .../TrimmedAnnotationBeanPostProcessor.java | 42 ++++ .../com/bobocode/BeanPostProcessorTest.java | 234 +++++++++++++++++- .../testService/NotTrimmedTextService.java | 7 + 3-0-spring-framework/pom.xml | 1 + pom.xml | 2 +- 11 files changed, 375 insertions(+), 13 deletions(-) create mode 100644 3-0-spring-framework/bean-post-processor/README.MD create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/config/ApplicationConfig.java create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/StringTrimmingConfiguration.java create mode 100644 3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/TrimmedAnnotationBeanPostProcessor.java create mode 100644 3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/testService/NotTrimmedTextService.java diff --git a/3-0-spring-framework/bean-post-processor/README.MD b/3-0-spring-framework/bean-post-processor/README.MD new file mode 100644 index 0000000..25a9dbc --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/README.MD @@ -0,0 +1,38 @@ +# Bean Post Processor exercise +Improve your *Spring* advanced skills 💪 +### Task +Implement automatic String trimming functionality. So if I pass " Hello " as an argument to my bean’s method, or return it from the bean’s method, it gets automatically trimmed to "Hello". + +- create annotation @Trimmed that you can put on class + +- create annotation @EnableStringTrimming that will enable automatic String trimming for all beans that are annotated with @Trimmed + +- create TrimmedAnnotationBeanPostProcessor that will check for beans that are marked with @Trimmed, + +- create a proxy of those classes, and override methods. Proxy methods should: + + - trim all String arguments + + - trim all String return values + +- extract TrimmedAnnotationBeanPostProcessor into a separate StringTrimmingConfiguration that is imported by @EnableStringTrimming + +To verify your configuration, run `BeanPostProcessorTest.java` + + +### Pre-conditions ❗ +You're supposed to be familiar with *Spring* + +### How to start ❓ +* Just clone the repository and start implementing the **todo** section, verify your changes by running tests +* If you don't have enough knowledge about this domain, check out the [links below](#Related-materials-ℹ) +* Don't worry if you got stuck, checkout the **exercise/completed** branch and see the final implementation + +### Related materials ℹ + todo + +--- +#### 🆕 First time here? – [See Introduction](https://github.com/bobocode-projects/java-fundamentals-course/tree/main/0-0-intro#introduction) + +## +
\ No newline at end of file diff --git a/3-0-spring-framework/bean-post-processor/pom.xml b/3-0-spring-framework/bean-post-processor/pom.xml index a7d2ae5..dddb1d2 100644 --- a/3-0-spring-framework/bean-post-processor/pom.xml +++ b/3-0-spring-framework/bean-post-processor/pom.xml @@ -16,4 +16,12 @@ 11 + + + cglib + cglib + 3.3.0 + + + \ No newline at end of file diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java index 314988b..4ae5384 100644 --- a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/annotation/EnableStringTrimming.java @@ -1,8 +1,14 @@ package com.bobocode.annotation; +import com.bobocode.util.StringTrimmingConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) +@Configuration +@Import(StringTrimmingConfiguration.class) public @interface EnableStringTrimming { } diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/config/ApplicationConfig.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/config/ApplicationConfig.java new file mode 100644 index 0000000..ead5c5b --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/config/ApplicationConfig.java @@ -0,0 +1,16 @@ +package com.bobocode.config; + +import com.bobocode.annotation.EnableStringTrimming; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Configure the application for using @Trimmed annotation and corresponding logic. + */ + +@Configuration +@ComponentScan(basePackages = "com.bobocode.service") +@EnableStringTrimming +public class ApplicationConfig { + +} diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java index 9b9fffd..549e43d 100644 --- a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/service/TextService.java @@ -1,7 +1,23 @@ package com.bobocode.service; -import org.springframework.stereotype.Component; +import com.bobocode.annotation.Trimmed; +import org.springframework.stereotype.Service; -@Component +/** + * Configure the service to provide trimming process. + */ + +@Service +@Trimmed public class TextService { + public String savedText; + private final static String AVAILABLE_TEXT = " Who cares about tabbing? "; + + public void saveText(String text){ + savedText = text; + } + + public String getAvailableText(){ + return AVAILABLE_TEXT; + } } diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/StringTrimmingConfiguration.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/StringTrimmingConfiguration.java new file mode 100644 index 0000000..f566114 --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/StringTrimmingConfiguration.java @@ -0,0 +1,14 @@ +package com.bobocode.util; + +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class StringTrimmingConfiguration { + + @Bean + public BeanPostProcessor trimmedAnnotationBeanPostProcessor(){ + return new TrimmedAnnotationBeanPostProcessor(); + } +} diff --git a/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/TrimmedAnnotationBeanPostProcessor.java b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/TrimmedAnnotationBeanPostProcessor.java new file mode 100644 index 0000000..22c143d --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/main/java/com/bobocode/util/TrimmedAnnotationBeanPostProcessor.java @@ -0,0 +1,42 @@ +package com.bobocode.util; + +import com.bobocode.annotation.Trimmed; +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +import java.lang.reflect.Method; +import java.util.Arrays; + +public class TrimmedAnnotationBeanPostProcessor implements BeanPostProcessor { + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + Class beanClass = bean.getClass(); + if (beanClass.isAnnotationPresent(Trimmed.class)) { + return createTrimmedProxy(beanClass); + } + return bean; + } + + private Object createTrimmedProxy(Class beanClass) { + var enhancer = new Enhancer(); + enhancer.setSuperclass(beanClass); + enhancer.setInterfaces(beanClass.getInterfaces()); + MethodInterceptor interceptor = (Object obj, Method method, Object[] args, MethodProxy proxy) -> { + Object[] arguments = Arrays.stream(args) + .filter(arg -> arg instanceof String) + .map(arg -> ((String) arg).trim()) + .toArray(); + + if (method.getReturnType().equals(String.class)) { + String invokeSuper = (String) proxy.invokeSuper(obj, arguments); + return (String) invokeSuper.trim(); + } + return proxy.invokeSuper(obj, arguments); + }; + enhancer.setCallback(interceptor); + return beanClass.cast(enhancer.create()); + } +} diff --git a/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java index 6f83862..80993d3 100644 --- a/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java +++ b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/BeanPostProcessorTest.java @@ -1,35 +1,62 @@ package com.bobocode; +import com.bobocode.config.ApplicationConfig; +import com.bobocode.service.TextService; +import com.bobocode.testService.NotTrimmedTextService; import lombok.SneakyThrows; import org.junit.jupiter.api.*; import org.reflections.Reflections; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Optional; import java.util.Set; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +@SpringJUnitConfig(BeanPostProcessorTest.TestConfig.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BeanPostProcessorTest { + @Configuration + @ComponentScan(basePackages = "com.bobocode") + static class TestConfig { + } - private Class trimmedAnnotationClass; - private Class enableStringTrimmingAnnotationClass; + @Autowired + TextService textService; - public static final String ANNOTATIONS_PACKAGE = "com.bobocode.annotation"; - public static final String TRIMMED = "Trimmed"; - public static final String ENABLE_STRING_TRIMMING = "EnableStringTrimming"; + @Autowired + NotTrimmedTextService notTrimmedTextService; + private static final String ANNOTATIONS_PACKAGE = "com.bobocode.annotation"; + private static final String UTIL_PACKAGE = "com.bobocode.util"; + private static final String TRIMMED = "Trimmed"; + private static final String ENABLE_STRING_TRIMMING = "EnableStringTrimming"; + private static final String TRIMMED_ANNOTATION_BEAN_POST_PROCESSOR = "TrimmedAnnotationBeanPostProcessor"; + private static final String STRING_TRIMMING_CONFIGURATION = "StringTrimmingConfiguration"; private Reflections annotationReflections; + private Reflections configurationReflections; @BeforeEach @SneakyThrows - public void init(){ + public void init() { annotationReflections = new Reflections(ANNOTATIONS_PACKAGE); - trimmedAnnotationClass = Class.forName(ANNOTATIONS_PACKAGE + "." + TRIMMED); - enableStringTrimmingAnnotationClass = Class.forName(ANNOTATIONS_PACKAGE + "." + ENABLE_STRING_TRIMMING); + configurationReflections = new Reflections(UTIL_PACKAGE); } @Test @@ -42,7 +69,7 @@ void trimmedAnnotationExists() throws ClassNotFoundException { @Test @Order(2) @DisplayName("Trimmed annotation class is marked by @Retention with \"Runtime\" policy") - void trimmedAnnotationClassIsMarkedByRetentionRuntimePolicy(){ + void trimmedAnnotationClassIsMarkedByRetentionRuntimePolicy() { Optional> annotationClass = getAnnotationClassMarketByRetentionRuntimePolicy(TRIMMED); assertThat(annotationClass).isPresent(); @@ -58,12 +85,199 @@ void enableStringTrimmingAnnotationExists() throws ClassNotFoundException { @Test @Order(4) @DisplayName("EnableStringTrimming annotation class is marked by @Retention with \"Runtime\" policy") - void enableStringTrimmingAnnotationClassIsMarkedByRetentionRuntimePolicy(){ + void enableStringTrimmingAnnotationClassIsMarkedByRetentionRuntimePolicy() { Optional> annotationClass = getAnnotationClassMarketByRetentionRuntimePolicy(ENABLE_STRING_TRIMMING); assertThat(annotationClass).isPresent(); } + @Test + @Order(5) + @DisplayName("ApplicationConfig class is marked by @Configuration annotation") + void applicationConfigurationClassExists() { + Configuration annotation = ApplicationConfig.class.getAnnotation(Configuration.class); + + assertThat(annotation).isNotNull(); + } + + @Test + @Order(6) + @DisplayName("Package scanning is configured for service package") + void applicationComponentScanIsConfiguredForService() { + ComponentScan annotation = ApplicationConfig.class.getAnnotation(ComponentScan.class); + + Optional scannedPackage = Arrays.stream(annotation.basePackages()) + .filter(p -> p.equals("com.bobocode.service")) + .findAny(); + assertThat(annotation).isNotNull(); + assertThat(scannedPackage).isPresent(); + } + + @Test + @Order(7) + @DisplayName("TextService is marked by @Service annotation") + void textServiceIcMarkedAsComponent() { + Service annotation = TextService.class.getAnnotation(Service.class); + + assertThat(annotation).isNotNull(); + } + + @Test + @Order(8) + @DisplayName("TextService is marked by @Trimmed annotation") + void textServiceIcMarkedAsTrimmed() { + Annotation[] annotations = TextService.class.getAnnotations(); + + Optional anyTrimmedAnnotation = Arrays.stream(annotations) + .map(a -> a.annotationType().getSimpleName()) + .filter(n -> n.equals(TRIMMED)) + .findAny(); + + assertThat(anyTrimmedAnnotation).isPresent(); + } + + @Test + @Order(9) + @DisplayName("TrimmedAnnotationBeanPostProcessor class exists in util package") + void trimmedAnnotationBeanPostProcessorClassExists() throws ClassNotFoundException { + Class.forName(UTIL_PACKAGE + "." + TRIMMED_ANNOTATION_BEAN_POST_PROCESSOR); + } + + @Test + @Order(10) + @DisplayName("TrimmedAnnotationBeanPostProcessor class implements BeanPostProcessor interface") + void trimmedAnnotationBeanPostProcessorClassImplementsBeanPostProcessor() { + Set> beanPostProcessorsClasses = configurationReflections + .getSubTypesOf(BeanPostProcessor.class); + + Optional anyTrimmedBeanPostProcessor = beanPostProcessorsClasses.stream() + .map(Class::getSimpleName) + .filter(n -> n.equals(TRIMMED_ANNOTATION_BEAN_POST_PROCESSOR)) + .findAny(); + + assertThat(anyTrimmedBeanPostProcessor).isPresent(); + } + + @Test + @Order(11) + @DisplayName("TrimmedAnnotationBeanPostProcessor class is not marked as Spring bean") + void trimmedAnnotationBeanPostProcessorClassIsNotMarkedAsBean() { + Set> beanPostProcessorsClasses = configurationReflections + .getSubTypesOf(BeanPostProcessor.class); + + Optional> anyTrimmedBeanPostProcessor = beanPostProcessorsClasses.stream() + .filter(c -> c.getSimpleName().equals(TRIMMED_ANNOTATION_BEAN_POST_PROCESSOR)) + .findAny(); + + boolean componentAnnotationIsPresent = anyTrimmedBeanPostProcessor + .orElseThrow().isAnnotationPresent(Component.class); + boolean serviceAnnotationIsPresent = anyTrimmedBeanPostProcessor + .orElseThrow().isAnnotationPresent(Service.class); + + assertFalse(componentAnnotationIsPresent); + assertFalse(serviceAnnotationIsPresent); + } + + @Test + @Order(12) + @DisplayName("StringTrimmedConfiguration class exists in \"util\" package") + void stringTrimmedConfigurationClassExists() throws ClassNotFoundException { + Class.forName(UTIL_PACKAGE + "." + STRING_TRIMMING_CONFIGURATION); + } + + @Test + @Order(13) + @DisplayName("StringTrimmedConfiguration class is marked by @Configuration annotation") + void stringTrimmedConfigurationClassIsMarkedByConfiguration() { + Optional> anyMarkedClass = configurationReflections.getTypesAnnotatedWith(Configuration.class).stream() + .filter(c -> c.getSimpleName().equals(STRING_TRIMMING_CONFIGURATION)) + .findAny(); + + assertThat(anyMarkedClass).isPresent(); + } + + @Test + @Order(14) + @DisplayName("StringTrimmedConfiguration class creates TrimmedAnnotationBeanPostProcessor bean using method") + void stringTrimmedConfigurationClassCreatesTrimmedBeanPostProcessor() throws ClassNotFoundException { + Class configurationClass = Class.forName(UTIL_PACKAGE + "." + STRING_TRIMMING_CONFIGURATION); + + Optional optionalMethod = Arrays + .stream(configurationClass.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(Bean.class)) + .filter(method -> method.getReturnType().equals(BeanPostProcessor.class)) + .findAny(); + + assertThat(optionalMethod).isPresent(); + } + + @Test + @Order(15) + @DisplayName("EnableStringAnnotation class is marked with @Configuration") + void enableStringTrimmingAnnotationClassIsMarkedAsConfiguration() { + Optional> anyAnnotationClass = annotationReflections + .getTypesAnnotatedWith(Configuration.class).stream() + .filter(c -> c.getSimpleName().equals(ENABLE_STRING_TRIMMING)) + .findAny(); + + assertThat(anyAnnotationClass).isPresent(); + } + + @Test + @Order(16) + @DisplayName("EnableStringAnnotation class is marked with @Configuration") + void enableStringTrimmingAnnotationClassIsMarkedWithConfiguredImport() { + Optional> anyAnnotationClass = annotationReflections + .getTypesAnnotatedWith(Import.class).stream() + .filter(c -> c.getSimpleName().equals(ENABLE_STRING_TRIMMING)) + .filter(c -> c.getAnnotation(Import.class).value()[0].getSimpleName().equals(STRING_TRIMMING_CONFIGURATION)) + .findAny(); + + assertThat(anyAnnotationClass).isPresent(); + } + + @Test + @Order(17) + @DisplayName("TextService bean is CGLIB proxy") + void textServiceHasProxy() { + String className = textService.getClass().getSimpleName(); + + boolean isCGLibCreature = className.contains("CGLIB"); + + assertThat(className).isNotEqualTo("TextService"); + assertTrue(isCGLibCreature); + } + + @Test + @Order(18) + @DisplayName("Bean is not proxy when @Trimmed annotation is missed") + void beanIsNotProxyWhenTrimmedAnnotationIsNotUsed() { + String name = notTrimmedTextService.getClass().getSimpleName(); + + assertThat(name).isEqualTo("NotTrimmedTextService"); + } + + @Test + @Order(19) + @DisplayName("TextService receives trimmed String method arguments") + void textServiceReceivesTrimmedStringMethodArguments() { + String text = " Need more space "; + textService.saveText(text); + + String result = textService.savedText; + + assertThat(result).isEqualTo("Need more space"); + } + + @Test + @Order(20) + @DisplayName("TextService method returns trimmed String value") + void textServiceMethodReturnsTrimmedStringValue() { + String result = textService.getAvailableText(); + + assertThat(result).isEqualTo("Who cares about tabbing?"); + } + private Optional> getAnnotationClassMarketByRetentionRuntimePolicy(String className) { Set> annotations = annotationReflections.getTypesAnnotatedWith(Retention.class); diff --git a/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/testService/NotTrimmedTextService.java b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/testService/NotTrimmedTextService.java new file mode 100644 index 0000000..3bc848a --- /dev/null +++ b/3-0-spring-framework/bean-post-processor/src/test/java/com/bobocode/testService/NotTrimmedTextService.java @@ -0,0 +1,7 @@ +package com.bobocode.testService; + +import org.springframework.stereotype.Service; + +@Service +public class NotTrimmedTextService { +} diff --git a/3-0-spring-framework/pom.xml b/3-0-spring-framework/pom.xml index 6beb492..3a73db4 100644 --- a/3-0-spring-framework/pom.xml +++ b/3-0-spring-framework/pom.xml @@ -18,6 +18,7 @@ 3-1-1-dispatcher-servlet-initializer 3-0-2-view-resolver 3-2-1-account-rest-api + bean-post-processor diff --git a/pom.xml b/pom.xml index a006708..5884de2 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ org.reflections reflections - 0.9.12 + 0.10.2 org.hamcrest