From 4a2d37729158fe096f6861c38732a599c7a8ad44 Mon Sep 17 00:00:00 2001 From: Mike Duff Date: Wed, 30 Oct 2013 17:32:49 -0500 Subject: [PATCH] Issue #2450 - Send notifications to Notification Center when subscriptions expire. peer review comments Change-Id: I18eb322d73ee25f30cd1d032b403ea156bc63653 Former-commit-id: a3931125dae451adc27f6667865b705eee6421b1 [formerly 2a97da896df806e82281d197e2797cb91568541d] [formerly 00bb57b48102619f3a11bdc5d8765b67dcb7015e] [formerly f3d07a91859be1bffcec061e04416b0083fbf2c1 [formerly 00bb57b48102619f3a11bdc5d8765b67dcb7015e [formerly fc6fe49db0576c70e2068cd8fac125553806e4c0]]] Former-commit-id: f3d07a91859be1bffcec061e04416b0083fbf2c1 Former-commit-id: 34a98de0a9a5f176bd195b64d5d776dae48eef59 [formerly cc3846be0b9670658227f7b44e256343ccb5cdfb] Former-commit-id: ecaef4d63941fea23d375ebf51039517f9b2bbea --- edexOsgi/build.edex/esb/conf/modes.xml | 2 +- .../META-INF/MANIFEST.MF | 3 + .../META-INF/MANIFEST.MF | 1 + .../res/spring/adhoc-datadelivery-wfo.xml | 22 -- .../res/spring/datadelivery-wfo-cron.xml | 46 +++ ....uf.edex.datadelivery.retrieval.properties | 7 +- .../retrieval/util/SubscriptionChecker.java | 271 ++++++++++++++++++ .../util/SubscriptionCheckerTest.java | 163 +++++++++++ 8 files changed, 491 insertions(+), 24 deletions(-) delete mode 100644 edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/adhoc-datadelivery-wfo.xml create mode 100644 edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/datadelivery-wfo-cron.xml create mode 100644 edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/src/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionChecker.java create mode 100644 tests/unit/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionCheckerTest.java diff --git a/edexOsgi/build.edex/esb/conf/modes.xml b/edexOsgi/build.edex/esb/conf/modes.xml index ebcf64e368..e36452e92f 100644 --- a/edexOsgi/build.edex/esb/conf/modes.xml +++ b/edexOsgi/build.edex/esb/conf/modes.xml @@ -234,7 +234,7 @@ ebxml.*\.xml dataDeliveryTemplate - adhoc-datadelivery-wfo.xml + datadelivery-wfo-cron.xml .*datadelivery-ncf.* harvester-* crawler-* diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.event/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.edex.datadelivery.event/META-INF/MANIFEST.MF index b0703b550e..fd50098ee5 100644 --- a/edexOsgi/com.raytheon.uf.edex.datadelivery.event/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.edex.datadelivery.event/META-INF/MANIFEST.MF @@ -24,3 +24,6 @@ Require-Bundle: com.google.guava;bundle-version="1.0.0", com.raytheon.uf.common.util;bundle-version="1.12.1174", org.hibernate;bundle-version="1.0.0", com.raytheon.uf.common.dataplugin;bundle-version="1.12.1174" +Export-Package: com.raytheon.uf.edex.datadelivery.event.dao, + com.raytheon.uf.edex.datadelivery.event.handler, + com.raytheon.uf.edex.datadelivery.event.notification diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/META-INF/MANIFEST.MF index 18217e905a..8296daad7e 100644 --- a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/META-INF/MANIFEST.MF @@ -32,6 +32,7 @@ Require-Bundle: com.raytheon.uf.common.status;bundle-version="1.12.1174", com.raytheon.uf.common.stats;bundle-version="1.0.0", com.raytheon.uf.edex.decodertools;bundle-version="1.12.1174", com.raytheon.uf.edex.registry.ebxml;bundle-version="1.0.0", + com.raytheon.uf.edex.datadelivery.event;bundle-version="1.0.0", org.apache.http;bundle-version="4.1.2" Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/adhoc-datadelivery-wfo.xml b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/adhoc-datadelivery-wfo.xml deleted file mode 100644 index 3e64cff4a6..0000000000 --- a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/adhoc-datadelivery-wfo.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/datadelivery-wfo-cron.xml b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/datadelivery-wfo-cron.xml new file mode 100644 index 0000000000..4b97227ca8 --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/res/spring/datadelivery-wfo-cron.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/resources/com.raytheon.uf.edex.datadelivery.retrieval.properties b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/resources/com.raytheon.uf.edex.datadelivery.retrieval.properties index 5f3bc456f0..24e25148d9 100644 --- a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/resources/com.raytheon.uf.edex.datadelivery.retrieval.properties +++ b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/resources/com.raytheon.uf.edex.datadelivery.retrieval.properties @@ -10,4 +10,9 @@ sbn.retrieval.transfer.wmo.header=LZ{0}{1}9{2} KWBC {3,date,ddHH00}\r\r\n retrieval.task.frequency=1 MINUTES # How often to check for retrieved subscriptions to notify of # Valid units: [MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS] -subnotify.task.frequency=1 MINUTES \ No newline at end of file +subnotify.task.frequency=1 MINUTES + +# Cron for Subscription Expiration Checker - currently 2 minutes past the hour +checkExpiredSubscription.cron=0+2+*+*+*+? +# Cron for subscriptions nearing the end of their active period - currently once/day at 00:15Z +checkEndingSubscription.cron=0+15+0+*+*+? \ No newline at end of file diff --git a/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/src/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionChecker.java b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/src/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionChecker.java new file mode 100644 index 0000000000..698f38028c --- /dev/null +++ b/edexOsgi/com.raytheon.uf.edex.datadelivery.retrieval/src/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionChecker.java @@ -0,0 +1,271 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.datadelivery.retrieval.util; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.springframework.transaction.annotation.Transactional; + +import com.google.common.annotations.VisibleForTesting; +import com.raytheon.uf.common.datadelivery.event.notification.NotificationRecord; +import com.raytheon.uf.common.datadelivery.registry.Subscription; +import com.raytheon.uf.common.datadelivery.registry.handlers.DataDeliveryHandlers; +import com.raytheon.uf.common.datadelivery.registry.handlers.ISubscriptionHandler; +import com.raytheon.uf.common.registry.handler.RegistryHandlerException; +import com.raytheon.uf.common.serialization.SerializationException; +import com.raytheon.uf.common.serialization.SerializationUtil; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.uf.common.time.util.TimeUtil; +import com.raytheon.uf.edex.core.EDEXUtil; +import com.raytheon.uf.edex.core.EdexException; +import com.raytheon.uf.edex.datadelivery.event.notification.NotificationDao; + +/** + * A class to hold various subscription checks. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 29, 2013    2450    mpduff      Initial creation
+ * 
+ * 
+ * + * @author mpduff + * @version 1.0 + */ +@Transactional +public class SubscriptionChecker { + private static final IUFStatusHandler statusHandler = UFStatus + .getHandler(SubscriptionChecker.class); + + /** Notification DAO for sending/storing notification messages */ + private NotificationDao notificationDao; + + /** End point URI */ + private String uri; + + /** + * Constructor. + */ + public SubscriptionChecker() { + + } + + /** + * Constructor with notification URI parameter. + * + * @param uri + * Notification uri + */ + public SubscriptionChecker(String uri) { + this.uri = uri; + } + + /** + * Check for subscriptions that have expired in the last hour. + */ + public void expirationCheck() { + statusHandler.info("Starting expiration check..."); + ISubscriptionHandler handler = DataDeliveryHandlers + .getSubscriptionHandler(); + List subList = null; + try { + subList = handler.getAll(); + + List expiredList = getNewlyExpiredSubscriptions( + subList, TimeUtil.MILLIS_PER_HOUR); + + if (!expiredList.isEmpty()) { + StringBuilder buffer = new StringBuilder( + "Expired Subscriptions: "); + + for (Subscription sub : expiredList) { + buffer.append(sub.getName()).append(" "); + } + + NotificationRecord record = new NotificationRecord(); + record.setCategory("Subscription"); + record.setDate(TimeUtil.newGmtCalendar()); + record.setMessage(buffer.toString()); + record.setPriority(3); + record.setUsername("System"); + + // Send the notification + storeAndSend(record, uri); + statusHandler.debug(buffer.toString()); + } + } catch (RegistryHandlerException e) { + statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); + } + } + + /** + * Check for subscriptions that will become inactive in the next 24 hours. + */ + public void activePeriodEndCheck() { + statusHandler.info("Starting active period check..."); + ISubscriptionHandler handler = DataDeliveryHandlers + .getSubscriptionHandler(); + List subList = null; + try { + subList = handler.getAll(); + + List stoppingList = getSubscriptionsNearingEndOfActivePeriod(subList); + + if (!stoppingList.isEmpty()) { + StringBuilder buffer = new StringBuilder( + "Subscriptions becoming inactive in Next 24 hrs: "); + + for (Subscription sub : stoppingList) { + buffer.append(sub.getName()).append(" "); + } + + NotificationRecord record = new NotificationRecord(); + record.setCategory("Subscription"); + record.setDate(TimeUtil.newGmtCalendar()); + record.setMessage(buffer.toString()); + record.setPriority(3); + record.setUsername("System"); + + // Send the notification + storeAndSend(record, uri); + statusHandler.debug(buffer.toString()); + } + } catch (RegistryHandlerException e) { + statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); + } + + } + + /** + * Check for subscriptions that have expired within a time period defined by + * expiredPeriod. + * + * If expiredPeriod is one hour then a subscription will be flagged as + * expired if it expired within the last hour + * + * @param subList + * List of subscriptions to check + * @param expiredPeriod + * amount of time for expiration, in milliseconds + * + * @return List of subscriptions marked as expired + */ + @VisibleForTesting + List getNewlyExpiredSubscriptions(List subList, + long expiredPeriod) { + List expired = new ArrayList(); + statusHandler.debug("Checking for expired subs"); + if (subList != null && !subList.isEmpty()) { + Date now = TimeUtil.newGmtCalendar().getTime(); + for (Subscription sub : subList) { + Date end = sub.getSubscriptionEnd(); + if (end == null) { + continue; + } else if (end.before(now)) { + if (now.getTime() - end.getTime() < expiredPeriod) { + expired.add(sub); + } + } + } + } + + return expired; + } + + /** + * Check for subscriptions that are nearing the end of their active period. + * + * @param subList + * List of subscriptions to check + * @return List of subscriptions that are nearing the end of their active + * period + */ + @VisibleForTesting + List getSubscriptionsNearingEndOfActivePeriod( + List subList) { + List endingList = new ArrayList(); + statusHandler.debug("Checking for subs nearing end of active period"); + + if (subList != null && !subList.isEmpty()) { + Calendar now = TimeUtil.newGmtCalendar(); + Calendar cal = TimeUtil.newGmtCalendar(); + + for (Subscription sub : subList) { + Date end = sub.getActivePeriodEnd(); + if (end == null) { + continue; + } + + cal.setTimeInMillis(end.getTime()); + cal.set(Calendar.YEAR, now.get(Calendar.YEAR)); + if (now.before(cal) + && (cal.getTimeInMillis() - now.getTimeInMillis() < TimeUtil.MILLIS_PER_DAY)) { + endingList.add(sub); + } + } + } + + return endingList; + } + + /** + * Stores the record in the notification table. + * + * @param record + */ + private void storeAndSend(NotificationRecord record, String endpoint) { + if (record != null) { + notificationDao.createOrUpdate(record); + try { + byte[] bytes = SerializationUtil.transformToThrift(record); + EDEXUtil.getMessageProducer().sendAsyncUri(endpoint, bytes); + } catch (EdexException e) { + statusHandler.error("Error sending record to " + endpoint, e); + } catch (SerializationException e) { + statusHandler.error("Error serializing record to " + endpoint, + e); + } + } + } + + /** + * @return the notificationDao + */ + public NotificationDao getNotificationDao() { + return notificationDao; + } + + /** + * @param notificationDao + * the notificationDao to set + */ + public void setNotificationDao(NotificationDao notificationDao) { + this.notificationDao = notificationDao; + } +} diff --git a/tests/unit/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionCheckerTest.java b/tests/unit/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionCheckerTest.java new file mode 100644 index 0000000000..4cc8ae83af --- /dev/null +++ b/tests/unit/com/raytheon/uf/edex/datadelivery/retrieval/util/SubscriptionCheckerTest.java @@ -0,0 +1,163 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.uf.edex.datadelivery.retrieval.util; + +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.junit.Test; + +import com.raytheon.uf.common.datadelivery.registry.Subscription; +import com.raytheon.uf.common.datadelivery.registry.SubscriptionBuilder; +import com.raytheon.uf.common.time.CalendarBuilder; +import com.raytheon.uf.common.time.util.TimeUtil; + +/** + * JUnit test class for {@link SubscriptionChecker}. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Oct 29, 2013   2450     mpduff     Initial creation
+ * 
+ * 
+ * + * @author mpduff + * @version 1.0 + */ + +public class SubscriptionCheckerTest { + + @Test + public void checkNonExpiredSubscriptionIsNotFlaggedAsExpired() { + CalendarBuilder calBuilder = new CalendarBuilder(); + + Calendar cal = calBuilder.build(); + cal.add(Calendar.MONTH, 1); + final Date januaryFirstTwelveZ = cal.getTime(); + + // Create subscription with an end date one month from now so it isn't + // expired + List subList = new ArrayList(); + + subList.add(new SubscriptionBuilder().withSubscriptionEnd( + januaryFirstTwelveZ).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List expired = checker.getNewlyExpiredSubscriptions( + subList, TimeUtil.MILLIS_PER_HOUR); + + assertTrue("Non-expired subscription marked as expired", + expired.isEmpty()); + } + + @Test + public void checkExpiredSubscriptionIsFlaggedAsExpired() { + CalendarBuilder calBuilder = new CalendarBuilder(); + + Calendar cal = calBuilder.build(); + cal.add(Calendar.MINUTE, -10); + final Date januaryFirstTwelveZ = cal.getTime(); + + // Create a subscription with an end date ten minutes in the past so it + // is expired + List subList = new ArrayList(); + + subList.add(new SubscriptionBuilder().withSubscriptionEnd( + januaryFirstTwelveZ).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List expired = checker.getNewlyExpiredSubscriptions( + subList, TimeUtil.MILLIS_PER_HOUR); + + assertTrue("Expired subscription marked as Not Expired", + !expired.isEmpty()); + } + + @Test + public void checkNonEndingSubscriptionIsNotFlaggedAsExpired() { + List subList = new ArrayList(); + + subList.add(new SubscriptionBuilder().withSubscriptionEnd(null).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List expired = checker.getNewlyExpiredSubscriptions( + subList, TimeUtil.MILLIS_PER_HOUR); + + assertTrue("Non-expired subscription marked as expired", + expired.isEmpty()); + } + + @Test + public void checkSubsNearingEndOfActivePeriodAreFlaggedAsSuch() { + List subList = new ArrayList(); + + CalendarBuilder calBuilder = new CalendarBuilder(); + + Calendar cal = calBuilder.build(); + cal.add(Calendar.HOUR, 5); + + subList.add(new SubscriptionBuilder() + .withActivePeriodEnd(cal.getTime()).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List subs = checker + .getSubscriptionsNearingEndOfActivePeriod(subList); + assertTrue("Sub nearing end of active period not flagged", + !subs.isEmpty()); + } + + @Test + public void checkSubsPastEndOfActivePeriodAreNotFlagged() { + List subList = new ArrayList(); + + CalendarBuilder calBuilder = new CalendarBuilder(); + + Calendar cal = calBuilder.build(); + cal.add(Calendar.DAY_OF_MONTH, -5); + + subList.add(new SubscriptionBuilder() + .withActivePeriodEnd(cal.getTime()).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List subs = checker + .getSubscriptionsNearingEndOfActivePeriod(subList); + assertTrue("Sub past end of active period flagged", subs.isEmpty()); + } + + @Test + public void checkSubsWithNoActivePeriodAreNotFlagged() { + List subList = new ArrayList(); + + subList.add(new SubscriptionBuilder().withActivePeriodEnd(null).build()); + + SubscriptionChecker checker = new SubscriptionChecker(); + List subs = checker + .getSubscriptionsNearingEndOfActivePeriod(subList); + assertTrue("Sub past end of active period flagged", subs.isEmpty()); + } +}