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