diff --git a/configuration/buildAndUpdateProduct.sh b/configuration/buildAndUpdateProduct.sh
index 4b4f9137874..efbc8ad4001 100755
--- a/configuration/buildAndUpdateProduct.sh
+++ b/configuration/buildAndUpdateProduct.sh
@@ -249,7 +249,7 @@ if [[ "$@" == "release" ]]; then
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties configuration/jetty/etc
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties configuration/
- cp -v $PROJECT_HOME/java/target/configuration/security.properties configuration/
+ cp -v $PROJECT_HOME/java/target/configuration/mail.properties configuration/
cp -v $PROJECT_HOME/configuration/mongodb.cfg $ACDIR/
cp -v $PROJECT_HOME/java/target/udpmirror $ACDIR/
cp -v $PROJECT_HOME/java/target/http2udpmirror $ACDIR
@@ -718,7 +718,7 @@ if [[ "$@" == "install" ]] || [[ "$@" == "all" ]]; then
if [ ! -f "$ACDIR/env.sh" ]; then
cp -v $PROJECT_HOME/java/target/env.sh $ACDIR/
cp -v $PROJECT_HOME/java/target/configuration/monitoring.properties $ACDIR/configuration/
- cp -v $PROJECT_HOME/java/target/configuration/security.properties $ACDIR/configuration/
+ cp -v $PROJECT_HOME/java/target/configuration/mail.properties $ACDIR/configuration/
cp -v $PROJECT_HOME/java/target/configuration/logging.properties $ACDIR/configuration/
fi
@@ -799,7 +799,7 @@ if [[ "$@" == "remote-deploy" ]]; then
$SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/jetty-deployer.xml $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
$SCP_CMD $PROJECT_HOME/java/target/configuration/jetty/etc/realm.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/jetty/etc
$SCP_CMD $PROJECT_HOME/java/target/configuration/monitoring.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
- $SCP_CMD $PROJECT_HOME/java/target/configuration/security.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
+ $SCP_CMD $PROJECT_HOME/java/target/configuration/mail.properties $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/configuration/
$SCP_CMD $PROJECT_HOME/java/target/env.sh $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
$SCP_CMD $PROJECT_HOME/java/target/start $REMOTE_SERVER_LOGIN:$REMOTE_SERVER/
diff --git a/java/com.sap.sailing.feature.p2build/raceanalysis.product b/java/com.sap.sailing.feature.p2build/raceanalysis.product
index 71b6e30fe1b..6f0f4447caf 100755
--- a/java/com.sap.sailing.feature.p2build/raceanalysis.product
+++ b/java/com.sap.sailing.feature.p2build/raceanalysis.product
@@ -58,6 +58,7 @@
+
diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml
index 543e8a8950d..c83bd9b70f2 100755
--- a/java/com.sap.sailing.feature/feature.xml
+++ b/java/com.sap.sailing.feature/feature.xml
@@ -409,4 +409,11 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
+
+
diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SailingServiceImplMock.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SailingServiceImplMock.java
index 31bba094c52..7803176ca7c 100755
--- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SailingServiceImplMock.java
+++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/SailingServiceImplMock.java
@@ -1,8 +1,5 @@
package com.sap.sailing.gwt.ui.test;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
import java.io.IOException;
import java.net.UnknownHostException;
import java.text.ParseException;
@@ -11,9 +8,6 @@ import java.util.List;
import javax.xml.bind.JAXBException;
-import org.osgi.framework.BundleContext;
-import org.osgi.util.tracker.ServiceTracker;
-
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.common.RegattaIdentifier;
@@ -27,7 +21,6 @@ import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory;
import com.sap.sailing.domain.swisstimingadapter.SwissTimingFactory;
import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayService;
-import com.sap.sailing.domain.swisstimingreplayadapter.SwissTimingReplayServiceFactory;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tractracadapter.TracTracAdapterFactory;
@@ -43,72 +36,37 @@ import com.sap.sse.replication.impl.ReplicationServiceImpl;
public class SailingServiceImplMock extends SailingServiceImpl {
private static final long serialVersionUID = 8564037671550730455L;
- private RacingEventService service;
-
+
+ private final RacingEventService service;
+ private ReplicationService replicationService;
+ private final ScoreCorrectionProvider scoreCorrectionProvider = null;
+ private final TracTracAdapterFactory tracTracAdapterFactory = TracTracAdapterFactory.INSTANCE;
+ private final SwissTimingAdapterFactory swissTimingAdapterFactory;
+ private final SwissTimingReplayService swissTimingReplayService = null;
+ private final RaceLogTrackingAdapterFactory raceLogTrackingAdapterFactory = RaceLogTrackingAdapterFactory.INSTANCE;
+
public SailingServiceImplMock() {
super();
service = new RacingEventServiceImpl(true, new MockSmartphoneImeiServiceFinderFactory());
- }
-
- @Override
- protected ServiceTracker createAndOpenRacingEventServiceTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
- when(result.getService()).thenReturn(new RacingEventServiceImpl());
- return result;
- }
-
- @Override
- protected ServiceTracker createAndOpenReplicationServiceTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
try {
- final RacingEventService replicable = new RacingEventServiceImpl();
- final ReplicationServiceImpl replicationService =
- new ReplicationServiceImpl("test exchange", "localhost", 0,
- new ReplicationInstancesManager(), new AbstractReplicablesProvider() {
- @Override
- public Iterable> getReplicables() {
- return Collections.>singleton(replicable);
- }
- });
- when(result.getService()).thenReturn(replicationService);
+ replicationService = new ReplicationServiceImpl("test exchange", "localhost", 0,
+ new ReplicationInstancesManager(), new AbstractReplicablesProvider() {
+ @Override
+ public Iterable> getReplicables() {
+ return Collections.> singleton(service);
+ }
+ });
} catch (IOException e) {
- throw new RuntimeException();
+ e.printStackTrace();
}
- return result;
- }
-
- @Override
- protected ServiceTracker createAndOpenScoreCorrectionProviderServiceTracker(
- BundleContext bundleContext) {
- return null;
- }
-
- @Override
- protected ServiceTracker createAndOpenTracTracAdapterTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
- when(result.getService()).thenReturn(TracTracAdapterFactory.INSTANCE);
- return result;
- }
-
- @Override
- protected ServiceTracker createAndOpenSwissTimingAdapterTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
- when(result.getService()).thenReturn(new SwissTimingAdapterFactory() {
+ swissTimingAdapterFactory = new SwissTimingAdapterFactory() {
@Override
public SwissTimingAdapter getOrCreateSwissTimingAdapter(DomainFactory baseDomainFactory) {
return new SwissTimingAdapter() {
@Override
public List getSwissTimingRaceRecords(
- String hostname, int port) throws InterruptedException,
- UnknownHostException, IOException, ParseException {
+ String hostname, int port) throws InterruptedException, UnknownHostException, IOException,
+ ParseException {
// TODO Auto-generated method stub
return null;
}
@@ -148,45 +106,43 @@ public class SailingServiceImplMock extends SailingServiceImpl {
}
};
}
- });
- return result;
+ };
+ }
+
+ public ScoreCorrectionProvider getScoreCorrectionProvider() {
+ return scoreCorrectionProvider;
}
@Override
- protected ServiceTracker createAndOpenSwissTimingReplayServiceTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
- final SwissTimingReplayServiceFactory swissTimingReplayService = new SwissTimingReplayServiceFactory() {
- @Override
- public SwissTimingReplayService createSwissTimingReplayService(
- com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory) {
- // TODO Auto-generated method stub
- return null;
- }
- };
- when(result.getService()).thenReturn(swissTimingReplayService);
- return result;
+ protected TracTracAdapterFactory getTracTracAdapterFactory() {
+ return tracTracAdapterFactory;
+ }
+
+ @Override
+ protected SwissTimingAdapterFactory getSwissTimingAdapterFactory() {
+ return swissTimingAdapterFactory;
+ }
+
+ @Override
+ protected SwissTimingReplayService getSwissTimingReplayService() {
+ return swissTimingReplayService;
+ }
+
+ @Override
+ protected RaceLogTrackingAdapterFactory getRaceLogTrackingAdapterFactory() {
+ return raceLogTrackingAdapterFactory;
}
@Override
- protected ServiceTracker createAndOpenRaceLogTrackingAdapterTracker(
- BundleContext context) {
- @SuppressWarnings("unchecked")
- ServiceTracker result = mock(ServiceTracker.class);
- RaceLogTrackingAdapterFactory factory = RaceLogTrackingAdapterFactory.INSTANCE;
- when(result.getService()).thenReturn(factory);
- return result;
+ protected ReplicationService getReplicationService() {
+ return replicationService;
}
@Override
protected RacingEventService getService() {
- if (service == null) {
- service = new RacingEventServiceImpl();
- }
return service;
}
-
+
public RacingEventService getRacingEventService() {
return getService();
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
index 8ad3755d8cd..5e50344d0d5 100755
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
@@ -429,6 +429,7 @@ import com.sap.sse.replication.ReplicationFactory;
import com.sap.sse.replication.ReplicationMasterDescriptor;
import com.sap.sse.replication.ReplicationService;
import com.sap.sse.replication.impl.ReplicaDescriptor;
+import com.sap.sse.util.ServiceTrackerFactory;
import com.sapsailing.xrr.structureimport.eventimport.RegattaJSON;
/**
@@ -501,21 +502,25 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
activator.setSailingService(this); // register so this service is informed when the bundle shuts down
}
quickRanksLiveCache = new QuickRanksLiveCache(this);
- racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context);
- replicationServiceTracker = createAndOpenReplicationServiceTracker(context);
- resultUrlRegistryServiceTracker = createAndOpenResultUrlRegistryServiceTracker(context);
- swissTimingAdapterTracker = createAndOpenSwissTimingAdapterTracker(context);
- tractracAdapterTracker = createAndOpenTracTracAdapterTracker(context);
- raceLogTrackingAdapterTracker = createAndOpenRaceLogTrackingAdapterTracker(context);
- deviceIdentifierStringSerializationHandlerTracker = createAndOpenDeviceIdentifierStringSerializationHandlerTracker(context);
- igtimiAdapterTracker = createAndOpenIgtimiTracker(context);
+ racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class);
+ replicationServiceTracker = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class);
+ resultUrlRegistryServiceTracker = ServiceTrackerFactory.createAndOpen(context, ResultUrlRegistry.class);
+ swissTimingAdapterTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class);
+ tractracAdapterTracker = ServiceTrackerFactory.createAndOpen(context, TracTracAdapterFactory.class);
+ raceLogTrackingAdapterTracker = ServiceTrackerFactory.createAndOpen(context,
+ RaceLogTrackingAdapterFactory.class);
+ deviceIdentifierStringSerializationHandlerTracker = ServiceTrackerFactory.createAndOpen(context,
+ DeviceIdentifierStringSerializationHandler.class);
+ igtimiAdapterTracker = ServiceTrackerFactory.createAndOpen(context, IgtimiConnectionFactory.class);
baseDomainFactory = getService().getBaseDomainFactory();
mongoObjectFactory = getService().getMongoObjectFactory();
domainObjectFactory = getService().getDomainObjectFactory();
// TODO what about passing on the mongo/domain object factory to obtain an according SwissTimingAdapterPersistence instance similar to how the tractracDomainObjectFactory etc. are created below?
swissTimingAdapterPersistence = SwissTimingAdapterPersistence.INSTANCE;
- swissTimingReplayService = getSwissTimingReplayService(context);
- scoreCorrectionProviderServiceTracker = createAndOpenScoreCorrectionProviderServiceTracker(context);
+ swissTimingReplayService = ServiceTrackerFactory.createAndOpen(context, SwissTimingReplayServiceFactory.class)
+ .getService().createSwissTimingReplayService(getSwissTimingAdapter().getSwissTimingDomainFactory());
+ scoreCorrectionProviderServiceTracker = ServiceTrackerFactory.createAndOpen(context,
+ ScoreCorrectionProvider.class);
tractracDomainObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory.INSTANCE
.createDomainObjectFactory(mongoObjectFactory.getDatabase(), getTracTracAdapter()
.getTracTracDomainFactory());
@@ -554,89 +559,26 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
quickRanksLiveCache.stop();
}
- protected SwissTimingReplayService getSwissTimingReplayService(BundleContext context) {
- return createAndOpenSwissTimingReplayServiceTracker(context).getService().createSwissTimingReplayService(getSwissTimingAdapter().getSwissTimingDomainFactory());
+ protected SwissTimingAdapterFactory getSwissTimingAdapterFactory() {
+ return swissTimingAdapterTracker.getService();
}
protected SwissTimingAdapter getSwissTimingAdapter() {
- return swissTimingAdapterTracker.getService().getOrCreateSwissTimingAdapter(baseDomainFactory);
+ return getSwissTimingAdapterFactory().getOrCreateSwissTimingAdapter(baseDomainFactory);
+ }
+
+ protected TracTracAdapterFactory getTracTracAdapterFactory() {
+ return tractracAdapterTracker.getService();
}
protected TracTracAdapter getTracTracAdapter() {
- return tractracAdapterTracker.getService().getOrCreateTracTracAdapter(baseDomainFactory);
- }
-
- protected ServiceTracker createAndOpenTracTracAdapterTracker(BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, TracTracAdapterFactory.class.getName(), null);
- result.open();
- return result;
- }
-
- protected ServiceTracker createAndOpenIgtimiTracker(BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, IgtimiConnectionFactory.class.getName(), null);
- result.open();
- return result;
- }
-
- protected ServiceTracker createAndOpenSwissTimingAdapterTracker(
- BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, SwissTimingAdapterFactory.class.getName(), null);
- result.open();
- return result;
- }
-
- protected ServiceTracker createAndOpenSwissTimingReplayServiceTracker(
- BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, SwissTimingReplayServiceFactory.class.getName(), null);
- result.open();
- return result;
- }
-
- protected ServiceTracker createAndOpenRaceLogTrackingAdapterTracker(
- BundleContext context) {
- ServiceTracker result =
- new ServiceTracker(
- context, RaceLogTrackingAdapterFactory.class.getName(), null);
- result.open();
- return result;
+ return getTracTracAdapterFactory().getOrCreateTracTracAdapter(baseDomainFactory);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
- protected ServiceTracker createAndOpenRacingEventServiceTracker(
- BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, RacingEventService.class.getName(), null);
- result.open();
- return result;
- }
-
- protected ServiceTracker createAndOpenResultUrlRegistryServiceTracker(
- BundleContext context) {
- ServiceTracker result = new ServiceTracker(
- context, ResultUrlRegistry.class.getName(), null);
- result.open();
- return result;
- }
-
- /**
- * Asks the OSGi system for registered score correction provider services
- */
- protected ServiceTracker createAndOpenScoreCorrectionProviderServiceTracker(
- BundleContext bundleContext) {
- ServiceTracker tracker = new ServiceTracker(bundleContext,
- ScoreCorrectionProvider.class.getName(),
- /* customizer */null);
- tracker.open();
- return tracker;
- }
-
@Override
public Iterable getScoreCorrectionProviderNames() {
List result = new ArrayList();
@@ -683,15 +625,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return new ScoreCorrectionProviderDTO(scoreCorrectionProvider.getName(), hasResultsForBoatClassFromDateByEventName);
}
- protected ServiceTracker createAndOpenReplicationServiceTracker(
- BundleContext context) {
- ServiceTracker result =
- new ServiceTracker(
- context, ReplicationService.class.getName(), null);
- result.open();
- return result;
- }
-
/**
* If date is null, the {@link LiveLeaderboardUpdater} for the
* leaderboardName requested is obtained or created if it doesn't exist yet. The request is then passed
@@ -1114,10 +1047,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
- private SwissTimingReplayService getSwissTimingReplayService() {
- return swissTimingReplayService;
- }
-
@Override
public List getPreviousTracTracConfigurations() throws Exception {
Iterable configs = tractracDomainObjectFactory.getTracTracConfigurations();
@@ -2237,7 +2166,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return racingEventServiceTracker.getService(); // grab the service
}
- private ReplicationService getReplicationService() {
+ protected ReplicationService getReplicationService() {
return replicationServiceTracker.getService();
}
@@ -2698,6 +2627,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
}
+
+ protected SwissTimingReplayService getSwissTimingReplayService() {
+ return swissTimingReplayService;
+ }
@Override
public List listSwissTiminigReplayRaces(String swissTimingUrl) {
@@ -4735,9 +4668,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private IgtimiConnectionFactory getIgtimiConnectionFactory() {
return igtimiAdapterTracker.getService();
}
+
+ protected RaceLogTrackingAdapterFactory getRaceLogTrackingAdapterFactory() {
+ return raceLogTrackingAdapterTracker.getService();
+ }
protected RaceLogTrackingAdapter getRaceLogTrackingAdapter() {
- return raceLogTrackingAdapterTracker.getService().getAdapter(getBaseDomainFactory());
+ return getRaceLogTrackingAdapterFactory().getAdapter(getBaseDomainFactory());
}
@Override
@@ -5056,7 +4993,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
getRaceLogTrackingAdapter().copyCourseAndCompetitors(fromRaceLog, toRaceLogs, baseDomainFactory, getService());
}
- private TypeBasedServiceFinder getDeviceIdentifierStringSerializerHandlerFinder(
+ protected TypeBasedServiceFinder getDeviceIdentifierStringSerializerHandlerFinder(
boolean withFallback) {
TypeBasedServiceFinderFactory factory = getService().getTypeBasedServiceFinderFactory();
TypeBasedServiceFinder finder = factory.createServiceFinder(DeviceIdentifierStringSerializationHandler.class);
@@ -5179,16 +5116,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
raceLog.add(event);
}
- private ServiceTracker
- createAndOpenDeviceIdentifierStringSerializationHandlerTracker(BundleContext context) {
- ServiceTracker tracker =
- new ServiceTracker(
- context, DeviceIdentifierStringSerializationHandler.class, null);
-
- tracker.open();
- return tracker;
- }
-
@Override
public List getDeserializableDeviceIdentifierTypes() {
List result = new ArrayList();
@@ -5429,7 +5356,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return data;
}
- private FileStorageService getFileStorageService(String name) {
+ protected FileStorageService getFileStorageService(String name) {
if (name == null || name.equals("")) {
return null;
}
diff --git a/java/com.sap.sse.mail/.classpath b/java/com.sap.sse.mail/.classpath
new file mode 100755
index 00000000000..b862a296d38
--- /dev/null
+++ b/java/com.sap.sse.mail/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/java/com.sap.sse.mail/.project b/java/com.sap.sse.mail/.project
new file mode 100755
index 00000000000..05362453cda
--- /dev/null
+++ b/java/com.sap.sse.mail/.project
@@ -0,0 +1,28 @@
+
+
+ com.sap.sse.mail
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/java/com.sap.sse.mail/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sse.mail/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 00000000000..295926d9641
--- /dev/null
+++ b/java/com.sap.sse.mail/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/java/com.sap.sse.mail/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sse.mail/.settings/org.eclipse.pde.core.prefs
new file mode 100755
index 00000000000..b7e72d019c2
--- /dev/null
+++ b/java/com.sap.sse.mail/.settings/org.eclipse.pde.core.prefs
@@ -0,0 +1,3 @@
+eclipse.preferences.version=1
+pluginProject.extensions=false
+resolve.requirebundle=false
diff --git a/java/com.sap.sse.mail/META-INF/MANIFEST.MF b/java/com.sap.sse.mail/META-INF/MANIFEST.MF
new file mode 100755
index 00000000000..2402b0d40ef
--- /dev/null
+++ b/java/com.sap.sse.mail/META-INF/MANIFEST.MF
@@ -0,0 +1,15 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Sse Email
+Bundle-SymbolicName: com.sap.sse.mail
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: SAP
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Export-Package: com.sap.sse.mail
+Import-Package: javax.mail,
+ javax.mail.internet,
+ org.osgi.framework
+Require-Bundle: com.sap.sse.replication,
+ com.sap.sse.operationaltransformation,
+ com.sap.sse.common,
+ com.sap.sse
diff --git a/java/com.sap.sse.mail/build.properties b/java/com.sap.sse.mail/build.properties
new file mode 100755
index 00000000000..41eb6ade2b4
--- /dev/null
+++ b/java/com.sap.sse.mail/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/java/com.sap.sse.mail/pom.xml b/java/com.sap.sse.mail/pom.xml
new file mode 100644
index 00000000000..5d7f50f4093
--- /dev/null
+++ b/java/com.sap.sse.mail/pom.xml
@@ -0,0 +1,12 @@
+
+
+ 4.0.0
+
+ root
+ com.sap.sailing
+ 1.0.0-SNAPSHOT
+
+ com.sap.sse.mail
+ eclipse-plugin
+
diff --git a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/MailException.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailException.java
similarity index 87%
rename from java/com.sap.sse.security.common/src/com/sap/sse/security/shared/MailException.java
rename to java/com.sap.sse.mail/src/com/sap/sse/mail/MailException.java
index 9d3491ee00b..79013c8aebb 100755
--- a/java/com.sap.sse.security.common/src/com/sap/sse/security/shared/MailException.java
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailException.java
@@ -1,4 +1,4 @@
-package com.sap.sse.security.shared;
+package com.sap.sse.mail;
import java.io.Serializable;
diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/MailOperation.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailOperation.java
new file mode 100644
index 00000000000..908e751563a
--- /dev/null
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailOperation.java
@@ -0,0 +1,15 @@
+package com.sap.sse.mail;
+
+import com.sap.sse.mail.impl.ReplicableMailService;
+import com.sap.sse.replication.OperationWithResult;
+
+public interface MailOperation extends OperationWithResult {
+ /**
+ * By default, the mail-related operations will perform an update on the receiving replica that in turn
+ * will trigger replication of this change transitively.
+ */
+ default boolean isRequiresExplicitTransitiveReplication() {
+ return false;
+ }
+
+}
diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/MailService.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailService.java
new file mode 100644
index 00000000000..810f473c0c1
--- /dev/null
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/MailService.java
@@ -0,0 +1,30 @@
+package com.sap.sse.mail;
+
+import javax.mail.Multipart;
+
+import com.sap.sse.mail.impl.ReplicableMailService;
+import com.sap.sse.replication.Replicable;
+import com.sap.sse.replication.impl.ReplicableWithObjectInputStream;
+
+
+/**
+ * Announced via OSGi service registry. Requires {@code mail.properties} file in {@code configuration} directory
+ * to be correctly configured.
+ *
+ * A mail should only be sent out once by one instance. This is similar to only adding an item once to a store
+ * (database). Therefore - while a sendMail() operation is replicated - it is only executed by one instance,
+ * for now the master instance. All replicas receive an empty ({@code null}) mail configuration during
+ * {@link Replicable#initiallyFillFrom initial replication}, and therefore do not try to send mails themselves.
+ *
+ * @author Axel Uhl
+ * @author Fredrik Teschke
+ *
+ */
+public interface MailService extends ReplicableWithObjectInputStream> {
+ void sendMail(String toAddress, String subject, String body) throws MailException;
+
+ /**
+ * Send mail with multipart content (e.g. inline image).
+ */
+ void sendMail(String toAddress, String subject, Multipart multipartContent) throws MailException;
+}
diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/Activator.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/Activator.java
new file mode 100644
index 00000000000..45b6b3e46ec
--- /dev/null
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/Activator.java
@@ -0,0 +1,56 @@
+package com.sap.sse.mail.impl;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import com.sap.sse.mail.MailService;
+import com.sap.sse.replication.Replicable;
+import com.sap.sse.util.ClearStateTestSupport;
+
+public class Activator implements BundleActivator {
+ private static final Logger logger = Logger.getLogger(Activator.class.getName());
+
+ private ServiceRegistration> registration;
+
+ public void start(BundleContext context) throws Exception {
+ // Load mail properties
+ final String jettyHome = System.getProperty("jetty.home", "configuration");
+ final File propertiesDir = new File(jettyHome).getParentFile();
+ File propertiesfile = new File(propertiesDir, "mail.properties");
+ Properties mailProperties = new Properties();
+ try {
+ mailProperties.load(new FileReader(propertiesfile));
+ } catch (IOException ioe) {
+ logger.log(Level.SEVERE, "Couldn't read mail properties from " + propertiesfile.getCanonicalPath(), ioe);
+ }
+
+ MailService mailService = new MailServiceImpl(mailProperties);
+ registration = context.registerService(MailService.class, mailService, null);
+ final Dictionary replicableServiceProperties = new Hashtable<>();
+ replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, mailService.getId()
+ .toString());
+ context.registerService(Replicable.class.getName(), mailService, replicableServiceProperties);
+ context.registerService(ClearStateTestSupport.class.getName(), mailService, null);
+ Logger.getLogger(Activator.class.getName()).info("Mail Service registered.");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext bundleContext) throws Exception {
+ registration.unregister();
+ }
+
+}
diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java
new file mode 100644
index 00000000000..111a5b0c7f4
--- /dev/null
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java
@@ -0,0 +1,209 @@
+package com.sap.sse.mail.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.mail.Message.RecipientType;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+import com.sap.sse.mail.MailException;
+import com.sap.sse.replication.OperationExecutionListener;
+import com.sap.sse.replication.OperationWithResult;
+import com.sap.sse.replication.ReplicationMasterDescriptor;
+import com.sap.sse.replication.impl.OperationWithResultWithIdWrapper;
+import com.sap.sse.util.ClearStateTestSupport;
+
+public class MailServiceImpl implements ReplicableMailService, ClearStateTestSupport {
+ private static final Logger logger = Logger.getLogger(MailServiceImpl.class.getName());
+
+ private Properties mailProperties;
+
+ /**
+ * The master from which this replicable is currently replicating, or null if this replicable is not
+ * currently replicated from any master.
+ */
+ private ReplicationMasterDescriptor replicatingFromMaster;
+ private final ConcurrentHashMap, OperationExecutionListener> operationExecutionListeners;
+ private Set> operationsSentToMasterForReplication;
+ private ThreadLocal currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster = ThreadLocal
+ .withInitial(() -> false);
+
+ public MailServiceImpl(Properties mailProperties) {
+ this.mailProperties = mailProperties;
+ this.operationExecutionListeners = new ConcurrentHashMap<>();
+ }
+
+ private class SMTPAuthenticator extends javax.mail.Authenticator {
+ public PasswordAuthentication getPasswordAuthentication() {
+ String username = mailProperties.getProperty("mail.smtp.user");
+ String password = mailProperties.getProperty("mail.smtp.password");
+ return new PasswordAuthentication(username, password);
+ }
+ }
+
+ private static interface ContentSetter {
+ void setContent(MimeMessage msg) throws MessagingException;
+ }
+
+ private void internalSendMail(String toAddress, String subject, ContentSetter contentSetter) throws MailException {
+ if (mailProperties != null && mailProperties.containsKey("mail.transport.protocol")) {
+ if (toAddress != null) {
+ Session session = Session.getInstance(mailProperties, new SMTPAuthenticator());
+ MimeMessage msg = new MimeMessage(session);
+ try {
+ msg.setFrom(new InternetAddress(mailProperties.getProperty("mail.from", "root@sapsailing.com")));
+ msg.setSubject(subject);
+ contentSetter.setContent(msg);
+ msg.addRecipient(RecipientType.TO, new InternetAddress(toAddress.trim()));
+ Transport ts = session.getTransport();
+ ts.connect();
+ ts.sendMessage(msg, msg.getRecipients(RecipientType.TO));
+ ts.close();
+ logger.info("mail sent to " + toAddress + " with subject " + subject);
+ } catch (MessagingException e) {
+ logger.log(Level.SEVERE, "Error trying to send mail to " + toAddress, e);
+ throw new MailException(e.getMessage());
+ }
+ }
+ } else {
+ logger.warning("No mail properties provided. Cannot send e-mail about "
+ + subject
+ + " to "
+ + toAddress
+ + ". This could also mean that this is running on a replica server in which case this is perfectly fine.");
+ }
+ }
+
+ @Override
+ public Void internalSendMail(String toAddress, String subject, String body) throws MailException {
+ internalSendMail(toAddress, subject, new ContentSetter() {
+ @Override
+ public void setContent(MimeMessage msg) throws MessagingException {
+ msg.setContent(body, "text/plain");
+ }
+ });
+ return null;
+ }
+
+ @Override
+ public void sendMail(String toAddress, String subject, String body) throws MailException {
+ apply(s -> s.internalSendMail(toAddress, subject, body));
+ }
+
+ @Override
+ public Void internalSendMail(String toAddress, String subject, Multipart multipartContent) throws MailException {
+ internalSendMail(toAddress, subject, new ContentSetter() {
+ @Override
+ public void setContent(MimeMessage msg) throws MessagingException {
+ msg.setContent(multipartContent);
+ }
+ });
+ return null;
+ }
+
+ @Override
+ public void sendMail(String toAddress, String subject, Multipart multipartContent) throws MailException {
+ apply(s -> s.internalSendMail(toAddress, subject, multipartContent));
+ }
+
+ // ----------------- Replication -------------
+ @Override
+ public void clearReplicaState() throws MalformedURLException, IOException, InterruptedException {
+ // do nothing
+ }
+
+ @Override
+ public Serializable getId() {
+ return getClass().getName();
+ }
+
+ @Override
+ public ObjectInputStream createObjectInputStreamResolvingAgainstCache(InputStream is) throws IOException {
+ return new ObjectInputStream(is);
+ }
+
+ @Override
+ public void initiallyFillFromInternal(ObjectInputStream is) throws IOException, ClassNotFoundException,
+ InterruptedException {
+ // send mails only from master
+ mailProperties = null;
+ }
+
+ @Override
+ public void serializeForInitialReplicationInternal(ObjectOutputStream objectOutputStream) throws IOException {
+ // do nothing
+ }
+
+ @Override
+ public Iterable> getOperationExecutionListeners() {
+ return operationExecutionListeners.keySet();
+ }
+
+ @Override
+ public void addOperationExecutionListener(OperationExecutionListener listener) {
+ operationExecutionListeners.put(listener, listener);
+ }
+
+ @Override
+ public void removeOperationExecutionListener(OperationExecutionListener listener) {
+ operationExecutionListeners.remove(listener);
+ }
+
+ @Override
+ public ReplicationMasterDescriptor getMasterDescriptor() {
+ return replicatingFromMaster;
+ }
+
+ @Override
+ public void startedReplicatingFrom(ReplicationMasterDescriptor master) {
+ this.replicatingFromMaster = master;
+ }
+
+ @Override
+ public void stoppedReplicatingFrom(ReplicationMasterDescriptor master) {
+ this.replicatingFromMaster = null;
+ }
+
+ @Override
+ public void addOperationSentToMasterForReplication(
+ OperationWithResultWithIdWrapper operationWithResultWithIdWrapper) {
+ this.operationsSentToMasterForReplication.add(operationWithResultWithIdWrapper);
+ }
+
+ @Override
+ public boolean hasSentOperationToMaster(OperationWithResult operation) {
+ return this.operationsSentToMasterForReplication.remove(operation);
+ }
+
+ @Override
+ public boolean isCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster() {
+ return currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster.get();
+ }
+
+ @Override
+ public void setCurrentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster(
+ boolean currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster) {
+ this.currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster
+ .set(currentlyFillingFromInitialLoadOrApplyingOperationReceivedFromMaster);
+ }
+
+ @Override
+ public void clearState() throws Exception {
+ mailProperties.clear();
+ }
+}
diff --git a/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/ReplicableMailService.java b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/ReplicableMailService.java
new file mode 100644
index 00000000000..3d1508e500d
--- /dev/null
+++ b/java/com.sap.sse.mail/src/com/sap/sse/mail/impl/ReplicableMailService.java
@@ -0,0 +1,12 @@
+package com.sap.sse.mail.impl;
+
+import javax.mail.Multipart;
+
+import com.sap.sse.mail.MailException;
+import com.sap.sse.mail.MailService;
+
+public interface ReplicableMailService extends MailService {
+ Void internalSendMail(String toAddress, String subject, String body) throws MailException;
+
+ Void internalSendMail(String toAddress, String subject, Multipart multipartContent) throws MailException;
+}
diff --git a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/AbstractSecurityReplicationTest.java b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/AbstractSecurityReplicationTest.java
index e1b5b5a94fb..54d7628f87f 100755
--- a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/AbstractSecurityReplicationTest.java
+++ b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/AbstractSecurityReplicationTest.java
@@ -2,7 +2,6 @@ package com.sap.sse.security.replication.test;
import java.io.IOException;
import java.net.MalformedURLException;
-import java.util.Properties;
import com.sap.sse.mongodb.MongoDBService;
import com.sap.sse.replication.testsupport.AbstractServerReplicationTest;
@@ -28,14 +27,14 @@ public abstract class AbstractSecurityReplicationTest extends AbstractServerRepl
@Override
protected SecurityServiceImpl createNewMaster() throws MalformedURLException, IOException, InterruptedException {
- SecurityServiceImpl result = new SecurityServiceImpl(new UserStoreImpl(), new Properties());
+ SecurityServiceImpl result = new SecurityServiceImpl(new UserStoreImpl());
result.clearReplicaState();
return result;
}
@Override
protected SecurityServiceImpl createNewReplica() {
- return new SecurityServiceImpl(new UserStoreImpl(), new Properties());
+ return new SecurityServiceImpl(new UserStoreImpl());
}
@Override
diff --git a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SimpleSecurityReplicationTest.java b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SimpleSecurityReplicationTest.java
index 439018643f9..1c22fdaedae 100755
--- a/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SimpleSecurityReplicationTest.java
+++ b/java/com.sap.sse.security.replication.test/src/com/sap/sse/security/replication/test/SimpleSecurityReplicationTest.java
@@ -9,8 +9,8 @@ import static org.junit.Assert.assertTrue;
import org.junit.Test;
+import com.sap.sse.mail.MailException;
import com.sap.sse.security.User;
-import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.UserManagementException;
public class SimpleSecurityReplicationTest extends AbstractSecurityReplicationTest {
diff --git a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF
index a1f9525f9a2..101740dfa00 100644
--- a/java/com.sap.sse.security.test/META-INF/MANIFEST.MF
+++ b/java/com.sap.sse.security.test/META-INF/MANIFEST.MF
@@ -9,4 +9,6 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.junit4;bundle-version="4.8.2",
com.sap.sse.security.userstore.mongodb,
com.mongodb.driver,
- com.sap.sse.mongodb
+ com.sap.sse.mongodb,
+ org.mockito.mockito-core,
+ com.sap.sse.mail
diff --git a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
index 71d9f9da6f1..5d4cf1fbee5 100644
--- a/java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
+++ b/java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
@@ -6,7 +6,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.net.UnknownHostException;
-import java.util.Properties;
import org.apache.shiro.SecurityUtils;
import org.junit.Before;
@@ -40,7 +39,7 @@ public class LoginTest {
UsernamePasswordRealm.setTestUserStore(store);
Activator.setTestUserStore(store);
Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); // to enable shiro to find classes from com.sap.sse.security
- new SecurityServiceImpl(store, /* mailProperties */ new Properties());
+ new SecurityServiceImpl(store);
}
@Test
diff --git a/java/com.sap.sse.security.ui/META-INF/MANIFEST.MF b/java/com.sap.sse.security.ui/META-INF/MANIFEST.MF
index 347baaf4407..fb55d40f181 100644
--- a/java/com.sap.sse.security.ui/META-INF/MANIFEST.MF
+++ b/java/com.sap.sse.security.ui/META-INF/MANIFEST.MF
@@ -21,7 +21,8 @@ Require-Bundle: javax.servlet;bundle-version="2.5.0",
org.apache.shiro.core;bundle-version="1.2.2",
org.apache.shiro.web;bundle-version="1.2.2",
com.sap.sse.replication,
- com.sap.sse.security.common
+ com.sap.sse.security.common,
+ com.sap.sse.mail
Web-ContextPath: /security/ui
Export-Package: com.sap.sse.security.ui.client,
com.sap.sse.security.ui.client.component,
diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
index 6ae80791520..8b727a0f63f 100644
--- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
+++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/UserManagementService.java
@@ -4,8 +4,8 @@ import java.util.Collection;
import java.util.Map;
import com.google.gwt.user.client.rpc.RemoteService;
+import com.sap.sse.mail.MailException;
import com.sap.sse.security.shared.DefaultRoles;
-import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.ui.oauth.client.CredentialDTO;
import com.sap.sse.security.ui.oauth.shared.OAuthException;
diff --git a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
index a04ae8a236d..f7949841b2a 100644
--- a/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
+++ b/java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
@@ -27,6 +27,7 @@ import org.osgi.util.tracker.ServiceTracker;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.sap.sailing.domain.common.impl.NaturalComparator;
import com.sap.sse.common.Util;
+import com.sap.sse.mail.MailException;
import com.sap.sse.security.Credential;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.SessionUtils;
@@ -35,7 +36,6 @@ import com.sap.sse.security.User;
import com.sap.sse.security.shared.Account;
import com.sap.sse.security.shared.Account.AccountType;
import com.sap.sse.security.shared.DefaultRoles;
-import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UsernamePasswordAccount;
diff --git a/java/com.sap.sse.security/META-INF/MANIFEST.MF b/java/com.sap.sse.security/META-INF/MANIFEST.MF
index 524101252cc..e8ca35e3510 100644
--- a/java/com.sap.sse.security/META-INF/MANIFEST.MF
+++ b/java/com.sap.sse.security/META-INF/MANIFEST.MF
@@ -6,7 +6,8 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sse.security.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
-Import-Package: javax.mail;version="1.4.0",
+Import-Package: com.sap.sse.util,
+ javax.mail;version="1.4.0",
javax.mail.internet;version="1.4.0",
javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
@@ -31,6 +32,7 @@ Require-Bundle: javax.servlet;bundle-version="2.5.0",
com.sap.sse.replication,
com.sap.sse.operationaltransformation,
com.sap.sse.security.common,
- com.sap.sse
+ com.sap.sse,
+ com.sap.sse.mail
Web-ContextPath: /security
Export-Package: com.sap.sse.security
diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
index b3840233f21..1ac82ca09f7 100644
--- a/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
@@ -8,11 +8,11 @@ import javax.servlet.ServletContext;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
+import com.sap.sse.mail.MailException;
import com.sap.sse.replication.impl.ReplicableWithObjectInputStream;
import com.sap.sse.security.impl.ReplicableSecurityService;
import com.sap.sse.security.operations.SecurityOperation;
import com.sap.sse.security.shared.DefaultRoles;
-import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UserManagementException;
diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/Activator.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/Activator.java
index f8c54dfdbe5..97db2ae01ee 100644
--- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/Activator.java
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/Activator.java
@@ -1,11 +1,7 @@
package com.sap.sse.security.impl;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
-import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -14,11 +10,13 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
+import com.sap.sse.mail.MailService;
import com.sap.sse.replication.Replicable;
import com.sap.sse.security.SecurityService;
import com.sap.sse.security.UserStore;
import com.sap.sse.security.UsernamePasswordRealm;
import com.sap.sse.util.ClearStateTestSupport;
+import com.sap.sse.util.ServiceTrackerFactory;
public class Activator implements BundleActivator {
private static final Logger logger = Logger.getLogger(Activator.class.getName());
@@ -57,25 +55,15 @@ public class Activator implements BundleActivator {
* registered as an OSGi service.
*/
public void start(BundleContext bundleContext) throws Exception {
- // Load mail properties
- final String jettyHome = System.getProperty("jetty.home", "configuration");
- final File propertiesDir = new File(jettyHome).getParentFile();
- File propertiesfile = new File(propertiesDir, "security.properties");
- Properties mailProperties = new Properties();
- try {
- mailProperties.load(new FileReader(propertiesfile));
- } catch (IOException ioe) {
- logger.log(Level.SEVERE, "Couldn't read security properties from "+propertiesfile.getCanonicalPath(), ioe);
- }
if (testUserStore != null) {
- createAndRegisterSecurityService(testUserStore, mailProperties);
+ createAndRegisterSecurityService(bundleContext, testUserStore);
} else {
- waitForUserStoreService(bundleContext, mailProperties);
+ waitForUserStoreService(bundleContext);
}
}
- private void createAndRegisterSecurityService(UserStore store, Properties mailProperties) {
- securityService = new SecurityServiceImpl(store, mailProperties);
+ private void createAndRegisterSecurityService(BundleContext bundleContext, UserStore store) {
+ securityService = new SecurityServiceImpl(ServiceTrackerFactory.createAndOpen(context, MailService.class), store);
registration = context.registerService(SecurityService.class.getName(),
securityService, null);
final Dictionary replicableServiceProperties = new Hashtable<>();
@@ -85,7 +73,7 @@ public class Activator implements BundleActivator {
Logger.getLogger(Activator.class.getName()).info("Security Service registered.");
}
- private void waitForUserStoreService(BundleContext bundleContext, final Properties mailProperties) {
+ private void waitForUserStoreService(BundleContext bundleContext) {
context = bundleContext;
final ServiceTracker tracker = new ServiceTracker<>(bundleContext, UserStore.class, /* customizer */ null);
tracker.open();
@@ -97,7 +85,7 @@ public class Activator implements BundleActivator {
logger.info("Waiting for UserStore service...");
UserStore userStore = tracker.waitForService(0);
logger.info("Obtained UserStore service "+userStore);
- createAndRegisterSecurityService(userStore, mailProperties);
+ createAndRegisterSecurityService(bundleContext, userStore);
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "Interrupted while waiting for UserStore service", e);
}
diff --git a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
index db50118e4bc..314e5baea7a 100644
--- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
+++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
@@ -13,20 +13,12 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.mail.Message.RecipientType;
-import javax.mail.MessagingException;
-import javax.mail.PasswordAuthentication;
-import javax.mail.Session;
-import javax.mail.Transport;
-import javax.mail.internet.InternetAddress;
-import javax.mail.internet.MimeMessage;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
@@ -54,6 +46,7 @@ import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
+import org.osgi.util.tracker.ServiceTracker;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.FacebookApi;
import org.scribe.builder.api.FlickrApi;
@@ -71,6 +64,8 @@ import org.scribe.oauth.OAuthService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.sap.sse.common.Util;
+import com.sap.sse.mail.MailException;
+import com.sap.sse.mail.MailService;
import com.sap.sse.replication.OperationExecutionListener;
import com.sap.sse.replication.OperationWithResult;
import com.sap.sse.replication.ReplicationMasterDescriptor;
@@ -89,7 +84,6 @@ import com.sap.sse.security.User;
import com.sap.sse.security.UserStore;
import com.sap.sse.security.shared.Account.AccountType;
import com.sap.sse.security.shared.DefaultRoles;
-import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UserManagementException;
import com.sap.sse.security.shared.UsernamePasswordAccount;
@@ -111,7 +105,7 @@ public class SecurityServiceImpl extends RemoteServiceServlet implements Replica
private final ReplicatingCacheManager cacheManager;
private UserStore store;
- private final Properties mailProperties;
+ private final ServiceTracker mailServiceTracker;
private final ConcurrentHashMap, OperationExecutionListener> operationExecutionListeners;
/**
@@ -129,18 +123,21 @@ public class SecurityServiceImpl extends RemoteServiceServlet implements Replica
shiroConfiguration = new Ini();
shiroConfiguration.loadFromPath("classpath:shiro.ini");
}
+
+ public SecurityServiceImpl(UserStore store) {
+ this(null, store);
+ }
/**
* @param mailProperties must not be null
*/
- public SecurityServiceImpl(UserStore store, Properties mailProperties) {
- assert mailProperties != null;
- logger.info("Initializing Security Service with user store " + store+" and mail properties "+mailProperties);
+ public SecurityServiceImpl(ServiceTracker mailServiceTracker, UserStore store) {
+ logger.info("Initializing Security Service with user store " + store);
operationsSentToMasterForReplication = new HashSet<>();
cacheManager = new ReplicatingCacheManager();
this.operationExecutionListeners = new ConcurrentHashMap<>();
this.store = store;
- this.mailProperties = mailProperties;
+ this.mailServiceTracker = mailServiceTracker;
// Create default users if no users exist yet.
initEmptyStore();
Factory factory = new WebIniSecurityManagerFactory(shiroConfiguration);
@@ -189,44 +186,14 @@ public class SecurityServiceImpl extends RemoteServiceServlet implements Replica
}
}
- private class SMTPAuthenticator extends javax.mail.Authenticator {
- public PasswordAuthentication getPasswordAuthentication() {
- String username = mailProperties.getProperty("mail.smtp.user");
- String password = mailProperties.getProperty("mail.smtp.password");
- return new PasswordAuthentication(username, password);
- }
- }
-
@Override
public void sendMail(String username, String subject, String body) throws MailException {
- if (this.mailProperties != null && this.mailProperties.containsKey("mail.transport.protocol")) {
- final User user = getUserByName(username);
- if (user != null) {
- final String toAddress = user.getEmail();
- if (toAddress != null) {
- Session session = Session.getInstance(this.mailProperties, new SMTPAuthenticator());
- MimeMessage msg = new MimeMessage(session);
- try {
- msg.setFrom(new InternetAddress(mailProperties.getProperty("mail.from", "root@sapsailing.com")));
- msg.setSubject(subject);
- msg.setContent(body, "text/plain");
- msg.addRecipient(RecipientType.TO, new InternetAddress(toAddress.trim()));
- Transport ts = session.getTransport();
- ts.connect();
- ts.sendMessage(msg, msg.getRecipients(RecipientType.TO));
- ts.close();
- logger.info("mail sent to user " + username + " with e-mail address " + toAddress
- + " with subject " + subject);
- } catch (MessagingException e) {
- logger.log(Level.SEVERE, "Error trying to send mail to user " + username
- + " with e-mail address " + toAddress, e);
- throw new MailException(e.getMessage());
- }
- }
+ final User user = getUserByName(username);
+ if (user != null) {
+ final String toAddress = user.getEmail();
+ if (toAddress != null) {
+ mailServiceTracker.getService().sendMail(username, subject, body);
}
- } else {
- logger.warning("No mail properties provided. Cannot send e-mail about "+subject+" to user "+username+
- ". This could also mean that this is running on a replica server in which case this is perfectly fine.");
}
}
@@ -904,7 +871,6 @@ public class SecurityServiceImpl extends RemoteServiceServlet implements Replica
// ----------------- Replication -------------
@Override
public void clearReplicaState() throws MalformedURLException, IOException, InterruptedException {
- mailProperties.clear();
store.clear();
}
diff --git a/java/com.sap.sse/src/com/sap/sse/util/ServiceTrackerFactory.java b/java/com.sap.sse/src/com/sap/sse/util/ServiceTrackerFactory.java
new file mode 100644
index 00000000000..3af71035c02
--- /dev/null
+++ b/java/com.sap.sse/src/com/sap/sse/util/ServiceTrackerFactory.java
@@ -0,0 +1,18 @@
+package com.sap.sse.util;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class ServiceTrackerFactory {
+ /**
+ * @return {@code null} if {@code context} is {@code null}
+ */
+ public static ServiceTracker createAndOpen(BundleContext context, Class clazz) {
+ if (context == null) {
+ return null;
+ }
+ ServiceTracker result = new ServiceTracker(context, clazz, null);
+ result.open();
+ return result;
+ }
+}
diff --git a/java/pom.xml b/java/pom.xml
index ed10a73d536..c8e44dced62 100755
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -17,6 +17,7 @@
com.sap.sse
com.sap.sse.common
com.sap.sse.shared.android
+ com.sap.sse.mail
com.sap.sse.mongodb
com.sap.sse.gwt
com.sap.sse.gwt.adminconsole
diff --git a/java/target/configuration/security.properties b/java/target/configuration/mail.properties
similarity index 100%
rename from java/target/configuration/security.properties
rename to java/target/configuration/mail.properties
diff --git a/java/target/env.sh b/java/target/env.sh
index 8ef97faa381..80556db3319 100644
--- a/java/target/env.sh
+++ b/java/target/env.sh
@@ -41,7 +41,7 @@ EXPEDITION_PORT=2010
# set the REPLICATE_MASTER_EXCHANGE_NAME variable to the
# same channel the master is using in its REPLICATION_CHANNEL variable
-# REPLICATE_ON_START=com.sap.sailing.server.impl.RacingEventServiceImpl,com.sap.sse.security.impl.SecurityServiceImpl,com.sap.sse.filestorage.impl.FileStorageManagementServiceImpl
+# REPLICATE_ON_START=com.sap.sailing.server.impl.RacingEventServiceImpl,com.sap.sse.security.impl.SecurityServiceImpl,com.sap.sse.filestorage.impl.FileStorageManagementServiceImpl,com.sap.sse.mail.impl.MailServiceImpl
# Host where the master Java instance is running
# Make sure firewall configurations allow access