| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.fineract.infrastructure.event.business.service; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import lombok.RequiredArgsConstructor; |
| import lombok.extern.slf4j.Slf4j; |
| import org.apache.fineract.infrastructure.core.config.FineractProperties; |
| import org.apache.fineract.infrastructure.event.business.BusinessEventListener; |
| import org.apache.fineract.infrastructure.event.business.domain.BulkBusinessEvent; |
| import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent; |
| import org.apache.fineract.infrastructure.event.business.domain.NoExternalEvent; |
| import org.apache.fineract.infrastructure.event.external.repository.ExternalEventConfigurationRepository; |
| import org.apache.fineract.infrastructure.event.external.service.ExternalEventService; |
| import org.springframework.beans.factory.InitializingBean; |
| import org.springframework.stereotype.Service; |
| |
| @Service |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| @RequiredArgsConstructor |
| @Slf4j |
| public class BusinessEventNotifierServiceImpl implements BusinessEventNotifierService, InitializingBean { |
| |
| private final Map<Class, List<BusinessEventListener>> preListeners = new HashMap<>(); |
| private final Map<Class, List<BusinessEventListener>> postListeners = new HashMap<>(); |
| |
| private final ThreadLocal<Boolean> eventRecordingEnabled = ThreadLocal.withInitial(() -> false); |
| private final ThreadLocal<List<BusinessEvent<?>>> recordedEvents = ThreadLocal.withInitial(ArrayList::new); |
| |
| private final ExternalEventService externalEventService; |
| private final ExternalEventConfigurationRepository eventConfigurationRepository; |
| private final FineractProperties fineractProperties; |
| |
| @Override |
| public void afterPropertiesSet() throws Exception { |
| if (isExternalEventPostingEnabled()) { |
| log.info("External event posting is enabled"); |
| } else { |
| log.info("External event posting is disabled"); |
| } |
| } |
| |
| @Override |
| public void notifyPreBusinessEvent(BusinessEvent<?> businessEvent) { |
| throwExceptionIfBulkEvent(businessEvent); |
| List<BusinessEventListener> businessEventListeners = findSuitableListeners(preListeners, businessEvent.getClass()); |
| for (BusinessEventListener eventListener : businessEventListeners) { |
| eventListener.onBusinessEvent(businessEvent); |
| } |
| } |
| |
| @Override |
| public <T extends BusinessEvent<?>> void addPreBusinessEventListener(Class<T> eventType, BusinessEventListener<T> listener) { |
| List<BusinessEventListener> businessEventListeners = preListeners.get(eventType); |
| if (businessEventListeners == null) { |
| businessEventListeners = new ArrayList<>(); |
| preListeners.put(eventType, businessEventListeners); |
| } |
| businessEventListeners.add(listener); |
| } |
| |
| @Override |
| public void notifyPostBusinessEvent(BusinessEvent<?> businessEvent) { |
| throwExceptionIfBulkEvent(businessEvent); |
| boolean isExternalEvent = !(businessEvent instanceof NoExternalEvent); |
| List<BusinessEventListener> businessEventListeners = findSuitableListeners(postListeners, businessEvent.getClass()); |
| for (BusinessEventListener eventListener : businessEventListeners) { |
| eventListener.onBusinessEvent(businessEvent); |
| } |
| if (isExternalEvent && isExternalEventPostingEnabled()) { |
| // we only want to create external events for operations that were successful, hence the post listener |
| if (isExternalEventConfiguredForPosting(businessEvent.getType())) { |
| if (isExternalEventRecordingEnabled()) { |
| recordedEvents.get().add(businessEvent); |
| } else { |
| externalEventService.postEvent(businessEvent); |
| } |
| } |
| } |
| } |
| |
| private List<BusinessEventListener> findSuitableListeners(Map<Class, List<BusinessEventListener>> listeners, Class<?> eventClazz) { |
| List<BusinessEventListener> result = new ArrayList<>(); |
| for (Map.Entry<Class, List<BusinessEventListener>> entry : listeners.entrySet()) { |
| Class<?> registeredClazz = entry.getKey(); |
| if (registeredClazz.isAssignableFrom(eventClazz)) { |
| result.addAll(entry.getValue()); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public <T extends BusinessEvent<?>> void addPostBusinessEventListener(Class<T> eventType, BusinessEventListener<T> listener) { |
| List<BusinessEventListener> businessEventListeners = postListeners.get(eventType); |
| if (businessEventListeners == null) { |
| businessEventListeners = new ArrayList<>(); |
| postListeners.put(eventType, businessEventListeners); |
| } |
| businessEventListeners.add(listener); |
| } |
| |
| private boolean isExternalEventRecordingEnabled() { |
| return eventRecordingEnabled.get(); |
| } |
| |
| private boolean isExternalEventPostingEnabled() { |
| return fineractProperties.getEvents().getExternal().isEnabled(); |
| } |
| |
| private boolean isExternalEventConfiguredForPosting(String eventType) { |
| return eventConfigurationRepository.findExternalEventConfigurationByTypeWithNotFoundDetection(eventType).isEnabled(); |
| } |
| |
| private void throwExceptionIfBulkEvent(BusinessEvent<?> businessEvent) { |
| if (businessEvent instanceof BulkBusinessEvent) { |
| throw new IllegalArgumentException("BulkBusinessEvent cannot be raised directly"); |
| } |
| } |
| |
| @Override |
| public void startExternalEventRecording() { |
| eventRecordingEnabled.set(true); |
| } |
| |
| @Override |
| public void stopExternalEventRecording() { |
| eventRecordingEnabled.set(false); |
| try { |
| List<BusinessEvent<?>> recordedBusinessEvents = recordedEvents.get(); |
| if (isExternalEventPostingEnabled()) { |
| if (recordedBusinessEvents.isEmpty()) { |
| log.debug("Not posting a BulkBusinessEvent since there were no events recorded"); |
| } else { |
| if (recordedBusinessEvents.size() == 1) { |
| log.debug("Posting a singular event instead of a BulkBusinessEvent since there was only a single event recorded"); |
| externalEventService.postEvent(recordedBusinessEvents.get(0)); |
| } else { |
| log.debug("Posting the BulkBusinessEvent for the recorded {} events", recordedBusinessEvents.size()); |
| externalEventService.postEvent(new BulkBusinessEvent(recordedBusinessEvents)); |
| } |
| } |
| } |
| } finally { |
| recordedEvents.remove(); |
| } |
| } |
| |
| @Override |
| public void resetEventRecording() { |
| eventRecordingEnabled.set(false); |
| recordedEvents.remove(); |
| } |
| } |