public BalanceUnderMinimumEventListener(final SupportedListener listener, final AbstractTargetHandler handler) { super(listener, handler); this.minimumBalance = handler.getListenerSpecificIntProperty(SupportedListener.BALANCE_UNDER_MINIMUM, "minimumBalance", 200); }
private boolean shouldNotify(final SupportedListener listener, final SessionInfo sessionInfo) { final boolean global = allowGlobal(listener, sessionInfo); return global && getSpecificCounter(sessionInfo, listener).allow(); }
boolean isEnabled(final SupportedListener listener) { final boolean noLongerDelinquentEnabled = listener == SupportedListener.LOAN_NO_LONGER_DELINQUENT && enableNoLongerDelinquentNotifications(); if (noLongerDelinquentEnabled || listener == SupportedListener.TESTING) { // testing is always enabled so that notification testing in the installer has something to work with return true; } else { return isEnabledInSettings(listener); } }
private boolean isEnabledInSettings(final SupportedListener listener) { final String propName = getCompositePropertyName(listener, "enabled"); return this.isEnabledInSettings() && config.readBoolean(target, propName, false); }
@Test void check() throws Exception { final AbstractTargetHandler h = mock(AbstractTargetHandler.class); doNothing().when(h).offer(any()); when(h.getTarget()).thenReturn(Target.EMAIL); when(h.getListenerSpecificIntProperty(eq(SupportedListener.BALANCE_ON_TARGET), eq("targetBalance"), anyInt())) .thenAnswer(i -> i.getArgument(2)); final PortfolioOverview p = mockPortfolioOverview(1000); final ExecutionStartedEvent evt = new TestingExecutionStartedEvent(p); final AbstractListener<ExecutionStartedEvent> l = new BalanceOnTargetEventListener(SupportedListener.BALANCE_ON_TARGET, h); l.handle(evt, SESSION_INFO); // balance change verify(h, times(1)).offer(any()); l.handle(evt, SESSION_INFO); // no change, no notification verify(h, times(1)).offer(any()); final PortfolioOverview p2 = mockPortfolioOverview(0); final ExecutionStartedEvent evt2 = new TestingExecutionStartedEvent(p2); l.handle(evt2, SESSION_INFO); // no change, no notification verify(h, times(1)).offer(any()); l.handle(evt, SESSION_INFO); // back over threshold, send notification verify(h, times(2)).offer(any()); } }
public void offer(final Submission s) throws Exception { LOGGER.trace("Received submission."); final SupportedListener listener = s.getSupportedListener(); final SessionInfo session = s.getSessionInfo(); if (!shouldNotify(listener, session)) { LOGGER.debug("Will not notify."); return; } final Map<String, Object> data = s.getData(); LOGGER.trace("Triggering."); send(session, s.getSubject(), s.getMessage(data), s.getFallbackMessage(data)); LOGGER.trace("Triggered."); getSpecificCounter(session, listener).increase(); getCounter(session).increase(); LOGGER.trace("Finished."); }
@Test void notifying() throws Exception { final AbstractTargetHandler h = AbstractListenerTest.getHandler(); final EventListener<LoanDelinquentEvent> l = new LoanDelinquentEventListener(SupportedListener.LOAN_DELINQUENT_10_PLUS, h); final EventListener<LoanNoLongerDelinquentEvent> l2 = new LoanNoLongerDelinquentEventListener(SupportedListener.LOAN_NO_LONGER_DELINQUENT, h); final LoanNoLongerDelinquentEvent evt = new MyLoanNoLongerDelinquentEvent(); l2.handle(evt, SESSION); verify(h, never()).send(any(), any(), any(), any()); // not delinquent before, not sending l.handle(new MyLoanDelinquent10DaysOrMoreEvent(), SESSION); verify(h).send(eq(SESSION), any(), any(), any()); l2.handle(evt, SESSION); verify(h, times(2)).send(eq(SESSION), any(), any(), any()); // delinquency now registered, send l2.handle(evt, SESSION); verify(h, times(2)).send(eq(SESSION), any(), any(), any()); // already unregistered, send }
@Override public final void handle(final T event, final SessionInfo sessionInfo) { try { if (!this.shouldNotify(event, sessionInfo)) { LOGGER.debug("Will not notify."); } else { // only do the heavy lifting in the handler, after the final send/no-send decision was made LOGGER.debug("Notifying {}.", event); handler.offer(createSubmission(event, sessionInfo)); } } catch (final Exception ex) { throw new IllegalStateException("Event processing failed.", ex); } finally { try { finish(event, sessionInfo); } catch (final Exception ex) { LOGGER.trace("Finisher failed.", ex); } finally { LOGGER.debug("Notified {}.", event); } } } }
private synchronized Counter getSpecificCounter(final SessionInfo sessionInfo, final SupportedListener listener) { return specificNotifications.computeIfAbsent(listener, key -> new HashMap<>(1)) .computeIfAbsent(sessionInfo, s -> new Counter(s, this.getClass().getSimpleName(), getHourlyLimit(listener))); }
private boolean allowGlobal(final SupportedListener listener, final SessionInfo sessionInfo) { final boolean override = listener.overrideGlobalGag(); return override || getCounter(sessionInfo).allow(); }
private OptionalInt getListenerSpecificIntProperty(final SupportedListener listener, final String property) { return config.readInt(target, getCompositePropertyName(listener, property)); }
@Test void check() throws Exception { final AbstractTargetHandler h = mock(AbstractTargetHandler.class); doNothing().when(h).offer(any()); when(h.getTarget()).thenReturn(Target.EMAIL); when(h.getListenerSpecificIntProperty(eq(SupportedListener.BALANCE_UNDER_MINIMUM), eq("minimumBalance"), anyInt())) .thenAnswer(i -> i.getArgument(2)); final PortfolioOverview p = mockPortfolioOverview(1); final ExecutionStartedEvent evt = new TestingExecutionStartedEvent(p); final AbstractListener<ExecutionStartedEvent> l = new BalanceUnderMinimumEventListener(SupportedListener.BALANCE_UNDER_MINIMUM, h); l.handle(evt, SESSION_INFO); // balance change verify(h, times(1)).offer(any()); l.handle(evt, SESSION_INFO); // no change, no notification verify(h, times(1)).offer(any()); final PortfolioOverview p2 = mockPortfolioOverview(1000); final ExecutionStartedEvent evt2 = new TestingExecutionStartedEvent(p2); l.handle(evt2, SESSION_INFO); // no change, no notification verify(h, times(1)).offer(any()); l.handle(evt, SESSION_INFO); // back over threshold, send notification verify(h, times(2)).offer(any()); } }
public void offer(final Submission s) throws Exception { LOGGER.trace("Received submission."); final SupportedListener listener = s.getSupportedListener(); final SessionInfo session = s.getSessionInfo(); if (!shouldNotify(listener, session)) { LOGGER.debug("Will not notify."); return; } final Map<String, Object> data = s.getData(); LOGGER.trace("Triggering."); send(session, s.getSubject(), s.getMessage(data), s.getFallbackMessage(data)); LOGGER.trace("Triggered."); getSpecificCounter(session, listener).increase(); getCounter(session).increase(); LOGGER.trace("Finished."); }
private static <T extends Event> void testTriggered(final AbstractTargetHandler h, final AbstractListener<T> listener, final T event) throws Exception { BalanceTracker.reset(SESSION_INFO); listener.handle(event, SESSION_INFO); verify(h, times(1)).send(eq(SESSION_INFO), notNull(), notNull(), notNull()); }
private boolean isEnabledInSettings(final SupportedListener listener) { final String propName = getCompositePropertyName(listener, "enabled"); return this.isEnabledInSettings() && config.readBoolean(target, propName, false); }
@Override public final void handle(final T event, final SessionInfo sessionInfo) { try { if (!this.shouldNotify(event, sessionInfo)) { logger.debug("Will not notify."); } else { // only do the heavy lifting in the handler, after the final send/no-send decision was made logger.debug("Notifying {}.", event); handler.offer(createSubmission(event, sessionInfo)); } } catch (final Exception ex) { throw new IllegalStateException("Event processing failed.", ex); } finally { try { finish(event, sessionInfo); } catch (final Exception ex) { logger.trace("Finisher failed.", ex); } finally { logger.debug("Notified {}.", event); } } } }
private synchronized Counter getSpecificCounter(final SessionInfo sessionInfo, final SupportedListener listener) { return specificNotifications.computeIfAbsent(listener, key -> new HashMap<>(1)) .computeIfAbsent(sessionInfo, s -> new Counter(s, this.getClass().getSimpleName(), getHourlyLimit(listener))); }
private boolean allowGlobal(final SupportedListener listener, final SessionInfo sessionInfo) { final boolean override = listener.overrideGlobalGag(); return override || getCounter(sessionInfo).allow(); }