package org.gcube.application.cms.notifications; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import org.bson.Document; import org.gcube.application.cms.implementations.utils.UserUtils; import org.gcube.application.cms.notifications.config.SubscribeNotificationEvent; import org.gcube.application.cms.notifications.manage.ManageDoActionNotification; import org.gcube.application.cms.notifications.substitutor.NMessagesPlaceholdersSubstitutorUtil; import org.gcube.application.cms.plugins.EventListenerPluginInterface; import org.gcube.application.cms.plugins.events.EventListener; import org.gcube.application.cms.plugins.events.EventManager; import org.gcube.application.cms.plugins.events.EventManager.Event; import org.gcube.application.cms.plugins.events.ItemObserved; import org.gcube.application.cms.plugins.faults.InitializationException; import org.gcube.application.cms.plugins.faults.InvalidProfileException; import org.gcube.application.cms.plugins.faults.MaterializationException; import org.gcube.application.cms.plugins.faults.ShutDownException; import org.gcube.application.cms.plugins.implementations.AbstractPlugin; import org.gcube.application.cms.plugins.reports.InitializationReport; import org.gcube.application.cms.plugins.reports.Report; import org.gcube.application.cms.plugins.reports.Report.Status; import org.gcube.application.cms.serialization.Serialization; import org.gcube.application.geoportal.common.model.document.Project; import org.gcube.application.geoportal.common.model.plugins.PluginDescriptor; import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor; import com.vdurmont.semver4j.Semver; import lombok.Synchronized; import lombok.extern.slf4j.Slf4j; /** * The Class NotificationsPlugin. * * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it * * Jan 30, 2024 */ @Slf4j public class NotificationsPlugin extends AbstractPlugin implements EventListenerPluginInterface { public static final String SUBSCRIBE_NOTIFICATIONS_CONFIG = "subscribeNotifications"; public static final String PLUGIN_ID = "Notifications-Plugin"; public static final String PLUGIN_TYPE = "EventListener"; private static final String CONFIG_ENABLED = "enabled"; private static final String CONFIG_LINK_TO_NOTIFICATIONS_MESSAGES = "link_to_notifications_messages"; public static final PluginDescriptor DESCRIPTOR = new PluginDescriptor(PLUGIN_ID, PLUGIN_TYPE); static { DESCRIPTOR.setVersion(new Semver("1.0.0")); DESCRIPTOR.setDescription("Manage the notification from Geoportal engine"); } // This is a map "" protected Map> notificationEventsBindingMap = null; /** * Inits the. * * @return the initialization report * @throws InitializationException the initialization exception */ @Override public InitializationReport init() throws InitializationException { log.debug("Called init"); InitializationReport report = null; try { // Creating all listeners EventListener> listenerCreated = new EventListener>() { @Override public void updated(ItemObserved observerd) { log.info("listenerCreated fired on item: {} ", observerd); boolean subscribed = checkIfSubscribedEvent(observerd); if (subscribed) { doAction(observerd); } } }; EventListener> listenerUpdated = new EventListener>() { @Override public void updated(ItemObserved observerd) { log.info("listenerUpdated fired on item: {} ", observerd); boolean subscribed = checkIfSubscribedEvent(observerd); if (subscribed) { doAction(observerd); } } }; EventListener> listenerDeleted = new EventListener>() { @Override public void updated(ItemObserved observerd) { log.info("listenerDeleted fired on item: {} ", observerd); boolean subscribed = checkIfSubscribedEvent(observerd); if (subscribed) { doAction(observerd); } } }; EventListener> listenerLCStepPerformed = new EventListener>() { @Override public void updated(ItemObserved observerd) { log.info("listenerLCStepPerformed fired on item: {} ", observerd); boolean subscribed = checkIfSubscribedEvent(observerd); if (subscribed) { doAction(observerd); } } }; // Subscribing all events EventManager eventMngInst = EventManager.getInstance(); eventMngInst.subscribe(Event.PROJECT_CREATED, listenerCreated); eventMngInst.subscribe(Event.PROJECT_UPDATED, listenerUpdated); eventMngInst.subscribe(Event.PROJECT_DELETED, listenerDeleted); eventMngInst.subscribe(Event.LIFECYCLE_STEP_PERFORMED, listenerLCStepPerformed); report = new InitializationReport(Status.OK, PLUGIN_ID + " init performed"); } catch (Exception e) { InitializationException exc = new InitializationException("Unable to initialize " + DESCRIPTOR.getId(), e); log.error("init error: {} ", exc); throw exc; } return report; } /** * Gets the descriptor. * * @return the descriptor */ @Override public PluginDescriptor getDescriptor() { return DESCRIPTOR; } /** * Inits the in context. * * @return the initialization report * @throws InitializationException the initialization exception */ @Override @Synchronized public InitializationReport initInContext() throws InitializationException { log.debug("Called initInContext"); InitializationReport report = new InitializationReport(); try { String context = UserUtils.getCurrent().getContext(); if (getNotificationBindingMapPerContext() == null) { log.info("Initializing in " + context); notificationEventsBindingMap.put(context, new HashMap()); } report.setStatus(Report.Status.OK); report.putMessage("Initialized " + DESCRIPTOR.getId() + " in the " + context); } catch (Exception e) { InitializationException exc = new InitializationException("Unable to initialize " + DESCRIPTOR.getId(), e); log.error("initInContext error: {} ", exc); throw exc; } return report; } /** * Check if subscribed event. * * @param observerd the observerd * @return true, if successful */ public boolean checkIfSubscribedEvent(ItemObserved observerd) { log.info("Checking if {} is an subscribed event", observerd.getEvent()); try { NotificationEventsSubscribedConfig eventsSub = readNotificationsSubscribedFromConfigurationInTheUCD( observerd.getUseCaseDescriptor()); if (eventsSub != null) { List listEvents = eventsSub.getListNotificationEventSubscribed(); log.info("List events is {}", listEvents); if(listEvents==null) { log.info("no event subscribed, returning false"); return false; } if (listEvents.stream().anyMatch(sne -> sne.getEvent().equals(observerd.getEvent()))) { log.info("the event {} is subscribed from config ", observerd.getEvent()); return true; } log.info("the event {} is not subscribed from config ", observerd.getEvent()); return false; } log.info("the event {} is not subscribed from config ", observerd.getEvent()); return false; } catch (Exception e) { log.error("Exception, Error on checking subscribed events", e); return false; } } /** * Gets the notification binding map per context. * * @return the notification binding map per context */ protected Map getNotificationBindingMapPerContext() { String context = UserUtils.getCurrent().getContext(); log.debug("Getting {} from cache map for context {}", NotificationsPlugin.PLUGIN_ID, context); if (notificationEventsBindingMap == null) { notificationEventsBindingMap = new LinkedHashMap>(); } // read notification events binding subscribed in the context Map map = notificationEventsBindingMap.get(context); return map == null ? new LinkedHashMap() : map; } /** * Sets the notification binding map per context. * * @param context the context * @param ucd the ucd * @param notification the notification */ private void setNotificationBindingMapPerContext(String context, UseCaseDescriptor ucd, NotificationEventsSubscribedConfig notification) { Map mapNotificationConfig = notificationEventsBindingMap .get(context); if (mapNotificationConfig == null) { mapNotificationConfig = new LinkedHashMap(); } mapNotificationConfig.put(ucd.getId(), notification); notificationEventsBindingMap.put(context, mapNotificationConfig); } /** * Read notifications subscribed from configuration in the UCD. * * @param useCaseDescriptor the use case descriptor * @return the notification events subscribed config * @throws Exception the exception */ public NotificationEventsSubscribedConfig readNotificationsSubscribedFromConfigurationInTheUCD( UseCaseDescriptor useCaseDescriptor) throws Exception { log.debug("Reading subscribed events from UCD"); NotificationEventsSubscribedConfig notificationMapPerContext = null; if (useCaseDescriptor == null) throw new Exception("Error reading UCD null found"); try { String context = UserUtils.getCurrent().getContext(); Map mapPerContext = getNotificationBindingMapPerContext(); notificationMapPerContext = mapPerContext.get(useCaseDescriptor.getId()); if (notificationMapPerContext == null) { notificationMapPerContext = new NotificationEventsSubscribedConfig(); setNotificationBindingMapPerContext(context, useCaseDescriptor, notificationMapPerContext); } List listNotificationEventsSubscribedPerUCD = notificationMapPerContext .getListNotificationEventSubscribed(); if (listNotificationEventsSubscribedPerUCD == null) { listNotificationEventsSubscribedPerUCD = new ArrayList(); Document profileConfiguration = getConfigurationFromProfile(useCaseDescriptor).getConfiguration(); log.debug("UseCaseDescriptor Configuration is {} ", profileConfiguration); // JSONPathWrapper schemaNavigator = new // JSONPathWrapper(useCaseDescriptor.getSchema().toJson()); if (profileConfiguration != null) { for (Object fsConfigObj : profileConfiguration.get(SUBSCRIBE_NOTIFICATIONS_CONFIG, List.class)) { log.debug("Managing {}", fsConfigObj); SubscribeNotificationEvent fsConfig = Serialization.convert(fsConfigObj, SubscribeNotificationEvent.class); log.debug("Converted config {}", fsConfig); try { Event theEventSubsribed = Serialization.convert(fsConfig.getEvent(), Event.class); if (theEventSubsribed == null) throw new MaterializationException( "Invalid Field Definition path in configuration [NO MATCH] : " + theEventSubsribed); } catch (Exception e) { log.error("MaterializationException: ", e); throw new MaterializationException( "Invalid Event Definition path in configuration [NO MATCH Event] : " + Event.values()); } listNotificationEventsSubscribedPerUCD.add(fsConfig); } notificationMapPerContext.setUcd(useCaseDescriptor); notificationMapPerContext .setListNotificationEventSubscribed(listNotificationEventsSubscribedPerUCD); String linkToMessages = profileConfiguration.get(CONFIG_LINK_TO_NOTIFICATIONS_MESSAGES, String.class); Boolean enabledNotifications = profileConfiguration.get(CONFIG_ENABLED, Boolean.class); notificationMapPerContext.setLinkToNotificationsMessages(linkToMessages); notificationMapPerContext.setEnabled(enabledNotifications); } setNotificationBindingMapPerContext(context, useCaseDescriptor, notificationMapPerContext); log.info("Events subscribed read from config {} ", notificationMapPerContext); } } catch (InvalidProfileException e) { log.warn("Unable to read configuration for {} in the UCD {}", NotificationsPlugin.PLUGIN_ID, useCaseDescriptor.getId()); } catch (Exception e) { log.error("Unable to read configuration for " + NotificationsPlugin.PLUGIN_ID, e); } catch (Throwable t) { log.error("Exception, Unable to read configuration ", t); } return notificationMapPerContext; } /** * Shutdown. * * @throws ShutDownException the shut down exception */ @Override public void shutdown() throws ShutDownException { // TODO Auto-generated method stub } /** * Do action. * * @param itemObserved the item observed */ @Override public void doAction(ItemObserved itemObserved) { log.debug("doAction called..."); boolean isSubscribedEvent = checkIfSubscribedEvent(itemObserved); log.info("Is the event {} subscribed in notification plugin configured in the UCD: {} ", itemObserved.getEvent(), isSubscribedEvent); if (isSubscribedEvent) { // Map (UCD_ID, Notification) Map notificationMapPerContext = getNotificationBindingMapPerContext(); NotificationEventsSubscribedConfig subscribedConfig = notificationMapPerContext .get(itemObserved.getUCD_Id()); boolean subscribeNotificationsEnabled = subscribedConfig.isEnabled(); log.info("subscribeNotifications enabled: {}", subscribeNotificationsEnabled); if (subscribeNotificationsEnabled) { String linkToFileWithMessages = subscribedConfig.getLinkToNotificationsMessages(); List list = subscribedConfig.getListNotificationEventSubscribed(); // Filtering list of SubscribeNotificationEvent for itemObserved Event List filterList = list.stream() .filter(sne -> sne.getEvent().equals(itemObserved.getEvent())).collect(Collectors.toList()); log.debug(linkToFileWithMessages); NMessagesPlaceholdersSubstitutorUtil nMPlaceholdersSUtil = null; try { nMPlaceholdersSUtil = new NMessagesPlaceholdersSubstitutorUtil(linkToFileWithMessages); } catch (IOException e) { log.error("Error instancing the {}. Please check the URL {}. Returning!!", NMessagesPlaceholdersSubstitutorUtil.class.getSimpleName(), linkToFileWithMessages); return; } ManageDoActionNotification mdoact = new ManageDoActionNotification(itemObserved, nMPlaceholdersSUtil, filterList); mdoact.manage(); } } } }