Merge branch '60_inch' into 60_inch_bug4100

# Conflicts:
#	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
This commit is contained in:
Papick Garcia Taboada committed 2017-06-28 13:06:56 +02:00
commit e8ced96f26
35 files changed
+339 -171

No files matched your search

@@ -528,7 +528,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
logger.info("Couldn't find regatta "+regattaName+" for corresponding regatta leaderboard. Not loading regatta leaderboard.");
} else {
result = new RegattaLeaderboardImpl(regatta, resultDiscardingRule);
result.setName(leaderboardName);
result.setName(leaderboardName); // this will temporarily set the display name; it will be adjusted later if a display name is found
}
return result;
}
@@ -1,7 +1,6 @@
package com.sap.sailing.domain.racelogtracking.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -11,17 +10,10 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.StreamSupport;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import org.osgi.framework.ServiceReference;
import com.google.zxing.WriterException;
import com.sap.sailing.domain.abstractlog.impl.LastEventOfTypeFinder;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -73,7 +65,9 @@ import com.sap.sse.common.mail.MailException;
import com.sap.sse.common.media.MediaTagConstants;
import com.sap.sse.i18n.ResourceBundleStringMessages;
import com.sap.sse.mail.MailService;
import com.sap.sse.qrcode.QRCodeGenerationUtil;
import com.sap.sse.mail.QRCodeMimeBodyPartSupplier;
import com.sap.sse.mail.SerializableDefaultMimeBodyPartSupplier;
import com.sap.sse.mail.SerializableMultipartSupplier;
import com.sap.sse.shared.media.ImageDescriptor;
import com.sap.sse.util.impl.NonGwtUrlHelper;
@@ -322,9 +316,6 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
final ResourceBundleStringMessages B = RaceLogTrackingI18n.STRING_MESSAGES;
String subject = String.format("%s %s", B.get(locale, "trackingInvitationFor"), invitee);
// taken from http://www.tutorialspoint.com/javamail_api/javamail_api_send_inlineimage_in_email.htm
BodyPart messageTextPart = new MimeBodyPart();
boolean hasIOSAppUrl = iOSAppUrl != null && !iOSAppUrl.isEmpty();
boolean hasAndroidAppUrl = androidAppUrl != null && !androidAppUrl.isEmpty();
boolean hasLogoUrl = logoUrl != null && !logoUrl.isEmpty();
@@ -373,26 +364,16 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
htmlText.append("</html>");
try {
messageTextPart.setContent(htmlText.toString(), "text/html");
BodyPart messageImagePart = new MimeBodyPart();
InputStream imageIs = QRCodeGenerationUtil.create(url, 250);
DataSource imageDs = new ByteArrayDataSource(imageIs, "image/png");
messageImagePart.setDataHandler(new DataHandler(imageDs));
messageImagePart.setHeader("Content-ID", "<image>");
messageImagePart.setHeader("Content-Disposition", "inline;filename=\"qr.png\"");
MimeMultipart multipart = new MimeMultipart();
multipart.addBodyPart(messageTextPart);
multipart.addBodyPart(messageImagePart);
getMailService().sendMail(toAddress, subject, multipart);
} catch (MessagingException | MailException | WriterException | IOException e) {
final SerializableMultipartSupplier multipartSupplier = new SerializableMultipartSupplier("Invite",
new SerializableDefaultMimeBodyPartSupplier(htmlText.toString(), "text/html"),
new QRCodeMimeBodyPartSupplier(url));
getMailService().sendMail(toAddress, subject, multipartSupplier);
} catch (MessagingException | MailException | IOException e) {
logger.log(Level.SEVERE, "Error trying to send mail to " + invitee + " with e-mail address " + toAddress, e);
throw new MailException(e.getMessage());
}
}
@Override
public void inviteBuoyTenderViaEmail(Event event, Leaderboard leaderboard, String serverUrlWithoutTrailingSlash,
String emails, String iOSAppUrl, String androidAppUrl, Locale locale) throws MailException {
@@ -5,7 +5,6 @@ import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.domain.swisstimingadapter.RaceType;
@@ -53,14 +52,4 @@ public class DomainFactoryTest {
assertThat(raceType, is(notNullValue()));
assertThat(raceType.getRaceCode(), is(OlympicRaceCode.UNKNOWN));
}
@Ignore
@Test
public void testCompetitorLookup() throws Exception {
before();
// swissTimingDomainFactory.getOrCreateCompetitor(boatID, threeLetterIOCCode, name, boatClass);
}
}
@@ -41,7 +41,6 @@ public class SwissTimingRaceConfigurationTest {
}
}
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
@Test
public void testRaceConfig_446483_no_detail_config() throws Exception {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "446483"));
@@ -59,7 +58,6 @@ public class SwissTimingRaceConfigurationTest {
}
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
@Test
public void testRaceConfig_6260_with_detail_config() throws Exception {
URL configFileURL = new URL(MessageFormat.format(SwissTimingReplayServiceImpl.RACE_CONFIG_URL_TEMPLATE, "6260"));
@@ -74,12 +74,11 @@ public class SwissTimingReplayAdapterServiceTest {
assertEquals(321920, replayCountListener.rankingMarkCount);
}
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
@Test
public void testRaceData_SAW005905_20120805_EqualsOnlineVersion() throws Exception {
byte[] localCopy = read(getClass().getResourceAsStream("/SAW005905.20120805.replay"));
byte[] onlineCopy = read((InputStream) new URL(
"http://live.ota.st-sportservice.com/Replay?id=446495&_start=0").getContent());
"http://ota2.sportresult.com/Replay?id=446495&_start=0").getContent());
assertArrayEquals(localCopy, onlineCopy);
}
@@ -114,13 +113,12 @@ public class SwissTimingReplayAdapterServiceTest {
assertEquals(9500, replayCountListener.rankingMarkCount);
}
@Ignore("According to Radek Masnica ('masnica.r@st-software.com') the OTA stuff may not be supported any longer by the SwissTiming Leipzip lab... 2017-05-19")
@Test
public void testRaceData_SAW005905_20120805_online() throws Exception {
SwissTimingReplayTestListener replayCountListener = new SwissTimingReplayTestListener();
// race ID is 450053, as extracted from http://live.ota.st-sportservice.com/service?cmd=unity_race_overview&id=91
byte[] onlineCopy = read((InputStream) new URL(
"http://live.ota.st-sportservice.com/Replay?id=446495&_start=0").getContent());
"http://ota2.sportresult.com/Replay?id=446495&_start=0").getContent());
new SwissTimingReplayParserImpl().readData(new ByteArrayInputStream(onlineCopy), replayCountListener);
assertEquals(0, replayCountListener.keyFrameIndexSum);
assertEquals(715, replayCountListener.keyFrameIndexPositionCount);
@@ -34,7 +34,7 @@ import com.sap.sse.util.ByteArrayOutputStreamWithVisibleBuffer;
public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
public static final String RACE_CONFIG_URL_TEMPLATE = "http://live.ota.st-sportservice.com/configuration?_race={0}&effective=1&additional=config";
public static final String RACE_CONFIG_URL_TEMPLATE = "http://ota2.sportresult.com/configuration?_race={0}&effective=1&additional=config";
private static final String SWISSTIMING_DATEFORMAT_PATTERN = "dd.MM.yyyy HH:mm";
private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getTimeZone("GMT");
@@ -8,9 +8,6 @@ import com.sap.sailing.gwt.autoplay.client.places.screens.liveraceloop.racemapwi
import com.sap.sailing.gwt.autoplay.client.places.screens.preliveraceloop.racemap.PreRaceRacemapPlace;
import com.sap.sailing.gwt.autoplay.client.utils.AutoplayHelper;
import com.sap.sailing.gwt.autoplay.client.utils.AutoplayHelper.RVWrapper;
import com.sap.sailing.gwt.home.communication.event.minileaderboard.GetMiniLeaderboardDTO;
import com.sap.sailing.gwt.home.communication.event.minileaderboard.GetMiniLeaderbordAction;
import com.sap.sse.gwt.dispatch.shared.commands.ResultWithTTL;
public class PreLiveRaceWithRacemapNode extends FiresPlaceNode {
private final AutoPlayClientFactory cf;
@@ -21,12 +18,12 @@ public class PreLiveRaceWithRacemapNode extends FiresPlaceNode {
}
public void onStart() {
AutoplayHelper.create(cf.getSailingService(), cf.getErrorReporter(),
cf.getAutoPlayCtx().getContextDefinition().getLeaderboardName(), cf.getAutoPlayCtx().getContextDefinition().getEventId(),
cf.getAutoPlayCtx().getEvent(), cf.getEventBus(), cf.getDispatch(),
cf.getAutoPlayCtx().getPreLiveRace(), new AsyncCallback<RVWrapper>() {
cf.getAutoPlayCtx().getContextDefinition().getLeaderboardName(),
cf.getAutoPlayCtx().getContextDefinition().getEventId(), cf.getAutoPlayCtx().getEvent(),
cf.getEventBus(), cf.getDispatch(), cf.getAutoPlayCtx().getPreLiveRace(),
new AsyncCallback<RVWrapper>() {
@Override
public void onFailure(Throwable caught) {
@@ -39,33 +36,16 @@ public class PreLiveRaceWithRacemapNode extends FiresPlaceNode {
@Override
public void onSuccess(RVWrapper result) {
cf.getDispatch().execute(
new GetMiniLeaderbordAction(cf.getAutoPlayCtx().getEvent().id,
cf.getAutoPlayCtx().getContextDefinition().getLeaderboardName()),
new AsyncCallback<ResultWithTTL<GetMiniLeaderboardDTO>>() {
@Override
public void onFailure(Throwable caught) {
// fireEvent(new DataLoadFailureEvent(MiniLeaderboardLoader.this, caught));
firePlaceChangeAndStartTimer();
}
PreRaceRacemapPlace place = new PreRaceRacemapPlace();
place.setRaceMap(result.raceboardPerspective, result.csel);
// add later with settings here
place.setURL(cf.getAutoPlayCtx().getEvent().getOfficialWebsiteURL());
setPlaceToGo(place);
getBus().fireEvent(
new AutoPlayHeaderEvent(cf.getAutoPlayCtx().getPreLiveRace().getRegattaName(),
cf.getAutoPlayCtx().getPreLiveRace().getRaceName()));
firePlaceChangeAndStartTimer();
@Override
public void onSuccess(ResultWithTTL<GetMiniLeaderboardDTO> resultTTL) {
GetMiniLeaderboardDTO dto = resultTTL.getDto();
PreRaceRacemapPlace place = new PreRaceRacemapPlace();
place.setLeaderBoardDTO(dto);
place.setRaceMap(result.raceboardPerspective, result.csel);
// add later with settings here
place.setURL(cf.getAutoPlayCtx().getEvent().getOfficialWebsiteURL());
setPlaceToGo(place);
getBus().fireEvent(
new AutoPlayHeaderEvent(
cf.getAutoPlayCtx().getPreLiveRace().getRegattaName(),
cf.getAutoPlayCtx().getPreLiveRace().getRaceName()));
firePlaceChangeAndStartTimer();
}
});
}
});
}
@@ -2,6 +2,7 @@ package com.sap.sailing.gwt.autoplay.client.places.screens.afterliveraceloop.boa
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.Label;
import com.sap.sailing.domain.common.Distance;
@@ -95,10 +96,17 @@ public class RaceEndWithBoatsPresenterImpl extends AutoPlayPresenterConfigured<A
}
private void determinePlacement(RegattaAndRaceIdentifier lifeRace) {
RaceColumnDTO preselectedRaceColumn = leaderboardPanel.getLeaderboard().getRaceList().get(0);
//TODO this is not multifleet capable currently, think about alternatives
List<RaceColumnDTO> lifeRaceResult = leaderboardPanel.getLeaderboard().getRaceList();
RaceColumnDTO raceColumn = null;
for(RaceColumnDTO column:lifeRaceResult){
if(column.containsRace(lifeRace)){
raceColumn = column;
}
}
GWT.debugger();
List<CompetitorDTO> sortedCompetitors = leaderboardPanel.getLeaderboard()
.getCompetitorsFromBestToWorst(preselectedRaceColumn);
.getCompetitorsFromBestToWorst(raceColumn);
if (sortedCompetitors.size() >= 3) {
view.setFirst(sortedCompetitors.get(0));
@@ -20,7 +20,9 @@ import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings;
import com.sap.sailing.gwt.settings.client.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceCompetitorSet;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint;
import com.sap.sailing.gwt.ui.raceboard.QuickRanksDTOFromLeaderboardDTOProvider;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
import com.sap.sse.gwt.client.ErrorReporter;
@@ -117,7 +119,11 @@ public class LiveRaceWithRacemapAndLeaderBoardPresenterImpl
List<LeaderboardRowDTO> sortedCompetitors = leaderboardPanel.getLeaderboardTable().getVisibleItems();
if (sortedCompetitors.size() > 0) {
view.setStatistic(windSpeed,getPlace().getStatistic().getDistance(), AutoplayHelper.durationOfCurrentLiveRaceRunning());
if(getPlace().getStatistic() == null){
view.setStatistic(windSpeed,null, AutoplayHelper.durationOfCurrentLiveRaceRunning());
}else{
view.setStatistic(windSpeed,getPlace().getStatistic().getDistance(), AutoplayHelper.durationOfCurrentLiveRaceRunning());
}
}
}
@@ -154,6 +160,9 @@ public class LiveRaceWithRacemapAndLeaderBoardPresenterImpl
true, lifeRace, getPlace().getRaceMapSelectionProvider(), timer, null,
getSlideCtx().getContextDefinition().getLeaderboardName(), errorReporter, StringMessages.INSTANCE, null,
false, null, false, null, false, true, false, false, false);
getPlace().getRaceMap().setQuickRanksDTOProvider(new QuickRanksDTOFromLeaderboardDTOProvider(new RaceCompetitorSet(getPlace().getRaceMapSelectionProvider()), lifeRace));
view.startingWith(this, panel, getPlace().getRaceMap(), leaderboardPanel);
selectionTimer.schedule(SWITCH_COMPETITOR_DELAY);
}
@@ -53,13 +53,4 @@ public class PreRaceRacemapPlace extends Place {
public String getUrl() {
return url;
}
public void setLeaderBoardDTO(GetMiniLeaderboardDTO dto) {
this.leaderboard = dto;
}
public GetMiniLeaderboardDTO getLeaderboard() {
return leaderboard;
}
}
@@ -77,7 +77,7 @@ public class PreRaceRacemapPresenterImpl extends AutoPlayPresenterConfigured<Pre
}
}
String url = getRaceViewerURL(getSlideCtx().getContextDefinition().getLeaderboardName(), null,
getSlideCtx().getLiveRace());
getSlideCtx().getPreLiveRace());
view.updateStatistic(lastStatisticResult, url, windSpeed, windDegree);
}
}
@@ -110,7 +110,7 @@ public class PreRaceRacemapPresenterImpl extends AutoPlayPresenterConfigured<Pre
}
private void reloadStatistics() {
RegattaAndRaceIdentifier upcomingRace = getSlideCtx().getLiveRace();
RegattaAndRaceIdentifier upcomingRace = getSlideCtx().getPreLiveRace();
getClientFactory().getDispatch().execute(
new GetSixtyInchStatisticAction(upcomingRace.getRaceName(), upcomingRace.getRegattaName()),
new AsyncCallback<GetSixtyInchStatisticDTO>() {
@@ -31,6 +31,7 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapResources;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes;
import com.sap.sailing.gwt.ui.raceboard.AbstractQuickRanksDTOProvider;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
@@ -50,15 +51,19 @@ public class AutoplayHelper {
private static Timer raceboardTimer = new Timer(PlayModes.Live, /* delayBetweenAutoAdvancesInMilliseconds */1000l);
private static Date startOfLifeRace;
public static final AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
/**
* If a racestart is longer ago, the race is never considered live, even if all other checks pass
*/
private static final long NEGATIVE_SANITY_CHECK = -24 * 60 * 60 * 1000;
public static long durationOfCurrentLiveRaceRunning(){
if(startOfLifeRace != null){
return raceboardTimer.getLiveTimePointInMillis()-startOfLifeRace.getTime();
}else{
public static long durationOfCurrentLiveRaceRunning() {
if (startOfLifeRace != null) {
return raceboardTimer.getLiveTimePointInMillis() - startOfLifeRace.getTime();
} else {
return 0;
}
}
public static void getLiveRace(SailingServiceAsync sailingService, ErrorReporter errorReporter, EventDTO event,
String leaderBoardName, SailingDispatchSystem dispatch,
AsyncCallback<Pair<Long, RegattaAndRaceIdentifier>> callback) {
@@ -139,7 +144,7 @@ public class AutoplayHelper {
long startTimeInMs = raceTimes.getStartOfRace().getTime();
long delayToLiveInMs = raceTimes.delayToLiveInMs;
long startIn = startTimeInMs - serverTimeDuringRequest.getTime() - delayToLiveInMs;
if (startIn <= PRE_RACE_DELAY) {
if (startIn <= PRE_RACE_DELAY && startIn > NEGATIVE_SANITY_CHECK) {
startOfLifeRace = raceTimes.getStartOfRace();
return new Pair<Long, RegattaAndRaceIdentifier>(startIn, raceIdentifier);
}
@@ -203,7 +208,7 @@ public class AutoplayHelper {
AutoplayHelper.asyncActionsExecutor, errorReporter, raceboardTimer,
callback, clientTimeWhenResponseWasReceived,
serverTimeDuringRequest, clientTimeWhenRequestWasSent,
raceTimesInfo);
raceTimesInfo, new DefaultQuickRanksDTOProvider());
}
@Override
@@ -261,7 +266,8 @@ public class AutoplayHelper {
Iterable<CompetitorDTO> competitors, SailingServiceAsync sailingService,
AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer raceboardTimer,
AsyncCallback<RVWrapper> callback, long clientTimeWhenResponseWasReceived, Date serverTimeDuringRequest,
long clientTimeWhenRequestWasSent, Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos) {
long clientTimeWhenRequestWasSent, Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos,
AbstractQuickRanksDTOProvider provider) {
ArrayList<ZoomTypes> typesToConsiderOnZoom = new ArrayList<>();
// Other zoom types such as BOATS, TAILS or WINDSENSORS are not currently used as default zoom types.
@@ -282,7 +288,7 @@ public class AutoplayHelper {
RaceMap raceboardPerspective = new RaceMap(null, null, raceMapLifecycle, settings, sailingService,
asyncActionsExecutor, errorReporter, raceboardTimer, competitorSelectionProvider,
new RaceCompetitorSet(competitorSelectionProvider), StringMessages.INSTANCE, currentLiveRace,
raceMapResources, false, new DefaultQuickRanksDTOProvider());
raceMapResources, false, provider);
raceboardPerspective.raceTimesInfosReceived(raceTimesInfos, clientTimeWhenRequestWasSent,
serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
// raceTimesInfoProvider.addRaceTimesInfoProviderListener(raceboardPerspective);
@@ -238,19 +238,21 @@ public class RegattaDetailsComposite extends Composite {
editRacesOfRegattaSeries(regatta, series);
} else if (SeriesConfigImagesBarCell.ACTION_REMOVE.equals(value)) {
RegattaIdentifier identifier = new RegattaName(regatta.getName());
sailingService.removeSeries(identifier, series.getName(), new MarkedAsyncCallback<Void>(
new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable cause) {
errorReporter.reportError("Error trying to remove series " + series.getName()
+ ": " + cause.getMessage());
}
@Override
public void onSuccess(Void result) {
regattaRefresher.fillRegattas();
}
}));
if (Window.confirm(stringMessages.reallyRemoveSeries(series.getName()))) {
sailingService.removeSeries(identifier, series.getName(),
new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable cause) {
errorReporter.reportError("Error trying to remove series " + series.getName()
+ ": " + cause.getMessage());
}
@Override
public void onSuccess(Void result) {
regattaRefresher.fillRegattas();
}
}));
}
}
}
@@ -1817,7 +1817,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String showRacePlaceColumn();
String unknownLeaderboardType(String name);
String selectCompetitorsToEliminate();
String trackedRaceAlreadyLinked();
String trackedRaceAlreadyLinked();
String reallyRemoveSeries(String seriesName);
String noData();
String settingsId();
String documentSettingsId();
@@ -1796,6 +1796,7 @@ showRacePlaceColumn=Show race place
estimatedDistance=Estimated Distance
estimatedTime=Estimated Time
nothingUpcoming=There are currently no further planned races
reallyRemoveSeries=Do you really want to remove the series ''{0}''? Note: all race logs for the attached race columns will also permanently be removed!
noData=No data is available
settingsId=Settings ID
documentSettingsId=Document settings ID
@@ -1784,3 +1784,4 @@ resetToDefault=Auf Standard zurücksetzen
resetToDefaultInProgress=Setze zurück...
settingsRemoved=Standardeinstellungen wiederhergestellt
settingsRemovedError=Fehler beim Widerherstellen der Standardeinstellungen
reallyRemoveSeries=Möchten Sie die Serie ''{0}'' wirklich entfernen? Achtung: alle Ereignisprotokolle (RaceLogs) der zugehörigen Renn-Spalten werden unwiederbringlich gelöscht!
@@ -1,5 +1,6 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
@@ -8,13 +9,13 @@ import com.sap.sailing.gwt.ui.shared.QuickRankDTO;
import com.sap.sse.common.Util;
public class DefaultQuickRanksDTOProvider extends AbstractQuickRanksDTOProvider {
private Map<String, QuickRankDTO> quickRanksFromServer;
private Map<String, QuickRankDTO> currentQuickRanksFromServer = Collections.emptyMap();
@Override
public void quickRanksReceivedFromServer(Map<String, QuickRankDTO> quickRanksFromServer) {
final Map<String, QuickRankDTO> oldQuickRanksFromServer = quickRanksFromServer;
this.quickRanksFromServer = quickRanksFromServer;
for (final Entry<String, QuickRankDTO> e : quickRanksFromServer.entrySet()) {
public void quickRanksReceivedFromServer(Map<String, QuickRankDTO> receivedQuickRanksFromServer) {
final Map<String, QuickRankDTO> oldQuickRanksFromServer = this.currentQuickRanksFromServer;
this.currentQuickRanksFromServer = Util.nullToEmptyMap(receivedQuickRanksFromServer);
for (final Entry<String, QuickRankDTO> e : currentQuickRanksFromServer.entrySet()) {
final QuickRankDTO oldQuickRank = oldQuickRanksFromServer.get(e.getKey());
if (Util.equalsWithNull(oldQuickRank, e.getValue())) {
notifyListeners(e.getKey(), oldQuickRank, e.getValue());
@@ -24,7 +25,7 @@ public class DefaultQuickRanksDTOProvider extends AbstractQuickRanksDTOProvider
@Override
public Map<String, QuickRankDTO> getQuickRanks() {
return quickRanksFromServer;
return currentQuickRanksFromServer;
}
}
@@ -31,6 +31,13 @@ public interface QuickRanksDTOProvider {
void removeQuickRanksListener(QuickRanksListener listener);
/**
* When switching the underlying provider, this method allows to move current listeners to the new provider.
*
* @param newProvider
*/
public void moveListernersTo(QuickRanksDTOProvider newProvider);
/**
* The strategy may or may not use this quick ranks information provided directly from the server. This information
* may have been provided with some delay and may be slightly inconsistent with a {@link LeaderboardDTO leaderboard}
@@ -38,7 +45,8 @@ public interface QuickRanksDTOProvider {
* {@link LeaderboardDTO leaderboard} should ignore this call if it already has quick ranks information available
* from a leaderboard.
*
* @param quickRanksFromServer keys are the competitor IDs as strings
* @param quickRanksFromServer
* keys are the competitor IDs as strings
*/
void quickRanksReceivedFromServer(Map<String, QuickRankDTO> quickRanksFromServer);
@@ -353,7 +353,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
* quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, e.g.,
* if it can assume that more current information about ranks and leg numbers is available from a {@link LeaderboardDTO}.
*/
private final QuickRanksDTOProvider quickRanksDTOProvider;
private QuickRanksDTOProvider quickRanksDTOProvider;
private final CombinedWindPanel combinedWindPanel;
private final TrueNorthIndicatorPanel trueNorthIndicatorPanel;
@@ -487,6 +487,12 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
this.setSize("100%", "100%");
}
public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) {
if (this.quickRanksDTOProvider != null) {
this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider);
}
this.quickRanksDTOProvider = newQuickRanksDTOProvider;
}
/**
* The {@link WindDTO#dampenedTrueWindFromDeg} direction if {@link #lastCombinedWindTrackInfoDTO} has a
* {@link WindSourceType#COMBINED} source which has at least one fix recorded; <code>null</code> otherwise.
@@ -1403,7 +1409,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme
final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616
private void showAdvantageLine(Iterable<CompetitorDTO> competitorsToShow, Date date, long timeForPositionTransitionMillis) {
if (map != null && lastRaceTimesInfo != null && quickRanksDTOProvider.getQuickRanks() != null && lastCombinedWindTrackInfoDTO != null) {
if (map != null && lastRaceTimesInfo != null && quickRanksDTOProvider.getQuickRanks().isEmpty()
&& lastCombinedWindTrackInfoDTO != null) {
boolean drawAdvantageLine = false;
if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) {
// find competitor with highest rank
@@ -23,6 +23,13 @@ public abstract class AbstractQuickRanksDTOProvider implements QuickRanksDTOProv
listeners.remove(listener);
}
public void moveListernersTo(QuickRanksDTOProvider newProvider) {
for (QuickRanksListener quickRanksListener : listeners) {
newProvider.addQuickRanksListener(quickRanksListener);
}
listeners.clear();
}
protected void notifyListeners(String competitorIdAsString, QuickRankDTO oldQuickRank, QuickRankDTO newQuickRank) {
for (QuickRanksListener listener : listeners) {
listener.rankChanged(competitorIdAsString, oldQuickRank, newQuickRank);
@@ -66,8 +66,8 @@ public class RegattaDetailsCompositePO extends PageArea {
if (entry != null) {
WebElement removeAction = ActionsHelper.findRemoveAction(entry.getWebElement());
removeAction.click();
// ActionsHelper.acceptAlert(this.driver);
// waitForAjaxRequests();
ActionsHelper.acceptAlert(this.driver);
waitForAjaxRequests();
}
}
@@ -1,12 +1,20 @@
package com.sap.sailing.server.notification.impl;
import java.io.Serializable;
import java.net.URL;
import java.util.Arrays;
import com.sap.sse.common.Util.Pair;
public class NotificationMailTemplate {
/**
* The class needs to implement the {@link Serializable} interface because it can become part of a
* {@link SerializableMultipartSupplier}.
*
* @author Axel Uhl (D043530)
*
*/
public class NotificationMailTemplate implements Serializable {
private static final long serialVersionUID = 3291811210425722552L;
private final String subject;
private final String title;
private final String text;
@@ -10,17 +10,12 @@ import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.BodyPart;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.util.ByteArrayDataSource;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
import com.sap.sse.mail.MailService;
import com.sap.sse.mail.SerializableDefaultMimeBodyPartSupplier;
import com.sap.sse.mail.SerializableImageMimeBodyPartSupplier;
import com.sap.sse.mail.SerializableMultipartSupplier;
import com.sap.sse.mail.queue.MailNotification;
import com.sap.sse.security.PreferenceObjectBasedNotificationSet;
import com.sap.sse.security.User;
@@ -81,22 +76,12 @@ public abstract class NotificationSetNotification<T> implements MailNotification
// send batches with the actual mail addresses as bcc?
associatedNotificationSet.forUsersWithVerifiedEmailMappedTo(objectToNotifyAbout, (user) -> {
Locale locale = user.getLocaleOrDefault();
Multipart multipart = new MimeMultipart("related");
BodyPart bodyPart = new MimeBodyPart();
BodyPart messageImagePart = new MimeBodyPart();
final NotificationMailTemplate mailTemplate = getMailTemplate(objectToNotifyAbout, locale);
try {
// TODO: add cid:saplogo
NotificationMailTemplate mailTemplate = getMailTemplate(objectToNotifyAbout, locale);
bodyPart.setContent(getMailContent(mailTemplate, user, locale), "text/html");
multipart.addBodyPart(bodyPart);
DataSource imageDs = new ByteArrayDataSource(LOGO_BYTES, "image/png");
messageImagePart.setDataHandler(new DataHandler(imageDs));
messageImagePart.setHeader("Content-ID", "saplogo");
messageImagePart.setHeader("Content-Disposition", "inline;filename=\"saplogo.png\"");
multipart.addBodyPart(messageImagePart);
mailService.sendMail(user.getEmail(), mailTemplate.getSubject(), multipart);
final SerializableMultipartSupplier multipartSupplier = new SerializableMultipartSupplier("related",
new SerializableDefaultMimeBodyPartSupplier(getMailContent(mailTemplate, user, locale), "text/html"),
new SerializableImageMimeBodyPartSupplier(LOGO_BYTES, "image/png", "saplogo", "saplogo.png"));
mailService.sendMail(user.getEmail(), mailTemplate.getSubject(), multipartSupplier);
} catch (Exception e) {
logger.log(Level.SEVERE,
"Could not send mail notification for \"" + objectToNotifyAbout + "\" to user \"" + user + "\"", e);
@@ -514,7 +514,7 @@ public class Util {
* {@link #isEmpty(Iterable) isEmpty(splitAlongWhitespaceRespectingDoubleQuotedPhrases(" \n\t "))
* </pre>
*
* @return if {@code s==null}, then {@code null}, else a non-{@code null} but possibly empty sequence of {@link Strings} whose iteration order corresponds with
* @return if {@code s==null}, then {@code null}, else a non-{@code null} but possibly empty sequence of {@link String} whose iteration order corresponds with
* the occurrence of the split results, left to right, in {@code s}
*/
public static Iterable<String> splitAlongWhitespaceRespectingDoubleQuotedPhrases(String s) {
@@ -734,4 +734,14 @@ public class Util {
public static <T extends Comparable<T>> T max(T... elements) {
return Collections.max(Arrays.asList(elements));
}
/**
* Checks if the given map is null, and if, returns an empty map.
*/
public static <K, V> Map<K, V> nullToEmptyMap(Map<K, V> map) {
if (map == null) {
return Collections.emptyMap();
}
return map;
}
}
@@ -8,6 +8,7 @@ import org.junit.Before;
import com.sap.sse.common.mail.MailException;
import com.sap.sse.mail.MailService;
import com.sap.sse.mail.MailServiceResolver;
import com.sap.sse.mail.SerializableMultipartSupplier;
import com.sap.sse.mail.impl.MailServiceImpl;
import com.sap.sse.replication.testsupport.AbstractServerReplicationTestSetUp;
import com.sap.sse.replication.testsupport.AbstractServerWithSingleServiceReplicationTest;
@@ -34,12 +35,13 @@ public class AbstractMailServiceReplicationTest extends AbstractServerWithSingle
};
mailService[0] = new MailServiceImpl(null, mailServiceResolver) {
@Override
protected void internalSendMail(String toAddress, String subject, ContentSetter contentSetter)
public Void internalSendMail(String toAddress, String subject, SerializableMultipartSupplier multipartSupplier)
throws MailException {
if (canSendMail) {
Integer old = numberOfMailsSent.get(this);
numberOfMailsSent.put(this, old == null ? 1 : old + 1);
}
return null;
}
};
return mailService[0];
@@ -12,6 +12,7 @@ Export-Package: com.sap.sse.mail,
com.sap.sse.mail.queue.impl
Import-Package: javax.mail,
javax.mail.internet,
javax.mail.util;version="1.4.0",
org.osgi.framework,
org.osgi.util.tracker;version="1.5.1"
Require-Bundle: com.sap.sse.replication,
@@ -1,6 +1,8 @@
package com.sap.sse.mail;
import javax.mail.Multipart;
import java.io.IOException;
import javax.mail.MessagingException;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.mail.MailException;
@@ -29,5 +31,5 @@ public interface MailService extends ReplicableWithObjectInputStream<ReplicableM
/**
* Send mail with multipart content (e.g. inline image).
*/
void sendMail(String toAddress, String subject, Multipart multipartContent) throws MailException;
void sendMail(String toAddress, String subject, SerializableMultipartSupplier multipartContent) throws MailException, IOException, MessagingException;
}
@@ -0,0 +1,44 @@
package com.sap.sse.mail;
import java.io.InputStream;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.internet.MimeBodyPart;
import javax.mail.util.ByteArrayDataSource;
import com.sap.sse.qrcode.QRCodeGenerationUtil;
/**
* Constructs a MIME body part containing a QR code for a {@link #text} provided to this object
* at construction time.
*
* @author Axel Uhl (D043530)
*
*/
public class QRCodeMimeBodyPartSupplier implements SerializableMimeBodyPartSupplier {
private static final long serialVersionUID = 6073707813859319317L;
private final String text;
public QRCodeMimeBodyPartSupplier(String text) {
super();
this.text = text;
}
@Override
public MimeBodyPart get() {
final MimeBodyPart messageImagePart = new MimeBodyPart();
InputStream imageIs;
try {
imageIs = QRCodeGenerationUtil.create(text, 250);
final DataSource imageDs = new ByteArrayDataSource(imageIs, "image/png");
messageImagePart.setDataHandler(new DataHandler(imageDs));
messageImagePart.setHeader("Content-ID", "<image>");
messageImagePart.setHeader("Content-Disposition", "inline;filename=\"qr.png\"");
return messageImagePart;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,36 @@
package com.sap.sse.mail;
import java.io.Serializable;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
/**
* Can construct a {@link MimeBodyPart} out of a serializable object and a content type specification string.
*
* @author Axel Uhl (D043530)
*
*/
public class SerializableDefaultMimeBodyPartSupplier implements SerializableMimeBodyPartSupplier {
private static final long serialVersionUID = -2893291156595020570L;
private final Serializable serializableContent;
private final String contentType;
public SerializableDefaultMimeBodyPartSupplier(Serializable serializableContent, String contentType) {
super();
this.serializableContent = serializableContent;
this.contentType = contentType;
}
@Override
public MimeBodyPart get() {
final MimeBodyPart result = new MimeBodyPart();
try {
result.setContent(serializableContent, contentType);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
return result;
}
}
@@ -0,0 +1,39 @@
package com.sap.sse.mail;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.util.ByteArrayDataSource;
public class SerializableImageMimeBodyPartSupplier implements SerializableMimeBodyPartSupplier {
private static final long serialVersionUID = -1540223936240644379L;
private final byte[] imageBytes;
private final String contentType;
private final String contentId;
private final String imageFilename;
public SerializableImageMimeBodyPartSupplier(byte[] imageBytes, String contentType, String contentId,
String imageFilename) {
super();
this.imageBytes = imageBytes;
this.contentType = contentType;
this.contentId = contentId;
this.imageFilename = imageFilename;
}
@Override
public MimeBodyPart get() {
final MimeBodyPart messageImagePart = new MimeBodyPart();
DataSource imageDs = new ByteArrayDataSource(imageBytes, contentType);
try {
messageImagePart.setDataHandler(new DataHandler(imageDs));
messageImagePart.setHeader("Content-ID", contentId);
messageImagePart.setHeader("Content-Disposition", "inline;filename=\"" + imageFilename + "\"");
return messageImagePart;
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,18 @@
package com.sap.sse.mail;
import java.io.Serializable;
import java.util.function.Supplier;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
/**
* Plays together with {@link SerializableMultipartSupplier}. One or more instances of this type can be
* passed to its constructor so that after de-serialization a {@link Multipart} message can be constructed
* from the parts.
*
* @author Axel Uhl (D043530)
*
*/
public interface SerializableMimeBodyPartSupplier extends Supplier<MimeBodyPart>, Serializable {
}
@@ -0,0 +1,33 @@
package com.sap.sse.mail;
import java.util.function.Supplier;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.MimeMultipart;
import com.sap.sse.common.Named;
import com.sap.sse.common.impl.NamedImpl;
public class SerializableMultipartSupplier extends NamedImpl implements Supplier<Multipart>, Named {
private static final long serialVersionUID = 4316486727705670702L;
private final SerializableMimeBodyPartSupplier[] bodyPartSuppliers;
public SerializableMultipartSupplier(String name, SerializableMimeBodyPartSupplier... partSuppliers) {
super(name);
this.bodyPartSuppliers = partSuppliers;
}
@Override
public Multipart get() {
final Multipart result = new MimeMultipart(getName());
for (final SerializableMimeBodyPartSupplier bodyPartSupplier : bodyPartSuppliers) {
try {
result.addBodyPart(bodyPartSupplier.get());
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
return result;
}
}
@@ -17,7 +17,6 @@ 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;
@@ -28,6 +27,7 @@ import javax.mail.internet.MimeUtility;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.mail.MailException;
import com.sap.sse.mail.MailServiceResolver;
import com.sap.sse.mail.SerializableMultipartSupplier;
import com.sap.sse.replication.OperationExecutionListener;
import com.sap.sse.replication.OperationWithResult;
import com.sap.sse.replication.ReplicationMasterDescriptor;
@@ -68,7 +68,7 @@ public class MailServiceImpl implements ReplicableMailService {
protected static interface ContentSetter {
void setContent(MimeMessage msg) throws MessagingException;
}
private boolean canSendMail() {
return mailProperties != null && mailProperties.containsKey("mail.transport.protocol");
}
@@ -88,16 +88,12 @@ public class MailServiceImpl implements ReplicableMailService {
msg.setSubject(subject);
}
msg.addRecipient(RecipientType.TO, new InternetAddress(toAddress.trim()));
// this fixes the DCH MIME type error
// see http://tanyamadurapperuma.blogspot.de/2014/01/struggling-with-nosuchproviderexception.html
Thread.currentThread().setContextClassLoader(javax.mail.Session.class.getClassLoader());
contentSetter.setContent(msg);
// for testing with gmail
//Transport ts = session.getTransport("smtps");
Transport ts = session.getTransport();
ts.connect();
ts.sendMessage(msg, msg.getRecipients(RecipientType.TO));
@@ -134,19 +130,19 @@ public class MailServiceImpl implements ReplicableMailService {
}
@Override
public Void internalSendMail(String toAddress, String subject, Multipart multipartContent) throws MailException {
public Void internalSendMail(String toAddress, String subject, SerializableMultipartSupplier multipartSupplier) throws MailException {
internalSendMail(toAddress, subject, new ContentSetter() {
@Override
public void setContent(MimeMessage msg) throws MessagingException {
msg.setContent(multipartContent);
msg.setContent(multipartSupplier.get());
}
});
return null;
}
@Override
public void sendMail(String toAddress, String subject, Multipart multipartContent) throws MailException {
apply(s -> s.internalSendMail(toAddress, subject, multipartContent));
public void sendMail(String toAddress, String subject, SerializableMultipartSupplier multipartSupplier) throws MailException {
apply(s -> s.internalSendMail(toAddress, subject, multipartSupplier));
}
@Override
@@ -1,12 +1,18 @@
package com.sap.sse.mail.impl;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import com.sap.sse.common.mail.MailException;
import com.sap.sse.mail.MailService;
import com.sap.sse.mail.SerializableMultipartSupplier;
public interface ReplicableMailService extends MailService {
Void internalSendMail(String toAddress, String subject, String body) throws MailException;
Void internalSendMail(String toAddress, String subject, String body) throws MailException, MessagingException;
Void internalSendMail(String toAddress, String subject, Multipart multipartContent) throws MailException;
/**
* @param multipartSupplier
* can produce a {@link Multipart} object and must be serializable
*/
Void internalSendMail(String toAddress, String subject, SerializableMultipartSupplier multipartSupplier) throws MailException, MessagingException;
}
@@ -27,7 +27,7 @@ import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
*
*/
public class QRCodeGenerationUtil {
public static InputStream create(String text, int sizeInPixels) throws WriterException, IOException {
public static InputStream create(String text, int sizeInPixels) throws Exception {
Hashtable<EncodeHintType, ErrorCorrectionLevel> hintMap = new Hashtable<EncodeHintType,
ErrorCorrectionLevel>();
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);