Merge "Issue #2450 - Send notifications to Notification Center when subscriptions expire. peer review comments" into development

Former-commit-id: e52828f36303bd39521381149b0f585b6eb38439
This commit is contained in:
Richard Peter 2013-11-06 14:02:51 -06:00 committed by Gerrit Code Review
commit d9c878bc51
8 changed files with 491 additions and 24 deletions

View file

@ -234,7 +234,7 @@
<!-- Registry production mode -->
<include>ebxml.*\.xml</include>
<includeMode>dataDeliveryTemplate</includeMode>
<include>adhoc-datadelivery-wfo.xml</include>
<include>datadelivery-wfo-cron.xml</include>
<exclude>.*datadelivery-ncf.*</exclude>
<exclude>harvester-*</exclude>
<exclude>crawler-*</exclude>

View file

@ -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

View file

@ -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

View file

@ -1,22 +0,0 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<!-- Cleans up old Adhoc subscriptions in the registry when the data for
them expires This only runs within the WFO registry mode (Adhoc subscriptions
are only allowed there) -->
<bean id="AdhocSubscriptionCleaner"
class="com.raytheon.uf.edex.datadelivery.retrieval.adhoc.AdhocSubscriptionCleaner" />
<camelContext id="adhocsubscription-cleanup" xmlns="http://camel.apache.org/schema/spring"
errorHandlerRef="errorHandler">
<endpoint id="processAdhocSubscriptions"
uri="clusteredquartz://registry/processAdhocSubscriptions/?cron=${adhocsubscription-process.cron}" />
<route id="processSubscriptionsFromQuartz">
<from uri="processAdhocSubscriptions" />
<bean ref="AdhocSubscriptionCleaner" method="processSubscriptions" />
</route>
</camelContext>
</beans>

View file

@ -0,0 +1,46 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<!-- Cleans up old Adhoc subscriptions in the registry when the data for
them expires This only runs within the WFO registry mode (Adhoc subscriptions
are only allowed there) -->
<bean id="AdhocSubscriptionCleaner"
class="com.raytheon.uf.edex.datadelivery.retrieval.adhoc.AdhocSubscriptionCleaner" />
<!-- Subscription check bean. Functions the check subscriptions should be placed in this file. -->
<bean id="subscriptionChecker" class="com.raytheon.uf.edex.datadelivery.retrieval.util.SubscriptionChecker">
<constructor-arg type="java.lang.String" value="jms-generic:topic:notify.msg" />
<property name="notificationDao" ref="notificationDao" />
</bean>
<camelContext id="wfo-cron" xmlns="http://camel.apache.org/schema/spring"
errorHandlerRef="errorHandler">
<endpoint id="processAdhocSubscriptions"
uri="quartz://registry/processAdhocSubscriptions/?cron=${adhocsubscription-process.cron}" />
<endpoint id="checkSubscriptions"
uri="quartz://registry/checkExpiredSubscriptions/?cron=${checkExpiredSubscription.cron}" />
<endpoint id="checkEndingSubscriptions"
uri="quartz://registry/checkEndingSubscriptions/?cron=${checkEndingSubscription.cron}" />
<route id="processSubscriptionsFromQuartz">
<from uri="processAdhocSubscriptions" />
<bean ref="AdhocSubscriptionCleaner" method="processSubscriptions" />
</route>
<route id="subscriptionEndingCheckFromQuartz">
<from uri="checkEndingSubscriptions" />
<bean ref="subscriptionChecker" method="activePeriodEndCheck" />
</route>
<route id="subscriptionCheckFromQuartz">
<from uri="checkSubscriptions" />
<bean ref="subscriptionChecker" method="expirationCheck" />
</route>
</camelContext>
</beans>

View file

@ -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
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+*+*+?

View file

@ -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.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 29, 2013 2450 mpduff Initial creation
*
* </pre>
*
* @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<Subscription> subList = null;
try {
subList = handler.getAll();
List<Subscription> 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<Subscription> subList = null;
try {
subList = handler.getAll();
List<Subscription> 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<Subscription> getNewlyExpiredSubscriptions(List<Subscription> subList,
long expiredPeriod) {
List<Subscription> expired = new ArrayList<Subscription>();
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<Subscription> getSubscriptionsNearingEndOfActivePeriod(
List<Subscription> subList) {
List<Subscription> endingList = new ArrayList<Subscription>();
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;
}
}

View file

@ -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}.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 29, 2013 2450 mpduff Initial creation
*
* </pre>
*
* @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<Subscription> subList = new ArrayList<Subscription>();
subList.add(new SubscriptionBuilder().withSubscriptionEnd(
januaryFirstTwelveZ).build());
SubscriptionChecker checker = new SubscriptionChecker();
List<Subscription> 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<Subscription> subList = new ArrayList<Subscription>();
subList.add(new SubscriptionBuilder().withSubscriptionEnd(
januaryFirstTwelveZ).build());
SubscriptionChecker checker = new SubscriptionChecker();
List<Subscription> expired = checker.getNewlyExpiredSubscriptions(
subList, TimeUtil.MILLIS_PER_HOUR);
assertTrue("Expired subscription marked as Not Expired",
!expired.isEmpty());
}
@Test
public void checkNonEndingSubscriptionIsNotFlaggedAsExpired() {
List<Subscription> subList = new ArrayList<Subscription>();
subList.add(new SubscriptionBuilder().withSubscriptionEnd(null).build());
SubscriptionChecker checker = new SubscriptionChecker();
List<Subscription> expired = checker.getNewlyExpiredSubscriptions(
subList, TimeUtil.MILLIS_PER_HOUR);
assertTrue("Non-expired subscription marked as expired",
expired.isEmpty());
}
@Test
public void checkSubsNearingEndOfActivePeriodAreFlaggedAsSuch() {
List<Subscription> subList = new ArrayList<Subscription>();
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<Subscription> subs = checker
.getSubscriptionsNearingEndOfActivePeriod(subList);
assertTrue("Sub nearing end of active period not flagged",
!subs.isEmpty());
}
@Test
public void checkSubsPastEndOfActivePeriodAreNotFlagged() {
List<Subscription> subList = new ArrayList<Subscription>();
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<Subscription> subs = checker
.getSubscriptionsNearingEndOfActivePeriod(subList);
assertTrue("Sub past end of active period flagged", subs.isEmpty());
}
@Test
public void checkSubsWithNoActivePeriodAreNotFlagged() {
List<Subscription> subList = new ArrayList<Subscription>();
subList.add(new SubscriptionBuilder().withActivePeriodEnd(null).build());
SubscriptionChecker checker = new SubscriptionChecker();
List<Subscription> subs = checker
.getSubscriptionsNearingEndOfActivePeriod(subList);
assertTrue("Sub past end of active period flagged", subs.isEmpty());
}
}