|
28 | 28 | import java.util.List;
|
29 | 29 | import java.util.Optional;
|
30 | 30 |
|
| 31 | +import org.apache.geode.cache.CacheEvent; |
| 32 | +import org.apache.geode.cache.CacheListener; |
31 | 33 | import org.apache.geode.cache.EntryEvent;
|
32 | 34 | import org.apache.geode.cache.Region;
|
33 | 35 | import org.apache.geode.cache.RegionEvent;
|
|
40 | 42 | import org.springframework.core.annotation.AnnotationAttributes;
|
41 | 43 | import org.springframework.core.annotation.AnnotationUtils;
|
42 | 44 | import org.springframework.data.gemfire.config.annotation.AsCacheListener;
|
| 45 | +import org.springframework.data.gemfire.config.annotation.AsRegionEventListener; |
43 | 46 | import org.springframework.data.gemfire.eventing.EventProcessorUtils;
|
44 | 47 | import org.springframework.data.gemfire.eventing.config.CacheListenerEventType;
|
| 48 | +import org.springframework.data.gemfire.eventing.config.PojoCacheListenerWrapper; |
| 49 | +import org.springframework.data.gemfire.eventing.config.PojoRegionEventCacheListenerWrapper; |
45 | 50 | import org.springframework.data.gemfire.eventing.config.RegionCacheListenerEventType;
|
46 | 51 | import org.springframework.data.gemfire.util.ArrayUtils;
|
47 | 52 | import org.springframework.util.Assert;
|
48 | 53 | import org.springframework.util.ObjectUtils;
|
49 | 54 | import org.springframework.util.ReflectionUtils;
|
50 | 55 |
|
51 | 56 | /**
|
52 |
| - * |
| 57 | + * A {@link BeanPostProcessor} to create and register {@link CacheListener}, annotated with {@link AsCacheListener} |
| 58 | + * and {@link AsRegionEventListener} onto the configured {@link Region}s |
53 | 59 | */
|
54 | 60 | @Configuration public class CacheListenerPostProcessor implements BeanPostProcessor, BeanFactoryAware {
|
55 | 61 |
|
56 | 62 | private ConfigurableListableBeanFactory beanFactory;
|
57 | 63 |
|
58 | 64 | @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
59 | 65 |
|
60 |
| - registerAnyDeclaredCacheListenerAnnotatedMethods(bean); |
| 66 | + registerAnyDeclaredCacheListenerAnnotatedMethods(bean, AsCacheListener.class); |
| 67 | + registerAnyDeclaredCacheListenerAnnotatedMethods(bean, AsRegionEventListener.class); |
61 | 68 |
|
62 | 69 | return bean;
|
63 | 70 | }
|
64 | 71 |
|
65 |
| - private void registerAnyDeclaredCacheListenerAnnotatedMethods(Object bean) { |
| 72 | + private <T extends Annotation> void registerAnyDeclaredCacheListenerAnnotatedMethods(Object bean, |
| 73 | + Class<T> listenerAnnotationClazz) { |
66 | 74 |
|
67 | 75 | stream(nullSafeArray(ReflectionUtils.getAllDeclaredMethods(bean.getClass()), Method.class)).forEach(method -> {
|
68 | 76 |
|
69 |
| - AsCacheListener cacheListenerAnnotation = AnnotationUtils.getAnnotation(method, AsCacheListener.class); |
| 77 | + Optional<T> cacheListenerAnnotation = Optional.ofNullable(AnnotationUtils |
| 78 | + .getAnnotation(method, listenerAnnotationClazz)); |
70 | 79 |
|
71 |
| - if (cacheListenerAnnotation != null) { |
| 80 | + cacheListenerAnnotation.ifPresent(asCacheListener -> { |
72 | 81 |
|
73 | 82 | Assert.isTrue(Modifier.isPublic(method.getModifiers()), String
|
74 | 83 | .format("The bean [%s] method [%s] annotated with [%s] must be public", bean.getClass().getName(),
|
75 |
| - method.getName(), AsCacheListener.class.getName())); |
76 |
| - |
77 |
| - AnnotationAttributes cacheListenerAttributes = resolveAnnotationAttributes(cacheListenerAnnotation); |
| 84 | + method.getName(), listenerAnnotationClazz.getName())); |
78 | 85 |
|
79 |
| - CacheListenerEventType[] eventTypes = (CacheListenerEventType[]) cacheListenerAttributes |
80 |
| - .get("eventTypes"); |
81 |
| - RegionCacheListenerEventType[] regionEventTypes = (RegionCacheListenerEventType[]) cacheListenerAttributes |
82 |
| - .get("regionEventTypes"); |
| 86 | + AnnotationAttributes cacheListenerAttributes = resolveAnnotationAttributes(asCacheListener); |
83 | 87 |
|
84 |
| - if (regionEventTypes.length > 0 && eventTypes.length > 0) { |
85 |
| - throw new IllegalArgumentException(String |
86 |
| - .format("Populating both event and regionEvent types is not allowed for bean [%s] method [%s]", |
87 |
| - bean.getClass().getName(), method.getName())); |
88 |
| - } |
| 88 | + registerEventHandlers(bean, listenerAnnotationClazz, method, cacheListenerAttributes); |
89 | 89 |
|
90 |
| - registerEventHandlerToRegion(bean, method, cacheListenerAttributes, eventTypes, regionEventTypes); |
91 |
| - } |
| 90 | + }); |
92 | 91 | });
|
93 | 92 | }
|
94 | 93 |
|
95 |
| - private void registerEventHandlerToRegion(Object bean, Method method, AnnotationAttributes cacheListenerAttributes, |
96 |
| - CacheListenerEventType[] eventTypes, RegionCacheListenerEventType[] regionEventTypes) { |
97 |
| - String[] regions = getRegionsForEventRegistration(cacheListenerAttributes.getStringArray("regions"), |
98 |
| - getBeanFactory()); |
99 |
| - |
100 |
| - if (eventTypes.length > 0) { |
101 |
| - EventProcessorUtils.validateCacheListenerMethodParameters(method,EntryEvent.class); |
102 |
| - registerCacheListenerEvents(bean, method, regions, eventTypes); |
| 94 | + private <T extends Annotation> void registerEventHandlers(Object bean, Class<T> listenerAnnotationClazz, |
| 95 | + Method method, AnnotationAttributes cacheListenerAttributes) { |
| 96 | + if (listenerAnnotationClazz.isAssignableFrom(AsCacheListener.class)) { |
| 97 | + registerCacheListenerEventHandler(bean, method, cacheListenerAttributes); |
103 | 98 | }
|
104 |
| - if (regionEventTypes.length > 0) { |
105 |
| - EventProcessorUtils.validateCacheListenerMethodParameters(method, RegionEvent.class); |
106 |
| - registerRegionCacheListenerEvents(bean, method, regions, regionEventTypes); |
| 99 | + else if (listenerAnnotationClazz.isAssignableFrom(AsRegionEventListener.class)) { |
| 100 | + registerRegionEventHandler(bean, method, cacheListenerAttributes); |
107 | 101 | }
|
108 | 102 | }
|
109 | 103 |
|
110 |
| - private void registerCacheListenerEvents(Object bean, Method method, String[] regions, |
111 |
| - CacheListenerEventType[] eventTypes) { |
| 104 | + /** |
| 105 | + * Lookup {@link CacheListenerEventType} from the {@link AsCacheListener} annotation and create a {@link PojoCacheListenerWrapper} |
| 106 | + * of type {@link CacheListener} that would register itself onto a {@link Region} for the configured events |
| 107 | + */ |
| 108 | + private void registerCacheListenerEventHandler(Object bean, Method method, |
| 109 | + AnnotationAttributes cacheListenerAttributes) { |
| 110 | + CacheListenerEventType[] eventTypes = (CacheListenerEventType[]) cacheListenerAttributes |
| 111 | + .get("eventTypes"); |
| 112 | + registerEventHandlerToRegion(method, cacheListenerAttributes, |
| 113 | + new PojoCacheListenerWrapper(method, bean, eventTypes), EntryEvent.class); |
| 114 | + } |
112 | 115 |
|
113 |
| - EventProcessorUtils.registerCacheListenerOntoRegions(regions, method, bean, eventTypes, getBeanFactory()); |
| 116 | + /** |
| 117 | + * Lookup {@link RegionCacheListenerEventType} from the {@link AsRegionEventListener} annotation and |
| 118 | + * create a {@link PojoRegionEventCacheListenerWrapper} |
| 119 | + * of type {@link CacheListener} that would register itself onto a {@link Region} for the configured |
| 120 | + * {@link Region} specific events |
| 121 | + */ |
| 122 | + private void registerRegionEventHandler(Object bean, Method method, |
| 123 | + AnnotationAttributes cacheListenerAttributes) { |
| 124 | + RegionCacheListenerEventType[] eventTypes = (RegionCacheListenerEventType[]) cacheListenerAttributes |
| 125 | + .get("regionEventTypes"); |
| 126 | + registerEventHandlerToRegion(method, cacheListenerAttributes, |
| 127 | + new PojoRegionEventCacheListenerWrapper(method, bean, eventTypes), RegionEvent.class); |
114 | 128 | }
|
115 | 129 |
|
116 |
| - private void registerRegionCacheListenerEvents(Object bean, Method method, String[] regions, |
117 |
| - RegionCacheListenerEventType[] regionEventTypes) { |
118 |
| - EventProcessorUtils |
119 |
| - .registerCacheListenerForRegionEventsOntoRegions(regions, method, bean, regionEventTypes, getBeanFactory()); |
| 130 | + /** |
| 131 | + * Validates the method parameters to be of the correct type dependent on the eventing Annotation. It then registers |
| 132 | + * the defined {@link CacheListener} onto the defined set of {@link Region}. |
| 133 | + * |
| 134 | + * @param method - The event handler callback method for event handling type |
| 135 | + * @param cacheListenerAttributes - A set of {@link Annotation} attributes used to get the region names configured |
| 136 | + * on the annotation |
| 137 | + * @param cacheListener - The {@link CacheListener} to be registered onto the {@link Region} |
| 138 | + * @param eventClass - The expected method parameter type. Can be either {@link EntryEvent} or {@link RegionEvent} |
| 139 | + */ |
| 140 | + private <T extends CacheEvent> void registerEventHandlerToRegion(Method method, |
| 141 | + AnnotationAttributes cacheListenerAttributes, CacheListener cacheListener, Class<T> eventClass) { |
| 142 | + String[] regions = getRegionsForEventRegistration(cacheListenerAttributes.getStringArray("regions"), |
| 143 | + getBeanFactory()); |
| 144 | + |
| 145 | + EventProcessorUtils.validateCacheListenerMethodParameters(method, eventClass); |
| 146 | + EventProcessorUtils.registerCacheListenerToRegions(regions, beanFactory, cacheListener); |
120 | 147 | }
|
121 | 148 |
|
| 149 | + /** |
| 150 | + * Takes an array of Region names. If empty, returns all configured {@link Region} names, otherwise returns the input |
| 151 | + * region name array |
| 152 | + * |
| 153 | + * @param regions - An Array of {@link Region} names. This can be empty and thus defaults to all configured {@link Region} |
| 154 | + * @param beanFactory - A {@link org.springframework.data.gemfire.ConfigurableRegionFactoryBean} |
| 155 | + * @return An array of {@link Region} names. If the input regions array is empty, the result will be an array with all |
| 156 | + * configured {@link Region} names |
| 157 | + */ |
122 | 158 | private String[] getRegionsForEventRegistration(String[] regions, ConfigurableListableBeanFactory beanFactory) {
|
123 | 159 | if (ArrayUtils.isEmpty(regions)) {
|
124 | 160 | List<String> regionNames = new ArrayList<>();
|
|
0 commit comments