Preparations for mail notifications

This commit is contained in:
Steffen Schaefer committed 2016-07-12 16:53:48 +02:00
1 parent 4be2211bab
commit 579f83fade
17 files changed
+307 -47

No files matched your search

+8 -7
View File
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="output" path="bin"/>
</classpath>
@@ -7,6 +7,8 @@ Bundle-Activator: com.sap.sailing.server.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.server.gateway.deserialization,
javax.mail;version="1.4.0",
javax.mail.internet;version="1.4.0",
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
@@ -33,7 +35,8 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.security,
org.apache.shiro.web;bundle-version="1.2.2",
org.apache.shiro.core;bundle-version="1.2.2",
javax.servlet;bundle-version="3.1.0"
javax.servlet;bundle-version="3.1.0",
com.sap.sse.mail
Bundle-ClassPath: .
Export-Package: com.sap.sailing.server,
com.sap.sailing.server.impl;
+2 -1
View File
@@ -1,4 +1,5 @@
source.. = src/
source.. = src/,\
resources/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,6 @@
boatClassResultsSubject=New results for boat class {0} in regatta {1} of event {2}
boatClassResultsBody=New results for boat class {0} in regatta {1} of event {2} are available. See: {3}
boatClassUpcomingRaceSubject=A race for boat class {0} in regatta {1} of event {2} will start soon
boatClassUpcomingRaceBody=A race for boat class {0} in regatta {1} of event {2} will start at {4}. See: {3}
competitorResultsSubject=New results for competitor {0} in regatta {1} of event {2}
competitorResultsBody=New results for competitor {0} in regatta {1} of event {2} are available. See: {3}
@@ -46,49 +46,56 @@ public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
private static final String CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME = "persistentcompetitors.clear";
private static ExtenderBundleTracker extenderBundleTracker;
private CachedOsgiTypeBasedServiceFinderFactory serviceFinderFactory;
private RacingEventServiceImpl racingEventService;
private final boolean clearPersistentCompetitors;
private Set<ServiceRegistration<?>> registrations = new HashSet<>();
private ObjectName mBeanName;
private ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService> masterDataImportClassLoaderServiceTracker;
private ServiceTracker<PolarDataService, PolarDataService> polarDataServiceTracker;
private OSGiBasedTrackedRegattaListener trackedRegattaListener;
public Activator() {
clearPersistentCompetitors = Boolean.valueOf(System.getProperty(CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME, ""+false));
logger.log(Level.INFO, "setting "+CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME+" to "+clearPersistentCompetitors);
clearPersistentCompetitors = Boolean
.valueOf(System.getProperty(CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME, "" + false));
logger.log(Level.INFO,
"setting " + CLEAR_PERSISTENT_COMPETITORS_PROPERTY_NAME + " to " + clearPersistentCompetitors);
// there is exactly one instance of the racingEventService in the whole server
}
public void start(BundleContext context) throws Exception {
extenderBundleTracker = new ExtenderBundleTracker(context);
extenderBundleTracker.open();
// TODO implement
// MailQueue mailQueue = new ExecutorMailQueue(ServiceTrackerFactory.createAndOpen(context, MailService.class));
// SailingNotificationService notificationService = new SailingNotificationServiceImpl(context, mailQueue);
trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context);
// At this point the OSGi resolver is used as device type service finder.
// In the case that we are not in an OSGi context (e.g. running a JUnit test instead),
// this code block is not run, and the test case can inject some other type of finder
// instead.
serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context);
racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, serviceFinderFactory, trackedRegattaListener);
racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, serviceFinderFactory,
trackedRegattaListener);
masterDataImportClassLoaderServiceTracker = new ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService>(
context, MasterDataImportClassLoaderService.class,
new MasterDataImportClassLoaderServiceTrackerCustomizer(context, racingEventService));
masterDataImportClassLoaderServiceTracker.open();
polarDataServiceTracker = new ServiceTracker<PolarDataService, PolarDataService>(context,
PolarDataService.class, new PolarDataServiceTrackerCustomizer(context, racingEventService));
polarDataServiceTracker.open();
@@ -96,41 +103,39 @@ public class Activator implements BundleActivator {
// register the racing service in the OSGi registry
racingEventService.setBundleContext(context);
final Dictionary<String, String> replicableServiceProperties = new Hashtable<>();
replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, racingEventService.getId().toString());
replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name,
racingEventService.getId().toString());
context.registerService(Replicable.class.getName(), racingEventService, replicableServiceProperties);
context.registerService(RacingEventService.class.getName(), racingEventService, null);
context.registerService(ClearStateTestSupport.class.getName(), racingEventService, null);
Dictionary<String, String> properties = new Hashtable<String, String>();
final GPSFixMongoHandlerImpl gpsFixMongoHandler = new GPSFixMongoHandlerImpl(racingEventService.getMongoObjectFactory(),
racingEventService.getDomainObjectFactory());
final GPSFixMongoHandlerImpl gpsFixMongoHandler = new GPSFixMongoHandlerImpl(
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory());
properties.put(TypeBasedServiceFinder.TYPE, GPSFixImpl.class.getName());
registrations.add(context.registerService(FixMongoHandler.class,
gpsFixMongoHandler, properties));
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
// legacy type name; some DBs may still contain fixes marked with this old package name:
properties.put(TypeBasedServiceFinder.TYPE, "com.sap.sailing.domain.tracking.impl.GPSFixImpl");
registrations.add(context.registerService(FixMongoHandler.class,
gpsFixMongoHandler, properties));
final GPSFixMovingMongoHandlerImpl gpsFixMovingMongoHandler = new GPSFixMovingMongoHandlerImpl(racingEventService.getMongoObjectFactory(),
racingEventService.getDomainObjectFactory());
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMongoHandler, properties));
final GPSFixMovingMongoHandlerImpl gpsFixMovingMongoHandler = new GPSFixMovingMongoHandlerImpl(
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory());
properties.put(TypeBasedServiceFinder.TYPE, GPSFixMovingImpl.class.getName());
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
// legacy type name; some DBs may still contain fixes marked with this old package name:
properties.put(TypeBasedServiceFinder.TYPE, "com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl");
registrations.add(context.registerService(FixMongoHandler.class, gpsFixMovingMongoHandler, properties));
properties.put(TypeBasedServiceFinder.TYPE, DoubleVectorFixImpl.class.getName());
registrations.add(context.registerService(FixMongoHandler.class, new DoubleVectorFixMongoHandlerImpl(racingEventService.getMongoObjectFactory(),
racingEventService.getDomainObjectFactory()), properties));
registrations.add(context.registerService(FixMongoHandler.class, new DoubleVectorFixMongoHandlerImpl(
racingEventService.getMongoObjectFactory(), racingEventService.getDomainObjectFactory()), properties));
registerPreferenceConvertersForUserStore(context);
// Add an MBean for the service to the JMX bean server:
RacingEventServiceMXBean mbean = new RacingEventServiceMXBeanImpl(racingEventService);
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mBeanName = new ObjectName("com.sap.sailing:type=RacingEventService");
mbs.registerMBean(mbean, mBeanName);
logger.log(Level.INFO, "Started "+context.getBundle().getSymbolicName()+". Character encoding: "+
Charset.defaultCharset());
logger.log(Level.INFO, "Started " + context.getBundle().getSymbolicName() + ". Character encoding: "
+ Charset.defaultCharset());
}
/**
@@ -171,14 +176,15 @@ public class Activator implements BundleActivator {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
mbs.unregisterMBean(mBeanName);
}
private class MasterDataImportClassLoaderServiceTrackerCustomizer implements
ServiceTrackerCustomizer<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService> {
private final BundleContext context;
private RacingEventServiceImpl racingEventService;
public MasterDataImportClassLoaderServiceTrackerCustomizer(BundleContext context, RacingEventServiceImpl racingEventService) {
public MasterDataImportClassLoaderServiceTrackerCustomizer(BundleContext context,
RacingEventServiceImpl racingEventService) {
this.context = context;
this.racingEventService = racingEventService;
}
@@ -193,7 +199,8 @@ public class Activator implements BundleActivator {
@Override
public void modifiedService(ServiceReference<MasterDataImportClassLoaderService> reference,
MasterDataImportClassLoaderService service) {}
MasterDataImportClassLoaderService service) {
}
@Override
public void removedService(ServiceReference<MasterDataImportClassLoaderService> reference,
@@ -203,8 +210,8 @@ public class Activator implements BundleActivator {
}
private class PolarDataServiceTrackerCustomizer implements
ServiceTrackerCustomizer<PolarDataService, PolarDataService> {
private class PolarDataServiceTrackerCustomizer
implements ServiceTrackerCustomizer<PolarDataService, PolarDataService> {
private final BundleContext context;
private RacingEventServiceImpl racingEventService;
@@ -231,5 +238,5 @@ public class Activator implements BundleActivator {
}
}
}
@@ -0,0 +1,16 @@
package com.sap.sailing.server.notification;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.common.TimePoint;
public interface SailingNotificationService {
void notifyUserOnBoatClassResults(BoatClass boatClass, String eventName, String regattaDisplayName, String link);
void notifyUserOnBoatClassUpcomingRace(BoatClass boatClass, String eventName, String regattaDisplayName,
TimePoint when, String link);
void notifyUserOnCompetitorResults(Competitor competitor, String eventName, String regattaDisplayName, String link);
}
@@ -0,0 +1,50 @@
package com.sap.sailing.server.notification.impl;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import com.sap.sse.mail.MailService;
import com.sap.sse.mail.queue.MailNotification;
import com.sap.sse.security.PreferenceObjectBasedNotificationSet;
public abstract class NotificationSetNotification<T> implements MailNotification {
private static final Logger logger = Logger.getLogger(NotificationSetNotification.class.getName());
private final T objectToNotifyAbout;
private final PreferenceObjectBasedNotificationSet<?, T> associatedNotificationSet;
public NotificationSetNotification(T objectToNotifyAbout, PreferenceObjectBasedNotificationSet<?, T> associatedNotificationSet) {
this.objectToNotifyAbout = objectToNotifyAbout;
this.associatedNotificationSet = associatedNotificationSet;
}
@Override
public void sendNotifications(final MailService mailService) {
// TODO idea (c) by Axel Uhl: In case of performance/mail queue problems: group mail addresses by locale and
// send batches with the actual mail addresses as bcc?
associatedNotificationSet.forUsersWithVerifiedEmailMappedTo(objectToNotifyAbout, (user) -> {
// TODO user defined locale
Locale locale = Locale.ENGLISH;
Multipart multipart = new MimeMultipart();
BodyPart bodyPart = new MimeBodyPart();
try {
bodyPart.setContent(constructBody(objectToNotifyAbout, locale), "text/html");
multipart.addBodyPart(bodyPart);
mailService.sendMail(user.getEmail(), constructSubject(objectToNotifyAbout, locale), multipart);
} catch (Exception e) {
logger.log(Level.SEVERE,
"Could not send mail notification for \"" + objectToNotifyAbout + "\" to user \" + user + \"", e);
}
});
}
protected abstract String constructSubject(T objectToNotifyAbout, Locale locale);
protected abstract String constructBody(T objectToNotifyAbout, Locale locale);
}
@@ -0,0 +1,121 @@
package com.sap.sailing.server.notification.impl;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import org.osgi.framework.BundleContext;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.server.impl.preferences.BoatClassResultsNotificationSet;
import com.sap.sailing.server.impl.preferences.BoatClassUpcomingRaceNotificationSet;
import com.sap.sailing.server.impl.preferences.CompetitorResultsNotificationSet;
import com.sap.sailing.server.notification.SailingNotificationService;
import com.sap.sse.common.Stoppable;
import com.sap.sse.common.TimePoint;
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
import com.sap.sse.mail.queue.MailQueue;
public class SailingNotificationServiceImpl implements Stoppable, SailingNotificationService {
private static final String STRING_MESSAGES_BASE_NAME = "stringmessages/StringMessages";
private final Set<Stoppable> toStop = new HashSet<>();
private final MailQueue mailQueue;
private final ResourceBundleStringMessagesImpl messages;
private final BoatClassResultsNotificationSet boatClassResults;
private final BoatClassUpcomingRaceNotificationSet boatClassUpcomingRace;
private final CompetitorResultsNotificationSet competitorResults;
public SailingNotificationServiceImpl(BundleContext bundleContext, MailQueue mailQueue) {
this.mailQueue = mailQueue;
this.messages = new ResourceBundleStringMessagesImpl(STRING_MESSAGES_BASE_NAME,
this.getClass().getClassLoader());
toStop.add(boatClassResults = new BoatClassResultsNotificationSet(bundleContext));
toStop.add(boatClassUpcomingRace = new BoatClassUpcomingRaceNotificationSet(bundleContext));
toStop.add(competitorResults = new CompetitorResultsNotificationSet(bundleContext));
}
@Override
public void stop() {
toStop.forEach(Stoppable::stop);
}
/*
* (non-Javadoc)
*
* @see com.sap.sailing.server.notification.SailingNotificationService#notifyUserOnBoatClassResults(com.sap.sailing.
* domain.base.BoatClass, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void notifyUserOnBoatClassResults(BoatClass boatClass, String eventName, String regattaDisplayName,
String link) {
mailQueue.addNotification(new NotificationSetNotification<BoatClass>(boatClass, boatClassResults) {
@Override
protected String constructSubject(BoatClass objectToNotifyAbout, Locale locale) {
return messages.get(locale, "boatClassResultsSubject", boatClass.getDisplayName(), regattaDisplayName,
eventName);
}
@Override
protected String constructBody(BoatClass objectToNotifyAbout, Locale locale) {
return messages.get(locale, "boatClassResultsBody", boatClass.getDisplayName(), regattaDisplayName,
eventName);
}
});
}
/*
* (non-Javadoc)
*
* @see
* com.sap.sailing.server.notification.SailingNotificationService#notifyUserOnBoatClassUpcomingRace(com.sap.sailing.
* domain.base.BoatClass, java.lang.String, java.lang.String, com.sap.sse.common.TimePoint, java.lang.String)
*/
@Override
public void notifyUserOnBoatClassUpcomingRace(BoatClass boatClass, String eventName, String regattaDisplayName,
TimePoint when, String link) {
mailQueue.addNotification(new NotificationSetNotification<BoatClass>(boatClass, boatClassUpcomingRace) {
@Override
protected String constructSubject(BoatClass objectToNotifyAbout, Locale locale) {
return messages.get(locale, "boatClassUpcomingRaceSubject", boatClass.getDisplayName(),
regattaDisplayName, eventName);
}
@Override
protected String constructBody(BoatClass objectToNotifyAbout, Locale locale) {
// TODO properly format time in the user's locale
String time = when.toString();
return messages.get(locale, "boatClassUpcomingRaceBody", boatClass.getDisplayName(), regattaDisplayName,
eventName, time);
}
});
}
/*
* (non-Javadoc)
*
* @see
* com.sap.sailing.server.notification.SailingNotificationService#notifyUserOnCompetitorResults(com.sap.sailing.
* domain.base.Competitor, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void notifyUserOnCompetitorResults(Competitor competitor, String eventName, String regattaDisplayName,
String link) {
mailQueue.addNotification(new NotificationSetNotification<Competitor>(competitor, competitorResults) {
@Override
protected String constructSubject(Competitor objectToNotifyAbout, Locale locale) {
return messages.get(locale, "competitorResultsSubject", competitor.getName(), regattaDisplayName,
eventName);
}
@Override
protected String constructBody(Competitor objectToNotifyAbout, Locale locale) {
return messages.get(locale, "competitorResultsBody", competitor.getName(), regattaDisplayName,
eventName);
}
});
}
}